jackson 10 luni în urmă
părinte
comite
9aabc1b83a

+ 2 - 2
index.html

@@ -2,10 +2,10 @@
 <html lang="en">
   <head>
     <meta charset="UTF-8" />
-    <link rel="icon" href="/favicon.ico" />
+    <link rel="icon" href="/logo.jpg" />
     <meta name="format-detection" content="telephone=no">
     <meta name="viewport" content="width=device-width,minimum-scale=1,maximum-scale=1,user-scalable=no,initial-scale=1,viewport-fit=cover">
-    <title>Vite App</title>
+    <title>Telegram</title>
   </head>
   <body>
     <div id="app"></div>

BIN
public/logo.jpg


+ 2 - 2
src/App.vue

@@ -7,6 +7,6 @@
   </router-view>
 </template>
 
-<script lang="ts" setup>
-import Config from "./config";
+<script setup lang="ts">
+import Config from './config'
 </script>

+ 1 - 6
src/assets/styles/auth.less

@@ -1,9 +1,5 @@
 .auth-page{
-  height: 100%;
-  display: flex;
-  flex-direction: column;
-  justify-content: center;
-  align-items: center;
+  text-align: center;
 }
 
 .auth-logo{
@@ -53,7 +49,6 @@
 
 .auth-btn{
   margin-top: 30px;
-  text-align: center;
 }
 
 .auth-btn button{

+ 5 - 6
src/components/V3Emoji.vue

@@ -209,14 +209,13 @@ onBeforeUnmount(() => {
 }
 
 .emoji-item {
-  width: 100%;
-  height: 100%;
+  flex: 1;
+  margin-bottom: 3px;
 }
 
 .emoji-textarea {
   position: relative;
-  width: 100%;
-  height: 100%;
+  padding-right: 16px;
 
   .van-cell:after {
     --van-cell-border-color: transparent;
@@ -225,8 +224,8 @@ onBeforeUnmount(() => {
   .emoji-textarea-pollup-container {
     font-size: 20px;
     position: absolute;
-    bottom: 12px;
-    right: 5px;
+    bottom: 9px;
+    right: 8px;
 
     .emoji-textarea-open-btn {
       cursor: pointer;

+ 1 - 0
src/config/index.ts

@@ -9,5 +9,6 @@ export default {
   zoomDomain: isProduction ? 'https://www.ops777777.cc' : 'http://localhost:3000',
   // zoom的websocket地址
   zoomSocketUrl: isProduction ? 'wss://m.ops777777.cc' : 'ws://20.189.74.163:7880',
+  // 是否是手机端
   isPhone: /Android|webOS|iPhone|BlackBerry/i.test(window.navigator.userAgent),
 }

+ 5 - 5
src/router/index.ts

@@ -2,12 +2,12 @@ import {createRouter, createWebHashHistory, RouteRecordRaw} from "vue-router";
 
 const routes: Array<RouteRecordRaw> = [
   {
-    path: "/home",
+    path: "/",
     name: "Home",
     component: () => import("@/views/Home.vue")
   },
   {
-    path: "/",
+    path: "/login",
     name: "Login",
     component: () => import("@/views/Login.vue"),
   },
@@ -17,9 +17,9 @@ const routes: Array<RouteRecordRaw> = [
     component: () => import("@/views/Register.vue"),
   },
   {
-    path: "/phoneChat",
-    name: "PhoneChat",
-    component: () => import("@/views/home/right/index.vue"),
+    path: "/right",
+    name: "Right",
+    component: () => import("@/views/home/right/index.vue")
   },
   {
     path: '/zoomLoading',

+ 29 - 9
src/store/index.ts

@@ -14,6 +14,8 @@ interface State {
   isShowRight: boolean,
   isSearching: boolean,
   userList: any[],
+  client: any,
+  groupClient: Obj,
 }
 
 export default createStore({
@@ -36,15 +38,33 @@ export default createStore({
     isShowRight: true,
     isSearching: false,
     userList: [],
+    client: null,
+    groupClient: {},
   } as State,
   mutations: {
-    logout() {
+    logout(state) {
+      state.userInfo = {}
       setLocalStorage('userInfo', {})
-      router.push('/')
+      router.push('/login')
+      // 退出登录关闭所有webSocket
+      if (state.client) {
+        state.client.closeWs()
+        state.client = null
+      }
+      for (let key in state.groupClient) {
+        state.groupClient[key].closeWs()
+      }
+      state.groupClient = {}
+    },
+    setClient(state, client) {
+      state.client = client
+    },
+    setGroupClient(state, client) {
+      state.groupClient = {...state.groupClient, ...client}
     },
     setUserInfo(state, data) {
       state.userInfo = data
-      setLocalStorage('userInfo', data)
+      setLocalStorage('userInfo', data);
     },
     updateUserInfo(state, obj) {
       state.userInfo = {...state.userInfo, ...obj}
@@ -78,17 +98,17 @@ export default createStore({
         }
       })
     },
-    // 把有未读消息的好友移到上面
-    hasNotReadMsgUserSort(state) {
-      const userList: Obj[] = state.userList.filter(item => item.noReadNum > 0)
-      state.userList = Array.from(new Set([...userList, ...state.userList]))
-    },
     // 正在聊天的好友移到上面
     chatUserSort(state, obj: Obj) {
-      const i = state.userList.findIndex(item => item === obj)
+      const i = state.userList.findIndex(item => item.friendId === obj.friendId)
       state.userList.splice(i, 1)
       state.userList.unshift(obj)
     },
+    // 把有未读消息的好友移到上面
+    hasNotReadMsgUserSort(state) {
+      const userList: Obj[] = state.userList.filter(item => item.noReadNum > 0)
+      state.userList = Array.from(new Set([...userList, ...state.userList]))
+    },
   },
   actions: {
     async logout({commit}) {

+ 12 - 7
src/utils/client.ts

@@ -9,7 +9,7 @@ const seqOffset = 12;
 
 function Client(options: any) {
   let MAX_CONNECT_TIMES = 10;
-  let DELAY = 15000;
+  let DELAY = 15 * 1000;
   // @ts-ignore
   let self = this;
   self.connectStatus = 'connecting';
@@ -50,7 +50,7 @@ Client.prototype.createConnect = function (max: number, delay: number) {
           self.connectStatus = 'connected'
           // send a heartbeat to server
           heartbeat();
-          heartbeatInterval = setInterval(heartbeat, 30 * 1000);
+          heartbeatInterval = setInterval(heartbeat, delay);
           break;
         case 3:
           // receive a heartbeat from server
@@ -76,13 +76,18 @@ Client.prototype.createConnect = function (max: number, delay: number) {
       }
     }
 
-    ws.onclose = function () {
-      if (heartbeatInterval) clearInterval(heartbeatInterval);
-      self.connectStatus = 'fail'
+    ws.onerror = function () {
+      self.connectStatus = 'error';
+      ws.close();
       setTimeout(reConnect, delay);
     }
 
-    self.createNewConnect = function () {
+    ws.onclose = function () {
+      self.connectStatus = 'close'
+      clearInterval(heartbeatInterval)
+    }
+
+    self.closeWs = function () {
       ws.close();
     }
 
@@ -131,7 +136,7 @@ Client.prototype.createConnect = function (max: number, delay: number) {
   }
 
   function reConnect() {
-    self.createConnect(--max, delay * 2);
+    self.createConnect(--max, delay);
   }
 }
 

+ 6 - 6
src/views/Home.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="home" :class="{phone: Config.isPhone}">
+  <div class="home" :class="{phone: Config.isPhone}" v-if="store.state.userInfo.token">
     <Left/>
     <Right v-if="!Config.isPhone"/>
   </div>
@@ -19,13 +19,13 @@ export default defineComponent({
   setup() {
     const store = useStore()
     const router = useRouter()
-    if (!store.state.userInfo.token) {
-      router.push('/')
+    if (store.state.userInfo.token) {
+      document.title = store.state.userInfo.username
+    } else {
+      router.push('/login')
     }
-    document.title = store.state.userInfo.username
-    store.commit('setIsShowChat', false)
-    store.commit('setIsShowRight', false)
     return {
+      store,
       Config
     }
   }

+ 1 - 1
src/views/Login.vue

@@ -46,7 +46,7 @@ export default defineComponent({
       let {data} = await login(form)
       store.commit('setUserInfo', data)
       Toast.clear()
-      router.push('/home')
+      router.push('/')
     }
     return {
       form,

+ 3 - 4
src/views/Register.vue

@@ -22,7 +22,7 @@
     </div>
     <div class="auth-btn">
       <van-button type="primary" round @click="submit">注册</van-button>
-      <van-button type="default" round to="/">返回</van-button>
+      <van-button type="default" round to="/login">返回</van-button>
     </div>
   </div>
 </template>
@@ -46,7 +46,7 @@ export default defineComponent({
     })
     const store = useStore()
     const router = useRouter()
-    
+
     const submit = async () => {
       if (!form.username) {
         return Toast("请输入用户名")
@@ -76,8 +76,7 @@ export default defineComponent({
         forbidClick: true,
         duration: 2000,
         message: "注册成功", onClose: () => {
-          // location.href = '/'
-          router.push({name:'Home'})
+          router.push('/')
         }
       });
     }

+ 9 - 4
src/views/home/left/components/PersonalCenter.vue

@@ -12,7 +12,7 @@
       <van-cell title="修改头像" @click="changePicture"/>
       <van-cell title="昵称" :value="store.state.userInfo.nickname"/>
       <van-cell title="邮箱" v-if="store.state.userInfo.email" :value="store.state.userInfo.email"/>
-      <van-cell title="退出" @click="store.dispatch('logout')"/>
+      <van-cell title="退出" @click="logout"/>
     </van-cell-group>
     <van-popup v-model:show="show" @click="show = false" class="common-pop">
       <img :src="store.state.userInfo.avatar" alt="" v-if="store.state.userInfo.avatar">
@@ -31,19 +31,23 @@ interface State {
 
 export default defineComponent({
   name: 'PersonalInfo',
-  setup() {
+  setup(props, {emit}) {
     const state = reactive<State>({
       show: false
     })
 
     const fileInput = ref<HTMLElement | null>(null)
-
     const store = useStore()
 
     const changePicture = () => {
       (fileInput.value as HTMLElement).click()
     }
 
+    const logout = async () => {
+      await store.dispatch('logout')
+      emit('reset-tab')
+    }
+
     onMounted(() => {
       (fileInput.value as HTMLElement).onchange = function (ev: any) {
         const reader = new FileReader();
@@ -58,7 +62,8 @@ export default defineComponent({
       ...toRefs(state),
       fileInput,
       store,
-      changePicture
+      changePicture,
+      logout
     }
   }
 })

+ 21 - 15
src/views/home/left/components/UserSearchList.vue

@@ -70,13 +70,13 @@
 </template>
 
 <script lang="ts">
-import {defineComponent, reactive, ref, toRefs, onMounted, nextTick, onActivated} from "vue";
+import {defineComponent, reactive, ref, toRefs, nextTick, onMounted, onActivated} from "vue";
 import {addFriend, addGroup, findUser, getFriends, noReadNum} from "../../../../api"
 import {useStore} from 'vuex'
 import {Obj, StrNumObj} from "../../../../interface"
 import {Toast} from "vant"
 import type {FieldInstance} from 'vant';
-import {onBeforeRouteLeave, useRouter} from "vue-router";
+import {useRouter, onBeforeRouteLeave} from "vue-router";
 import Config from "../../../../config";
 import {setLocalStorage} from "../../../../utils";
 
@@ -124,6 +124,12 @@ export default defineComponent({
     const store = useStore()
     const router = useRouter()
 
+    const getUserList = async () => {
+      const {data} = await getFriends()
+      store.commit('setUserList', data)
+      store.commit('hasNotReadMsgUserSort')
+    }
+
     const userChange = () => {
       if (state.username) {
         state.isSearching = true
@@ -173,17 +179,6 @@ export default defineComponent({
       })
     }
 
-    const getUserList = async () => {
-      const {data} = await getFriends()
-      store.commit('setUserList', data)
-      store.commit('hasNotReadMsgUserSort')
-    }
-
-    onActivated(() => {
-      // 用于h5
-      getUserList()
-    })
-
     const getNoReadNum = async (item: StrNumObj) => {
       await noReadNum({
         fromId: item.userId,
@@ -204,8 +199,8 @@ export default defineComponent({
       store.commit('setIsShowUserInfo', false)
       store.commit('setIsShowGroupInfo', false)
       if (Config.isPhone) {
-        router.push('/phoneChat')
         setLocalStorage('chatUserInfo', item)
+        router.push('/right')
       }
     }
 
@@ -244,8 +239,19 @@ export default defineComponent({
       state.show = false
     }
 
+    // 为了兼容h5
+    onBeforeRouteLeave((to: any, from: any, next: any) => {
+      to.name === 'Login' && store.commit('setUserList', [])
+      next()
+    })
+
+    // 为了兼容h5
+    onActivated(() => {
+      store.state.userList.length === 0 && getUserList()
+    })
+
     onMounted(() => {
-      getUserList()
+      !Config.isPhone && getUserList()
     })
 
     return {

+ 1 - 1
src/views/home/left/index.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="left" :class="{phone: Config.isPhone}">
     <UserSearchList v-show="currentComponent === 'UserSearchList'"/>
-    <PersonalCenter v-show="currentComponent === 'PersonalCenter'"/>
+    <PersonalCenter @reset-tab="currentComponent='UserSearchList'" v-show="currentComponent === 'PersonalCenter'"/>
     <ul class="tab">
       <li class="item" :class="{active: currentComponent===item.component}" :key="item.component"
           v-for="item in tabData" @click="handleClick(item)">

+ 42 - 27
src/views/home/right/components/Chat.vue

@@ -80,7 +80,8 @@ import {
   watch,
   nextTick,
   onUnmounted,
-  onActivated, onMounted
+  onMounted,
+  onActivated
 } from "vue";
 import Client from "../../../../utils/client";
 import {
@@ -212,7 +213,6 @@ export default defineComponent({
         if (data.length < 20) {
           state.isFinished = true;
         }
-        let beforeListHeight = (messageListDom.value as HTMLElement).offsetHeight;
         state.messageList.unshift(...data.reverse())
         nextTick(() => {
           if (state.page === 1) {
@@ -221,6 +221,7 @@ export default defineComponent({
           } else if (state.page === 2) {
             scrollToBottom()
           } else {
+            const beforeListHeight = (messageListDom.value as HTMLElement).offsetHeight;
             setScrollTop(beforeListHeight)
           }
           state.page++
@@ -231,7 +232,7 @@ export default defineComponent({
     }
 
     const scrollToBottom = () => {
-      (messageContentDom.value as HTMLElement).scrollTop = (messageContentDom.value as HTMLElement).scrollHeight
+      (messageContentDom.value as HTMLElement).scrollTop = (messageContentDom.value as HTMLElement)?.scrollHeight
     }
 
     const setScrollTop = (beforeListHeight: number) => {
@@ -253,34 +254,40 @@ export default defineComponent({
       }
     })
 
-    // @ts-ignore
-    new Client({ //这里是私聊
-      userId: store.state.userInfo.id,
-      notify: function (data: string) {
-        const msg = JSON.parse(data)
-        if (store.state.chatUserInfo.friendId === msg.fromUserId) {
-          state.messageList.push(msg)
-          nextTick(() => {
-            scrollToBottom()
-          })
-        } else {
-          store.commit('setNotReadMessage', msg.fromUserId)
-          store.commit('hasNotReadMsgUserSort')
-        }
-      }
+    onActivated(() => {
+      scrollToBottom()
     })
 
-    const groupClient: Obj = {}
+    if (!store.state.client) {
+      // @ts-ignore
+      const client = new Client({ //这里是私聊
+        userId: store.state.userInfo.id,
+        notify: function (data: string) {
+          const msg = JSON.parse(data)
+          if (store.state.chatUserInfo.friendId === msg.fromUserId) {
+            state.messageList.push(msg)
+            nextTick(() => {
+              scrollToBottom()
+            })
+          } else {
+            store.commit('setNotReadMessage', msg.fromUserId)
+            store.commit('hasNotReadMsgUserSort')
+          }
+        }
+      })
+      store.commit('setClient', client)
+    }
+
     watch(() => store.state.chatUserInfo, val => {
       // isGroup:1是私聊,2是群聊
       if (val.isGroup !== 2) {
         return;
       }
-      if (groupClient[val.friendId]) {
+      if (store.state.groupClient[val.friendId]) {
         return
       }
       // @ts-ignore
-      groupClient[val.friendId] = new Client({ // 这里是群聊
+      const client = new Client({ // 这里是群聊
         friendId: val.friendId,
         notify: function (data: string) {
           const msg = JSON.parse(data)
@@ -297,9 +304,10 @@ export default defineComponent({
           }
         }
       })
+      store.commit('setGroupClient', {[val.friendId]: client})
     })
 
-    const sendMsg = async () => {
+    const sendMsg = async (isSendLink?: any) => {
       let content = state.message.trim()
       // 把表情包转成:__表情包的英文名字__
       content = content.replace(RE.emoji, (a, emoji) => `__${emojiMap[emoji.codePointAt(0)]}__`)
@@ -315,7 +323,7 @@ export default defineComponent({
       }
       state.message = ''
       state.messageList.push(data)
-      document.getElementById('textarea')?.focus()
+      isSendLink !== true && document.getElementById('textarea')?.focus()
       nextTick(() => {
         scrollToBottom()
       })
@@ -348,7 +356,7 @@ export default defineComponent({
           confirmButtonText: '发送链接'
         }).then(() => {
           state.message = message
-          sendMsg()
+          sendMsg(true)
           Toast(store.state.chatUserInfo.isGroup === 1 ? '会议链接已发送给好友' : '会议链接已发送到群里')
         }).catch(() => {
           toClipboard(message).then(() => {
@@ -378,7 +386,11 @@ export default defineComponent({
           await deleteFriend({id: chatUserInfo.friendId})
           let i = store.state.userList.indexOf(chatUserInfo)
           store.state.userList.splice(i, 1)
-          store.commit('setIsShowRight', false)
+          if (Config.isPhone) {
+            router.back()
+          } else {
+            store.commit('setIsShowRight', false)
+          }
           Toast.success('删除成功')
         })
       }
@@ -394,7 +406,11 @@ export default defineComponent({
           })
           let i = store.state.userList.indexOf(chatUserInfo)
           store.state.userList.splice(i, 1)
-          store.commit('setIsShowRight', false)
+          if (Config.isPhone) {
+            router.back()
+          } else {
+            store.commit('setIsShowRight', false)
+          }
         })
       }
     }
@@ -609,7 +625,6 @@ export default defineComponent({
   }
 
   .send-btn {
-    margin-left: 6px;
     margin-bottom: 9px;
 
     .text {

+ 3 - 12
src/views/home/right/components/UserInfo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div v-if="store.state.isShowUserInfo">
+  <div>
     <van-nav-bar
         title="用户信息"
         left-text="返回"
@@ -8,7 +8,7 @@
     />
     <div class="user-content">
       <div class="user-main">
-        <div class="common-pic">{{store.state.chatUserInfo.friendName.substring(0,1)}}</div>
+        <div class="common-pic">{{ store.state.chatUserInfo.friendName.substring(0, 1) }}</div>
         <div class="name">{{ store.state.chatUserInfo.friendName }}</div>
       </div>
       <van-cell-group inset>
@@ -20,20 +20,12 @@
 </template>
 
 <script lang="ts">
-import {defineComponent, reactive, toRefs} from "vue";
+import {defineComponent} from "vue";
 import {useStore} from "vuex";
 
-interface State {
-  show: boolean
-}
-
 export default defineComponent({
   name: 'UserInfo',
   setup() {
-    const state = reactive<State>({
-      show: true
-    })
-
     const store = useStore()
 
     const onClickLeft = () => {
@@ -41,7 +33,6 @@ export default defineComponent({
     }
 
     return {
-      ...toRefs(state),
       store,
       onClickLeft
     }

+ 3 - 8
src/views/home/right/index.vue

@@ -20,19 +20,14 @@ export default defineComponent({
   components: {Chat, UserInfo, GroupInfo},
   setup() {
     const store = useStore()
-
-    const setChatUserInfo = () => {
+    // h5页面刷新时,获取好友信息
+    if (Config.isPhone) {
       store.commit('setChatUserInfo', getLocalStorage('chatUserInfo'))
       store.commit('setIsShowChat', true)
     }
-    if (Config.isPhone) {
-      // h5页面刷新时,获取好友信息
-      setChatUserInfo()
-    }
-
     return {
       store,
-      Config
+      Config,
     }
   }
 })