diff --git a/App.vue b/App.vue index 2c8ce4a..bddf8cd 100644 --- a/App.vue +++ b/App.vue @@ -8,7 +8,7 @@ export default { console.log("App Launch: "); // 获取 openId 并初始化 IM - await this.initIMOnLaunch(); + // await this.initIMOnLaunch(); }, onShow: function () { const db = dbStore(); diff --git a/pages/archive/edit-archive.vue b/pages/archive/edit-archive.vue index b7eb18b..807dfb3 100644 --- a/pages/archive/edit-archive.vue +++ b/pages/archive/edit-archive.vue @@ -190,6 +190,7 @@ async function init() { await getCustomer(); } else { const res = await getArchives(); + console.log('res:', res) if (res.length > 0) { visible.value = true; } @@ -204,13 +205,9 @@ async function init() { async function getArchives() { const res = await api('getUnbindMiniAppCustomers', { corpId: corpId.value, mobile: account.value?.mobile || '' }); - if (res && res.success) { - corpName.value = res.corpName; - customers.value = Array.isArray(res.data) ? res.data : []; - } else { - // toast(res?.message || '查询档案信息失败'); - } - return [] + customers.value = res && Array.isArray(res.data) ? res.data : []; + corpName.value = res && res.corpName ? res.corpName : ''; + return customers.value } async function getBaseForm() { diff --git a/pages/home/home.vue b/pages/home/home.vue index 6315bfe..5c43a19 100644 --- a/pages/home/home.vue +++ b/pages/home/home.vue @@ -45,7 +45,7 @@ import pageLoading from "./loading.vue"; // const { useLoad, useShow } = useGuard(); const { account } = storeToRefs(useAccount()); -const { login } = useAccount(); +const { login, getTeams } = useAccount(); const team = ref(null); const teams = ref([]); @@ -77,10 +77,9 @@ async function changeTeam({ teamId, corpId, corpName }) { } } -async function getTeams(inviteTeamId = '') { +async function getMatchTeams(inviteTeamId = '') { loading.value = true; - const res = await api('getWxappRelateTeams', { openid: account.value.openid }); - teams.value = res && Array.isArray(res.data) ? res.data : []; + teams.value = await getTeams(); const matchTeamId = inviteTeamId || (team.value ? team.value.teamId : ''); const validTeam = teams.value.find(i => i.teamId && i.teamId === matchTeamId); const firstTeam = teams.value[0] @@ -110,7 +109,7 @@ onShow(async () => { corpUserIds.value[inviteTeam.teamId] = inviteTeam.corpUserId; } if (account.value && account.value.openid) { - getTeams(inviteTeam && inviteTeam.teamId ? inviteTeam.teamId : ''); + getMatchTeams(inviteTeam && inviteTeam.teamId ? inviteTeam.teamId : ''); } else { teams.value = []; } @@ -124,11 +123,13 @@ onShow(async () => { watch(account, (n, o) => { if (n && !o) { - getTeams(); + getMatchTeams(); } else if (!n && o) { teams.value = []; } }) + + diff --git a/store/account.js b/store/account.js index 21ed7f8..303e8de 100644 --- a/store/account.js +++ b/store/account.js @@ -1,4 +1,4 @@ -import { ref, watch } from "vue"; +import { ref, watch, computed } from "vue"; import { defineStore } from "pinia"; import api from '@/utils/api'; import { toast } from '@/utils/widget'; @@ -13,6 +13,9 @@ export default defineStore("accountStore", () => { const isIMInitialized = ref(false); const openid = ref(""); const externalUserId = ref(''); + const teams = ref([]); + const hasImCorpId = computed(() => teams.value.some(i => i.corpId === 'wpLgjyawAA8N0gWmXgyJq8wpjGcOT7fg')); + const teamsPromise = ref(null); async function login(phoneCode = '') { if (loading.value) return; @@ -38,7 +41,7 @@ export default defineStore("accountStore", () => { uni.setStorageSync('account', res.data); uni.setStorageSync('openid', res.data.openid); - initIMAfterLogin(openid.value) + // initIMAfterLogin(openid.value) return res.data } } @@ -77,10 +80,10 @@ export default defineStore("accountStore", () => { isIMInitialized.value = true; console.log('IM 初始化成功'); - + // IM 初始化成功后,设置全局未读消息监听 // globalUnreadListenerManager.setup(); - + return true; } catch (error) { console.log('IM初始化异常,跳过 IM 初始化:', error.message); @@ -96,7 +99,7 @@ export default defineStore("accountStore", () => { await globalTimChatManager.destroy(); console.log('腾讯IM退出成功'); } - + // 清除全局未读监听 if (globalUnreadListenerManager.isInitialized) { // globalUnreadListenerManager.destroy(); @@ -113,13 +116,28 @@ export default defineStore("accountStore", () => { // 清除本地存储 uni.removeStorageSync('account'); uni.removeStorageSync('openid'); - + // 清除 tabBar 徽章 uni.removeTabBarBadge({ index: 1 }); } + async function searchTeams() { + const res = await api('getWxappRelateTeams', { openid: account.value.openid }); + teams.value = res && Array.isArray(res.data) ? res.data : []; + return teams.value; + } + + async function getTeams() { + if (!teamsPromise.value) { + teamsPromise.value = searchTeams(); + } + await teamsPromise.value; + teamsPromise.value = null; + return teams.value; + } + async function getExternalUserId(corpId) { const unionid = account.value?.unionid; const openid = account.value?.openid; @@ -130,5 +148,17 @@ export default defineStore("accountStore", () => { } } - return { account, login, initIMAfterLogin, logout, openid, isIMInitialized, externalUserId, getExternalUserId } + watch(hasImCorpId, n => { + if (n) { + initIMAfterLogin(); + } + }, { immediate: true }) + + watch(openid, (n, o) => { + if (n && !o) { + getTeams(); + } + }, { immediate: true }) + + return { account, teams, hasImCorpId, login, initIMAfterLogin, logout, openid, isIMInitialized, externalUserId, getExternalUserId, getTeams } }) \ No newline at end of file diff --git a/utils/api.js b/utils/api.js index 1798a7c..96306a9 100644 --- a/utils/api.js +++ b/utils/api.js @@ -72,7 +72,10 @@ const urlsConfig = { getGroupListByGroupId: "getGroupListByGroupId", createConsultGroup: "createConsultGroup", cancelConsultApplication: "cancelConsultApplication", - getGroupList: "getGroupList" + getGroupList: "getGroupList", + getConversationSubscribeConfig: "getConversationSubscribeConfig", + 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..f3091e1 --- /dev/null +++ b/utils/subscribe-message.js @@ -0,0 +1,222 @@ +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"; +const subscribeDisplayConfigCache = new Map(); + +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 checkConversationSubscribeEntryVisible( + corpId = "", + forceRefresh = false +) { + const normalizedCorpId = String(corpId || "").trim(); + if (!normalizedCorpId) return false; + + if (!forceRefresh && subscribeDisplayConfigCache.has(normalizedCorpId)) { + return subscribeDisplayConfigCache.get(normalizedCorpId); + } + + try { + const result = await api( + "getConversationSubscribeConfig", + { corpId: normalizedCorpId }, + false + ); + const enabled = !!result?.data?.enabled; + subscribeDisplayConfigCache.set(normalizedCorpId, enabled); + return enabled; + } catch (error) { + console.error("获取订阅提醒显示配置失败:", error); + subscribeDisplayConfigCache.set(normalizedCorpId, false); + return false; + } +} + +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), + }; +}