From 823341183172c1be5cdd3db8a3d791fd8ced455f Mon Sep 17 00:00:00 2001 From: wangdongbo <949818794@qq.com> Date: Thu, 29 Jan 2026 11:42:56 +0800 Subject: [PATCH] no message --- App.vue | 260 +++++++++++++++++++--------------- pages/login/redirect-page.vue | 52 +++---- pages/message/message.vue | 211 +++++++++++++++++++-------- static/icon/zhaopian.png | Bin 0 -> 1350 bytes store/account.js | 32 ++++- utils/api.js | 3 +- utils/conversation-merger.js | 199 ++++++++++++++++++++++++++ utils/tim-chat.js | 27 ++++ 8 files changed, 588 insertions(+), 196 deletions(-) create mode 100644 static/icon/zhaopian.png create mode 100644 utils/conversation-merger.js diff --git a/App.vue b/App.vue index 690d70b..250a8af 100644 --- a/App.vue +++ b/App.vue @@ -1,356 +1,392 @@ diff --git a/pages/login/redirect-page.vue b/pages/login/redirect-page.vue index 6a0da11..98d2a73 100644 --- a/pages/login/redirect-page.vue +++ b/pages/login/redirect-page.vue @@ -4,53 +4,55 @@ diff --git a/static/icon/zhaopian.png b/static/icon/zhaopian.png new file mode 100644 index 0000000000000000000000000000000000000000..6f55dffa75afce2d9a8be931282cf5b509a96344 GIT binary patch literal 1350 zcmeAS@N?(olHy`uVBq!ia0vp^86eET1|(%=wk`xxoCO|{#S9E$svykh8Km+7D9BhG zks&&vP#|?7qa}y`b## zCXKrx$%=VV!RZT1f(3SI=-&O3{B&Jf;EQu9!Cb9U;c21ATbq8kvN--ZaB+n+v)f{B z!BUYPjVw12pF2m2@9nYnJNC0e`0<_pDgW$uRO-K1Zux%N^820YbhSpEiLI@zQ}V85 z-xkT={n*Xr)2*Vy-D`BESscTrIb1k#RO=|qpM|-r1Kbur*^;`ocg32ROS6SIyN>2e zZj$))Ek$MFid{`1eXeIV7$i-bJx4?(H7hnGc*3chdOfqs&#v&2T2;1b(E^L5ve%{w zUE*1`Woc;0zCSLej??RVe2Y1RDlr|;a^v*5$eq^8@q8 z&g)+lCV#%ajyWL7>*cGO__n(L2Un}C*UbC#q%y+tSohEAfhE6>tX`09Uet8%OtbqV z;rF!xn*aZ8brtyWdH%*n`&=jbM;vW*c6{KlbcdIZkKgGdN3{cwZeLcul_xJkojKcn zav5XPg(qK1Ef-Clm+W@jD=hTv+4HB5HI`oDb6CD(v-ompt9?R^VkQ3{z7{G!eSUWL z?`iLDv1T|fRsOMYuY1?;>?-?7AR`{MZRM4X?{pC7Y>wh6DJ{zqHDAkmXtBm3AHTI) z=eW8*28%DM+_L`btuTGH2d8r;Pu#L*Ugd`!7UgNhHILQxPfY$(Ja_89ps1;$#sOOo zDHgxDA$?q3ZT*S+*59|}Br?i7y6xSP%3r@+Oz^78j?ee=WHNR->eaB!oRd+oO7pbb zmZfWjpR0+-PPVLAlh?AN_28%Ki;ZGWo=ho@k^dASV`z3c!I;N$WA*m``}d~?$*ozC zRU~8n{+!F|jb`E3HPo&I-P8Rm`NDx~mHP3IW~(=@i+A#`ENGf^DpF79>07@G?LV2k z0yaOjSoi8u^;)e9>H$qP`y38GxlzHX`?Sz?YtLg57pX?{c;3VHD(b4{60_W zE^IU}`U#Ax`TM+`-7jkWz56D%%M&{T+Mz{{HN-2-M1{F z%c~w~NC`fgEvSAx!bE(zx?AhYbL!St9G87@2zaUGTmG$)Ra5b7ir3Scz`6~$a=v{( z#F%4zGRr&MDll-uSNDy+%XXLdFWp)a!@S4w>{o@onXB{aCOf4%P7@b460v{0ie(bR z+sRIMd9r`Io+wI~nYknC%9KeS6BfH%QOjL8!L492<6_Sv7E(eXU8lS}7hZZjy-6oq q=84#e<7c^aqt9*R($)5UAo^+ZwpOLH=QV-F9)qW=pUXO@geCy|2xP|q literal 0 HcmV?d00001 diff --git a/store/account.js b/store/account.js index b58b09a..55b4326 100644 --- a/store/account.js +++ b/store/account.js @@ -32,6 +32,11 @@ export default defineStore("accountStore", () => { if (res.success && res.data && res.data.mobile) { account.value = res.data; openid.value = res.data.openid; + + // 保存账户信息和 openId 到本地存储 + uni.setStorageSync('account', res.data); + uni.setStorageSync('openid', res.data.openid); + initIMAfterLogin(openid.value) return res.data } @@ -48,8 +53,29 @@ export default defineStore("accountStore", () => { return true; } try { - await initGlobalTIM(); + // 使用 openid 作为 userID 初始化 IM + const userID = openid.value || uni.getStorageSync('openid'); + if (!userID) { + console.error('无法获取 openid,IM 初始化失败'); + return false; + } + + console.log('开始初始化 IM,userID:', userID); + const success = await initGlobalTIM(userID); + + if (!success) { + console.error('initGlobalTIM 返回失败'); + return false; + } + + // 验证 TIM 实例是否真正创建成功 + if (!globalTimChatManager || !globalTimChatManager.tim) { + console.error('IM 初始化后 TIM 实例不存在'); + return false; + } + isIMInitialized.value = true; + console.log('IM 初始化成功'); return true; } catch (error) { console.error('IM初始化失败:', error); @@ -73,6 +99,10 @@ export default defineStore("accountStore", () => { account.value = null; openid.value = ""; isIMInitialized.value = false; + + // 清除本地存储 + uni.removeStorageSync('account'); + uni.removeStorageSync('openid'); } return { account, login, initIMAfterLogin, logout, openid, isIMInitialized } diff --git a/utils/api.js b/utils/api.js index 332109c..c1d3c99 100644 --- a/utils/api.js +++ b/utils/api.js @@ -57,7 +57,8 @@ const urlsConfig = { endConsultation: "endConsultation", getGroupListByGroupId: "getGroupListByGroupId", createConsultGroup: "createConsultGroup", - cancelConsultApplication: "cancelConsultApplication" + cancelConsultApplication: "cancelConsultApplication", + getGroupList: "getGroupList" } } const urls = Object.keys(urlsConfig).reduce((acc, path) => { diff --git a/utils/conversation-merger.js b/utils/conversation-merger.js new file mode 100644 index 0000000..ed3cb8f --- /dev/null +++ b/utils/conversation-merger.js @@ -0,0 +1,199 @@ +/** + * 会话列表与群组详细信息合并工具 + * + * 功能: + * 1. 从 conversationList 中提取所有 groupID + * 2. 调用后端 getGroupList 接口获取群组详细信息 + * 3. 合并会话数据和患者信息 + * 4. 过滤掉后端不存在的会话 + */ + +import api from "@/utils/api.js" + +/** + * 合并会话列表和群组详细信息 + * + * @param {Array} conversationList - 前端会话列表 + * @param {Object} options - 可选参数 + * @param {string} options.corpId - 企业ID(可选) + * @param {string} options.teamId - 团队ID(可选) + * @param {string} options.keyword - 搜索关键词(可选) + * @returns {Promise} 合并后的会话列表 + */ +export async function mergeConversationWithGroupDetails(conversationList, options = {}) { + try { + // 1. 参数校验 + if (!Array.isArray(conversationList) || conversationList.length === 0) { + console.log('会话列表为空,无需合并') + return [] + } + + // 2. 提取所有 groupID + const groupIds = conversationList + .map(conv => conv.groupID) + .filter(id => id) // 过滤掉空值 + + if (groupIds.length === 0) { + console.log('没有有效的 groupID,无需合并') + return [] + } + + console.log('提取到的 groupID 列表:', groupIds) + + // 3. 调用后端接口获取群组详细信息 + const requestData = { + groupIds, + ...options // 支持传入额外的查询参数(corpId, teamId, keyword等) + } + + const response = await api('getGroupList', requestData) + + // 4. 检查响应 + if (!response || !response.success) { + console.error('获取群组详细信息失败:', response?.message || '未知错误') + return [] + } + + const groupDetailsMap = createGroupDetailsMap(response.data?.list || []) + console.log('获取到的群组详细信息数量:', Object.keys(groupDetailsMap).size) + + // 5. 合并数据并过滤 + const mergedList = conversationList + .map(conversation => mergeConversationData(conversation, groupDetailsMap)) + .filter(item => item !== null) // 过滤掉后端不存在的会话 + + console.log('合并后的会话列表数量:', mergedList.length) + console.log('过滤掉的会话数量:', conversationList.length - mergedList.length) + + // 6. 格式化并排序会话列表 + const formattedList = mergedList.sort((a, b) => b.lastMessageTime - a.lastMessageTime) + + return formattedList + + } catch (error) { + console.error('合并会话列表失败:', error) + // 发生错误时返回空数组,避免影响页面渲染 + return [] + } +} + +/** + * 创建群组详细信息映射表 + * + * @param {Array} groupDetailsList - 后端返回的群组详细信息列表 + * @returns {Map} groupID -> 群组详细信息的映射 + */ +function createGroupDetailsMap(groupDetailsList) { + const map = new Map() + + groupDetailsList.forEach(groupDetail => { + if (groupDetail.groupId) { + map.set(groupDetail.groupId, groupDetail) + } + }) + + return map +} + +/** + * 合并单个会话数据 + * + * @param {Object} conversation - 前端会话数据 + * @param {Map} groupDetailsMap - 群组详细信息映射表 + * @returns {Object|null} 合并后的会话数据,如果后端不存在则返回 null + */ +function mergeConversationData(conversation, groupDetailsMap) { + const groupDetail = groupDetailsMap.get(conversation.groupID) + // 如果后端没有该群组信息,返回 null(会被过滤掉) + if (!groupDetail) { + console.log(`会话 ${conversation.groupID} 在后端不存在,已过滤`) + return null + } + + // 合并数据 + return { + // 保留原有的会话信息 + ...conversation, + + // 合并后端的群组信息 + _id: groupDetail._id, + corpId: groupDetail.corpId, + teamId: groupDetail.teamId, + customerId: groupDetail.customerId, + doctorId: groupDetail.doctorId, + patientId: groupDetail.patientId, + orderStatus: groupDetail.orderStatus, + + // 合并患者信息(优先使用后端数据) + patientName: groupDetail.patientName || conversation.patientName, + patientSex: groupDetail.patient?.sex, + patientAge: groupDetail.patient?.age, + patientMobile: groupDetail.patient?.mobile, + patientAvatar: groupDetail.patient?.avatar || conversation.avatar, + + // 合并团队信息 + teamName: groupDetail.team?.name, + teamMemberList: groupDetail.team?.memberList, + teamDescription: groupDetail.team?.description, + + // 时间信息 + createdAt: groupDetail.createdAt, + updatedAt: groupDetail.updatedAt, + + // 更新显示名称(使用后端的患者信息) + name: formatConversationName(groupDetail), + + // 更新头像 + avatar: groupDetail.patient?.avatar || conversation.avatar || '/static/default-avatar.png' + } +} + +/** + * 格式化会话显示名称 + * + * @param {Object} groupDetail - 群组详细信息 + * @returns {string} 格式化后的名称 + */ +function formatConversationName(groupDetail) { + const patientName = groupDetail.patientName || '患者' + const sex = groupDetail.patient?.sex === 1 ? '男' : groupDetail.patient?.sex === 2 ? '女' : '' + const age = groupDetail.patient?.age ? `${groupDetail.patient.age}岁` : '' + + // 拼接名称:患者名 性别 年龄 + const nameParts = [patientName, sex, age].filter(part => part) + const displayName = nameParts.join(' ') + + return `${displayName}的问诊` +} + +/** + * 批量合并会话列表(支持分页) + * + * @param {Array} conversationList - 前端会话列表 + * @param {Object} options - 可选参数 + * @param {number} options.batchSize - 每批处理的数量(默认50) + * @returns {Promise} 合并后的会话列表 + */ +export async function mergeConversationWithGroupDetailsBatch(conversationList, options = {}) { + const { batchSize = 50, ...otherOptions } = options + + if (!Array.isArray(conversationList) || conversationList.length === 0) { + return [] + } + + // 分批处理 + const batches = [] + for (let i = 0; i < conversationList.length; i += batchSize) { + batches.push(conversationList.slice(i, i + batchSize)) + } + + console.log(`会话列表分为 ${batches.length} 批处理,每批 ${batchSize} 条`) + + // 并发处理所有批次 + const results = await Promise.all( + batches.map(batch => mergeConversationWithGroupDetails(batch, otherOptions)) + ) + + // 合并所有结果 + return results.flat() +} diff --git a/utils/tim-chat.js b/utils/tim-chat.js index dd60bd9..c741ed8 100644 --- a/utils/tim-chat.js +++ b/utils/tim-chat.js @@ -149,6 +149,7 @@ class TimChatManager { this.isInitializing = true console.log('=== 开始初始化IM ===') + console.log('传入的 userID:', userID) try { // 重置重连次数,允许重新登录 @@ -170,9 +171,12 @@ class TimChatManager { console.log('创建TIM实例,SDKAppID:', TIM_CONFIG.SDKAppID) this.tim = TIM.create({ SDKAppID: TIM_CONFIG.SDKAppID }) + console.log('TIM实例创建成功:', !!this.tim) // 等待TIM实例初始化完成 + console.log('等待TIM实例就绪...') await this.waitForTIMInstanceReady() + console.log('TIM实例已就绪') // 注册上传插件 this.tim.registerPlugin({ "tim-upload-plugin": TIMUploadPlugin }) @@ -180,6 +184,7 @@ class TimChatManager { // 注册事件监听器 this.registerEventListeners() + console.log('事件监听器已注册') // 设置日志级别 if (typeof this.tim.setLogLevel === 'function') { @@ -187,21 +192,29 @@ class TimChatManager { } // 获取用户信息并登录 + console.log('开始获取用户信息并登录...') await this.getUserInfoAndLogin(userID) + console.log('用户信息获取并登录成功') // 等待SDK Ready console.log('等待SDK Ready...') await this.waitForSDKReady(IM_CONNECTION_CONFIG.SDK_READY_TIMEOUT) console.log('=== IM初始化完成 ===') + console.log('最终状态 - this.tim 存在:', !!this.tim) + console.log('最终状态 - this.isLoggedIn:', this.isLoggedIn) return true } catch (error) { console.error('=== IM初始化失败 ===', error) + console.error('错误详情:', error.message || error) + console.error('错误堆栈:', error.stack) this.triggerCallback('onError', `初始化失败: ${error.message || error}`) // 初始化失败时清理资源 + console.log('初始化失败,开始清理资源...') await this.cleanupOldInstance() + console.log('资源清理完成,this.tim 已设为 null') return false } finally { this.isInitializing = false @@ -365,20 +378,34 @@ class TimChatManager { async getUserInfoAndLogin(userID) { try { + console.log('getUserInfoAndLogin 开始,传入 userID:', userID) + if (userID) { this.currentUserID = userID uni.setStorageSync('userInfo', { userID }) + console.log('使用传入的 userID:', userID) } else { + console.log('未传入 userID,尝试从本地存储获取') const userInfo = uni.getStorageSync('userInfo') + console.log('本地存储的 userInfo:', userInfo) + if (!userInfo?.userID) { throw new Error('未找到用户信息,请先登录') } this.currentUserID = userInfo.userID + console.log('从本地存储获取到 userID:', this.currentUserID) } + + console.log('开始获取 userSig...') this.currentUserSig = await this.getUserSig(this.currentUserID) + console.log('userSig 获取成功') + + console.log('开始登录 TIM...') await this.loginTIM() + console.log('TIM 登录成功') } catch (error) { console.error('获取用户信息失败:', error) + console.error('错误详情:', error.message || error) this.triggerCallback('onError', `登录失败: ${error.message || error}`) throw error // 重新抛出错误,让调用者知道登录失败 }