diff --git a/pages/case/components/archive-detail/service-info-tab.vue b/pages/case/components/archive-detail/service-info-tab.vue index d351882..819da88 100644 --- a/pages/case/components/archive-detail/service-info-tab.vue +++ b/pages/case/components/archive-detail/service-info-tab.vue @@ -34,7 +34,7 @@ {{ i.timeStr }} - + {{ i.fileType === 'article' ? '查看文章' : '查看问卷' }} @@ -137,6 +137,11 @@ const loading = ref(false); const list = ref([]); const expandMap = ref({}); const userNameMap = ref({}); +const teamNameMap = ref({}); +const loadedTeamNameIds = new Set(); + +// 先隐藏“查看问卷/文章”入口(保留相关代码,后续可随时打开) +const showFileEntry = ref(false); const accountStore = useAccountStore(); const { account, doctorInfo } = storeToRefs(accountStore); @@ -183,6 +188,7 @@ function getExecutorId(r) { const row = r && typeof r === 'object' ? r : {}; return String( row.executorUserId || + row.executorUserID || row.executorId || row.executor || row.creatorUserId || @@ -194,11 +200,11 @@ function getExecutorId(r) { function executorText(r) { const row = r && typeof r === 'object' ? r : {}; - const fromRow = normalizeName(row.executorName || row.executorUserName || row.creatorName || row.updateUserName || ''); - if (fromRow) return fromRow; const uid = getExecutorId(row); const mapped = normalizeName(resolveUserName(uid)); - return mapped || (uid ? uid : '--'); + const fromRow = normalizeName(row.executorName || row.executorUserName || row.creatorName || row.updateUserName || ''); + if (mapped && uid && mapped !== uid && (!fromRow || fromRow === uid)) return mapped; + return fromRow || mapped || (uid ? uid : '--'); } function getExecuteTeamId(r) { @@ -209,9 +215,46 @@ function getExecuteTeamId(r) { function resolveTeamName(teamId) { const tid = String(teamId || '') || ''; if (!tid) return ''; + const cached = teamNameMap.value?.[tid]; + if (cached) return String(cached); const list = teamList.value || []; const hit = list.find((i) => i && i.value === tid); - return hit?.label ? String(hit.label) : ''; + if (hit?.label) return String(hit.label); + // 不阻塞渲染:后台补齐团队名 + void loadTeamName(tid); + return ''; +} + +async function loadTeamName(teamId) { + const tid = String(teamId || '') || ''; + if (!tid) return; + if (loadedTeamNameIds.has(tid)) return; + const corpId = getCorpId(); + if (!corpId) return; + + loadedTeamNameIds.add(tid); + try { + const res = await api('getTeamBaseInfo', { corpId, teamId: tid }); + if (res?.success) { + const data = res?.data && typeof res.data === 'object' ? res.data : {}; + const name = String(data?.name || data?.teamName || data?.team || '').trim(); + if (name) teamNameMap.value = { ...(teamNameMap.value || {}), [tid]: name }; + return; + } + } catch { + // ignore + } + + // 兜底:用 getTeamData 再试一次 + try { + const res = await api('getTeamData', { corpId, teamId: tid }); + if (!res?.success) return; + const data = res?.data && typeof res.data === 'object' ? res.data : {}; + const name = String(data?.name || data?.teamName || data?.team || '').trim(); + if (name) teamNameMap.value = { ...(teamNameMap.value || {}), [tid]: name }; + } catch { + // ignore + } } function executeTeamText(r) { @@ -264,21 +307,110 @@ async function loadTeamMembers(teamId) { const tid = String(teamId || '') || ''; if (!tid) return; if (loadedTeamMemberIds.has(tid)) return; - loadedTeamMemberIds.add(tid); await ensureDoctor(); const corpId = getCorpId(); if (!corpId) return; - const res = await api('getTeamData', { corpId, teamId: tid }); - if (!res?.success) return; - const t = res?.data && typeof res.data === 'object' ? res.data : {}; + loadedTeamMemberIds.add(tid); + + // 以 getTeamData 为准(getTeamMemberAvatarsAndName 存在不全/不准的情况) + const fallback = await api('getTeamData', { corpId, teamId: tid }); + if (!fallback?.success) { + loadedTeamMemberIds.delete(tid); + return; + } + const t = fallback?.data && typeof fallback.data === 'object' ? fallback.data : {}; const members = Array.isArray(t.memberList) ? t.memberList : []; const map = members.reduce((acc, m) => { - const uid = String(m?.userid || ''); + if (typeof m === 'string') { + const id = String(m || ''); + if (id) acc[id] = id; + return acc; + } + const uid = String(m?.userid || m?.userId || m?.corpUserId || m?._id || m?.id || ''); if (!uid) return acc; - acc[uid] = String(m?.anotherName || m?.name || m?.userid || '') || uid; + acc[uid] = String(m?.anotherName || m?.name || m?.userid || m?.userId || '') || uid; return acc; }, {}); userNameMap.value = { ...(userNameMap.value || {}), ...map }; + + // 补缺:仅当当前没有映射时才用 avatars 接口补齐,避免覆盖正确姓名 + try { + const res = await api('getTeamMemberAvatarsAndName', { corpId, teamId: tid }); + if (res?.success && res?.data && typeof res.data === 'object') { + const raw = res.data; + const patch = Object.keys(raw).reduce((acc, uid) => { + const id = String(uid || ''); + if (!id) return acc; + const existing = userNameMap.value?.[id]; + if (existing && existing !== id) return acc; + const name = String(raw?.[uid]?.name || raw?.[uid]?.anotherName || '').trim(); + if (!name || name === id) return acc; + acc[id] = name; + return acc; + }, {}); + if (Object.keys(patch).length) userNameMap.value = { ...(userNameMap.value || {}), ...patch }; + } + } catch { + // ignore + } +} + +let corpMemberBatchInflight = null; // Promise | null +async function batchLoadCorpMembers(userIds) { + const ids = Array.isArray(userIds) ? userIds.map((v) => String(v || '').trim()).filter(Boolean) : []; + if (!ids.length) return; + const uniq = Array.from(new Set(ids)); + const unknown = uniq.filter((id) => { + const existing = userNameMap.value?.[id]; + return !existing || existing === id; + }); + if (!unknown.length) return; + + if (corpMemberBatchInflight) return corpMemberBatchInflight; + + await ensureDoctor(); + const corpId = getCorpId(); + if (!corpId) return; + + corpMemberBatchInflight = (async () => { + try { + const res = await api( + 'getCorpMember', + { + page: 1, + pageSize: Math.min(Math.max(unknown.length, 10), 500), + params: { + corpId, + memberList: unknown, + }, + }, + false + ); + if (!res?.success) return; + + const rows = Array.isArray(res?.data) ? res.data : Array.isArray(res?.data?.data) ? res.data.data : []; + if (!rows.length) return; + + const patch = rows.reduce((acc, m) => { + const id = String(m?.userid || m?.userId || m?.corpUserId || '').trim(); + if (!id) return acc; + const existing = userNameMap.value?.[id]; + if (existing && existing !== id) return acc; + const display = String(m?.anotherName || m?.name || '').trim(); + if (!display || display === id) return acc; + acc[id] = display; + return acc; + }, {}); + + if (Object.keys(patch).length) userNameMap.value = { ...(userNameMap.value || {}), ...patch }; + } catch { + // ignore + } + })().finally(() => { + corpMemberBatchInflight = null; + }); + + return corpMemberBatchInflight; } const moreStatus = computed(() => { @@ -375,6 +507,10 @@ async function getMore() { // 尽量加载记录所属团队成员,用于执行人展示 const teamIds = mapped.map((i) => i.executeTeamId).filter(Boolean); Array.from(new Set(teamIds)).forEach((tid) => loadTeamMembers(tid)); + + // 补齐非团队成员执行人姓名(例如其他团队创建/操作) + const executorIds = mapped.map((i) => i.executorUserId).filter(Boolean); + void batchLoadCorpMembers(executorIds); } finally { loading.value = false; } @@ -669,6 +805,7 @@ watch( display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; + line-clamp: 3; overflow: hidden; } .pen { diff --git a/pages/home/case-home.vue b/pages/home/case-home.vue index bdb645a..6f6558a 100644 --- a/pages/home/case-home.vue +++ b/pages/home/case-home.vue @@ -163,6 +163,10 @@ const groupNameMap = computed(() => { // Team Members Map const userNameMap = ref({}); +const loadedMembersTeamId = ref(''); +const corpMemberNameInflight = new Map(); // userId -> Promise +const corpMemberNameTried = new Set(); // avoid retry storms on failures +let corpMemberBatchInflight = null; // Promise | null // 新增流程所需状态(认证相关) const managedArchiveCountAllTeams = ref(0); // 在管档案数(所有团队) @@ -196,6 +200,272 @@ function asArray(value) { return Array.isArray(value) ? value : []; } +function normalizeUserId(value) { + if (value === null || value === undefined) return ''; + if (typeof value === 'object') { + const obj = value; + const picked = + obj.userid || + obj.userId || + obj.userID || + obj.corpUserId || + obj.corpUserID || + obj._id || + obj.id || + ''; + return String(picked || '').trim(); + } + return String(value || '').trim(); +} + +function resolveUserName(userId) { + const id = normalizeUserId(userId); + if (!id) return ''; + // 优先使用当前登录人的信息(避免映射未命中时只显示 userid) + const d = doctorInfo.value || {}; + const doctorId = normalizeUserId(d.userid || d.userId || d.corpUserId || ''); + if (doctorId && doctorId === id) { + const display = String(d.anotherName || d.name || d.username || d.userid || '').trim(); + if (display) return display; + } + const mapped = userNameMap.value[id] || userNameMap.value[id.toLowerCase()]; + if (mapped) return mapped; + // 不阻塞渲染:后台补齐非团队成员姓名(例如其他团队成员创建) + if (isLikelyUserId(id)) void fetchCorpMemberDisplayName(id).catch(() => {}); + return id; +} + +function isLikelyUserId(value) { + const id = normalizeUserId(value); + if (!id) return false; + if (/[\u4e00-\u9fa5]/.test(id)) return false; // already looks like a name + if (/\s/.test(id)) return false; + return true; +} + +function extractDisplayNameFromAny(payload) { + if (!payload) return ''; + if (typeof payload === 'string') return payload.trim(); + if (Array.isArray(payload)) return extractDisplayNameFromAny(payload[0]); + if (typeof payload !== 'object') return ''; + const obj = payload; + const candidate = + obj.anotherName || + obj.name || + obj.realName || + obj.username || + obj.nickName || + obj.nickname || + obj.displayName || + obj.userName || + obj.userid || + obj.userId || + obj.corpUserId || + ''; + return String(candidate || '').trim(); +} + +function extractDisplayNameFromCorpMember(row) { + const m = row && typeof row === 'object' ? row : {}; + const name = String(m.anotherName || m.name || '').trim(); + return name; +} + +async function batchFetchCorpMemberDisplayNames(userIds) { + const ids = Array.isArray(userIds) ? userIds.map(normalizeUserId).filter(Boolean) : []; + if (!ids.length) return; + const uniq = Array.from(new Set(ids)); + + const unresolved = uniq.filter((id) => { + if (!isLikelyUserId(id)) return false; + const cached = userNameMap.value?.[id] || userNameMap.value?.[id.toLowerCase()]; + return !cached || cached === id; + }); + if (!unresolved.length) return; + + if (corpMemberBatchInflight) return corpMemberBatchInflight; + + const corpId = getCorpId() || String(account.value?.corpId || doctorInfo.value?.corpId || ''); + if (!corpId) return; + + corpMemberBatchInflight = (async () => { + try { + const res = await api( + 'getCorpMember', + { + page: 1, + pageSize: Math.min(Math.max(unresolved.length, 10), 500), + params: { + corpId, + memberList: unresolved, + }, + }, + false + ); + + if (!res?.success) return; + const rows = Array.isArray(res?.data) ? res.data : Array.isArray(res?.data?.data) ? res.data.data : []; + if (!rows.length) return; + + const next = { ...(userNameMap.value || {}) }; + rows.forEach((m) => { + const id = normalizeUserId(m?.userid || m?.userId || m?.corpUserId || ''); + if (!id) return; + const display = extractDisplayNameFromCorpMember(m) || id; + const existing = next[id] || next[id.toLowerCase()]; + // 仅补缺:不覆盖已有的非空/非同值映射 + if (existing && existing !== id) return; + if (display && display !== id) { + next[id] = display; + next[id.toLowerCase()] = display; + } + }); + userNameMap.value = next; + } catch { + // ignore + } + })().finally(() => { + corpMemberBatchInflight = null; + }); + + return corpMemberBatchInflight; +} + +async function fetchCorpMemberDisplayName(userId) { + const id = normalizeUserId(userId); + if (!id) return ''; + if (!isLikelyUserId(id)) return ''; + + const cached = userNameMap.value[id] || userNameMap.value[id.toLowerCase()]; + if (cached && cached !== id) return cached; + + if (corpMemberNameInflight.has(id)) return corpMemberNameInflight.get(id); + if (corpMemberNameTried.has(id)) return ''; + + const corpId = getCorpId() || String(account.value?.corpId || doctorInfo.value?.corpId || ''); + if (!corpId) return ''; + + const p = (async () => { + corpMemberNameTried.add(id); + + // 1) 首选:企业成员主页信息(更可能支持用 userid 查询) + try { + const res = await api('getCorpMemberHomepageInfo', { corpId, corpUserId: id }, false); + if (res?.success) { + const name = + extractDisplayNameFromAny(res?.data) || + extractDisplayNameFromAny(res?.data?.data) || + extractDisplayNameFromAny(res?.data?.member) || + ''; + if (name) return name; + } + } catch { + // ignore + } + + // 1.1) 有的后端参数名为 userId + try { + const res = await api('getCorpMemberHomepageInfo', { corpId, userId: id }, false); + if (res?.success) { + const name = + extractDisplayNameFromAny(res?.data) || + extractDisplayNameFromAny(res?.data?.data) || + extractDisplayNameFromAny(res?.data?.member) || + ''; + if (name) return name; + } + } catch { + // ignore + } + + // 2) 兜底:成员数据接口(部分环境可能支持 corpUserId) + try { + const res = await api('getCorpMemberData', { corpId, corpUserId: id }, false); + if (res?.success) { + const name = + extractDisplayNameFromAny(res?.data) || + extractDisplayNameFromAny(res?.data?.data) || + ''; + if (name) return name; + } + } catch { + // ignore + } + + // 2.1) 同样尝试 userId + try { + const res = await api('getCorpMemberData', { corpId, userId: id }, false); + if (res?.success) { + const name = + extractDisplayNameFromAny(res?.data) || + extractDisplayNameFromAny(res?.data?.data) || + ''; + if (name) return name; + } + } catch { + // ignore + } + + return ''; + })() + .then((name) => { + const display = String(name || '').trim(); + if (display) { + const next = { ...(userNameMap.value || {}) }; + next[id] = display; + next[id.toLowerCase()] = display; + userNameMap.value = next; + } + return display; + }) + .finally(() => { + corpMemberNameInflight.delete(id); + }); + + corpMemberNameInflight.set(id, p); + return p; +} + +async function prefetchUserNamesFromPatients(patients) { + const list = Array.isArray(patients) ? patients : []; + if (!list.length) return; + + const ids = new Set(); + list.forEach((p) => { + const u1 = normalizeUserId(p?.recentAddOperatorUserId); + const u2 = normalizeUserId(p?.creator); + if (u1) ids.add(u1); + if (u2) ids.add(u2); + }); + + const targets = Array.from(ids).filter((id) => { + if (!isLikelyUserId(id)) return false; + const cached = userNameMap.value[id] || userNameMap.value[id.toLowerCase()]; + return !cached || cached === id; + }); + if (!targets.length) return; + + // 优先批量补齐(现成后端接口 getCorpMember 支持 memberList) + await batchFetchCorpMemberDisplayNames(targets); + + const limit = 6; + let idx = 0; + const workers = Array.from({ length: Math.min(limit, targets.length) }, async () => { + while (idx < targets.length) { + const cur = targets[idx++]; + try { + const cached = userNameMap.value?.[cur] || userNameMap.value?.[cur.toLowerCase()]; + if (cached && cached !== cur) continue; + await fetchCorpMemberDisplayName(cur); + } catch { + // ignore + } + } + }); + + await Promise.allSettled(workers); +} + function normalizeTeam(raw) { if (!raw || typeof raw !== 'object') return null; const teamId = raw.teamId || raw.id || raw._id || ''; @@ -235,23 +505,76 @@ function ensureUserInfoForFeature() { return false; } +async function ensureDoctorForQuery() { + if (account.value?.openid) { + try { + const a = account.value || {}; + const accountId = normalizeUserId(a.userid || a.userId || a.corpUserId || ''); + const d = doctorInfo.value || {}; + const doctorId = normalizeUserId(d.userid || d.userId || d.corpUserId || ''); + // doctorInfo 可能是旧缓存:当与当前账号不一致时强制刷新 + if (!doctorId || (accountId && doctorId && accountId !== doctorId)) { + await getDoctorInfo(); + } + } catch { + // ignore + } + } + return Boolean(getUserId()); +} + async function loadTeamMembers() { const corpId = getCorpId(); const teamId = getTeamId(); if (!corpId || !teamId) return; + if (loadedMembersTeamId.value === teamId && Object.keys(userNameMap.value || {}).length > 0) return; try { + const nextMap = { ...(userNameMap.value || {}) }; + + // 以团队详情为准(getTeamMemberAvatarsAndName 存在不全/不准的情况) const res = await api('getTeamData', { corpId, teamId }); - if (!res?.success) return; - const t = res?.data && typeof res.data === 'object' ? res.data : {}; - const members = Array.isArray(t.memberList) ? t.memberList : []; - // Update map - members.forEach(m => { - const uid = String(m?.userid || ''); - if (uid) { - userNameMap.value[uid] = String(m?.anotherName || m?.name || m?.userid || '') || uid; - } - }); + if (res?.success) { + const t = res?.data && typeof res.data === 'object' ? res.data : {}; + const members = Array.isArray(t.memberList) ? t.memberList : []; + members.forEach((m) => { + if (typeof m === 'string') { + const k = normalizeUserId(m); + if (k) { + nextMap[k] = nextMap[k] || k; + nextMap[k.toLowerCase()] = nextMap[k.toLowerCase()] || nextMap[k] || k; + } + return; + } + const display = String(m?.anotherName || m?.name || m?.userid || m?.userId || m?.corpUserId || '').trim(); + const keys = [m?.userid, m?.userId, m?.corpUserId].map(normalizeUserId).filter(Boolean); + keys.forEach((k) => { + nextMap[k] = display || nextMap[k] || k; + nextMap[String(k).toLowerCase()] = display || nextMap[String(k).toLowerCase()] || k; + }); + }); + } + + // 补缺:仅当当前没有映射时才用 avatars 接口补齐,避免覆盖正确姓名 + try { + const avatarRes = await api('getTeamMemberAvatarsAndName', { corpId, teamId }); + const mapObj = avatarRes?.success && avatarRes?.data && typeof avatarRes.data === 'object' ? avatarRes.data : {}; + Object.entries(mapObj).forEach(([uid, info]) => { + const k = normalizeUserId(uid); + if (!k) return; + const existing = nextMap[k] || nextMap[k.toLowerCase()]; + if (existing && existing !== k) return; + const display = String(info?.name || info?.anotherName || info?.userid || '').trim(); + if (!display || display === k) return; + nextMap[k] = display; + nextMap[k.toLowerCase()] = display; + }); + } catch { + // ignore + } + + userNameMap.value = nextMap; + loadedMembersTeamId.value = teamId; } catch (e) { console.error('获取团队成员失败', e); } @@ -260,7 +583,7 @@ async function loadTeamMembers() { function resolveCreatorName(patient) { const val = patient.creator; if (!val) return ''; - return userNameMap.value[val] || val; + return resolveUserName(val); } function resolveRecentAddTime(patient) { @@ -268,9 +591,12 @@ function resolveRecentAddTime(patient) { } function resolveRecentAddOperatorName(patient) { - const uid = patient?.recentAddOperatorUserId || patient?.creator || ''; + const nameFromApi = String(patient?.recentAddOperatorName || '').trim(); + // 后端部分场景会把 userid 填到 name 字段里,此时仍需通过 userid 解析姓名 + if (nameFromApi && !isLikelyUserId(nameFromApi)) return nameFromApi; + const uid = patient?.recentAddOperatorUserId || nameFromApi || patient?.creator || ''; if (!uid) return ''; - return userNameMap.value[uid] || uid; + return resolveUserName(uid); } function resolveRecentAddAction(patient) { @@ -396,6 +722,123 @@ function parseCreateTime(value) { return d2.isValid() ? d2 : null; } +function normalizeMedicalType(raw) { + const s = String(raw || '').trim(); + if (!s) return ''; + const lower = s.toLowerCase(); + // 中文兜底(部分接口返回展示名) + if (s.includes('门诊')) return 'outpatient'; + if (s.includes('住院') || s.includes('入院')) return 'inhospital'; + if (s.includes('预问诊') || s.includes('问诊')) return 'preConsultation'; + if (s.includes('体检')) return 'physicalExaminationTemplate'; + if (lower.includes('preconsult') || (lower.includes('pre') && lower.includes('consult'))) return 'preConsultation'; + if (lower === 'outpatient' || lower === 'out_patient' || lower === 'out-patient') return 'outpatient'; + if (lower === 'inhospital' || lower === 'in_hospital' || lower === 'in-hospital' || lower === 'inpatient') return 'inhospital'; + if (lower === 'physicalexaminationtemplate' || lower === 'physicalexamination' || lower === 'physical_examination') return 'physicalExaminationTemplate'; + if (s === 'outPatient') return 'outpatient'; + if (s === 'inHospital') return 'inhospital'; + if (s === 'preConsultation') return 'preConsultation'; + if (s === 'physicalExaminationTemplate') return 'physicalExaminationTemplate'; + return s; +} + +function normalizeText(v) { + if (Array.isArray(v)) { + const parts = v + .map((i) => normalizeText(i)) + .filter((i) => i !== null && i !== undefined && String(i).trim()); + return parts.join(','); + } + if (v === 0) return '0'; + if (v && typeof v === 'object') { + const o = v; + const candidate = o.label ?? o.name ?? o.text ?? o.title ?? o.value ?? o.diseaseName ?? o.code ?? ''; + return candidate ? String(candidate) : ''; + } + return v ? String(v) : ''; +} + +function formatAnyDate(value, fmt = 'YYYY-MM-DD') { + const d = parseCreateTime(value); + return d ? d.format(fmt) : ''; +} + +function resolveLatestRecord(lr) { + if (!lr || typeof lr !== 'object') return null; + + const formData = lr?.formData && typeof lr.formData === 'object' ? lr.formData : null; + const recordData = lr?.recordData && typeof lr.recordData === 'object' ? lr.recordData : null; + const data = lr?.data && typeof lr.data === 'object' ? lr.data : null; + + const hasValue = (v) => { + if (v === 0) return true; + if (Array.isArray(v)) return v.length > 0; + if (v && typeof v === 'object') return Object.keys(v).length > 0; + return v !== null && v !== undefined && String(v).trim() !== ''; + }; + + const pick = (...keys) => { + for (const k of keys) { + const v0 = lr?.[k]; + if (hasValue(v0)) return v0; + const v1 = formData?.[k]; + if (hasValue(v1)) return v1; + const v2 = recordData?.[k]; + if (hasValue(v2)) return v2; + const v3 = data?.[k]; + if (hasValue(v3)) return v3; + } + return undefined; + }; + + const rawType = String(pick('medicalType', 'templateType') || '').trim(); + const uiType = normalizeMedicalType(rawType || pick('type')); + const typeLabel = String(pick('tempName', 'templateName', 'name', 'type') || '').trim(); + + const rawDate = pick('date', 'visitTime', 'inhosDate', 'consultDate', 'inspectDate', 'sortTime'); + const rawDateStr = String(rawDate ?? '').trim(); + const date = (/^\d{10,13}$/.test(rawDateStr) ? (formatAnyDate(rawDateStr, 'YYYY-MM-DD') || rawDateStr) : rawDateStr) + || formatAnyDate(pick('visitTime', 'inhosDate', 'consultDate', 'inspectDate', 'sortTime'), 'YYYY-MM-DD') + || '-'; + + let third = ''; + if (uiType === 'outpatient' || uiType === 'inhospital') { + third = normalizeText(pick( + 'diagnosisName', + 'diagnosis', + 'diagnosisList', + 'diagnosisNames', + 'mainDiagnosis', + 'admissionDiagnosis', + 'inDiagnosis', + 'outDiagnosis', + 'outPatientDiagnosis', + 'inHospitalDiagnosis' + )); + } else if (uiType === 'preConsultation') { + third = normalizeText(pick('chiefComplaint', 'complaint', 'mainComplaint', 'mainSuit', 'chief')); + } else if (uiType === 'physicalExaminationTemplate') { + third = normalizeText(pick('summary', 'inspectSummary', 'conclusion', 'inspectConclusion', 'inspectResult', 'finalConclusion')); + } else { + third = normalizeText(pick('diagnosis', 'diagnosisName', 'summary', 'chiefComplaint')); + } + third = String(third || '').replace(/\s+/g, ' ').trim(); + if (!third) { + // 最后的兜底:避免经常展示为 '-' + third = normalizeText(pick('summary', 'inspectSummary', 'chiefComplaint', 'presentIllness', 'treatmentPlan', 'abstract', 'brief')); + third = String(third || '').replace(/\s+/g, ' ').trim(); + } + if (!String(third || '').trim()) third = '-'; + + const type = typeLabel || (uiType === 'outpatient' ? '门诊记录' + : uiType === 'inhospital' ? '住院记录' + : uiType === 'preConsultation' ? '预问诊记录' + : uiType === 'physicalExaminationTemplate' ? '体检档案' + : '-'); + + return { type, date, diagnosis: third }; +} + function formatPatient(raw) { const name = raw?.name || raw?.customerName || ''; const sex = raw?.sex || raw?.gender || ''; @@ -427,20 +870,16 @@ function formatPatient(raw) { // 解析病历信息 let record = null; - if (raw?.latestRecord && typeof raw.latestRecord === 'object') { - const lr = raw.latestRecord; - const type = lr.type || ''; - const date = lr.date || ''; - const diagnosis = lr.diagnosis || ''; - // 只有存在有效信息时才设置 record - if (type || date || diagnosis) { - record = { - type: type || '-', - date: date || '-', - diagnosis: diagnosis || '-' - }; - } - } + const latestRaw = + raw?.latestRecord ?? + raw?.latestMedicalRecord ?? + raw?.latestMedicalCase ?? + raw?.lastRecord ?? + raw?.lastMedicalRecord ?? + raw?.recentRecord ?? + null; + const latest = Array.isArray(latestRaw) ? latestRaw[0] : latestRaw; + if (latest && typeof latest === 'object') record = resolveLatestRecord(latest); return { ...raw, @@ -457,6 +896,14 @@ function formatPatient(raw) { recentAddTimeTs, recentAddType, recentAddOperatorUserId, + recentAddOperatorName: String( + raw?.recentAddOperatorName + || raw?.recentAddOperatorAnotherName + || raw?.recentAddOperatorUserName + || raw?.recentAddOperatorRealName + || raw?.recentAddOperatorDisplayName + || '' + ).trim(), creator: raw?.creatorName || raw?.creator || '', hospitalId: raw?.customerNumber || raw?.hospitalId || '', record, @@ -518,6 +965,7 @@ async function reload(reset = true) { if (!currentTeam.value) return; if (loading.value) return; + await ensureDoctorForQuery(); const userId = getUserId(); const corpId = getCorpId(); const teamId = getTeamId(); @@ -571,6 +1019,8 @@ async function reload(reset = true) { const list = Array.isArray(payload.list) ? payload.list : Array.isArray(payload.data) ? payload.data : []; const next = list.map(formatPatient); rawPatients.value = page.value === 1 ? next : [...rawPatients.value, ...next]; + // 补齐创建人/新增人姓名(部分创建人不在当前团队成员列表中) + void prefetchUserNamesFromPatients(next).catch(() => {}); pages.value = Number(payload.pages || 0) || 0; totalFromApi.value = Number(payload.total || 0) || rawPatients.value.length; managedArchiveCountAllTeams.value = @@ -665,13 +1115,13 @@ const toggleTeamPopup = () => { } uni.showActionSheet({ itemList: teams.value.map((i) => i.name), - success: function (res) { + success: async (res) => { currentTeam.value = teams.value[res.tapIndex] || teams.value[0] || null; if (currentTeam.value) uni.setStorageSync(CURRENT_TEAM_STORAGE_KEY, currentTeam.value); currentTabKey.value = 'all'; - loadGroups(); - loadTeamMembers(); - reload(true); + await loadGroups(); + await loadTeamMembers(); + await reload(true); } }); }; @@ -1016,6 +1466,7 @@ watch(currentTeam, (t) => { watch(currentTabKey, () => { if (!currentTeam.value) return; + loadTeamMembers(); reload(true); }); @@ -1032,7 +1483,7 @@ onLoad(async () => { await loadTeams(); if (currentTeam.value) { await loadGroups(); - loadTeamMembers(); + await loadTeamMembers(); await reload(true); } await refreshVerifyStatus(); @@ -1056,7 +1507,7 @@ onShow(async () => { } else { await loadGroups(); } - loadTeamMembers(); + await loadTeamMembers(); await refreshVerifyStatus(); }); diff --git a/utils/api.js b/utils/api.js index 18a53f9..188f62e 100644 --- a/utils/api.js +++ b/utils/api.js @@ -3,6 +3,8 @@ import request from "./http"; const urlsConfig = { corp: { getCorpMemberHomepageInfo: 'getCorpMemberHomepageInfo', + getCorpMember: 'getCorpMember', + getCorpMemberOptions: 'getCorpMemberOptions', // 企业信息/标签 getCorpInfo: 'getCorpInfo', getCorpTags: 'getCorpTags', diff --git a/utils/send-message-helper.js b/utils/send-message-helper.js index 9790add..a272e0d 100644 --- a/utils/send-message-helper.js +++ b/utils/send-message-helper.js @@ -6,6 +6,38 @@ import { globalTimChatManager } from './tim-chat.js'; import api from './api.js'; import { toast } from './widget.js'; const env = __VITE_ENV__; + +function nowTs() { + return Date.now(); +} + +async function tryAddServiceRecord(payload) { + try { + const res = await api('addServiceRecord', payload); + if (!res?.success) { + console.warn('写入服务记录失败:', res?.message || res); + } + } catch (e) { + console.error('写入服务记录异常:', e); + } +} + +function canWriteServiceRecord(options = {}) { + return Boolean(options?.corpId && options?.userId && options?.customerId); +} + +function normalizeServiceRecordBase(options = {}) { + return { + corpId: options.corpId, + executorUserId: String(options.userId || ''), + creatorUserId: String(options.userId || ''), + customerId: String(options.customerId || ''), + customerName: String(options.customerName || ''), + executeTeamId: String(options.teamId || options.executeTeamId || ''), + executionTime: nowTs(), + externalUserId: String(options.externalUserId || options.customerUserId || ''), + }; +} /** * 发送文字消息 * @param {string} content - 文字内容 @@ -168,6 +200,24 @@ export async function sendArticleMessage(article, options = {}) { console.error('记录文章发送失败:', err); }); } + + // 写入服务记录留痕(异步,不阻塞) + if (canWriteServiceRecord(options)) { + const base = normalizeServiceRecordBase(options); + tryAddServiceRecord({ + ...base, + eventType: 'ContentReminder', + taskContent: `推送文章:${article.title || '宣教文章'}`, + pannedEventSendFile: { + type: 'article', + articleId: article._id || options.articleId || '', + title: article.title || '', + url: article.url || '', + }, + pannedEventName: '宣教发送', + }); + } + return true; } else { toast(result?.error || '发送文章消息失败'); @@ -243,6 +293,22 @@ export async function sendSurveyMessage(survey, options = {}) { const result = await globalTimChatManager.sendCustomMessage(customMessageData); if (result?.success) { + // 写入服务记录留痕(异步,不阻塞) + if (canWriteServiceRecord(options)) { + const base = normalizeServiceRecordBase(options); + tryAddServiceRecord({ + ...base, + eventType: 'questionnaire', + taskContent: `推送问卷:${survey.name || '问卷'}`, + pannedEventSendFile: { + type: 'questionnaire', + surveryId: survey._id || survey.surveryId || '', + name: survey.name || '', + url: surveyLink || '', + }, + pannedEventName: '问卷调查', + }); + } return true; } else { toast(result?.error || '发送问卷消息失败');