diff --git a/App.vue b/App.vue index 896f648..b78c36e 100644 --- a/App.vue +++ b/App.vue @@ -200,11 +200,11 @@ export default { if (type === "pong") return; // 退出登录通知 - if (type === "Exit") { - try { - this.ws && this.ws.close(); - } catch (err) {} - // 清理本地登录信息并返回登录页 + if (type === "Exit") { + try { + this.ws && this.ws.close(); + } catch (err) {} + // 清理本地登录信息并返回登录页 this.$u.vuex("vuex_user", ""); this.$u.vuex("vuex_token", ""); uni.clearStorage(); @@ -220,24 +220,35 @@ export default { this.$u.vuex("vuex_tabbar", tab); } - // 收到私聊消息 - if (type === "PrivateChatMessage") { - const data = msgData.Data || {}; - const processData = { - dialogueManagementId: data.DialogueManagementId, - senderId: data.SenderId, + // 收到私聊消息 + if (type === "PrivateChatMessage") { + const data = msgData.Data || {}; + const processData = { + dialogueManagementId: data.DialogueManagementId, + senderId: data.SenderId, receiverId: data.ReceiverId, sendDate: data.SendDate, message: data.Message, }; - this.$store.commit("push_Msg", { - ...processData, - messageType: 0, - filePath: "", - id: Math.random().toString(36).substring(2), - }); - } - }, + this.$store.commit("push_Msg", { + ...processData, + messageType: 0, + filePath: "", + id: Math.random().toString(36).substring(2), + }); + + // 更新会话列表的未读数 / 文案 / 时间,保持实时展示 + if (processData.dialogueManagementId) { + this.$store.commit("apply_RealtimeMessageToList", { + dialogueId: processData.dialogueManagementId, + message: processData.message, + sendDate: processData.sendDate, + senderId: processData.senderId, + receiverId: processData.receiverId, + }); + } + } + }, // 连接关闭 handleWsClose(e) { // console.log(`[WebSocket] 连接关闭: code=${e.code}, reason=${e.reason}`); diff --git a/pages/consultation/index.vue b/pages/consultation/index.vue index 7acf212..f9592f8 100644 --- a/pages/consultation/index.vue +++ b/pages/consultation/index.vue @@ -63,12 +63,120 @@ export default { }, computed: { // 补充展示字段,便于模板直接使用 + unreadMap() { + const map = {}; + const list = this.$store?.state?.vuex_userMsgList || []; + list.forEach((item) => { + const id = this.getDialogueId(item); + const count = + item?.unReadCount ?? + item?.unreadCount ?? + item?.unread ?? + null; + if (id && typeof count === "number") { + map[id] = count; + } + }); + return map; + }, displayChatList() { - return (this.chatList || []).map((item) => ({ - ...item, - displayTime: item.lastMessageTime || "", - displayPreview: item.lastMessage || "暂无消息", - })); + // 合并接口首帧列表 + 实时更新列表,按 id 匹配覆盖 + const realtime = this.$store?.state?.vuex_userMsgList || []; + const baseList = this.chatList || []; + + const rtMap = {}; + realtime.forEach((item) => { + const id = this.getDialogueId(item); + if (id) rtMap[id] = item; + }); + + const merged = baseList.map((item) => { + const id = this.getDialogueId(item); + const rt = id ? rtMap[id] : null; + const source = rt || item; + const unread = + typeof this.unreadMap[id] === "number" + ? this.unreadMap[id] + : source?.unReadCount ?? + source?.unreadCount ?? + source?.unread ?? + 0; + + return { + ...item, + ...rt, + id, + dialogueManagementId: id, + DialogueManagementId: id, + userId: + source?.receiverId || + source?.friendId || + source?.userId || + item?.userId || + "", + avatar: + source?.receiverHeadSculptureUrl || + source?.avatar || + source?.friendAvatar || + "/static/avatar/default-avatar.png", + displayTime: this.formatTime( + source?.lastMessageTime || + source?.lastSendTime || + source?.sendDate || + item?.lastMessageTime || + "" + ), + displayPreview: + source?.lastMessage || + source?.message || + item?.lastMessage || + "暂无消息", + unreadCount: unread, + }; + }); + + // 追加实时列表中基线不存在的会话 + realtime.forEach((item) => { + const id = this.getDialogueId(item); + if (!id) return; + const exists = merged.some((row) => row.id === id); + if (!exists) { + const unread = + typeof this.unreadMap[id] === "number" + ? this.unreadMap[id] + : item?.unReadCount ?? + item?.unreadCount ?? + item?.unread ?? + 0; + merged.unshift({ + ...item, + id, + dialogueManagementId: id, + DialogueManagementId: id, + userId: + item?.receiverId || + item?.friendId || + item?.userId || + "", + avatar: + item?.receiverHeadSculptureUrl || + item?.avatar || + item?.friendAvatar || + "/static/avatar/default-avatar.png", + displayTime: this.formatTime( + item?.lastMessageTime || + item?.lastSendTime || + item?.sendDate || + "" + ), + displayPreview: + item?.lastMessage || item?.message || "暂无消息", + unreadCount: unread, + }); + } + }); + + return merged; }, }, data() { @@ -130,6 +238,8 @@ export default { const list = (res && res.data && res.data.item1) || []; this.chatList = this.normalizeDialogueList(list); + // 同步到全局会话列表,便于后续实时合并 + this.$store.commit("set_UserMsgList", this.chatList); this.totalCount = res?.data?.item2 || list.length; } catch (error) { console.error("[在线咨询] 获取会话列表失败", error); @@ -155,9 +265,20 @@ export default { ? item.unReadCount : item?.unreadCount || 0; + const id = + item?.dialogueManagementId || + item?.DialogueManagementId || + item?.dialogueId || + item?.friendId || + item?.id || + index; + return { - id: item?.dialogueManagementId || item?.dialogueId || item?.id || index, + id, + dialogueManagementId: id, + DialogueManagementId: id, userId: item?.receiverId || item?.friendId || item?.userId || "", + friendId: item?.receiverId || item?.friendId || item?.userId || "", name: item?.receiverName || item?.friendName || @@ -202,6 +323,18 @@ export default { // 回退到字符串裁剪 return String(timeStr).replace("T", " ").slice(5, 16); }, + + // 统一获取会话唯一标识 + getDialogueId(item) { + return ( + item?.dialogueManagementId || + item?.DialogueManagementId || + item?.dialogueId || + item?.friendId || + item?.id || + "" + ); + }, }, }; diff --git a/store/index.js b/store/index.js index 5b85cc8..58cf0d5 100644 --- a/store/index.js +++ b/store/index.js @@ -186,6 +186,63 @@ const store = new Vuex.Store({ return item; }); }, + // WebSocket 实时消息:更新会话列表的未读数、文案与时间 + apply_RealtimeMessageToList( + state, + { dialogueId, message, sendDate, senderId, receiverId } + ) { + if (!dialogueId) return; + const activeId = + state.vuex_msgUser?.dialogueManagementId || + state.vuex_msgUser?.friendId || + state.vuex_msgUser?.id; + const isActive = activeId && activeId === dialogueId; + + let found = false; + state.vuex_userMsgList = (state.vuex_userMsgList || []).map((item) => { + const itemId = + item?.dialogueManagementId || + item?.DialogueManagementId || + item?.friendId || + item?.id; + if (itemId === dialogueId) { + found = true; + const currentUnread = + Number( + item?.unReadCount ?? + item?.unreadCount ?? + item?.unread ?? + 0 + ) || 0; + const nextUnread = isActive ? 0 : currentUnread + 1; + return { + ...item, + lastMessage: message ?? item?.lastMessage ?? "", + lastMessageTime: sendDate ?? item?.lastMessageTime ?? "", + unReadCount: nextUnread, + unreadCount: nextUnread, + }; + } + return item; + }); + + // 未在列表中则插入一条最小信息的会话(保持实时感知) + if (!found) { + const nextUnread = isActive ? 0 : 1; + state.vuex_userMsgList.unshift({ + id: dialogueId, + dialogueManagementId: dialogueId, + DialogueManagementId: dialogueId, + friendId: receiverId || senderId || dialogueId, + name: "", + avatar: "", + lastMessage: message || "", + lastMessageTime: sendDate || "", + unReadCount: nextUnread, + unreadCount: nextUnread, + }); + } + }, // 更新消息窗口滚动位置 set_MsgScrollTop(state, top) { state.vuex_msgScrollTop = Number(top) || 0;