From 255254c617899df8e0c308f93d6f217a5965483f Mon Sep 17 00:00:00 2001
From: wangdongbo <949818794@qq.com>
Date: Mon, 26 Jan 2026 18:08:01 +0800
Subject: [PATCH 1/3] no message
---
pages.json | 12 +
pages/message/README.md | 95 ----
pages/message/article-list.vue | 503 ++++++++++++++++
pages/message/chat.scss | 110 +++-
pages/message/common-phrases.vue | 535 +++++++++++-------
pages/message/components/chat-input.vue | 20 +-
pages/message/components/consult-accept.vue | 88 +++
pages/message/components/message-types.vue | 128 ++++-
.../components/reject-reason-modal.vue | 297 ++++++++++
pages/message/components/system-message.vue | 108 +++-
pages/message/index.vue | 394 ++++++++++++-
pages/message/survey-list.vue | 423 ++++++++++++++
utils/api.js | 70 ++-
utils/tim-chat.js | 105 +++-
14 files changed, 2545 insertions(+), 343 deletions(-)
delete mode 100644 pages/message/README.md
create mode 100644 pages/message/article-list.vue
create mode 100644 pages/message/components/consult-accept.vue
create mode 100644 pages/message/components/reject-reason-modal.vue
create mode 100644 pages/message/survey-list.vue
diff --git a/pages.json b/pages.json
index 55a5b8a..06bf13e 100644
--- a/pages.json
+++ b/pages.json
@@ -19,6 +19,18 @@
"navigationBarTitleText": "常用语"
}
},
+ {
+ "path": "pages/message/article-list",
+ "style": {
+ "navigationBarTitleText": "宣教文章"
+ }
+ },
+ {
+ "path": "pages/message/survey-list",
+ "style": {
+ "navigationBarTitleText": "问卷列表"
+ }
+ },
{
"path": "pages/work/work",
"style": {
diff --git a/pages/message/README.md b/pages/message/README.md
deleted file mode 100644
index 4cf1f3c..0000000
--- a/pages/message/README.md
+++ /dev/null
@@ -1,95 +0,0 @@
-# 常用语功能说明
-
-## 功能概述
-在聊天页面添加了常用语功能,医生可以快速选择并发送预设的常用语内容。
-
-## 功能特性
-
-### 1. 常用语列表页面 (`common-phrases.vue`)
-- 支持分类管理(文字随访、语音随访、常用回复等)
-- 支持添加、编辑、删除常用语
-- 支持添加自定义分类
-- 点击常用语直接发送到聊天
-
-### 2. 聊天输入框集成
-- 在聊天输入框的"更多"面板中添加"常用语"入口
-- 点击后跳转到常用语列表页面
-- 选择常用语后自动返回并发送
-
-### 3. 数据持久化
-- 常用语数据存储在MongoDB的`common-words`集合中
-- 支持机构级别和个人级别的常用语
-- 与PC端管理后台数据互通
-
-## 文件结构
-
-```
-ykt-wx-app/pages/message/
-├── common-phrases.vue # 常用语列表页面
-├── components/
-│ └── chat-input.vue # 聊天输入框组件(已更新)
-├── index.vue # 聊天主页面(已更新)
-└── README.md # 本文档
-
-ykt-wx-app/utils/
-└── api.js # API配置(已添加常用语接口)
-
-ytk-customer-service/knowledgeBase/common-words/
-└── index.js # 后端常用语接口(已添加小程序接口)
-```
-
-## API接口
-
-### 1. 获取常用语列表
-```javascript
-api('getCommonPhrases', { corpId })
-```
-
-### 2. 保存常用语
-```javascript
-api('saveCommonPhrase', {
- id, // 可选,更新时传入
- corpId,
- userId,
- categoryId,
- content
-})
-```
-
-### 3. 删除常用语
-```javascript
-api('deleteCommonPhrase', {
- id,
- corpId
-})
-```
-
-### 4. 获取分类列表
-```javascript
-api('getCommonPhraseCategories', { corpId })
-```
-
-### 5. 保存分类
-```javascript
-api('saveCommonPhraseCategory', {
- corpId,
- userId,
- name
-})
-```
-
-## 使用流程
-
-1. 用户在聊天页面点击输入框右侧的"+"按钮
-2. 在弹出的功能面板中点击"常用语"
-3. 跳转到常用语列表页面
-4. 可以切换分类查看不同类型的常用语
-5. 点击任意常用语,自动返回聊天页面并发送该内容
-6. 在编辑模式下可以添加、编辑、删除常用语
-
-## 注意事项
-
-1. 常用语功能需要用户已登录并有有效的corpId
-2. 如果后端API调用失败,会使用本地模拟数据
-3. 常用语内容最多500字
-4. 分类名称最多6个字
diff --git a/pages/message/article-list.vue b/pages/message/article-list.vue
new file mode 100644
index 0000000..83f488d
--- /dev/null
+++ b/pages/message/article-list.vue
@@ -0,0 +1,503 @@
+
+
+
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+
+
+
+
+ {{ article.title }}
+ 创建时间:{{ article.date }}
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+ 没有更多了
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/message/chat.scss b/pages/message/chat.scss
index a885920..4ea23f9 100644
--- a/pages/message/chat.scss
+++ b/pages/message/chat.scss
@@ -1248,4 +1248,112 @@ $primary-color: #0877F1;
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10rpx); }
-}
\ No newline at end of file
+}
+
+/* 文章卡片样式 */
+.article-card {
+ display: flex;
+ align-items: center;
+ background-color: #fff;
+ border-radius: 12rpx;
+ padding: 24rpx;
+ max-width: 500rpx;
+ box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
+}
+
+.article-content {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ margin-right: 20rpx;
+ min-width: 0;
+}
+
+.article-title {
+ font-size: 28rpx;
+ color: #333;
+ font-weight: 500;
+ line-height: 1.4;
+ margin-bottom: 8rpx;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+}
+
+.article-desc {
+ font-size: 24rpx;
+ color: #999;
+ line-height: 1.3;
+}
+
+.article-image {
+ width: 120rpx;
+ height: 120rpx;
+ border-radius: 8rpx;
+ flex-shrink: 0;
+}
+
+/* 文章卡片在不同消息流中的样式 */
+.message-right .article-card {
+ background-color: #e8f4ff;
+}
+
+.message-left .article-card {
+ background-color: #fff;
+}
+
+/* 问卷卡片样式 */
+.survey-card {
+ display: flex;
+ align-items: center;
+ background-color: #fff;
+ border-radius: 12rpx;
+ padding: 24rpx;
+ max-width: 500rpx;
+ box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
+}
+
+.survey-content {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ margin-right: 20rpx;
+ min-width: 0;
+}
+
+.survey-title {
+ font-size: 28rpx;
+ color: #333;
+ font-weight: 500;
+ line-height: 1.4;
+ margin-bottom: 8rpx;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+}
+
+.survey-desc {
+ font-size: 24rpx;
+ color: #999;
+ line-height: 1.3;
+}
+
+.survey-image {
+ width: 120rpx;
+ height: 120rpx;
+ border-radius: 8rpx;
+ flex-shrink: 0;
+}
+
+/* 问卷卡片在不同消息流中的样式 */
+.message-right .survey-card {
+ background-color: #e8f4ff;
+}
+
+.message-left .survey-card {
+ background-color: #fff;
+}
diff --git a/pages/message/common-phrases.vue b/pages/message/common-phrases.vue
index f917d00..c5f549b 100644
--- a/pages/message/common-phrases.vue
+++ b/pages/message/common-phrases.vue
@@ -5,11 +5,11 @@
-
-
- +
-
+
+
+
-
-
-
- +
- 添加快捷回复
-
-
- {{ isEditMode ? "完成" : "编辑" }}
-
-
-
暂无常用语
- 点击上方按钮添加常用语
+ 点击下方按钮添加常用语
+
+
+
@@ -108,7 +109,9 @@
>
@@ -130,7 +134,12 @@
+
+
diff --git a/pages/message/components/message-types.vue b/pages/message/components/message-types.vue
index 4d358fe..f128132 100644
--- a/pages/message/components/message-types.vue
+++ b/pages/message/components/message-types.vue
@@ -63,8 +63,46 @@
-
+
+ {{ getArticleData(message).title }}
+ {{ getArticleData(message).desc }}
+
+
+
+
+
+
+
+ {{ getSurveyData(message).title }}
+ {{ getSurveyData(message).desc }}
+
+
+
+
+
+
+ -->
+
diff --git a/pages/message/components/system-message.vue b/pages/message/components/system-message.vue
index 0d28603..de4a907 100644
--- a/pages/message/components/system-message.vue
+++ b/pages/message/components/system-message.vue
@@ -1,9 +1,9 @@
-
+
{{ text }}
@@ -21,17 +21,111 @@ const props = defineProps({
const payload = computed(() => props.message?.payload || {});
-const extension = computed(() => {
+// 解析系统消息内容
+const systemMessageData = computed(() => {
try {
- return JSON.parse(payload.value.extension);
+ // 尝试从 payload.data 解析系统消息
+ if (payload.value.data) {
+ const data = typeof payload.value.data === 'string'
+ ? JSON.parse(payload.value.data)
+ : payload.value.data;
+
+ if (data.type === 'system_message') {
+ return data;
+ }
+ }
} catch (e) {
- return {};
+ console.error('解析系统消息失败:', e);
}
+ return null;
});
-const text = computed(() => extension.value.patient || payload.value.data || '')
+// 解析扩展信息
+const extension = computed(() => {
+ try {
+ if (payload.value.extension) {
+ return typeof payload.value.extension === 'string'
+ ? JSON.parse(payload.value.extension)
+ : payload.value.extension;
+ }
+ } catch (e) {
+ console.error('解析扩展信息失败:', e);
+ }
+ return {};
+});
-const notifyText = computed(() => extension.value.notifyText || '')
+// 显示的文本内容
+const text = computed(() => {
+ // 优先从系统消息数据中获取文本
+ if (systemMessageData.value?.text) {
+ return systemMessageData.value.text;
+ }
+
+ // 根据消息类型返回默认文本
+ if (systemMessageData.value?.messageType) {
+ const messageType = systemMessageData.value.messageType;
+ switch (messageType) {
+ case 'consult_pending':
+ return '患者已发起咨询申请,请及时接诊';
+ case 'consult_accepted':
+ return '医生已接诊';
+ case 'consult_rejected':
+ return '医生暂时无法接诊';
+ case 'consult_ended':
+ return '问诊已结束';
+ case 'consult_timeout':
+ return '问诊已超时';
+ default:
+ return systemMessageData.value.content || '[系统消息]';
+ }
+ }
+
+ // 兼容旧格式:从 extension 中获取
+ if (extension.value.patient) {
+ return extension.value.patient;
+ }
+
+ // 兼容旧格式:直接从 payload.data 获取
+ if (payload.value.data && typeof payload.value.data === 'string') {
+ // 如果 data 不是 JSON 格式,直接显示
+ try {
+ JSON.parse(payload.value.data);
+ } catch {
+ return payload.value.data;
+ }
+ }
+
+ return '[系统消息]';
+});
+
+// 通知文本(红色提示)
+const notifyText = computed(() => {
+ // 从扩展信息中获取通知文本
+ if (extension.value.notifyText) {
+ return extension.value.notifyText;
+ }
+
+ // 根据系统消息类型显示不同的通知
+ if (systemMessageData.value) {
+ const messageType = systemMessageData.value.messageType;
+ switch (messageType) {
+ case 'consult_pending':
+ return '待接诊';
+ case 'consult_rejected':
+ return '已拒绝';
+ case 'consult_timeout':
+ return '已超时';
+ case 'consult_accepted':
+ return '已接诊';
+ case 'consult_ended':
+ return '已结束';
+ default:
+ return '';
+ }
+ }
+
+ return '';
+});
diff --git a/pages/message/index.vue b/pages/message/index.vue
index e09f80f..495f85a 100644
--- a/pages/message/index.vue
+++ b/pages/message/index.vue
@@ -54,7 +54,9 @@
@@ -62,7 +64,9 @@
@@ -108,9 +112,23 @@
+
+
+
+
+
+
{
uni.setNavigationBarTitle({
- title: "群聊"
+ title: "群聊",
});
};
@@ -180,6 +198,12 @@ const chatInfo = ref({
// 评价弹窗状态
const isEvaluationPopupOpen = ref(false);
+// 接受问诊状态
+const showConsultAccept = ref(false);
+
+// 拒绝原因对话框状态
+const showRejectReasonModal = ref(false);
+
// 消息列表相关状态
const messageList = ref([]);
const isLoading = ref(false);
@@ -192,10 +216,73 @@ const lastFirstMessageId = ref("");
// 判断是否为系统消息
function isSystemMessage(message) {
- const description = message.payload?.description;
- return (
- message.type === "TIMCustomElem" && description === "SYSTEM_NOTIFICATION"
- );
+ if (message.type !== "TIMCustomElem") {
+ return false;
+ }
+
+ try {
+ // 检查 payload.data 是否包含系统消息标记
+ if (message.payload?.data) {
+ const data = typeof message.payload.data === 'string'
+ ? JSON.parse(message.payload.data)
+ : message.payload.data;
+
+ // 检查是否为系统消息类型
+ if (data.type === 'system_message') {
+ return true;
+ }
+ }
+
+ // 检查 description 是否为系统消息标记
+ if (message.payload?.description === '系统消息标记') {
+ return true;
+ }
+
+ // 兼容旧的系统消息格式
+ if (message.payload?.description === 'SYSTEM_NOTIFICATION') {
+ return true;
+ }
+ } catch (error) {
+ console.error('判断系统消息失败:', error);
+ }
+
+ return false;
+}
+
+// 检查是否有待接诊的系统消息
+function checkConsultPendingStatus() {
+ // 查找最新的系统消息
+ for (let i = messageList.value.length - 1; i >= 0; i--) {
+ const message = messageList.value[i];
+
+ if (message.type === "TIMCustomElem" && message.payload?.data) {
+ try {
+ const data = typeof message.payload.data === 'string'
+ ? JSON.parse(message.payload.data)
+ : message.payload.data;
+
+ // 如果是 consult_pending 类型的系统消息,显示接受组件
+ if (data.type === 'system_message' && data.messageType === 'consult_pending') {
+ showConsultAccept.value = true;
+ return;
+ }
+
+ // 如果是其他系统消息类型(如已接诊、已结束等),隐藏接受组件
+ if (data.type === 'system_message' &&
+ (data.messageType === 'consult_accepted' ||
+ data.messageType === 'consult_ended' ||
+ data.messageType === 'consult_rejected')) {
+ showConsultAccept.value = false;
+ return;
+ }
+ } catch (error) {
+ console.error('解析系统消息失败:', error);
+ }
+ }
+ }
+
+ // 如果没有找到相关系统消息,隐藏接受组件
+ showConsultAccept.value = false;
}
// 获取消息气泡样式类
@@ -204,7 +291,6 @@ function getBubbleClass(message) {
if (message.type === "TIMImageElem") {
return "image-bubble";
}
-
if (message.type === "TIMCustomElem") {
return message.flow === "out" ? "user-bubble" : "doctor-bubble-blue";
}
@@ -288,6 +374,10 @@ const initTIMCallbacks = async () => {
if (!existingMessage) {
messageList.value.push(message);
console.log("✓ 添加消息到列表,当前消息数量:", messageList.value.length);
+
+ // 检查是否有待接诊的系统消息
+ checkConsultPendingStatus();
+
// 立即滚动到底部,不使用延迟
nextTick(() => {
scrollToBottom(true);
@@ -346,6 +436,9 @@ const initTIMCallbacks = async () => {
isCompleted.value = data.isCompleted || false;
isLoadingMore.value = false;
+ // 检查是否有待接诊的系统消息
+ checkConsultPendingStatus();
+
nextTick(() => {
if (data.isRefresh) {
console.log("后台刷新完成,保持当前滚动位置");
@@ -485,16 +578,16 @@ const scrollToBottom = (immediate = false) => {
if (messageList.value.length > 0) {
const lastMessage = messageList.value[messageList.value.length - 1];
const targetId = `msg-${lastMessage.ID}`;
-
+
if (immediate) {
// 立即滚动:先清空再设置,触发滚动
- scrollIntoView.value = '';
+ scrollIntoView.value = "";
nextTick(() => {
scrollIntoView.value = targetId;
});
} else {
// 正常滚动,使用短延迟确保DOM更新
- scrollIntoView.value = '';
+ scrollIntoView.value = "";
setTimeout(() => {
scrollIntoView.value = targetId;
}, 50);
@@ -603,9 +696,110 @@ const sendCommonPhrase = (content) => {
// 暴露方法给常用语页面调用
defineExpose({
- sendCommonPhrase
+ sendCommonPhrase,
});
+// 处理接受问诊
+const handleAcceptConsult = async () => {
+ try {
+ uni.showLoading({
+ title: '处理中...',
+ });
+
+ // 发送接受问诊的系统消息
+ const customMessage = {
+ data: JSON.stringify({
+ type: 'system_message',
+ messageType: 'consult_accepted',
+ content: '医生已接诊',
+ timestamp: Date.now(),
+ }),
+ description: '系统消息标记',
+ extension: '',
+ };
+
+ const message = timChatManager.tim.createCustomMessage({
+ to: chatInfo.value.conversationID.replace('GROUP', ''),
+ conversationType: timChatManager.TIM.TYPES.CONV_GROUP,
+ payload: customMessage,
+ });
+
+ const sendResult = await timChatManager.tim.sendMessage(message);
+
+ if (sendResult.code === 0) {
+ showConsultAccept.value = false;
+ uni.hideLoading();
+ uni.showToast({
+ title: '已接受问诊',
+ icon: 'success',
+ });
+ } else {
+ throw new Error(sendResult.message || '发送失败');
+ }
+ } catch (error) {
+ console.error('接受问诊失败:', error);
+ uni.hideLoading();
+ uni.showToast({
+ title: error.message || '操作失败',
+ icon: 'none',
+ });
+ }
+};
+
+// 处理拒绝问诊
+const handleRejectConsult = () => {
+ // 显示拒绝原因选择对话框
+ showRejectReasonModal.value = true;
+};
+
+// 处理拒绝原因确认
+const handleRejectReasonConfirm = async (reason) => {
+ try {
+ showRejectReasonModal.value = false;
+
+ uni.showLoading({
+ title: '处理中...',
+ });
+
+ // 获取医生信息
+ const memberName = account.value?.name || '医生';
+
+ // 获取群组ID
+ const groupId = chatInfo.value.conversationID.replace('GROUP', '');
+
+ // 调用后端接口发送拒绝消息
+ const result = await sendConsultRejectedMessage({
+ groupId,
+ memberName,
+ reason,
+ });
+
+ uni.hideLoading();
+
+ if (result.success) {
+ showConsultAccept.value = false;
+ 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',
+ });
+ }
+};
+
+// 处理拒绝原因取消
+const handleRejectReasonCancel = () => {
+ showRejectReasonModal.value = false;
+};
+
// 页面卸载
onUnmounted(() => {
clearMessageCache();
@@ -615,6 +809,172 @@ onUnmounted(() => {
timChatManager.setCallback("onMessageReceived", null);
timChatManager.setCallback("onMessageListLoaded", null);
timChatManager.setCallback("onError", null);
+
+ // 移除文章发送监听
+ uni.$off("sendArticle");
+ // 移除问卷发送监听
+ uni.$off("sendSurvey");
+});
+
+// 监听文章发送事件
+uni.$on("sendArticle", async (data) => {
+ const { article, corpId, userId } = data;
+
+ if (!article || !article._id) {
+ uni.showToast({
+ title: "文章信息不完整",
+ icon: "none",
+ });
+ return;
+ }
+
+ try {
+ // 获取环境变量
+ const env = __VITE_ENV__;
+ const baseUrl = env.VITE_PATIENT_PAGE_BASE_URL || "";
+
+ // 构建文章链接
+ const articleUrl = `${baseUrl}pages/article/index?id=${article._id}&corpId=${corpId}`;
+
+ // 创建自定义消息
+ const customMessage = {
+ data: JSON.stringify({
+ type: "article",
+ title: article.title || "宣教文章",
+ desc: "宣教文章",
+ url: articleUrl,
+ imgUrl:
+ "https://796f-youcan-clouddev-1-8ewcqf31dbb2b5-1317294507.tcb.qcloud.la/other/19-%E9%97%AE%E5%8D%B7.png?sign=55a4cd77c418b2c548b65792a2cf6bce&t=1701328694",
+ }),
+ description: "ARTICLE",
+ extension: "",
+ };
+
+ // 发送自定义消息
+ const message = timChatManager.tim.createCustomMessage({
+ to: chatInfo.value.conversationID.replace("GROUP", ""),
+ conversationType: timChatManager.TIM.TYPES.CONV_GROUP,
+ payload: customMessage,
+ });
+
+ const sendResult = await timChatManager.tim.sendMessage(message);
+
+ if (sendResult.code === 0) {
+ uni.showToast({
+ title: "发送成功",
+ icon: "success",
+ });
+
+ // 记录发送记录(可选)
+ // await addArticleSendRecord({
+ // corpId,
+ // userId,
+ // articleId: article._id,
+ // customerId: chatInfo.value.userID
+ // });
+ } else {
+ throw new Error(sendResult.message || "发送失败");
+ }
+ } catch (error) {
+ console.error("发送文章失败:", error);
+ uni.showToast({
+ title: error.message || "发送失败",
+ icon: "none",
+ });
+ }
+});
+
+// 监听问卷发送事件
+uni.$on("sendSurvey", async (data) => {
+ const { survey, corpId, userId, sendSurveyId } = data;
+
+ if (!survey || !survey._id) {
+ uni.showToast({
+ title: "问卷信息不完整",
+ icon: "none",
+ });
+ return;
+ }
+
+ try {
+ // 获取环境变量
+ const env = __VITE_ENV__;
+ const baseUrl = env.VITE_PATIENT_PAGE_BASE_URL || "";
+ const surveyUrl = env.VITE_SURVEY_URL || "";
+
+ // 获取客户信息
+ const customerId = chatInfo.value.userID || "";
+ const customerName = chatInfo.value.customerName || "";
+
+ // 创建问卷记录
+ const { createSurveyRecord } = await import("@/utils/api.js");
+ const recordRes = await createSurveyRecord({
+ corpId,
+ userId,
+ surveryId: survey._id,
+ memberId: customerId,
+ customer: customerName,
+ sendSurveyId,
+ });
+
+ if (!recordRes.success) {
+ throw new Error(recordRes.message || "创建问卷记录失败");
+ }
+
+ const answerId = recordRes.data?.id || "";
+
+ // 构建问卷链接
+ let surveyLink = "";
+ if (survey.createBy === "system") {
+ // 系统问卷
+ surveyLink = `${surveyUrl}?corpId=${corpId}&surveyId=${survey.surveyId}&memberId=${customerId}&sendSurveyId=${sendSurveyId}&userId=${userId}`;
+ } else {
+ // 自定义问卷
+ surveyLink = `${baseUrl}pages/survery/fill?corpId=${corpId}&surveryId=${
+ survey._id
+ }&memberId=${customerId}&answerId=${answerId}&name=${encodeURIComponent(
+ customerName
+ )}`;
+ }
+
+ // 创建自定义消息
+ const customMessage = {
+ data: JSON.stringify({
+ 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",
+ }),
+ description: "SURVEY",
+ extension: "",
+ };
+
+ // 发送自定义消息
+ const message = timChatManager.tim.createCustomMessage({
+ to: chatInfo.value.conversationID.replace("GROUP", ""),
+ conversationType: timChatManager.TIM.TYPES.CONV_GROUP,
+ payload: customMessage,
+ });
+
+ const sendResult = await timChatManager.tim.sendMessage(message);
+
+ if (sendResult.code === 0) {
+ uni.showToast({
+ title: "发送成功",
+ icon: "success",
+ });
+ } else {
+ throw new Error(sendResult.message || "发送失败");
+ }
+ } catch (error) {
+ console.error("发送问卷失败:", error);
+ uni.showToast({
+ title: error.message || "发送失败",
+ icon: "none",
+ });
+ }
});
diff --git a/pages/message/survey-list.vue b/pages/message/survey-list.vue
new file mode 100644
index 0000000..0115693
--- /dev/null
+++ b/pages/message/survey-list.vue
@@ -0,0 +1,423 @@
+
+
+
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+
+
+
+
+ {{ survey.name }}
+ {{ survey.description || '暂无问卷说明' }}
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+ 没有更多了
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/utils/api.js b/utils/api.js
index db1e8aa..9ee1945 100644
--- a/utils/api.js
+++ b/utils/api.js
@@ -22,7 +22,26 @@ const urlsConfig = {
saveCommonPhrase: 'saveCommonPhrase',
deleteCommonPhrase: 'deleteCommonPhrase',
getCommonPhraseCategories: 'getCommonPhraseCategories',
- saveCommonPhraseCategory: 'saveCommonPhraseCategory'
+ saveCommonPhraseCategory: 'saveCommonPhraseCategory',
+ // 个人常用语接口
+ getPersonalPhrases: 'getPersonalPhrases',
+ savePersonalPhrase: 'savePersonalPhrase',
+ deletePersonalPhrase: 'deletePersonalPhrase',
+ getPersonalPhraseCategories: 'getPersonalPhraseCategories',
+ savePersonalPhraseCategory: 'savePersonalPhraseCategory',
+ deletePersonalPhraseCategory: 'deletePersonalPhraseCategory',
+ // 宣教文章接口
+ getArticleCateList: 'getArticleCateList',
+ getArticleList: 'getArticleList',
+ getArticle: 'getArticle',
+ addArticleSendRecord: 'addArticleSendRecord'
+ },
+
+ survery: {
+ getSurveyCateList: 'getSurveryCateList',
+ getSurveyList: 'getList',
+ createSurveyRecord: 'createRecord',
+ getSurveyDetail: 'getDetail'
},
member: {
addCustomer: 'add',
@@ -39,7 +58,8 @@ const urlsConfig = {
im: {
getUserSig: 'getUserSig',
sendSystemMessage: "sendSystemMessage",
- getChatRecordsByGroupId: "getChatRecordsByGroupId"
+ getChatRecordsByGroupId: "getChatRecordsByGroupId",
+ sendConsultRejectedMessage: "sendConsultRejectedMessage"
}
}
@@ -73,3 +93,49 @@ export default async function api(urlId, data) {
})
}
+// 宣教文章相关 API
+export async function getArticleCateList(data) {
+ return api('getArticleCateList', data);
+}
+
+export async function getArticleList(data) {
+ return api('getArticleList', data);
+}
+
+export async function getArticle(data) {
+ return api('getArticle', data);
+}
+
+export async function addArticleSendRecord(data) {
+ return api('addArticleSendRecord', data);
+}
+
+// 问卷相关 API
+export async function getSurveyCateList(data) {
+ return api('getSurveyCateList', data);
+}
+
+export async function getSurveyList(data) {
+ return api('getSurveyList', data);
+}
+
+export async function createSurveyRecord(data) {
+ return api('createSurveyRecord', data);
+}
+
+export async function getSurveyDetail(data) {
+ return api('getSurveyDetail', data);
+}
+
+
+
+// IM 系统消息相关 API
+export async function sendConsultRejectedMessage(data) {
+ return request({
+ url: '/getYoucanData/im',
+ data: {
+ type: 'sendConsultRejectedMessage',
+ ...data
+ }
+ });
+}
diff --git a/utils/tim-chat.js b/utils/tim-chat.js
index cbd6fb7..4ed5d28 100644
--- a/utils/tim-chat.js
+++ b/utils/tim-chat.js
@@ -363,7 +363,6 @@ class TimChatManager {
}
}
- // 获取用户信息并登录
async getUserInfoAndLogin(userID) {
try {
if (userID) {
@@ -376,7 +375,6 @@ class TimChatManager {
}
this.currentUserID = userInfo.userID
}
-
this.currentUserSig = await this.getUserSig(this.currentUserID)
await this.loginTIM()
} catch (error) {
@@ -711,26 +709,37 @@ class TimChatManager {
// 获取消息所属的会话ID
const messageConversationID = convertedMessage.conversationID
+ // 检查是否为系统消息
+ const isSystemMsg = this.isSystemMessage(convertedMessage)
+
console.log('收到新消息:', {
messageID: convertedMessage.ID,
messageConversationID: messageConversationID,
currentConversationID: this.currentConversationID,
messageType: convertedMessage.type,
- from: convertedMessage.from
+ from: convertedMessage.from,
+ isSystemMessage: isSystemMsg
})
- // 判断是否为当前会话的消息(必须有currentConversationID且匹配才显示)
- const isCurrentConversation = this.currentConversationID &&
- messageConversationID === this.currentConversationID
+
+ // 判断是否为当前会话的消息
+ // 系统消息:只要会话ID匹配就显示(不要求必须有currentConversationID)
+ // 普通消息:必须有currentConversationID且匹配才显示
+ const isCurrentConversation = isSystemMsg
+ ? messageConversationID === this.currentConversationID
+ : (this.currentConversationID && messageConversationID === this.currentConversationID)
+
console.log('消息会话匹配检查:', {
isCurrentConversation,
+ isSystemMessage: isSystemMsg,
hasCurrentConversationID: !!this.currentConversationID,
conversationIDMatch: messageConversationID === this.currentConversationID
})
+
if (isCurrentConversation) {
// 当前会话的消息,触发回调
console.log('✓ 消息属于当前会话,触发显示')
this.triggerCallback('onMessageReceived', convertedMessage)
- // 处理已读状态
+ // 处理已读状态(系统消息也标记为已读)
if (this.currentConversationID) {
this.markConversationAsRead(this.currentConversationID)
}
@@ -1093,7 +1102,52 @@ class TimChatManager {
} else if (lastMessage.type === 'TIMSoundElem') {
lastMessageText = '[语音]'
} else if (lastMessage.type === 'TIMCustomElem') {
- lastMessageText = lastMessage.payload.data || '[自定义消息]'
+ // 解析自定义消息
+ try {
+ const customData = JSON.parse(lastMessage.payload.data)
+ const messageType = customData.messageType
+ // 根据消息类型返回不同的预览文本
+ switch (messageType) {
+ case 'system_message':
+ lastMessageText = '[系统消息]'
+ break
+ case 'symptom':
+ lastMessageText = '[病情描述]'
+ break
+ case 'prescription':
+ lastMessageText = '[处方单]'
+ break
+ case 'refill':
+ lastMessageText = '[续方申请]'
+ break
+ case 'survey':
+ lastMessageText = '[问卷调查]'
+ break
+ case 'article':
+ lastMessageText = '[文章]'
+ break
+ case "consult_pending":
+ lastMessageText = '患者向团队发起咨询,请在1小时内接诊,超时将自动关闭会话'
+ break
+ case "consult_rejected":
+ lastMessageText = '患者向团队发起咨询,由于有紧急事务要处理暂时无法接受咨询.本次会话丿关闭'
+ break
+ case "consult_timeout":
+ lastMessageText = '患者向团队发起咨询,团队成员均未接受咨询,本次会话已自动关闭'
+ break
+ case "consult_accepted":
+ lastMessageText = '已接诊,会话已开始'
+ break
+ case "consult_ended":
+ lastMessageText = '已结束当前会话'
+ break
+ default:
+ lastMessageText = '[自定义消息]'
+ }
+ } catch (error) {
+ console.error('解析自定义消息失败:', error)
+ lastMessageText = '[自定义消息]'
+ }
} else {
lastMessageText = '[未知消息类型]'
}
@@ -2362,6 +2416,41 @@ class TimChatManager {
}
// 工具方法
+ // 判断是否为系统消息
+ isSystemMessage(message) {
+ if (message.type !== 'TIMCustomElem') {
+ return false
+ }
+
+ // 检查 payload.data 是否包含系统消息标记
+ try {
+ if (message.payload && message.payload.data) {
+ const data = typeof message.payload.data === 'string'
+ ? JSON.parse(message.payload.data)
+ : message.payload.data
+
+ // 检查是否为系统消息类型
+ if (data.type === 'system_message') {
+ return true
+ }
+ }
+
+ // 检查 description 是否为系统消息标记
+ if (message.payload && message.payload.description === '系统消息标记') {
+ return true
+ }
+
+ // 兼容旧的系统消息格式
+ if (message.payload && message.payload.description === 'SYSTEM_NOTIFICATION') {
+ return true
+ }
+ } catch (error) {
+ console.error('判断系统消息失败:', error)
+ }
+
+ return false
+ }
+
filterMessage(message) {
if (message.type === 'TIMCustomElem' && message.payload && message.payload.data) {
if (message.payload.data === 'group_create' || message.payload.data === 'purchased') {
From 5d03f0fc79b9a5fc0f10e9afce97891042bb2aca Mon Sep 17 00:00:00 2001
From: wangdongbo <949818794@qq.com>
Date: Tue, 27 Jan 2026 13:42:59 +0800
Subject: [PATCH 2/3] IM
---
App.vue | 25 ++-
pages.json | 12 ++
pages/message/article-detail.vue | 209 +++++++++++++++++++++
pages/message/article-list.vue | 132 +++++++++----
pages/message/chat.scss | 10 +-
pages/message/common-phrases.vue | 2 +-
pages/message/components/chat-input.vue | 22 ++-
pages/message/components/message-types.vue | 22 ++-
pages/message/index.vue | 149 ++++++++++-----
pages/message/survey-list.vue | 171 +++++++++--------
pages/webview/webview.vue | 24 +++
styles/theme.scss | 58 ++++++
uni.scss | 2 +-
utils/api.js | 20 +-
14 files changed, 680 insertions(+), 178 deletions(-)
create mode 100644 pages/message/article-detail.vue
create mode 100644 pages/webview/webview.vue
create mode 100644 styles/theme.scss
diff --git a/App.vue b/App.vue
index 0ea7c2d..9fedd72 100644
--- a/App.vue
+++ b/App.vue
@@ -29,13 +29,36 @@ export default {
diff --git a/pages/message/article-list.vue b/pages/message/article-list.vue
index 83f488d..6880edf 100644
--- a/pages/message/article-list.vue
+++ b/pages/message/article-list.vue
@@ -19,10 +19,10 @@
v-for="cate in categoryList"
:key="cate._id || 'all'"
class="category-item"
- :class="{ active: currentCateId === (cate._id || 'all') }"
+ :class="{ active: currentCateId === cate._id }"
@click="selectCategory(cate)"
>
- {{ cate.name }}
+ {{ cate.label }}
@@ -54,17 +54,17 @@
>
{{ article.title }}
- 创建时间:{{ article.date }}
-
-
-
+
+ {{ article.date }}
+
+
@@ -75,8 +75,7 @@
+ class="no-more">
没有更多了
@@ -94,7 +93,9 @@
-
+
+
+