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
new file mode 100644
index 0000000..3c54a8f
--- /dev/null
+++ b/pages/message/article-list.vue
@@ -0,0 +1,588 @@
+
+
+
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+
+
+
+
+ {{ article.title }}
+
+ {{ article.date }}
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+ 没有更多了
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/message/chat.scss b/pages/message/chat.scss
index a885920..d35fe0c 100644
--- a/pages/message/chat.scss
+++ b/pages/message/chat.scss
@@ -136,7 +136,7 @@ $primary-color: #0877F1;
.message-list {
padding: 0 16rpx;
- padding-bottom: 20rpx;
+ padding-bottom: 60rpx; /* 增加底部内边距,防止被小程序底部横线遮挡 */
}
.message-item {
@@ -350,10 +350,10 @@ $primary-color: #0877F1;
flex: 1;
padding: 0 46rpx;
background-color: #f3f5fa;
- border-radius: 50rpx;
+ border-radius: 20rpx;
margin: 0 16rpx;
font-size: 28rpx;
- height: 96rpx;
+ height: 80rpx;
border: none;
outline: none;
box-sizing: border-box;
@@ -372,8 +372,8 @@ $primary-color: #0877F1;
justify-content: flex-start;
background: #fff;
border-top: 1rpx solid #eee;
- padding: 20rpx 0 8rpx 60rpx;
- gap: 40rpx;
+ padding: 20rpx 0 40rpx 60rpx;
+ gap: 40rpx 50rpx;
flex-wrap: wrap;
background-color: #f5f5f5;
}
@@ -1248,4 +1248,113 @@ $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: transparent;
+ border-radius: 12rpx;
+ padding: 24rpx;
+ max-width: 500rpx;
+ box-shadow: none;
+ background-color: #fff;
+}
+
+.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: transparent;
+}
+
+.message-left .article-card {
+ background-color: transparent;
+}
+
+/* 问卷卡片样式 */
+.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..3f5bde1 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..4c19114 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..0c04c5a 100644
--- a/pages/message/index.vue
+++ b/pages/message/index.vue
@@ -54,7 +54,9 @@
@@ -62,7 +64,9 @@
@@ -81,7 +85,7 @@
}}
-
+
+
+
+
+
+
+
scrollToBottom(true)"
@messageSent="() => scrollToBottom(true)"
+ @endConsult="handleEndConsult"
/>
@@ -143,13 +165,20 @@ import {
handleViewDetail,
checkIMConnectionStatus,
} from "@/utils/chat-utils.js";
+import { sendConsultRejectedMessage, endConsultation, sendArticleMessage } from "@/utils/api.js";
import useGroupChat from "./hooks/use-group-chat";
import MessageTypes from "./components/message-types.vue";
import ChatInput from "./components/chat-input.vue";
import SystemMessage from "./components/system-message.vue";
+import ConsultAccept from "./components/consult-accept.vue";
+import RejectReasonModal from "./components/reject-reason-modal.vue";
const timChatManager = globalTimChatManager;
+// 获取环境变量
+const env = __VITE_ENV__;
+const corpId = env.MP_CORP_ID || '';
+
// 获取登录状态
const { account, openid, isIMInitialized } = storeToRefs(useAccountStore());
const { initIMAfterLogin } = useAccountStore();
@@ -158,15 +187,12 @@ const { initIMAfterLogin } = useAccountStore();
const chatInputRef = ref(null);
const groupId = ref("");
-const {
- chatMember,
- getGroupInfo
-} = useGroupChat(groupId);
+const { chatMember, getGroupInfo } = useGroupChat(groupId);
// 动态设置导航栏标题
const updateNavigationTitle = () => {
uni.setNavigationBarTitle({
- title: "群聊"
+ title: "群聊",
});
};
@@ -180,6 +206,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 +224,80 @@ 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,9 +306,8 @@ function getBubbleClass(message) {
if (message.type === "TIMImageElem") {
return "image-bubble";
}
-
if (message.type === "TIMCustomElem") {
- return message.flow === "out" ? "user-bubble" : "doctor-bubble-blue";
+ return message.flow === "out" ? "" : "";
}
return message.flow === "out" ? "user-bubble" : "doctor-bubble";
}
@@ -224,6 +325,7 @@ onLoad((options) => {
if (options.userID) {
chatInfo.value.userID = options.userID;
}
+
checkLoginAndInitTIM();
updateNavigationTitle();
});
@@ -288,6 +390,10 @@ const initTIMCallbacks = async () => {
if (!existingMessage) {
messageList.value.push(message);
console.log("✓ 添加消息到列表,当前消息数量:", messageList.value.length);
+
+ // 检查是否有待接诊的系统消息
+ checkConsultPendingStatus();
+
// 立即滚动到底部,不使用延迟
nextTick(() => {
scrollToBottom(true);
@@ -346,6 +452,9 @@ const initTIMCallbacks = async () => {
isCompleted.value = data.isCompleted || false;
isLoadingMore.value = false;
+ // 检查是否有待接诊的系统消息
+ checkConsultPendingStatus();
+
nextTick(() => {
if (data.isRefresh) {
console.log("后台刷新完成,保持当前滚动位置");
@@ -485,16 +594,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);
@@ -594,18 +703,152 @@ onHide(() => {
stopIMMonitoring();
});
-// 发送常用语(从常用语页面返回时调用)
+
const sendCommonPhrase = (content) => {
if (chatInputRef.value) {
chatInputRef.value.sendTextMessageFromPhrase(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;
+};
+// 处理结束问诊
+const handleEndConsult = async () => {
+ try {
+ uni.showLoading({
+ title: "处理中...",
+ });
+ // 调用结束问诊接口,直接传入 groupId
+ const result = await endConsultation({
+ groupId: groupId.value,
+ adminAccount: account.value?.userId || "",
+ extraData: {
+ endBy: account.value?.userId || "",
+ endByName: account.value?.name || "医生",
+ endReason: "问诊完成",
+ },
+ });
+ uni.hideLoading();
+ if (result.success) {
+ 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();
@@ -615,6 +858,101 @@ onUnmounted(() => {
timChatManager.setCallback("onMessageReceived", null);
timChatManager.setCallback("onMessageListLoaded", null);
timChatManager.setCallback("onError", null);
+ // 移除问卷发送监听
+ uni.$off("sendSurvey");
+});
+
+
+// 监听问卷发送事件
+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..53a5261
--- /dev/null
+++ b/pages/message/survey-list.vue
@@ -0,0 +1,446 @@
+
+
+
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+
+
+
+
+ {{ survey.name }}
+ {{
+ survey.description || "暂无问卷说明"
+ }}
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+ 没有更多了
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/webview/webview.vue b/pages/webview/webview.vue
new file mode 100644
index 0000000..d4d603f
--- /dev/null
+++ b/pages/webview/webview.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/styles/theme.scss b/styles/theme.scss
new file mode 100644
index 0000000..c147f45
--- /dev/null
+++ b/styles/theme.scss
@@ -0,0 +1,58 @@
+/**
+ * 项目主题色配置
+ * 统一管理项目中使用的主题色
+ */
+
+// 主题色
+$primary-color: #0877F1;
+$primary-light: #e8f3ff;
+$primary-dark: #0660c9;
+$primary-gradient-start: #1b5cc8;
+$primary-gradient-end: #0877F1;
+
+// 辅助色
+$success-color: #4cd964;
+$warning-color: #f0ad4e;
+$error-color: #dd524d;
+$info-color: #909399;
+
+// 文字颜色
+$text-color-primary: #333;
+$text-color-regular: #666;
+$text-color-secondary: #999;
+$text-color-placeholder: #c0c4cc;
+$text-color-white: #fff;
+
+// 背景颜色
+$bg-color-white: #ffffff;
+$bg-color-page: #f5f5f5;
+$bg-color-hover: #f1f1f1;
+$bg-color-mask: rgba(0, 0, 0, 0.4);
+
+// 边框颜色
+$border-color-base: #e4e7ed;
+$border-color-light: #ebeef5;
+$border-color-lighter: #f2f6fc;
+
+// 圆角
+$border-radius-small: 4rpx;
+$border-radius-base: 8rpx;
+$border-radius-large: 16rpx;
+$border-radius-round: 999rpx;
+
+// 间距
+$spacing-small: 16rpx;
+$spacing-base: 24rpx;
+$spacing-large: 32rpx;
+
+// 字体大小
+$font-size-small: 24rpx;
+$font-size-base: 28rpx;
+$font-size-medium: 32rpx;
+$font-size-large: 36rpx;
+$font-size-xlarge: 40rpx;
+
+// 阴影
+$box-shadow-light: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
+$box-shadow-base: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
+$box-shadow-dark: 0 8rpx 24rpx rgba(0, 0, 0, 0.12);
diff --git a/uni.scss b/uni.scss
index b9249e9..ac62a44 100644
--- a/uni.scss
+++ b/uni.scss
@@ -15,7 +15,7 @@
/* 颜色变量 */
/* 行为相关颜色 */
-$uni-color-primary: #007aff;
+$uni-color-primary: #0877F1;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
diff --git a/utils/api.js b/utils/api.js
index d89509b..ef19d80 100644
--- a/utils/api.js
+++ b/utils/api.js
@@ -26,7 +26,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',
@@ -43,7 +62,10 @@ const urlsConfig = {
im: {
getUserSig: 'getUserSig',
sendSystemMessage: "sendSystemMessage",
- getChatRecordsByGroupId: "getChatRecordsByGroupId"
+ getChatRecordsByGroupId: "getChatRecordsByGroupId",
+ sendConsultRejectedMessage: "sendConsultRejectedMessage",
+ endConsultation: "endConsultation",
+ getGroupListByGroupId: "getGroupListByGroupId"
}
}
@@ -77,3 +99,76 @@ 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
+ }
+ });
+}
+
+// 发送宣教文章消息
+export async function sendArticleMessage(data) {
+ return request({
+ url: '/getYoucanData/im',
+ data: {
+ type: 'sendArticleMessage',
+ ...data
+ }
+ });
+}
+
+// 结束问诊接口
+export async function endConsultation(data) {
+ return request({
+ url: '/getYoucanData/im',
+ data: {
+ type: 'endConsultation',
+ ...data
+ }
+ });
+}
+
+// 根据群组ID获取群组记录
+export async function getGroupListByGroupId(data) {
+ return api('getGroupListByGroupId', 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') {