diff --git a/TUICallKit-Vue3/TUIKit/pages/chat/chat.vue b/TUICallKit-Vue3/TUIKit/pages/chat/chat.vue index 4c06c085..e20499f2 100644 --- a/TUICallKit-Vue3/TUIKit/pages/chat/chat.vue +++ b/TUICallKit-Vue3/TUIKit/pages/chat/chat.vue @@ -499,6 +499,7 @@ const { proxy } = getCurrentInstance() uni.showToast({ title: '播放失败', icon: 'none' }); }); } + let patient_data=ref(); const video =async (userid)=>{ const uid=userid.replace('patient_', '') @@ -513,6 +514,7 @@ const { proxy } = getCurrentInstance() const {userId } = res.data; + patient_data.value=res.data.data const { userSig, SDKAppID } = GenerateTestUserSig.genTestUserSig({ userID:userId, }); @@ -583,19 +585,45 @@ const { proxy } = getCurrentInstance() chat.use(richMediaMessagePlugin); chat.use(groupPlugin); - try { - await chat.login({ userID, userSig }); - } catch (loginErr) { - const code = loginErr?.code ?? loginErr?.error?.code; - const msg = String(loginErr?.message ?? loginErr?.error?.message ?? ''); - const isDuplicateLogin = code === 2025 || msg.includes('重复登录'); - if (isDuplicateLogin) { - // 重复登录:SDK 可能已在内部处理,等待后继续;若仍不可用,后续 updateMyProfile/loadHistoryMessages 会静默失败 - await new Promise(r => setTimeout(r, 300)); - } else { - throw loginErr; + const doLogin = () => chat.login({ userID, userSig }); + const getLoginCode = (err) => { + const c = err?.code ?? err?.error?.code; + if (c != null) return c; + const msg = String(err?.message ?? err?.error?.message ?? ''); + const m = msg.match(/["']?code["']?\s*:\s*(\d+)/); + return m ? parseInt(m[1], 10) : null; + }; + let loginOk = false; + for (let retry = 0; retry <= 2; retry++) { + try { + await doLogin(); + loginOk = true; + break; + } catch (loginErr) { + const code = getLoginCode(loginErr); + const msg = String(loginErr?.message ?? loginErr?.error?.message ?? ''); + const isDuplicateLogin = code === 2025 || msg.includes('重复登录'); + if (isDuplicateLogin && retry < 2) { + // 2025 重复登录:销毁当前实例,等待后重建并重试 + try { + await chat.destroy?.(); + } catch (_) {} + chat = null; + await new Promise(r => setTimeout(r, 600)); + chat = TencentCloudChat.create({ + SDKAppID: Number(SDKAppID), + scene: 'uniapp-wx' + }); + chat.use(conversationPlugin); + chat.use(messageEnhancerPlugin); + chat.use(richMediaMessagePlugin); + chat.use(groupPlugin); + } else { + throw loginErr; + } } } + if (!loginOk) throw new Error('IM 登录失败'); // updateMyProfile 非必需,失败不阻塞聊天(如 2024 用户未登录时可忽略) try { await chat.updateMyProfile({ @@ -615,6 +643,9 @@ const { proxy } = getCurrentInstance() await loadHistoryMessages(); + // 打开会话时通知医生(IM自定义消息 + 后端全局事件) + notifyDoctorOnChatOpen(); + if(options.msg==1){ console.log('msg我进来了',options.msg) await sendConfirmationMessage(); // 进入聊天时自动发送知情确认 @@ -673,6 +704,47 @@ const { proxy } = getCurrentInstance() } }); + /** 打开会话时通知医生:发送IM自定义消息 + 调用后端全局事件 */ + async function notifyDoctorOnChatOpen() { + if (!targetUserID || !targetUserID.startsWith('doctor_')) return; + const doctorId = targetUserID.replace('doctor_', ''); + const userData = uni.getStorageSync('userData') || {}; + const patientName = userData.name || patient_data.value?.patient_name || '患者'; + const patientId = (currentUserID || '').replace('patient_', '') || ''; + try { + // 1. 发送 IM 自定义消息给医生 + const customPayload = { + businessID: 'patient_opened_chat', + patientId, + patientName, + doctorId, + time: Date.now() + }; + const imMsg = chat.createCustomMessage({ + to: targetUserID, + conversationType: TencentCloudChat.TYPES.CONV_C2C, + payload: { data: JSON.stringify(customPayload) } + }); + await chat.sendMessage(imMsg); + } catch (e) { + console.warn('IM 自定义消息发送失败:', e); + } + try { + // 2. 调用后端 API 触发全局事件(供 admin 轮询) + await proxy.apiUrl({ + url: '/api/chat/notifyOpen', + method: 'POST', + data: { + doctor_id: doctorId, + patient_id: patientId, + patient_name: patientName + } + }, false); + } catch (e) { + console.warn('后端通知失败:', e); + } + } + async function loadHistoryMessages() { try { const res = await chat.getMessageList({ conversationID, count: 15 }); @@ -745,10 +817,14 @@ const { proxy } = getCurrentInstance() try { const customData = JSON.parse(msg.payload.data); console.log('自定义消息:', customData); - + // 过滤掉输入状态消息 if (customData.businessID === 'user_typing_status') { console.log('过滤输入状态消息'); + return null; + } + // 过滤掉患者打开会话通知(仅用于医生端提醒,不在聊天列表展示) + if (customData.businessID === 'patient_opened_chat') { return null; // 返回null表示不显示此消息 } diff --git a/TUICallKit-Vue3/pages/order/monad/monad.vue b/TUICallKit-Vue3/pages/order/monad/monad.vue index 110ed1fc..64541033 100644 --- a/TUICallKit-Vue3/pages/order/monad/monad.vue +++ b/TUICallKit-Vue3/pages/order/monad/monad.vue @@ -824,31 +824,24 @@ const confirmDiagnosis = async (diagnosisId) => { - uni.showLoading({ title: '提交中...' }); + const app = getApp(); - const rawUserID = app?.globalData?.userID; - const userID = (typeof rawUserID === 'string' ? rawUserID : '').replace(/patient_/g, ''); - if (!userID) { - uni.hideLoading(); - uni.showToast({ title: '请先登录', icon: 'none' }); - return; - } + try { const res = await proxy.apiUrl({ url: '/api/tcm/confirmDiagnosis', method: 'POST', data: { id: diagnosisId, - user_id: userID || 0, share_user: share_user_id || 0 } }, false); if (res.code === 1) { - dingdan_ok.value=res.data.is_view?true:false + uni.setStorageSync('dingdan_ok', true); - dingdan_ok.value=true; + } else { uni.showToast({ title: res.msg || '确认失败', icon: 'none' }); diff --git a/admin/src/App.vue b/admin/src/App.vue index 3747bc29..2355165c 100644 --- a/admin/src/App.vue +++ b/admin/src/App.vue @@ -2,13 +2,15 @@ import { useDark, useThrottleFn, useWindowSize } from '@vueuse/core' import zhCn from 'element-plus/es/locale/lang/zh-cn' - import { ScreenEnum } from './enums/appEnums' import useAppStore from './stores/modules/app' import useSettingStore from './stores/modules/setting' +import useUserStore from './stores/modules/user' +import ChatNotifyToast from './components/chat-notify-toast/index.vue' const appStore = useAppStore() const settingStore = useSettingStore() +const userStore = useUserStore() const elConfig = { zIndex: 2000, locale: zhCn @@ -43,7 +45,7 @@ watch( diff --git a/admin/src/api/chat.ts b/admin/src/api/chat.ts new file mode 100644 index 00000000..cc7ff659 --- /dev/null +++ b/admin/src/api/chat.ts @@ -0,0 +1,6 @@ +import request from '@/utils/request' + +/** 获取患者打开会话通知(轮询接口,获取后即消费) */ +export function getChatNotifications() { + return request.get({ url: '/chat/notifications' }) +} diff --git a/admin/src/components/chat-notify-toast/index.vue b/admin/src/components/chat-notify-toast/index.vue new file mode 100644 index 00000000..9d4b69b5 --- /dev/null +++ b/admin/src/components/chat-notify-toast/index.vue @@ -0,0 +1,266 @@ + + + + + diff --git a/admin/src/components/tcm-prescription/index.vue b/admin/src/components/tcm-prescription/index.vue index a482a548..9709bd8b 100644 --- a/admin/src/components/tcm-prescription/index.vue +++ b/admin/src/components/tcm-prescription/index.vue @@ -109,19 +109,25 @@
- - -
- - - - -
+ + + - - - + + + + + + + + + + + + + + @@ -213,7 +219,7 @@