diff --git a/App.vue b/App.vue index b78c36e..e9f5c42 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,35 +220,45 @@ 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 dialogueManagementId = data.DialogueManagementId || ""; + const processData = { + 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), - }); - - // 更新会话列表的未读数 / 文案 / 时间,保持实时展示 - if (processData.dialogueManagementId) { - this.$store.commit("apply_RealtimeMessageToList", { - dialogueId: processData.dialogueManagementId, - message: processData.message, - sendDate: processData.sendDate, - senderId: processData.senderId, - receiverId: processData.receiverId, - }); - } - } - }, + const id = data.Id || data.id || Math.random().toString(36).substring(2); + const msg = { + ...processData, + messageType: 0, + filePath: "", + id, + }; + const storeState = (this.$store && this.$store.state) || {}; + const isTransferChat = !!storeState.vuex_isTransferChat; + + if (isTransferChat) { + this.$store.commit("push_AiLiveAgentMsg", msg); + } else { + this.$store.commit("push_Msg", msg); + } + + // 更新会话列表的未读数 / 文案 / 时间,保持实时展示 + 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/home/index/index.vue b/pages/home/index/index.vue index 44b0b90..8e9d3bf 100644 --- a/pages/home/index/index.vue +++ b/pages/home/index/index.vue @@ -54,7 +54,7 @@ + + + + + + + + + + @@ -485,14 +512,50 @@ export default { return "/static/common/images/avatar.png"; }, + receiverHeadSculptureUrl() { + const url = + (this.vuex_msgUser && + (this.vuex_msgUser.headSculptureUrl || + this.vuex_msgUser.HeadSculptureUrl)) || + ""; + if (url) { + return this.baseUrl + "/" + url; + } + + return "/static/common/images/avatar_default2.png"; + }, aiMessageGroups() { return Array.isArray(this.vuex_aiMessageGroups) ? this.vuex_aiMessageGroups : []; }, + displayFeatures() { + const list = Array.isArray(this.features) ? this.features : []; + return list.map((item) => { + if (item && item.title === "转人工") { + return { + ...item, + title: this.vuex_isTransferChat ? "结束会话" : "转人工", + }; + } + return item; + }); + }, + isLiveAgentChat() { + if (!this.vuex_isTransferChat) return false; + const current = this.currentDMid || ""; + const active = this.vuex_msgUser && this.vuex_msgUser.dialogueManagementId; + return !!(current && active && String(active) === String(current)); + }, }, watch: { + currentDMid: { + handler(val) { + this.$store.commit("set_AiActiveDMid", val || ""); + }, + immediate: true, + }, // 确保消息更新后自动滚动到底部 aiMessageGroups: { handler() { @@ -619,6 +682,10 @@ export default { this.handleTransferEntryClick(); return; } + if (item.title === "结束会话") { + this.$store.commit("set_IsTransferChat", false); + return; + } if (item.title === "电话咨询") { this.advicePhoneShow = true; @@ -709,6 +776,17 @@ export default { (m) => m && m.customType === "transferCard" ); }; + const updateTransferCard = (patch) => { + const list = this.aiMessageGroups; + const idx = findCardIndex(list); + if (idx < 0) return false; + const current = list[idx] || {}; + const next = { ...current, ...patch }; + const nextList = list.slice(); + nextList.splice(idx, 1, next); + this.$store.commit("push_AiMsgList", nextList); + return true; + }; const list = this.aiMessageGroups; const idx = findCardIndex(list); if (idx < 0) return; @@ -716,15 +794,11 @@ export default { const current = list[idx] || {}; if (this.isTransferSubmitting || current.transferStatus === "done") return; - const next = { - ...current, + updateTransferCard({ transferStatus: "idle", selectedConsultationType: consultationType, transferTipText: "", - }; - const nextList = list.slice(); - nextList.splice(idx, 1, next); - this.$store.commit("push_AiMsgList", nextList); + }); this.isTransferSubmitting = true; const params = { @@ -740,48 +814,59 @@ export default { this.$u.api .TransferToALiveAgentApi(params) .then((res) => { - this.isTransferSubmitting = false; - const latestList = this.aiMessageGroups; - const nextIdx = findCardIndex(latestList); - if (nextIdx < 0) return; const succeed = res && res.succeed !== false; - const serverTip = - (res && res.data && res.data.message) || - (res && res.error) || - ""; + const serverTip = (res && res.data && res.data.message) || (res && res.error) || ""; if (!succeed) { - const updated = { - ...latestList[nextIdx], + updateTransferCard({ transferStatus: "idle", transferTipText: serverTip, - }; - const updatedList = latestList.slice(); - updatedList.splice(nextIdx, 1, updated); - this.$store.commit("push_AiMsgList", updatedList); + }); return; } - const updated = { - ...latestList[nextIdx], + + const dialogueManagementId = + this.currentDMid || + (res && + res.data && + (res.data.dialogueManagementId || + res.data.DialogueManagementId)) || + ""; + const receiverId = + (res && + res.data && + (res.data.receiverId || + res.data.ReceiverId || + res.data.liveAgentId || + res.data.agentId || + res.data.userId)) || + ""; + + if (!this.currentDMid && dialogueManagementId) { + this.currentDMid = dialogueManagementId; + } + + updateTransferCard({ transferStatus: "done", transferTipText: serverTip, - }; - const updatedList = latestList.slice(); - updatedList.splice(nextIdx, 1, updated); - this.$store.commit("push_AiMsgList", updatedList); + }); + + if (dialogueManagementId && receiverId) { + this.$store.commit("set_IsTransferChat", true); + this.$store.dispatch("selectTeacherChatItem", { + id: dialogueManagementId, + receiverId, + navigate: false, + }); + } }) .catch(() => { - this.isTransferSubmitting = false; - const latestList = this.aiMessageGroups; - const nextIdx = findCardIndex(latestList); - if (nextIdx < 0) return; - const updated = { - ...latestList[nextIdx], + updateTransferCard({ transferStatus: "idle", transferTipText: "转人工失败,请稍后重试", - }; - const updatedList = latestList.slice(); - updatedList.splice(nextIdx, 1, updated); - this.$store.commit("push_AiMsgList", updatedList); + }); + }) + .finally(() => { + this.isTransferSubmitting = false; }); }, @@ -814,6 +899,11 @@ export default { this.$store.commit("push_AiMsg", userMessage); this.messageValue = ""; + if (this.isLiveAgentChat) { + this.sendLiveAgentTextMessage(sendMessage, userMessage.id); + return; + } + // 立即添加一个AI回复的加载状态消息 const loadingMessage = { id: "loading_" + Math.random().toString(36).substring(2, 15), @@ -929,6 +1019,11 @@ export default { // 添加到消息列表 this.$store.commit("push_AiMsg", userMessage); + if (this.isLiveAgentChat) { + this.sendLiveAgentTextMessage(sendMessage, userMessage.id); + return; + } + // 立即添加一个AI回复的加载状态消息 const loadingMessage = { id: "loading_" + Math.random().toString(36).substring(2, 15), @@ -1147,6 +1242,7 @@ export default { console.log("触发上拉刷新"); if (!this.currentDMid) return; + if (this.isLiveAgentChat) return; // 如果已经没有更多数据或正在切换对话或当前对话为空(新建对话),不再触发上拉刷新 if (this.noMoreData || this.isSwitchingConversation) { @@ -1203,6 +1299,41 @@ export default { }); }, + sendLiveAgentTextMessage(messageText, localId) { + const dialogueManagementId = this.currentDMid || ""; + const receiverId = + (this.vuex_msgUser && + (this.vuex_msgUser.id || + this.vuex_msgUser.Id || + this.vuex_msgUser.receiverId)) || + ""; + + if (!dialogueManagementId || !receiverId) { + this.$refs.uToast.show({ + title: "人工会话未初始化,请重新转人工", + type: "warning", + }); + return; + } + + const payload = { + dialogueManagementId, + receiverId, + message: messageText, + messageType: 0, + filePath: "", + ip: "", + }; + + this.$u.api.SendMessage_PrivateApi(payload).then((res) => { + if (res && res.succeed) return; + this.$refs.uToast.show({ + title: (res && (res.msg || res.message)) || "发送失败", + type: "warning", + }); + }); + }, + // 获取热门问题 hotQARefresh() { console.log("刷新问题"); diff --git a/store/index.js b/store/index.js index ab7363e..20d18b2 100644 --- a/store/index.js +++ b/store/index.js @@ -45,6 +45,8 @@ const store = new Vuex.Store({ vuex_version: "1.0.0", // 智能客服消息组列表 vuex_aiMessageGroups: [], + vuex_aiActiveDMid: "", + vuex_isTransferChat: false, // 当前聊天窗口消息列表(数组 vuex_msgList: [], // 最近联系人/会话列表 @@ -181,6 +183,40 @@ const store = new Vuex.Store({ }, // ===== 智能客服:消息组列表维护(参照 vuex_msgList 维护方式) ===== + set_AiActiveDMid(state, dmid) { + state.vuex_aiActiveDMid = dmid || ""; + }, + set_IsTransferChat(state, val) { + state.vuex_isTransferChat = !!val; + }, + push_AiLiveAgentMsg(state, msg) { + if (!msg) return; + const msgId = msg.id || msg.Id; + if (!msgId) return; + + const exists = (state.vuex_aiMessageGroups || []).some( + (item) => item && item.id === msgId + ); + if (exists) return; + + const userId = + (state.vuex_user && (state.vuex_user.Id || state.vuex_user.id)) || ""; + const senderId = msg.senderId || msg.SenderId || ""; + const isSelf = + userId && senderId && String(userId) === String(senderId); + + state.vuex_aiMessageGroups.push({ + id: msgId, + message: msg.message || msg.Message || "", + sendDate: msg.sendDate || msg.SendDate || "", + isSend: true, + isRead: true, + interactMode: isSelf ? 0 : 8, + messageType: msg.messageType || msg.MessageType || 0, + timeLabel: 0, + displayTime: "", + }); + }, // 覆盖整个列表 push_AiMsgList(state, list) { state.vuex_aiMessageGroups = Array.isArray(list) ? list : []; @@ -291,6 +327,8 @@ const store = new Vuex.Store({ state.vuex_userMsgList = []; state.vuex_msgUser = null; state.vuex_msgScrollTop = 0; + state.vuex_aiActiveDMid = ""; + state.vuex_isTransferChat = false; }, // 统一登出清理:清除 token 与用户数据,并同步到本地存储 // 注意:仅清理与登录态相关的关键字段,避免影响其他持久化数据 @@ -304,6 +342,8 @@ const store = new Vuex.Store({ state.vuex_userMsgList = []; state.vuex_msgUser = null; state.vuex_msgScrollTop = 0; + state.vuex_aiActiveDMid = ""; + state.vuex_isTransferChat = false; // 同步更新本地持久化 saveLifeData("vuex_token", state.vuex_token); saveLifeData("vuex_teacherInfo", state.vuex_teacherInfo); @@ -402,19 +442,25 @@ const store = new Vuex.Store({ }, // 点击聊天记录,切换到该会话 - selectTeacherChatItem({ commit, dispatch }, { id, receiverId }) { + selectTeacherChatItem( + { commit, dispatch }, + { id, receiverId, navigate = true } = {} + ) { + if (!id || !receiverId) return; // 清空消息列表,避免旧消息干扰 commit("push_MsgList", []); - Vue.prototype.$u.api + return Vue.prototype.$u.api .GetReceiverUserInfoApi({ Id: receiverId }) .then((res) => { if (res.succeed && res.data) { commit("set_MsgUser", { ...res.data, dialogueManagementId: id }); - uni.navigateTo({ - url: `/pages/chat/index`, - }); - return; + if (navigate) { + uni.navigateTo({ + url: `/pages/chat/index`, + }); + } + return res.data; } }); },