diff --git a/pages/message/chat.scss b/pages/message/chat.scss
index b44dbf3..285285d 100644
--- a/pages/message/chat.scss
+++ b/pages/message/chat.scss
@@ -37,6 +37,13 @@ $primary-color: #0877F1;
justify-content: space-between;
}
+.header-actions {
+ display: flex;
+ align-items: center;
+ gap: 12rpx;
+ flex-shrink: 0;
+}
+
.patient-basic-info {
display: flex;
align-items: center;
@@ -84,6 +91,26 @@ $primary-color: #0877F1;
}
}
+.remind-btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 8rpx 20rpx;
+ border-radius: 24rpx;
+ border: 2rpx solid #3876f6;
+ background: #fff;
+}
+
+.remind-btn:active {
+ opacity: 0.85;
+}
+
+.remind-btn-text {
+ font-size: 24rpx;
+ color: #3876f6;
+ line-height: 1.4;
+}
+
.badge-processing {
background: #d1ecf1;
diff --git a/pages/message/index.vue b/pages/message/index.vue
index 3dd1fb0..3c7b679 100644
--- a/pages/message/index.vue
+++ b/pages/message/index.vue
@@ -6,21 +6,26 @@
-
- {{ patientInfo.name }}
- {{ patientInfo.sex }} · {{ patientInfo.age }}岁
-
-
- {{ chatStatusInfo.badgeText }}
-
-
-
+
+ {{ patientInfo.name }}
+ {{ patientInfo.sex }} · {{ patientInfo.age }}岁
+
+
+
+
{
};
// 处理咨询申请
-const handleApplyConsult = async () => {
+const handleApplyConsult = async () => {
try {
uni.showModal({
title: "提示",
@@ -1060,8 +1070,26 @@ const handleApplyConsult = async () => {
title: error.message || "操作失败",
icon: "none",
});
- }
-};
+ }
+};
+
+const handleSubscribeReminder = async () => {
+ await requestConversationSubscribeMessage({
+ role: SUBSCRIBE_MESSAGE_ROLE.PATIENT,
+ scene: SUBSCRIBE_MESSAGE_SCENE.CHAT,
+ conversationId: chatInfo.value.conversationID || "",
+ groupId: groupId.value || "",
+ corpId: corpId.value || "",
+ patientId: patientId.value || "",
+ userId: openid.value || account.value?.openid || "",
+ openid: openid.value || account.value?.openid || "",
+ unionid: account.value?.unionid || "",
+ extraData: {
+ orderStatus: orderStatus.value || "",
+ page: "pages/message/index",
+ },
+ });
+};
// 页面卸载
onUnmounted(() => {
diff --git a/pages/message/message.vue b/pages/message/message.vue
index 0617fb8..6633d88 100644
--- a/pages/message/message.vue
+++ b/pages/message/message.vue
@@ -1,6 +1,5 @@
-
@@ -52,6 +51,10 @@
}}
+
+
+ 接收提醒
+
@@ -65,9 +68,16 @@ import { mergeConversationWithGroupDetails } from "@/utils/conversation-merger.j
import { globalUnreadListenerManager } from "@/utils/global-unread-listener.js";
import useGroupAvatars from "./hooks/use-group-avatars.js";
import GroupAvatar from "@/components/group-avatar.vue";
+import { requestConversationSubscribeMessage } from "@/utils/subscribe-message";
+import {
+ SUBSCRIBE_MESSAGE_ROLE,
+ SUBSCRIBE_MESSAGE_SCENE,
+} from "@/utils/subscribe-message-config";
// 获取登录状态
-const { account, openid, isIMInitialized, hasImCorpId } = storeToRefs(useAccountStore());
+const { account, openid, isIMInitialized, hasImCorpId, teams } = storeToRefs(
+ useAccountStore()
+);
const { initIMAfterLogin } = useAccountStore();
// 状态
@@ -492,6 +502,20 @@ const cleanMessageText = (text) => {
return text.replace(/[\r\n]+/g, " ").trim();
};
+const handleSubscribeReminder = async () => {
+ await requestConversationSubscribeMessage({
+ role: SUBSCRIBE_MESSAGE_ROLE.PATIENT,
+ scene: SUBSCRIBE_MESSAGE_SCENE.LIST,
+ corpId: teams.value.find((item) => item?.corpId)?.corpId || "",
+ userId: openid.value || account.value?.openid || "",
+ openid: openid.value || account.value?.openid || "",
+ unionid: account.value?.unionid || "",
+ extraData: {
+ page: "pages/message/message",
+ },
+ });
+};
+
// 页面显示
onShow(async () => {
// 页面显示时刷新 tabBar 徽章
@@ -718,4 +742,32 @@ onUnmounted(() => {
color: #999;
padding-bottom: 10rpx;
}
+
+.subscribe-entry {
+ position: fixed;
+ right: 32rpx;
+ bottom: 180rpx;
+ width: 116rpx;
+ height: 116rpx;
+ border-radius: 58rpx;
+ background: #fff;
+ border: 2rpx solid #3876f6;
+ box-shadow: 0 8rpx 24rpx rgba(56, 118, 246, 0.16);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 20;
+}
+
+.subscribe-entry:active {
+ opacity: 0.85;
+}
+
+.subscribe-entry-text {
+ width: 56rpx;
+ font-size: 24rpx;
+ line-height: 1.4;
+ color: #3876f6;
+ text-align: center;
+}
diff --git a/utils/api.js b/utils/api.js
index 1798a7c..210f151 100644
--- a/utils/api.js
+++ b/utils/api.js
@@ -72,7 +72,9 @@ const urlsConfig = {
getGroupListByGroupId: "getGroupListByGroupId",
createConsultGroup: "createConsultGroup",
cancelConsultApplication: "cancelConsultApplication",
- getGroupList: "getGroupList"
+ getGroupList: "getGroupList",
+ saveConversationSubscribeResult: "saveConversationSubscribeResult",
+ sendConversationSubscribeEvent: "sendConversationSubscribeEvent"
},
survery: {
getMiniAppReceivedSurveryList: 'getMiniAppReceivedSurveryList',
diff --git a/utils/subscribe-message-config.js b/utils/subscribe-message-config.js
new file mode 100644
index 0000000..df935c9
--- /dev/null
+++ b/utils/subscribe-message-config.js
@@ -0,0 +1,64 @@
+const env = __VITE_ENV__;
+
+export const SUBSCRIBE_MESSAGE_ROLE = {
+ PATIENT: "patient",
+ DOCTOR: "doctor",
+};
+
+export const SUBSCRIBE_MESSAGE_SCENE = {
+ DEFAULT: "default",
+ LIST: "list",
+ CHAT: "chat",
+};
+
+export const SUBSCRIBE_MESSAGE_EVENT = {
+ PATIENT_CONSULT_APPLY: "patient_consult_apply",
+ PATIENT_CHAT_MESSAGE: "patient_chat_message",
+ DOCTOR_ACCEPT: "doctor_accept",
+ DOCTOR_REJECT: "doctor_reject",
+ DOCTOR_CHAT_MESSAGE: "doctor_chat_message",
+};
+
+export const SUBSCRIBE_MESSAGE_TEMPLATES = {
+ consultationReply: {
+ code: "consultationReply",
+ role: SUBSCRIBE_MESSAGE_ROLE.PATIENT,
+ id:
+ env.MP_SUBSCRIBE_TEMPLATE_CONSULT_REPLY ||
+ "VF9AC-7Rr3E1drbxBCrxbC-rLTnidmlNXopKReSAd_w",
+ name: "咨询回复通知",
+ events: [
+ SUBSCRIBE_MESSAGE_EVENT.DOCTOR_ACCEPT,
+ SUBSCRIBE_MESSAGE_EVENT.DOCTOR_REJECT,
+ SUBSCRIBE_MESSAGE_EVENT.DOCTOR_CHAT_MESSAGE,
+ ],
+ fields: ["患者姓名", "回复时间", "回复者", "所属机构"],
+ },
+};
+
+export const SUBSCRIBE_MESSAGE_SCENE_TEMPLATE_MAP = {
+ [SUBSCRIBE_MESSAGE_ROLE.PATIENT]: {
+ [SUBSCRIBE_MESSAGE_SCENE.DEFAULT]: ["consultationReply"],
+ [SUBSCRIBE_MESSAGE_SCENE.LIST]: ["consultationReply"],
+ [SUBSCRIBE_MESSAGE_SCENE.CHAT]: ["consultationReply"],
+ },
+};
+
+export function resolveSubscribeTemplates({
+ role,
+ scene = SUBSCRIBE_MESSAGE_SCENE.DEFAULT,
+} = {}) {
+ const roleMap = SUBSCRIBE_MESSAGE_SCENE_TEMPLATE_MAP[role] || {};
+ const keys =
+ roleMap[scene] || roleMap[SUBSCRIBE_MESSAGE_SCENE.DEFAULT] || [];
+ const seen = new Set();
+
+ return keys
+ .map((key) => SUBSCRIBE_MESSAGE_TEMPLATES[key])
+ .filter((item) => item && item.id)
+ .filter((item) => {
+ if (seen.has(item.id)) return false;
+ seen.add(item.id);
+ return true;
+ });
+}
diff --git a/utils/subscribe-message.js b/utils/subscribe-message.js
new file mode 100644
index 0000000..8309fd8
--- /dev/null
+++ b/utils/subscribe-message.js
@@ -0,0 +1,194 @@
+import api from "@/utils/api";
+import { toast } from "@/utils/widget";
+import { resolveSubscribeTemplates } from "./subscribe-message-config";
+
+const SUBSCRIBE_ACCEPT_STATUS = "accept";
+const SUBSCRIBE_REJECT_STATUS = "reject";
+const SUBSCRIBE_BAN_STATUS = "ban";
+const SUBSCRIBE_FILTER_STATUS = "filter";
+const SUBSCRIBE_CANCEL_STATUS = "cancel";
+const SUBSCRIBE_FAILED_STATUS = "failed";
+
+function canUseSubscribeMessage() {
+ return (
+ typeof wx !== "undefined" &&
+ typeof wx.requestSubscribeMessage === "function"
+ );
+}
+
+function requestSubscribeMessage(tmplIds = []) {
+ return new Promise((resolve) => {
+ wx.requestSubscribeMessage({
+ tmplIds,
+ success(res) {
+ resolve({ ok: true, res });
+ },
+ fail(err) {
+ resolve({ ok: false, err });
+ },
+ });
+ });
+}
+
+function normalizeFailStatus(err = {}) {
+ const errCode = Number(err.errCode || 0);
+ const errMsg = String(err.errMsg || "").toLowerCase();
+
+ if (errCode === 20004 || errMsg.includes("main switch")) {
+ return SUBSCRIBE_BAN_STATUS;
+ }
+ if (errCode === 20005 || errMsg.includes("ban")) {
+ return SUBSCRIBE_BAN_STATUS;
+ }
+ if (errMsg.includes("filter")) {
+ return SUBSCRIBE_FILTER_STATUS;
+ }
+ if (errMsg.includes("cancel")) {
+ return SUBSCRIBE_CANCEL_STATUS;
+ }
+ return SUBSCRIBE_FAILED_STATUS;
+}
+
+function buildTemplateResultRecords(templates = [], requestResult = {}, context = {}) {
+ const requestedAt = Date.now();
+
+ if (requestResult.ok) {
+ const res = requestResult.res || {};
+ return templates.map((template) => ({
+ role: context.role || "",
+ scene: context.scene || "",
+ conversationId: context.conversationId || "",
+ groupId: context.groupId || "",
+ corpId: context.corpId || "",
+ teamId: context.teamId || "",
+ patientId: context.patientId || "",
+ doctorId: context.doctorId || "",
+ userId: context.userId || "",
+ openid: context.openid || "",
+ unionid: context.unionid || "",
+ templateId: template.id,
+ templateCode: template.code,
+ templateName: template.name,
+ eventTypes: template.events,
+ status: String(res[template.id] || SUBSCRIBE_FAILED_STATUS),
+ rawResult: res,
+ requestedAt,
+ extraData: context.extraData || {},
+ }));
+ }
+
+ const status = normalizeFailStatus(requestResult.err);
+ return templates.map((template) => ({
+ role: context.role || "",
+ scene: context.scene || "",
+ conversationId: context.conversationId || "",
+ groupId: context.groupId || "",
+ corpId: context.corpId || "",
+ teamId: context.teamId || "",
+ patientId: context.patientId || "",
+ doctorId: context.doctorId || "",
+ userId: context.userId || "",
+ openid: context.openid || "",
+ unionid: context.unionid || "",
+ templateId: template.id,
+ templateCode: template.code,
+ templateName: template.name,
+ eventTypes: template.events,
+ status,
+ rawResult: requestResult.err || {},
+ requestedAt,
+ extraData: context.extraData || {},
+ }));
+}
+
+function buildToastMessage(records = [], reportResult = { success: false }) {
+ const accepted = records.some((item) => item.status === SUBSCRIBE_ACCEPT_STATUS);
+
+ if (accepted && reportResult?.success === false) {
+ return reportResult?.message || "提醒开启失败,请稍后再试";
+ }
+
+ if (records.some((item) => item.status === SUBSCRIBE_ACCEPT_STATUS)) {
+ return "会话消息提醒开启";
+ }
+ if (records.some((item) => item.status === SUBSCRIBE_BAN_STATUS)) {
+ return "请先在微信设置中开启订阅消息提醒";
+ }
+ if (records.some((item) => item.status === SUBSCRIBE_FILTER_STATUS)) {
+ return "当前提醒模板暂不可用";
+ }
+ if (records.some((item) => item.status === SUBSCRIBE_REJECT_STATUS)) {
+ return "你已拒绝本次提醒订阅";
+ }
+ if (records.some((item) => item.status === SUBSCRIBE_CANCEL_STATUS)) {
+ return "你已取消本次提醒订阅";
+ }
+ return "提醒订阅请求失败,请稍后再试";
+}
+
+async function reportSubscribeResult(records = []) {
+ if (!records.length) return { success: false };
+ try {
+ return await api(
+ "saveConversationSubscribeResult",
+ {
+ records,
+ },
+ false
+ );
+ } catch (error) {
+ console.error("保存订阅结果失败:", error);
+ return { success: false, message: error?.message || "保存失败" };
+ }
+}
+
+export async function requestConversationSubscribeMessage(context = {}) {
+ const templates = resolveSubscribeTemplates({
+ role: context.role,
+ scene: context.scene,
+ });
+ const requestTemplates = templates.slice(0, 1);
+
+ if (!requestTemplates.length) {
+ await toast("暂未配置提醒模板");
+ return {
+ success: false,
+ code: "template_missing",
+ records: [],
+ };
+ }
+
+ if (!canUseSubscribeMessage()) {
+ await toast("当前微信版本不支持订阅消息");
+ return {
+ success: false,
+ code: "unsupported",
+ records: [],
+ };
+ }
+
+ const requestResult = await requestSubscribeMessage(
+ requestTemplates.map((item) => item.id)
+ );
+ const records = buildTemplateResultRecords(
+ requestTemplates,
+ requestResult,
+ context
+ );
+
+ const reportResult = await reportSubscribeResult(records);
+ await toast(buildToastMessage(records, reportResult));
+
+ const subscribeSuccess =
+ records.some((item) => item.status === SUBSCRIBE_ACCEPT_STATUS) &&
+ reportResult?.success !== false;
+
+ return {
+ success: subscribeSuccess,
+ reportResult,
+ records,
+ acceptedTemplateIds: records
+ .filter((item) => item.status === SUBSCRIBE_ACCEPT_STATUS)
+ .map((item) => item.templateId),
+ };
+}