jackson 11 місяців тому
батько
коміт
f76ef882cc

+ 10 - 1
src/App.vue

@@ -1,3 +1,12 @@
 <template>
-  <router-view/>
+  <router-view v-slot="{ Component }">
+    <keep-alive v-if="Config.isPhone">
+      <component :is="Component"/>
+    </keep-alive>
+    <component :is="Component" v-else/>
+  </router-view>
 </template>
+
+<script lang="ts" setup>
+import Config from "./config";
+</script>

+ 1 - 0
src/config/index.ts

@@ -9,4 +9,5 @@ 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),
 }

+ 6 - 1
src/router/index.ts

@@ -4,7 +4,7 @@ const routes: Array<RouteRecordRaw> = [
   {
     path: "/home",
     name: "Home",
-    component: () => import("@/views/Home.vue"),
+    component: () => import("@/views/Home.vue")
   },
   {
     path: "/",
@@ -16,6 +16,11 @@ const routes: Array<RouteRecordRaw> = [
     name: "Register",
     component: () => import("@/views/Register.vue"),
   },
+  {
+    path: "/phoneChat",
+    name: "PhoneChat",
+    component: () => import("@/views/home/right/index.vue"),
+  },
   {
     path: '/zoomLoading',
     name: "ZoomLoading",

+ 1 - 1
src/store/index.ts

@@ -33,7 +33,7 @@ export default createStore({
     isShowChat: false,
     isShowUserInfo: false,
     isShowGroupInfo: false,
-    isShowRight: false,
+    isShowRight: true,
     isSearching: false,
     userList: [],
   } as State,

+ 7 - 5
src/views/Home.vue

@@ -1,7 +1,7 @@
 <template>
-  <div class="home">
+  <div class="home" :class="{phone: Config.isPhone}">
     <Left/>
-    <Right/>
+    <Right v-if="!Config.isPhone"/>
   </div>
 </template>
 
@@ -11,6 +11,7 @@ import Left from "./home/left/index.vue";
 import Right from "./home/right/index.vue";
 import {useStore} from "vuex";
 import {useRouter} from "vue-router";
+import Config from "../config";
 
 export default defineComponent({
   name: 'Home',
@@ -24,6 +25,9 @@ export default defineComponent({
     document.title = store.state.userInfo.username
     store.commit('setIsShowChat', false)
     store.commit('setIsShowRight', false)
+    return {
+      Config
+    }
   }
 })
 </script>
@@ -41,10 +45,8 @@ export default defineComponent({
   border-radius: 10px;
   box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.1);
   overflow: hidden;
-}
 
-@media screen and (max-width: 768px) {
-  .home {
+  &.phone {
     width: 100%;
     height: 100%;
     max-width: 100%;

+ 17 - 6
src/views/home/left/components/UserSearchList.vue

@@ -70,12 +70,15 @@
 </template>
 
 <script lang="ts">
-import {defineComponent, reactive, ref, toRefs, onMounted, nextTick} from "vue";
+import {defineComponent, reactive, ref, toRefs, onMounted, nextTick, 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 Config from "../../../../config";
+import {setLocalStorage} from "../../../../utils";
 
 interface ActionObj {
   type: number,
@@ -119,6 +122,7 @@ export default defineComponent({
 
     const inputName = ref<FieldInstance>()
     const store = useStore()
+    const router = useRouter()
 
     const userChange = () => {
       if (state.username) {
@@ -170,13 +174,16 @@ export default defineComponent({
     }
 
     const getUserList = async () => {
-      if (store.state.userInfo.token) {
-        const {data} = await getFriends()
-        store.commit('setUserList', data)
-        store.commit('hasNotReadMsgUserSort')
-      }
+      const {data} = await getFriends()
+      store.commit('setUserList', data)
+      store.commit('hasNotReadMsgUserSort')
     }
 
+    onActivated(() => {
+      // 用于h5
+      getUserList()
+    })
+
     const getNoReadNum = async (item: StrNumObj) => {
       await noReadNum({
         fromId: item.userId,
@@ -196,6 +203,10 @@ export default defineComponent({
       store.commit('setIsShowChat', true)
       store.commit('setIsShowUserInfo', false)
       store.commit('setIsShowGroupInfo', false)
+      if (Config.isPhone) {
+        router.push('/phoneChat')
+        setLocalStorage('chatUserInfo', item)
+      }
     }
 
     const submit = async () => {

+ 10 - 11
src/views/home/left/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="left">
+  <div class="left" :class="{phone: Config.isPhone}">
     <UserSearchList v-show="currentComponent === 'UserSearchList'"/>
     <PersonalCenter v-show="currentComponent === 'PersonalCenter'"/>
     <ul class="tab">
@@ -17,6 +17,7 @@ import UserSearchList from "./components/UserSearchList.vue"
 import PersonalCenter from "./components/PersonalCenter.vue"
 import {StrObj} from "../../../interface";
 import {useStore} from "vuex";
+import Config from "../../../config";
 
 interface State {
   tabData: StrObj[],
@@ -39,13 +40,13 @@ export default defineComponent({
 
     const handleClick = (item: StrObj) => {
       state.currentComponent = item.component
-      store.commit('setIsShowRight', state.currentComponent === 'UserSearchList' && window.innerWidth > 768)
+      store.commit('setIsShowRight', state.currentComponent === 'UserSearchList')
     }
 
     return {
       ...toRefs(state),
-      store,
-      handleClick
+      handleClick,
+      Config
     }
   }
 })
@@ -58,6 +59,11 @@ export default defineComponent({
   flex-direction: column;
   border-right: 1px solid #eee;
 
+  &.phone {
+    width: 100%;
+    border-right: none;
+  }
+
   .content {
     flex: 1;
   }
@@ -79,11 +85,4 @@ export default defineComponent({
     }
   }
 }
-
-@media screen and (max-width: 768px) {
-  .left {
-    width: 100%;
-    border-right: none;
-  }
-}
 </style>

+ 56 - 50
src/views/home/right/components/Chat.vue

@@ -1,8 +1,8 @@
 <template>
-  <div class="chat" v-if="store.state.isShowChat">
+  <div class="chat" :class="{phone: Config.isPhone}">
     <div class="message-top">
-      <van-icon name="arrow-left" size="22" color="#1989fa" class="close-icon"
-                @click="store.commit('setIsShowRight', false)"/>
+      <van-icon name="arrow-left" size="22" color="#1989fa" class="close-icon" v-if="Config.isPhone"
+                @click="router.back()"/>
       <div class="common-pic" @click="showInfo">{{ store.state.chatUserInfo.friendName.charAt(0) }}</div>
       <div class="username" @click="showInfo">{{ friendName }}</div>
       <van-popover class="chat-popover"
@@ -33,12 +33,13 @@
             <template v-else>{{ store.state.chatUserInfo.friendName.charAt(0) }}</template>
           </div>
           <div class="warp">
-            <!--            <div class="username">{{ item.fromUsername }}</div>-->
-            <div class="message"
+            <!--<div class="username">{{ item.fromUsername }}</div>-->
+            <div class="message" :class="{bg: pop.isShow && messageIndex === index}"
+                 v-html="messageFormat(item.content)"
                  @contextmenu="onContextmenu($event,index)"
                  @touchstart="onTouchstart($event,index)"
                  @touchend="onTouchend"
-                 v-html="messageFormat(item.content)"/>
+            />
             <div class="time">{{ formatDateTime(item.createdAt) }}</div>
           </div>
         </li>
@@ -70,7 +71,17 @@
 
 <script lang="ts">
 import V3Emoji from '../../../../components/V3Emoji.vue'
-import {computed, defineComponent, reactive, ref, toRefs, watch, nextTick, onUnmounted} from "vue";
+import {
+  computed,
+  defineComponent,
+  reactive,
+  ref,
+  toRefs,
+  watch,
+  nextTick,
+  onUnmounted,
+  onActivated, onMounted
+} from "vue";
 import Client from "../../../../utils/client";
 import {
   messageList,
@@ -86,6 +97,8 @@ import EmojiData from '../../../../assets/emojidata/emoji-data.json';
 import RE from '../../../../utils/regExp'
 import {Toast, Dialog} from "vant"
 import useClipboard from "vue-clipboard3";
+import Config from "../../../../config";
+import {useRouter} from 'vue-router';
 
 const {toClipboard} = useClipboard()
 
@@ -121,7 +134,8 @@ interface State {
   showPopover: boolean,
   actions: Obj,
   currentAction: StrObj,
-  pop: Obj
+  pop: Obj,
+  messageIndex: number
 }
 
 export default defineComponent({
@@ -153,7 +167,8 @@ export default defineComponent({
         isShow: false,
         left: 0,
         top: 0
-      }
+      },
+      messageIndex: 0
     })
 
     const optionsName = {
@@ -172,6 +187,7 @@ export default defineComponent({
     const messageListDom = ref<HTMLElement | null>(null)
 
     const store = useStore()
+    const router = useRouter()
     const friendName = computed(() => store.state.chatUserInfo.friendName)
 
     const showInfo = () => {
@@ -227,17 +243,13 @@ export default defineComponent({
       state.page = 1
       state.messageList = []
       getMessageList()
-    })
+    }, {immediate: true})
 
-    watch(() => store.state.isShowChat, val => {
-      if (val) {
-        nextTick(() => {
-          (messageContentDom.value as HTMLElement).onscroll = (ev: any) => {
-            if (ev.target.scrollTop === 0) {
-              getMessageList();
-            }
-          }
-        })
+    onMounted(() => {
+      (messageContentDom.value as HTMLElement).onscroll = (ev: any) => {
+        if (ev.target.scrollTop === 0) {
+          getMessageList();
+        }
       }
     })
 
@@ -288,7 +300,6 @@ export default defineComponent({
     })
 
     const sendMsg = async () => {
-      document.getElementById('textarea')?.focus()
       let content = state.message.trim()
       // 把表情包转成:__表情包的英文名字__
       content = content.replace(RE.emoji, (a, emoji) => `__${emojiMap[emoji.codePointAt(0)]}__`)
@@ -304,6 +315,7 @@ export default defineComponent({
       }
       state.message = ''
       state.messageList.push(data)
+      document.getElementById('textarea')?.focus()
       nextTick(() => {
         scrollToBottom()
       })
@@ -366,7 +378,6 @@ export default defineComponent({
           await deleteFriend({id: chatUserInfo.friendId})
           let i = store.state.userList.indexOf(chatUserInfo)
           store.state.userList.splice(i, 1)
-          store.commit('setIsShowChat', false)
           store.commit('setIsShowRight', false)
           Toast.success('删除成功')
         })
@@ -383,52 +394,50 @@ export default defineComponent({
           })
           let i = store.state.userList.indexOf(chatUserInfo)
           store.state.userList.splice(i, 1)
-          store.commit('setIsShowChat', false)
           store.commit('setIsShowRight', false)
         })
       }
     }
 
-    let messageHtml: string
-    let messageIndex: number
-    let timer: any
     const onContextmenu = (event: any, index: number) => {
       event.preventDefault() // 阻止默认的右键菜单弹出
       showPop(event, index)
     };
 
+    let popTimer: any
     const onTouchstart = (event: any, index: number) => {
-      timer = setTimeout(() => {
+      popTimer = setTimeout(() => {
         showPop(event, index)
       }, 1000)
     }
 
     const onTouchend = () => {
-      clearTimeout(timer)
+      clearTimeout(popTimer)
     }
 
+    let messageText: string
     const showPop = (event: any, index: number) => {
       state.pop = {
         isShow: true,
         left: event.clientX + 'px',
         top: event.clientY + 'px'
       }
-      messageHtml = event.target.innerText
-      messageIndex = index
+      messageText = event.target.innerText
+      state.messageIndex = index
     }
 
     const copy = () => {
       closePop()
-      toClipboard(messageHtml)
+      toClipboard(messageText)
     }
 
     const removeMessage = async () => {
       closePop()
       const pageSize = 20 // 后端默认每页20条数据
-      const total = state.messageList.length - messageIndex
+      const total = state.messageList.length - state.messageIndex
       const page = Math.ceil(total / pageSize)
       let chatUserInfo = store.state.chatUserInfo
-      let id = state.messageList[messageIndex].id
+      let id = state.messageList[state.messageIndex].id
       if (id === 0) {
         let {data} = await messageList({
           fromId: store.state.userInfo.id,
@@ -436,14 +445,14 @@ export default defineComponent({
           messageType: store.state.chatUserInfo.isGroup,
           page,
         })
-        id = data.find((item: Obj) => item.content === state.messageList[messageIndex].content)?.id
+        id = data.find((item: Obj) => item.content === state.messageList[state.messageIndex].content)?.id
       }
       await deleteMessage({
         toId: chatUserInfo.friendId,
         messageType: chatUserInfo.isGroup,
         msgId: [id]
       })
-      state.messageList.splice(messageIndex, 1)
+      state.messageList.splice(state.messageIndex, 1)
     }
 
     const closePop = () => {
@@ -456,10 +465,10 @@ export default defineComponent({
       window.removeEventListener('click', closePop)
     })
 
-
     return {
       ...toRefs(state),
       store,
+      router,
       messageContentDom,
       messageListDom,
       friendName,
@@ -473,7 +482,8 @@ export default defineComponent({
       copy,
       removeMessage,
       onTouchstart,
-      onTouchend
+      onTouchend,
+      Config
     }
   }
 })
@@ -498,6 +508,11 @@ export default defineComponent({
   height: 100%;
   display: flex;
   flex-direction: column;
+
+  &.phone {
+    width: 100%;
+    border-right: none;
+  }
 }
 
 .message-top {
@@ -543,7 +558,6 @@ export default defineComponent({
         .message {
           //color: royalblue;
           color: #07c160;
-          display: inline-block;
         }
       }
     }
@@ -563,10 +577,15 @@ export default defineComponent({
       }
 
       .message {
+        display: inline-block;
         word-break: break-all;
         text-align: justify;
         -webkit-user-select: none;
         user-select: none;
+
+        &.bg {
+          background: #dbdbdb;
+        }
       }
     }
 
@@ -625,17 +644,4 @@ export default defineComponent({
     }
   }
 }
-
-@media screen and (max-width: 768px) {
-  .chat {
-    width: 100%;
-    border-right: none;
-  }
-}
-
-@media screen and (min-width: 769px) {
-  .close-icon {
-    display: none;
-  }
-}
 </style>

+ 19 - 21
src/views/home/right/index.vue

@@ -1,6 +1,6 @@
 <template>
-  <div class="right" :class="{show: store.state.isShowRight}">
-    <Chat/>
+  <div class="right" :class="{phone: Config.isPhone}" v-show="store.state.isShowRight">
+    <Chat v-if="store.state.isShowChat"/>
     <UserInfo class="user-info" v-if="store.state.isShowUserInfo"/>
     <GroupInfo class="user-info" v-if="store.state.isShowGroupInfo"/>
   </div>
@@ -12,14 +12,27 @@ import Chat from "./components/Chat.vue";
 import UserInfo from "./components/UserInfo.vue";
 import GroupInfo from "./components/GroupInfo.vue";
 import {useStore} from "vuex";
+import Config from "../../../config";
+import {getLocalStorage} from "../../../utils";
 
 export default defineComponent({
   name: 'Right',
   components: {Chat, UserInfo, GroupInfo},
   setup() {
     const store = useStore()
+
+    const setChatUserInfo = () => {
+      store.commit('setChatUserInfo', getLocalStorage('chatUserInfo'))
+      store.commit('setIsShowChat', true)
+    }
+    if (Config.isPhone) {
+      // h5页面刷新时,获取好友信息
+      setChatUserInfo()
+    }
+
     return {
-      store
+      store,
+      Config
     }
   }
 })
@@ -29,10 +42,10 @@ export default defineComponent({
 .right {
   position: relative;
   width: 70%;
-  display: none;
 
-  &.show {
-    display: block;
+  &.phone {
+    width: 100%;
+    height: 100%;
   }
 }
 
@@ -45,19 +58,4 @@ export default defineComponent({
   left: 0;
   top: 0;
 }
-
-@media screen and (max-width: 768px) {
-  .right {
-    position: absolute;
-    width: 100%;
-    height: 100%;
-    left: 100%;
-    background: #fff;
-    display: block;
-
-    &.show {
-      left: 0;
-    }
-  }
-}
 </style>