From 26bd604e45e89fa38cbc561f54d9625a57fbb7cd Mon Sep 17 00:00:00 2001 From: wangdongbo <949818794@qq.com> Date: Wed, 4 Feb 2026 17:09:20 +0800 Subject: [PATCH] no message --- .../archive-detail/follow-up-manage-tab.vue | 56 +-- pages/message/article-list.vue | 77 +--- pages/message/components/chat-input.vue | 25 +- pages/message/index.vue | 38 ++ pages/message/survey-list.vue | 58 +-- utils/send-message-helper.js | 330 ++++++++++++++++++ 6 files changed, 454 insertions(+), 130 deletions(-) create mode 100644 utils/send-message-helper.js diff --git a/pages/case/components/archive-detail/follow-up-manage-tab.vue b/pages/case/components/archive-detail/follow-up-manage-tab.vue index e041c9d..9d1ba92 100644 --- a/pages/case/components/archive-detail/follow-up-manage-tab.vue +++ b/pages/case/components/archive-detail/follow-up-manage-tab.vue @@ -64,7 +64,7 @@ {{ i.taskContent || "暂无内容" }} - + {{ i.sendContent }} @@ -204,6 +204,7 @@ import dayjs from "dayjs"; import api from "@/utils/api"; import useAccountStore from "@/store/account"; import { toast } from "@/utils/widget"; +import { handleFollowUpMessages } from "@/utils/send-message-helper"; import { getTodoEventTypeLabel, getTodoEventTypeOptions, @@ -221,6 +222,15 @@ const accountStore = useAccountStore(); const { account, doctorInfo } = storeToRefs(accountStore); const { getDoctorInfo } = accountStore; +function getUserId() { + return doctorInfo.value?.userid || ""; +} + +function getCorpId() { + const team = uni.getStorageSync("ykt_case_current_team") || {}; + return team.corpId || doctorInfo.value?.corpId || ""; +} + const statusTabs = [ { label: "全部", value: "all" }, { label: "待处理", value: "processing" }, @@ -275,23 +285,6 @@ const typeSelectedMap = computed(() => { }, {}); }); -function getUserId() { - const d = doctorInfo.value || {}; - const a = account.value || {}; - return ( - String( - d.userid || d.userId || d.corpUserId || a.userid || a.userId || "" - ) || "" - ); -} - -function getCorpId() { - const t = uni.getStorageSync("ykt_case_current_team") || {}; - const a = account.value || {}; - const d = doctorInfo.value || {}; - return String(t.corpId || a.corpId || d.corpId || "") || ""; -} - async function ensureDoctor() { if (doctorInfo.value) return; if (!account.value?.openid) return; @@ -537,9 +530,12 @@ async function sendFollowUp(todo) { messages.push({ type: "article", content: { + _id: file.file?._id || file._id, title: file.file?.name || file.name || "宣教文章", url: file.file?.url || file.URL, - desc: file.file?.subtitle || "", + subtitle: file.file?.subtitle || "", + cover: file.file?.cover || "", + articleId: file.file?._id || file._id, }, }); } else if (file.type === "questionnaire" && file.file?.surveryId) { @@ -547,7 +543,8 @@ async function sendFollowUp(todo) { messages.push({ type: "questionnaire", content: { - title: file.file?.name || file.name || "问卷", + _id: file.file?._id || file._id, + name: file.file?.name || file.name || "问卷", surveryId: file.file?.surveryId || file.surveryId, url: file.file?.url || file.URL, }, @@ -556,15 +553,19 @@ async function sendFollowUp(todo) { } } - // 触发事件,通知父组件发送消息 - uni.$emit("send-followup-message", { - messages, - followupId: todo._id, - followupData: todo, + // 调用统一的消息发送处理函数 + const success = await handleFollowUpMessages(messages, { + userId: getUserId(), + customerId: props.archiveId, + customerName: props.data?.name || "", + corpId: getCorpId(), + env: __VITE_ENV__, }); - toast("消息已发送"); - uni.navigateBack(); + if (success) { + toast("消息已发送"); + uni.navigateBack(); + } } // ---- filter popup ---- @@ -863,6 +864,7 @@ watch( text-overflow: ellipsis; white-space: nowrap; flex: 1; + width: 330rpx; } .file-type-image .file-name { color: #0877f1; diff --git a/pages/message/article-list.vue b/pages/message/article-list.vue index 9f7576a..4c68713 100644 --- a/pages/message/article-list.vue +++ b/pages/message/article-list.vue @@ -111,6 +111,7 @@ import { ref, onMounted } from "vue"; import { onLoad } from "@dcloudio/uni-app"; import api from "@/utils/api.js"; import useAccountStore from "@/store/account.js"; +import { sendArticleMessage } from "@/utils/send-message-helper.js"; import EmptyData from "@/components/empty-data.vue"; const accountStore = useAccountStore(); @@ -316,69 +317,25 @@ const sendArticle = async (article) => { try { const { doctorInfo } = useAccountStore(); - // 1. 调用后端API记录发送 - const result = await api("sendArticleMessage", { - groupId: pageParams.value.groupId, - fromAccount: doctorInfo.userid, - articleId: article._id, - title: article.title || "宣教文章", - imgUrl: article.cover || "", - desc: "点击查看详情", - }); - - if (result.success) { - // 2. 通过IM系统发送自定义消息到聊天列表 - try { - // 获取全局IM管理器 - const { globalTimChatManager } = await import("@/utils/tim-chat.js"); - - if (globalTimChatManager && globalTimChatManager.tim && globalTimChatManager.isLoggedIn) { - // 重要:设置当前会话ID,确保消息发送到正确的群聊 - const conversationID = `GROUP${pageParams.value.groupId}`; - globalTimChatManager.currentConversationID = conversationID; - - console.log("设置当前会话ID:", conversationID); - - // 构建自定义消息数据 - const customMessageData = { - messageType: "article", - title: article.title || "宣教文章", - articleId: article._id, - cover: article.cover || "", - desc: "点击查看详情", - content: article.title || "宣教文章" - }; - - // 发送自定义消息 - const sendResult = await globalTimChatManager.sendCustomMessage(customMessageData); - if (sendResult && sendResult.success) { - console.log("✓ 文章消息已通过IM系统发送"); - } else { - console.warn("⚠️ 文章消息发送失败:", sendResult?.error); - } - } else { - console.warn("⚠️ IM系统未就绪,消息可能不会显示在聊天列表"); - } - } catch (imError) { - console.error("通过IM系统发送消息失败:", imError); - // IM发送失败不影响后端记录,继续返回 - } - - // 3. 记录文章发送记录 - try { - await api("addArticleSendRecord", { - articleId: article._id, - userId: doctorInfo.userid, - customerId: pageParams.value.patientId, - corpId: corpId, - }); - } catch (recordError) { - console.error("记录文章发送失败:", recordError); + // 使用统一的消息发送助手 + const success = await sendArticleMessage( + { + _id: article._id, + title: article.title || "宣教文章", + cover: article.cover || "", + url: article.url || "", + subtitle: article.subtitle || "", + }, + { + articleId: article._id, + userId: doctorInfo?.userid, + customerId: pageParams.value.patientId, + corpId: corpId, } + ); + if (success) { uni.navigateBack(); - } else { - throw new Error(result.message || "发送失败"); } } catch (error) { console.error("发送文章失败:", error); diff --git a/pages/message/components/chat-input.vue b/pages/message/components/chat-input.vue index 391e1bf..f8f7ac0 100644 --- a/pages/message/components/chat-input.vue +++ b/pages/message/components/chat-input.vue @@ -81,7 +81,7 @@ const props = defineProps({ }); // Emits -const emit = defineEmits(["messageSent", "scrollToBottom", "endConsult"]); +const emit = defineEmits(["messageSent", "scrollToBottom", "endConsult", "openConsult"]); // 输入相关状态 const inputText = ref(""); @@ -404,6 +404,24 @@ const handleEndConsult = () => { }); }; +// 开启会话 +const handleOpenConsult = () => { + uni.showModal({ + title: "确认开启会话", + content: "确定要重新开启本次会话吗?", + confirmText: "确定开启", + cancelText: "取消", + success: (res) => { + if (res.confirm) { + // 关闭功能面板 + showMorePanel.value = false; + // 触发父组件的开启会话事件 + emit("openConsult"); + } + }, + }); +}; + const morePanelButtons = [ { text: "照片", icon: "/static/icon/zhaopian.png", action: showImagePicker }, { @@ -431,6 +449,11 @@ const morePanelButtons = [ icon: "/static/icon/jieshuzixun.png", action: handleEndConsult, }, + { + text: "开启会话", + icon: "/static/icon/kaiqihuihua.png", + action: handleOpenConsult, + }, ]; function handleInputFocus() { diff --git a/pages/message/index.vue b/pages/message/index.vue index a4061d8..f40deff 100644 --- a/pages/message/index.vue +++ b/pages/message/index.vue @@ -166,6 +166,7 @@ @scrollToBottom="() => scrollToBottom(true)" @messageSent="() => scrollToBottom(true)" @endConsult="handleEndConsult" + @openConsult="handleOpenConsult" /> @@ -962,6 +963,43 @@ const handleEndConsult = async () => { } }; +// 处理开启会话 +const handleOpenConsult = async () => { + try { + uni.showLoading({ + title: "处理中...", + }); + // 调用开启会话接口 + const result = await api("openConsultation", { + groupId: groupId.value, + adminAccount: account.value?.userId || "", + extraData: { + openedBy: account.value?.userId || "", + openedByName: account.value?.name || "医生", + openReason: "重新开启会话", + }, + }); + uni.hideLoading(); + if (result.success) { + // 重新获取订单状态 + await fetchGroupOrderStatus(); + uni.showToast({ + title: "会话已开启", + icon: "success", + }); + } else { + throw new Error(result.message || "操作失败"); + } + } catch (error) { + console.error("开启会话失败:", error); + uni.hideLoading(); + uni.showToast({ + title: error.message || "操作失败", + icon: "none", + }); + } +}; + // 页面卸载 onUnmounted(() => { clearMessageCache(); diff --git a/pages/message/survey-list.vue b/pages/message/survey-list.vue index c681815..03b6ce3 100644 --- a/pages/message/survey-list.vue +++ b/pages/message/survey-list.vue @@ -90,6 +90,7 @@ import { ref, onMounted } from "vue"; import api from "@/utils/api.js"; import useAccountStore from "@/store/account.js"; import { globalTimChatManager } from "@/utils/tim-chat.js"; +import { sendSurveyMessage } from "@/utils/send-message-helper.js"; import EmptyData from "@/components/empty-data.vue"; import { onLoad } from "@dcloudio/uni-app"; const env = __VITE_ENV__; @@ -315,48 +316,23 @@ const sendSurvey = async (survey) => { const doctorInfo = accountStore.doctorInfo; userId.value = doctorInfo?.userid; - // 生成发送ID - const sendSurveyId = generateRandomString(10); - - // 创建问卷记录 - const createRecordRes = await api("createSurveyRecord", { - corpId: corpId, - userId: userId.value, - surveryId: survey._id, - memberId: customerId.value, - customer: customerName.value, - sendSurveyId: sendSurveyId, - }); - - if (!createRecordRes.success) { - uni.showToast({ - title: createRecordRes.message || "创建问卷记录失败", - icon: "none", - }); - loading.value = false; - return; - } - - const answerId = createRecordRes?.id || ""; - - // 生成发送链接 - const sendLink = generateSendLink( - survey, - answerId, - customerId.value, - customerName.value, - sendSurveyId + // 使用统一的消息发送助手 + const success = await sendSurveyMessage( + { + _id: survey._id, + name: survey.name || "问卷", + surveryId: survey.surveryId || survey._id, + url: survey.url || "", + }, + { + userId: userId.value, + customerId: customerId.value, + customerName: customerName.value, + corpId: corpId, + } ); - // 构建自定义消息 - const customMessage = buildSurveyMessage(survey, sendLink); - - // 发送自定义消息到IM - const result = await timChatManager.sendCustomMessage( - customMessage, - "SURVEY" - ); - if (result.success) { + if (success) { uni.showToast({ title: "发送成功", icon: "success", @@ -365,8 +341,6 @@ const sendSurvey = async (survey) => { setTimeout(() => { uni.navigateBack(); }, 500); - } else { - throw new Error(result.error || "发送失败"); } } catch (error) { console.error("发送问卷失败:", error); diff --git a/utils/send-message-helper.js b/utils/send-message-helper.js new file mode 100644 index 0000000..05f477a --- /dev/null +++ b/utils/send-message-helper.js @@ -0,0 +1,330 @@ +/** + * 消息发送助手 - 统一处理不同类型消息的发送 + */ + +import { globalTimChatManager } from './tim-chat.js'; +import api from './api.js'; +import { toast } from './widget.js'; + +/** + * 发送文字消息 + * @param {string} content - 文字内容 + * @returns {Promise} 发送是否成功 + */ +export async function sendTextMessage(content) { + if (!content || !content.trim()) { + toast('文字内容不能为空'); + return false; + } + + try { + if (!globalTimChatManager?.isLoggedIn) { + toast('IM系统未就绪,请稍后重试'); + return false; + } + + // 直接调用 tim-chat 的 sendTextMessage 方法 + const result = await globalTimChatManager.sendTextMessage(content.trim()); + + if (result?.success) { + return true; + } else { + toast(result?.error || '发送文字消息失败'); + return false; + } + } catch (error) { + console.error('发送文字消息异常:', error); + toast('发送文字消息失败'); + return false; + } +} + +/** + * 发送图片消息 + * @param {string} imageUrl - 图片URL(字符串) + * @param {string} imageName - 图片名称(可选) + * @returns {Promise} 发送是否成功 + */ +export async function sendImageMessage(imageUrl, imageName = '图片') { + if (!imageUrl) { + toast('图片URL不能为空'); + return false; + } + + try { + if (!globalTimChatManager?.isLoggedIn) { + toast('IM系统未就绪,请稍后重试'); + return false; + } + + // 直接调用 tim-chat 的 sendImageMessage 方法 + // tim-chat.js 中的 getImageUrl 方法可以处理 URL 字符串 + const result = await globalTimChatManager.sendImageMessage(imageUrl); + + if (result?.success) { + return true; + } else { + toast(result?.error || '发送图片消息失败'); + return false; + } + } catch (error) { + console.error('发送图片消息异常:', error); + toast('发送图片消息失败'); + return false; + } +} + +/** + * 发送宣教文章消息 + * @param {Object} article - 文章对象 { _id, title, cover, url } + * @param {Object} options - 额外选项 { groupId, patientId, corpId } + * @returns {Promise} 发送是否成功 + */ +export async function sendArticleMessage(article, options = {}) { + if (!article || !article._id) { + toast('文章信息不完整'); + return false; + } + + try { + if (!globalTimChatManager?.isLoggedIn) { + toast('IM系统未就绪,请稍后重试'); + return false; + } + + // 构建自定义消息 - 直接传递对象,sendCustomMessage会处理JSON序列化 + const customMessageData = { + type: 'article', + title: article.title || '宣教文章', + articleId: article._id, + cover: article.cover || '', + desc: article.subtitle || '点击查看详情', + url: article.url || '', + messageType: 'article', + }; + + // 发送自定义消息 + const result = await globalTimChatManager.sendCustomMessage(customMessageData); + + if (result?.success) { + // 记录文章发送记录(异步,不阻塞) + if (options.articleId && options.userId && options.customerId && options.corpId) { + api('addArticleSendRecord', { + articleId: options.articleId, + userId: options.userId, + customerId: options.customerId, + corpId: options.corpId, + }).catch((err) => { + console.error('记录文章发送失败:', err); + }); + } + return true; + } else { + toast(result?.error || '发送文章消息失败'); + return false; + } + } catch (error) { + console.error('发送文章消息异常:', error); + toast('发送文章消息失败'); + return false; + } +} + +/** + * 发送问卷消息 + * @param {Object} survey - 问卷对象 { _id, name, surveryId, url, createBy } + * @param {Object} options - 额外选项 { userId, customerId, customerName, corpId, env } + * @returns {Promise} 发送是否成功 + */ +export async function sendSurveyMessage(survey, options = {}) { + if (!survey || !survey._id) { + toast('问卷信息不完整'); + return false; + } + + try { + if (!globalTimChatManager?.isLoggedIn) { + toast('IM系统未就绪,请稍后重试'); + return false; + } + + // 生成发送ID + const sendSurveyId = generateRandomString(10); + + // 创建问卷记录 + const createRecordRes = await api('createSurveyRecord', { + corpId: options.corpId, + userId: options.userId, + surveryId: survey._id, + memberId: options.customerId, + customer: options.customerName, + sendSurveyId: sendSurveyId, + }); + + if (!createRecordRes?.success) { + toast(createRecordRes?.message || '创建问卷记录失败'); + return false; + } + + const answerId = createRecordRes?.id || ''; + + // 生成问卷链接 + const surveyLink = generateSendLink( + survey, + answerId, + options.customerId, + options.customerName, + sendSurveyId, + { + corpId: options.corpId, + userId: options.userId, + env: options.env, + } + ); + + // 构建自定义消息 + const customMessageData = buildSurveyMessage(survey, surveyLink); + + // 发送自定义消息 - 直接传递消息对象,不再进行额外序列化 + const result = await globalTimChatManager.sendCustomMessage(customMessageData); + + if (result?.success) { + return true; + } else { + toast(result?.error || '发送问卷消息失败'); + return false; + } + } catch (error) { + console.error('发送问卷消息异常:', error); + toast('发送问卷消息失败'); + return false; + } +} + +/** + * 生成随机字符串 + * @param {number} length - 字符串长度 + * @returns {string} 随机字符串 + */ +function generateRandomString(length) { + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let result = ''; + for (let i = 0; i < length; i++) { + result += chars.charAt(Math.floor(Math.random() * chars.length)); + } + return result; +} + +/** + * 生成问卷发送链接 + * @param {Object} survey - 问卷对象 + * @param {string} answerId - 答卷ID + * @param {string} customerId - 客户ID + * @param {string} customerName - 客户名称 + * @param {string} sendSurveyId - 发送问卷ID + * @param {Object} context - 上下文信息 { corpId, userId, env } + * @returns {string} 问卷链接 + */ +function generateSendLink(survey, answerId, customerId, customerName, sendSurveyId, context = {}) { + const { corpId, userId, env } = context; + const isSystem = survey.createBy === 'system'; + let url = ''; + + if (isSystem) { + // 系统问卷:使用 VITE_SURVEY_URL + url = `${env?.MP_SURVEY_URL}?corpId=${corpId}&surveryId=${survey.surveryId}&memberId=${customerId}&sendSurveyId=${sendSurveyId}&userId=${userId}`; + } else { + // 自定义问卷:使用 VITE_PATIENT_PAGE_BASE_URL + url = `${env?.MP_PATIENT_PAGE_BASE_URL}pages/survery/fill?corpId=${corpId}&surveryId=${survey._id}&memberId=${customerId}&unionid=unionid&answerId=${answerId}&name=${customerName || ''}`; + } + + // 如果已有链接,直接使用并拼接额外参数 + if (survey.url && survey.url.trim()) { + const separator = survey.url.includes('?') ? '&' : '?'; + const params = []; + if (answerId) params.push(`answerId=${answerId}`); + if (sendSurveyId) params.push(`sendSurveyId=${sendSurveyId}`); + if (userId) params.push(`userId=${userId}`); + if (customerId) params.push(`memberId=${customerId}`); + + if (params.length > 0) { + url = `${survey.url}${separator}${params.join('&')}`; + } else { + url = survey.url; + } + } + + return url; +} + +/** + * 构建问卷自定义消息 + * @param {Object} survey - 问卷对象 + * @param {string} surveyLink - 问卷链接 + * @returns {Object} 自定义消息对象 + */ +function buildSurveyMessage(survey, surveyLink) { + return { + type: 'survey', + title: survey.name || '填写问卷', + desc: '请填写问卷', + url: surveyLink, + imgUrl: + 'https://796f-youcan-clouddev-1-8ewcqf31dbb2b5-1317294507.tcb.qcloud.la/other/19-%E9%97%AE%E5%8D%B7.png?sign=55a4cd77c418b2c548b65792a2cf6bce&t=1701328694', + messageType: 'survey', + }; +} + +/** + * 处理回访任务消息发送 + * @param {Object} messages - 消息数组 + * @param {Object} context - 上下文信息 { userId, customerId, customerName, corpId } + * @returns {Promise} 是否全部发送成功 + */ +export async function handleFollowUpMessages(messages, context = {}) { + if (!Array.isArray(messages) || messages.length === 0) { + toast('没有要发送的消息'); + return false; + } + + try { + let allSuccess = true; + + for (const msg of messages) { + let success = false; + + if (msg.type === 'text') { + success = await sendTextMessage(msg.content); + } else if (msg.type === 'image') { + success = await sendImageMessage(msg.content, msg.name); + } else if (msg.type === 'article') { + success = await sendArticleMessage(msg.content, { + articleId: msg.content.articleId, + userId: context.userId, + customerId: context.customerId, + corpId: context.corpId, + }); + } else if (msg.type === 'questionnaire') { + success = await sendSurveyMessage(msg.content, { + userId: context.userId, + customerId: context.customerId, + customerName: context.customerName, + corpId: context.corpId, + env: context.env, + }); + } + + if (!success) { + allSuccess = false; + // 继续发送其他消息,不中断 + } + } + + return allSuccess; + } catch (error) { + console.error('处理回访任务消息异常:', error); + toast('消息发送过程中出现错误'); + return false; + } +} +