Explorar o código

feat: 对接聊天功能

qinyuyue hai 2 meses
pai
achega
e05cb48d27

+ 42 - 7
src/pages/chat/chat-message/index.vue

@@ -1,6 +1,6 @@
 <template v-if="msg">
   <div class="chat-message" :class="msg.viewType ? 'chat-message--accept' : 'chat-message--send'">
-    <div class="text-center text-sm text-[#000]/40 mb-16">{{ msg.createTime }}</div>
+    <div class="h-20 grid place-items-center text-center text-sm text-[#000]/40 mb-16">{{ msg.createTime }}</div>
     <div class="chat-message__content">
       <van-image
           v-if="msg.viewType === 1"
@@ -10,8 +10,12 @@
           radius="100%"
           class="mr-8"
       ></van-image>
+      <div  v-if="msg.viewType === 0" class="self-center text-sm mx-5 text-black-9">
+        <van-loading v-if="sendStatus === 'loading'" size="16"/>
+        <van-icon v-if="sendStatus === 'error'" name="warning"  size="16"/>
+      </div>
       <div class="flex flex-col" :class=" msg.viewType ?  'items-start' : 'items-end'">
-        <div v-if="showName && msg.viewType === 1" class="text-black-9 text-sm mb-2">{{msg.showName}}</div>
+        <div v-if="showName && msg.viewType === 1" class="text-black-9 text-sm mb-2">{{ msg.nickName }}</div>
         <div class="flex-grow-0 w-fit">
           <TextMessage v-if="msg.type === 'text'" :message-content="msg.messageContent"
                        :view-type="msg.viewType"></TextMessage>
@@ -23,7 +27,6 @@
                        :view-type="msg.viewType"></LinkMessage>
         </div>
       </div>
-<!--      <div class="self-center text-sm mx-5 text-black-9">发送中</div>-->
       <van-image
           v-if="msg.viewType === 0"
           :src="userInfo?.headImageUrl || defaultAvatar"
@@ -55,33 +58,65 @@ const props = defineProps({
 });
 const msg = computed(() => {
   try {
-    console.log(props.message, 'propsprops')
-    const {createTime, getUserId, sendUserId, messageContent, messageType} = props.message
+    // console.log(props.message, 'msg')
+    const {
+      createTime,
+      getUserId,
+      sendUserId,
+      messageContent,
+      messageType,
+      object = {headImageUrl: defaultAvatar, showName: '无名大侠'}
+    } = props.message
     return {
       messageContent: messageContent,
       type: getMessageType(messageType, messageContent),
       viewType: sendUserId === userInfo?.value.pass ? 0 : 1,
       createTime: createTime,
-      showName: props.message?.showName
+      headImageUrl: object.headImageUrl,
+      nickName: object.showName
     }
   } catch (e) {
     console.log(e, '??')
     return null
   }
 })
+
+const sendStatus = ref('success')
+const handleSendStatus = () => {
+  if (msg?.value.createTime) {
+    sendStatus.value = ''
+    return
+  }
+  sendStatus.value = 'loading'
+  setTimeout(() => {
+    if (!msg?.value?.createTime) {
+      sendStatus.value = 'error'
+    } else {
+      sendStatus.value = 'success'
+    }
+  }, 2 * 1000)
+}
+
+onMounted(() => {
+  handleSendStatus()
+})
+
+
 const getMessageType = (messageType, messageContent) => {
   const types = ['text', 'image', 'audio', 'video', 'link']
-  if(messageType === 0 && findHyperlinks(messageContent)) return types[4]
+  if (messageType === 0 && findHyperlinks(messageContent)) return types[4]
   return types[messageType]
 }
 </script>
 <style scoped lang="scss">
 .chat-message {
   margin: 20px 0;
+
   .chat-message__content {
     width: max-content;
     display: flex;
   }
+
   &.chat-message--accept {
     .chat-message__content {
       margin-right: auto;

+ 19 - 20
src/pages/chat/group-chat.vue

@@ -26,14 +26,15 @@
           finished-text=""
       >
         <template v-for="(message, index) in currConversationChatList" :key="index">
-          <ChatMessage :show-name="true" :message="message" ></ChatMessage>
+          <ChatMessage :show-name="true" :message="message"></ChatMessage>
         </template>
       </van-list>
       <!--  <van-pull-refresh v-model="refreshing" @refresh="loadMore" class="flex-1">
             </van-pull-refresh>-->
-      <!--      <div class="fixed bottom-0 left-0 right-0 w-full">-->
-      <ChatInput :operates="['image', 'share-group']" @focus="scrollToBottom" @send="handleSendMessage"></ChatInput>
-      <!--      </div>-->
+      <div class="h-70 w-full bg-[#fff]"></div>
+      <div class="fixed bottom-0 left-0 right-0 w-full bg-[#fff]">
+        <ChatInput :operates="['image', 'share-group']" @focus="scrollToBottom" @send="handleSendMessage"></ChatInput>
+      </div>
     </template>
     <template v-else>
       <div class="flex-1 grid place-items-center text-black-9">
@@ -41,7 +42,7 @@
         <div v-else class="grid place-items-center">
           <div v-if="groupId">创建成功</div>
           <div v-else class="grid place-items-center">
-            <div  class="mb-10">创建会话失败</div>
+            <div class="mb-10">创建会话失败</div>
             <van-button size="small" @click="initGroupId">点击重试</van-button>
           </div>
         </div>
@@ -77,11 +78,11 @@ const showPage = computed(() => groupId.value)
 
 const initGroupId = async () => {
   try {
-/*    if (!groupId.value) return;
-    pageLoading.value = true;
-    const res = await chatsStore.getCurrConversationId(getUserId.value)
-    await handleResponse(res)
-    groupId.value = res.data;*/
+    /*    if (!groupId.value) return;
+        pageLoading.value = true;
+        const res = await chatsStore.getCurrConversationId(getUserId.value)
+        await handleResponse(res)
+        groupId.value = res.data;*/
     await getChatList('init')
   } catch (e) {
 
@@ -107,8 +108,7 @@ const getChatList = async (type = 'init') => {
     })
     pageNum.value = page;
     await handleResponse(res);
-    currConversationChatList.value = handleChatList(res.data?.data)
-    console.log(currConversationChatList.value, 'currConversationChatList')
+    currConversationChatList.value = chatsStore.handleMessageList(res.data?.data)
     if (type === 'init') await scrollToBottom()
   } catch (e) {
     console.error(e)
@@ -116,14 +116,10 @@ const getChatList = async (type = 'init') => {
 
   }
 }
-const handleChatList = (list = []) => {
-  return (list ?? []).filter(o => isValidJson(o.messageContent)).map(o => JSON.parse(o.messageContent));
-}
 
 // 发送文本消息
 const sendTextMessage = async (text) => {
   try {
-    console.log(text, 'sendTextMessage')
     if (!text) return
     let msg = {
       groupId: groupId.value,
@@ -134,7 +130,10 @@ const sendTextMessage = async (text) => {
       messageType: 0,
       noticeType: 2,
       object: {
-        id: getLocalId()
+        id: getLocalId(),
+        // TODO 聊天时候改了头像昵称 会出现找不到的情况
+        headImageUrl: userInfo?.value.headImageUrl,
+        showName: userInfo?.value.showName
       }
     }
     const isLink = !!findHyperlinks(text)
@@ -142,7 +141,6 @@ const sendTextMessage = async (text) => {
     currConversationChatList.value.push(msg)
     await scrollToBottom()
     const res = await chatsStore.sendSocketMessage(msg)
-    console.log('luck:', res)
   } catch (e) {
     console.log(e, '2')
   } finally {
@@ -218,14 +216,14 @@ const scrollToBottom = async () => {
 };
 
 
-
 // 加载更多
 const refreshing = ref(false)
 const loadMore = async () => {
   try {
     refreshing.value = true
     await getChatList('more')
-  } catch (e) { } finally {
+  } catch (e) {
+  } finally {
     refreshing.value = false
   }
 }
@@ -247,6 +245,7 @@ function getLocalId() {
 onMounted(() => {
   initGroupId()
   XYWebSocket.SocketEventsBus.on(XYWebSocket.SocketEvents.chatEvent, async (chat) => {
+    console.log(chat, '群聊页面消息订阅')
     const isCurrGroupId = chat.groupId && chat.groupId === groupId.value;
     const isOtherUserMessage = chat.sendUserId && chatsStore.isRealMessage(chat.sendUserId);
     if (isCurrGroupId && isOtherUserMessage) {

+ 41 - 38
src/pages/chat/single-chat.vue

@@ -26,17 +26,18 @@
           finished-text=""
       >
         <template v-for="(message, index) in currConversationChatList" :key="index">
-          <ChatMessage :message="message" ></ChatMessage>
+          <ChatMessage :message="message"></ChatMessage>
         </template>
         <div v-if="false" class="text-[#979797] text-sm text-center mt-auto mb-10">
           {{ followStatus }}在对方关注或回复前,最多只能发送1条信息
         </div>
       </van-list>
-<!--  <van-pull-refresh v-model="refreshing" @refresh="loadMore" class="flex-1">
-      </van-pull-refresh>-->
-<!--      <div class="fixed bottom-0 left-0 right-0 w-full">-->
+      <!--  <van-pull-refresh v-model="refreshing" @refresh="loadMore" class="flex-1">
+            </van-pull-refresh>-->
+      <div class="h-70 w-full bg-[#fff]"></div>
+      <div class="fixed bottom-0 left-0 right-0 w-full">
         <ChatInput :operates="['image']" @focus="scrollToBottom" @send="handleSendMessage"></ChatInput>
-<!--      </div>-->
+      </div>
     </template>
     <template v-else>
       <div class="flex-1 grid place-items-center text-black-9">
@@ -44,7 +45,7 @@
         <div v-else class="grid place-items-center">
           <div v-if="groupId">创建成功</div>
           <div v-else class="grid place-items-center">
-            <div  class="mb-10">创建会话失败</div>
+            <div class="mb-10">创建会话失败</div>
             <van-button size="small" @click="initGroupId">点击重试</van-button>
           </div>
         </div>
@@ -57,7 +58,6 @@ import ChatMessage from './chat-message'
 import ChatInput from "./chat-input";
 import {findHyperlinks} from "~/pages/chat/chat-message/link-message/handle";
 import {XYWebSocket} from "~/utils/XYWebSocket";
-import {isValidJson} from "~/utils";
 
 const route = useRoute()
 const router = useRouter()
@@ -107,13 +107,12 @@ const getChatList = async (type = 'init') => {
     const page = type === 'init' ? 1 : pageNum.value + 1;
     const res = await chatsStore.getChatHistory({
       pageNum: page,
-      pageSize: 100,
+      pageSize: 10,
       groupId: groupId.value,
     })
     pageNum.value = page;
     await handleResponse(res);
-    currConversationChatList.value = handleChatList(res.data?.data)
-    console.log(currConversationChatList.value, 'currConversationChatList')
+    currConversationChatList.value = chatsStore.handleMessageList(res.data?.data)
     if (type === 'init') await scrollToBottom()
     await getFollowStatus()
   } catch (e) {
@@ -122,14 +121,10 @@ const getChatList = async (type = 'init') => {
 
   }
 }
-const handleChatList = (list = []) => {
-  return (list ?? []).filter(o => isValidJson(o.messageContent)).map(o => JSON.parse(o.messageContent));
-}
 
 // 发送文本消息
 const sendTextMessage = async (text) => {
   try {
-    console.log(text, 'sendTextMessage')
     if (!text) return
     let msg = {
       groupId: groupId.value,
@@ -140,15 +135,18 @@ const sendTextMessage = async (text) => {
       messageType: 0,
       noticeType: 1,
       object: {
-        id: getLocalId()
+        id: getLocalId(),
+        // TODO 聊天时候改了头像昵称 会出现找不到的情况
+        headImageUrl: userInfo?.value.headImageUrl,
+        showName: userInfo?.value.showName
       }
     }
     const isLink = !!findHyperlinks(text)
     if (isLink) msg.messageType = 4;
     currConversationChatList.value.push(msg)
     await scrollToBottom()
-    const res = await chatsStore.sendSocketMessage(msg)
-    console.log('luck:', res)
+    await chatsStore.sendSocketMessage(msg)
+    await getChatList('init')
   } catch (e) {
     console.log(e, '2')
   } finally {
@@ -180,9 +178,9 @@ const sendImageMessage = async (file) => {
         id: getLocalId()
       }
     }
-   currConversationChatList.value.push(msg)
+    currConversationChatList.value.push(msg)
     await scrollToBottom()
-   await chatsStore.sendSocketMessage(msg)
+    await chatsStore.sendSocketMessage(msg)
   } catch (e) {
     console.error(e, '??')
   } finally {
@@ -213,14 +211,16 @@ const handleSendMessage = async ({type, messageContent}) => {
 }
 
 const scrollToBottom = async () => {
- setTimeout(async () => {
-   await nextTick(); // 确保DOM已经更新
-   const listElement = chatListRef.value?.$el;
-   if (listElement) {
-     const scrollContainer = listElement;
-     scrollContainer.scrollTop = scrollContainer.scrollHeight;
-   }
- }, 200)
+  // 操作向上加载不滚动 TODO 判断用户是否有向上滑的操作更准确
+  if (refreshing.value) return
+  setTimeout(async () => {
+    await nextTick(); // 确保DOM已经更新
+    const listElement = chatListRef.value?.$el;
+    if (listElement) {
+      const scrollContainer = listElement;
+      scrollContainer.scrollTop = scrollContainer.scrollHeight;
+    }
+  }, 200)
 };
 
 // 获取我与对方的关注情况
@@ -239,7 +239,8 @@ const loadMore = async () => {
   try {
     refreshing.value = true
     await getChatList('more')
-  } catch (e) { } finally {
+  } catch (e) {
+  } finally {
     refreshing.value = false
   }
 }
@@ -263,6 +264,7 @@ function getLocalId() {
 onMounted(() => {
   initGroupId()
   XYWebSocket.SocketEventsBus.on(XYWebSocket.SocketEvents.chatEvent, async (chat) => {
+    console.log(chat, '单聊页面消息订阅')
     const isCurrGroupId = chat.groupId && chat.groupId === groupId.value;
     const isOtherUserMessage = chat.sendUserId && chatsStore.isRealMessage(chat.sendUserId)
     if (isCurrGroupId && isOtherUserMessage) {
@@ -283,18 +285,19 @@ const delMessage = (messageId) => {
     message: '是否删除这条消息?',
     confirmButtonColor: '#FF9300'
   })
-    .then(async () => {
-      const res = await request('/website/tourMessage/delMessage', {
-        method: 'post',
-        body: {
-          messageId: [messageId]
+      .then(async () => {
+        const res = await request('/website/tourMessage/delMessage', {
+          method: 'post',
+          body: {
+            messageId: [messageId]
+          }
+        })
+
+        if (res && res?.success) {
         }
       })
-
-      if (res && res?.success) {
-      }
-    })
-    .catch(() => {})
+      .catch(() => {
+      })
 }
 
 definePageMeta({

+ 14 - 1
src/stores/useChats.js

@@ -1,4 +1,5 @@
 import {XYWebSocket} from "~/utils/XYWebSocket";
+import {isValidJson} from "~/utils";
 
 export const useChatsStore = defineStore('chats', () => {
   const userInfoStore = useUserInfoStore();
@@ -44,6 +45,7 @@ export const useChatsStore = defineStore('chats', () => {
   }
 
   const isRealMessage = (sendUserId) => {
+    console.log(userInfo.value.pass, 'userInfo.value.pass')
     return sendUserId !== userInfo.value.pass
   }
 
@@ -76,6 +78,15 @@ export const useChatsStore = defineStore('chats', () => {
 
   const createGroupId = () => Date.now() + '' + Math.floor(Math.random() * 100000)
 
+  // 处理后端接口回来的sendUserId被修改
+  const handleMessageList = (list) => {
+    return (list ?? []).filter(o => isValidJson(o.messageContent)).map(o => {
+      let messageContent = JSON.parse(o.messageContent)
+      messageContent.sendUserId = o.sendUserId
+      return messageContent
+    });
+  }
+
   const MESSAGE_TYPE = {
     'text': 0,
     'image': 1,
@@ -104,7 +115,9 @@ export const useChatsStore = defineStore('chats', () => {
     getChatList,
 
 
-    isRealMessage
+    // 排除自己发的消息
+    isRealMessage,
+    handleMessageList
 
   }
 })

+ 4 - 11
src/utils/XYWebSocket.ts

@@ -41,27 +41,20 @@ export namespace XYWebSocket {
         // 收到服务端消息
         private websocketOnmessage(e: MessageEvent) {
             try {
-                // console.error(e, '收到服务端消息')
                 if (!e) return;
                 if (!e.data) return;
                 const messagePackage = JSON.parse(e.data) || {};
                 console.log('---------接收---------', messagePackage, '-----------消息包------');
                 const {web_request_id = null} = messagePackage.map || {};
-                /**
-                 * 1、回复客户端消息
-                 */
+
+                SocketEventsBus.emit(SocketEvents.chatEvent, messagePackage)
+
+                // 处理自己本地发送的消息
                 if (web_request_id && this.eventPoll.has(web_request_id)) {
                     const eventItem = this.eventPoll.get(web_request_id)!;
                     eventItem.response = messagePackage;
                     eventItem.status = 'end'; // todo status属性的修改最好写在最后,写在前面好像也没啥问题
-                    return;
                 }
-                /**
-                 * 2、 服务端主动发起消息
-                 */
-                // if (!web_request_id) {
-                    SocketEventsBus.emit(SocketEvents.chatEvent, messagePackage)
-                // }
             } catch (e) {
 
             }