ykt-wxapp/pages/case/components/archive-detail/health-profile-tab.vue

781 lines
24 KiB
Vue
Raw Normal View History

2026-01-22 15:54:15 +08:00
<template>
<!-- Mobile 来源: ykt-management-mobile/src/pages/customer/customer-detail/health-profile/health-profile.vue -->
<view class="wrap">
<view class="filters">
<picker mode="selector" :range="typeRange" range-key="name" @change="pickType">
<view class="filter-pill">
2026-01-22 17:39:23 +08:00
<view class="pill-text">{{ currentType.value === 'ALL' ? '全部病历' : currentType.name }}</view>
2026-01-22 15:54:15 +08:00
<uni-icons type="arrowdown" size="12" color="#666" />
</view>
</picker>
2026-02-10 15:47:35 +08:00
<uni-datetime-picker type="daterange" v-model="dateRange" @change="pickTimeRange">
2026-01-22 17:39:23 +08:00
<view class="filter-pill">
<view class="pill-text">{{ dateRangeLabel }}</view>
2026-02-10 15:47:35 +08:00
<view class="pill-icons">
<view v-if="isDateRangeActive" class="pill-clear" @tap.stop="clearTimeRange" @click.stop="clearTimeRange">
<uni-icons type="closeempty" size="16" color="#666" />
</view>
<uni-icons type="arrowdown" size="12" color="#666" />
</view>
2026-01-22 15:54:15 +08:00
</view>
</uni-datetime-picker>
2026-01-22 17:39:23 +08:00
</view>
<view v-if="showShareTip" class="share-tip">
<text class="share-tip-text">患者已授权其他团队病历信息互通</text>
2026-01-22 15:54:15 +08:00
</view>
<view class="list">
<view v-for="r in records" :key="r._id" class="card record" @click="edit(r)">
<view class="record-head">
2026-01-22 17:39:23 +08:00
<view class="record-date">{{ r.dateStr || '--' }}</view>
<view class="record-tag" :class="tagClass[r.templateType] || 'bg-blue'">{{ r.tempName || '病历' }}</view>
<view v-if="r.corpName === '其他' || r.corp === '其他'" class="record-tag bg-rose">外院</view>
2026-01-22 15:54:15 +08:00
</view>
2026-01-22 17:39:23 +08:00
2026-01-22 15:54:15 +08:00
<view class="record-body">
2026-01-27 16:46:36 +08:00
<view v-for="(l, idx) in getDisplayLines(r)" :key="`${r._id}_${idx}`" class="line">
<text class="line-label">{{ l.label }}</text>
<text class="line-value">{{ l.value }}</text>
2026-01-22 15:54:15 +08:00
</view>
2026-01-22 17:39:23 +08:00
<view v-if="getFiles(r).length" class="thumbs">
<view v-for="(f, idx) in getFiles(r).slice(0, 3)" :key="idx" class="thumb" @click.stop="previewFiles(r, idx)">
<image class="thumb-img" :src="f.url" mode="aspectFill" />
</view>
<view v-if="getFiles(r).length > 3" class="thumb-more">+{{ getFiles(r).length - 3 }}</view>
2026-01-22 15:54:15 +08:00
</view>
</view>
<view class="record-foot">
2026-01-27 16:46:36 +08:00
<view class="foot-left">{{ getCreateFooter(r) }}</view>
2026-01-22 15:54:15 +08:00
</view>
</view>
<view v-if="records.length === 0" class="empty">暂无数据</view>
</view>
2026-02-06 17:15:09 +08:00
<picker
class="fab-picker"
mode="selector"
:range="selectableTemplates"
range-key="name"
2026-02-06 18:04:34 +08:00
:disabled="fabPickerDisabled"
2026-02-06 17:15:09 +08:00
:style="{ bottom: `${floatingBottom}px` }"
@change="pickAddType"
>
2026-02-06 18:04:34 +08:00
<view class="fab" :class="{ 'fab--disabled': selectableTemplates.length === 0 }" @tap="onFabTap">
2026-02-06 17:15:09 +08:00
<uni-icons type="plusempty" size="24" color="#fff" />
</view>
</picker>
2026-01-22 15:54:15 +08:00
</view>
</template>
<script setup>
2026-01-27 16:46:36 +08:00
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
import dayjs from 'dayjs';
import api from '@/utils/api';
2026-02-06 17:15:09 +08:00
import { loading, hideLoading, toast } from '@/utils/widget';
import { normalizeTemplate } from '../../utils/template';
2026-02-09 15:40:03 +08:00
import { normalizeVisitRecordFormData } from '../../utils/visit-record';
import { normalizeFileUrl } from '@/utils/file';
2026-01-22 15:54:15 +08:00
const props = defineProps({
data: { type: Object, default: () => ({}) },
archiveId: { type: String, default: '' },
floatingBottom: { type: Number, default: 16 },
});
2026-02-10 15:47:35 +08:00
const FALLBACK_TEMPLATE_TYPES = ['outpatient', 'inhospital', 'preConsultationRecord', 'physicalExaminationTemplate'];
2026-02-06 17:15:09 +08:00
const templates = ref([]);
const selectableTemplates = computed(() => templates.value.filter((i) => i && i.templateType && typeof i.name === 'string' && i.name.trim()));
2026-02-06 18:04:34 +08:00
const useActionSheet = computed(() => selectableTemplates.value.length > 0 && selectableTemplates.value.length <= 6);
const fabPickerDisabled = computed(() => selectableTemplates.value.length === 0 || useActionSheet.value);
2026-02-06 17:15:09 +08:00
const templateMap = computed(() => templates.value.reduce((m, t) => {
if (t?.templateType) m[String(t.templateType)] = t;
return m;
}, {}));
const availableTypes = computed(() => (templates.value.length ? templates.value.map((i) => i.templateType) : FALLBACK_TEMPLATE_TYPES));
2026-01-22 15:54:15 +08:00
const typeRange = computed(() => [{ name: '全部', value: 'ALL' }, ...templates.value.map((t) => ({ name: t.name, value: t.templateType }))]);
const currentType = ref({ name: '全部', value: 'ALL' });
const records = ref([]);
const dateRange = ref([]);
2026-02-10 15:47:35 +08:00
const isDateRangeActive = computed(
() => Array.isArray(dateRange.value) && dateRange.value.length === 2 && dateRange.value[0] && dateRange.value[1]
);
const dateRangeLabel = computed(() => {
if (Array.isArray(dateRange.value) && dateRange.value.length === 2 && dateRange.value[0] && dateRange.value[1]) {
return `${dateRange.value[0]}${dateRange.value[1]}`;
}
return '全部时间';
});
2026-01-22 17:39:23 +08:00
2026-01-27 16:46:36 +08:00
function getCurrentTeamId() {
const team = uni.getStorageSync('ykt_case_current_team') || {};
return team?.teamId ? String(team.teamId) : '';
}
2026-01-22 17:39:23 +08:00
const teamId = ref(getCurrentTeamId());
const showShareTip = computed(() => {
if (props.data && typeof props.data.shareAllTeams === 'boolean') return props.data.shareAllTeams;
const list = props.data?.authorizedTeams;
return Array.isArray(list) && list.length > 1;
});
const shareAllTeamsForQuery = computed(() => {
if (props.data && typeof props.data.shareAllTeams === 'boolean') return props.data.shareAllTeams;
const list = props.data?.authorizedTeams;
if (Array.isArray(list)) return list.length > 1;
// mock 场景:没有授权信息时默认互通,避免误隐藏数据
return true;
});
2026-01-27 16:46:36 +08:00
function getCorpId() {
const team = uni.getStorageSync('ykt_case_current_team') || {};
return team?.corpId ? String(team.corpId) : '';
}
2026-02-06 17:15:09 +08:00
const loadedCorpId = ref('');
2026-02-09 15:40:03 +08:00
let loadVisitTemplatesPromise = null;
let loadVisitTemplatesCorpId = '';
2026-02-06 17:15:09 +08:00
async function loadVisitTemplates() {
const corpId = getCorpId();
if (!corpId) return;
if (loadedCorpId.value === corpId && templates.value.length) return;
2026-02-09 15:40:03 +08:00
if (loadVisitTemplatesPromise && loadVisitTemplatesCorpId === corpId) return loadVisitTemplatesPromise;
2026-02-06 17:15:09 +08:00
2026-02-09 15:40:03 +08:00
loadVisitTemplatesCorpId = corpId;
loadVisitTemplatesPromise = (async () => {
2026-02-06 17:15:09 +08:00
const groupRes = await api('getTemplateGroup', { corpId, parentType: 'medicalRecord' });
const group = groupRes?.data && Array.isArray(groupRes.data?.data) ? groupRes.data.data : Array.isArray(groupRes?.data) ? groupRes.data : [];
const list = Array.isArray(group) ? group : [];
2026-02-09 15:40:03 +08:00
const groupNameMap = list.reduce((m, i) => {
const t = i?.templateType ? String(i.templateType) : '';
const name = i?.name ? String(i.name) : '';
if (t && name) m[t] = name;
return m;
}, {});
2026-02-06 17:15:09 +08:00
const enabled = list.filter((i) => i && i.templateType !== 'healthTemplate' && i.templateStatus !== 'disable');
const typeList = enabled.map((i) => String(i.templateType || '')).filter(Boolean);
if (!typeList.length) return;
const detailRes = await api('getTemplateListByTemptype', { corpId, templateTypeList: typeList });
const detail = detailRes?.data && Array.isArray(detailRes.data?.data) ? detailRes.data.data : Array.isArray(detailRes?.data) ? detailRes.data : [];
const temps = Array.isArray(detail) ? detail : [];
const byType = temps.reduce((m, t) => {
const k = t?.templateType ? String(t.templateType) : '';
if (k) m[k] = t;
return m;
}, {});
const ordered = typeList.map((t) => byType[String(t)]).filter(Boolean);
const next = ordered
.map((t) => {
const temp = normalizeTemplate(t);
2026-02-09 15:40:03 +08:00
const rawType = String(temp?.templateType || '');
const name = String(groupNameMap[rawType] || temp?.name || temp?.templateName || temp?.templateTypeName || '') || rawType;
2026-02-06 17:15:09 +08:00
return {
2026-02-09 15:40:03 +08:00
templateType: rawType,
2026-02-06 17:15:09 +08:00
name,
service: temp?.service || {},
templateList: Array.isArray(temp?.templateList) ? temp.templateList : [],
};
})
.filter((i) => i && i.templateType);
2026-02-09 15:40:03 +08:00
if (next.length) {
templates.value = next;
loadedCorpId.value = corpId;
}
})()
.catch((e) => {
console.error('loadVisitTemplates error:', e);
})
.finally(() => {
if (loadVisitTemplatesCorpId === corpId) {
loadVisitTemplatesPromise = null;
loadVisitTemplatesCorpId = '';
}
});
return loadVisitTemplatesPromise;
2026-02-06 17:15:09 +08:00
}
2026-01-27 16:46:36 +08:00
const userNameMap = ref({});
const loadedTeamId = ref('');
function resolveUserName(userId) {
const id = String(userId || '');
if (!id) return '';
const map = userNameMap.value || {};
return String(map[id] || '') || id;
}
async function loadTeamMembers() {
const team = uni.getStorageSync('ykt_case_current_team') || {};
const teamId = team?.teamId ? String(team.teamId) : '';
const corpId = getCorpId();
if (!teamId || !corpId) return;
if (loadedTeamId.value === teamId && Object.keys(userNameMap.value || {}).length > 0) return;
loadedTeamId.value = teamId;
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 : [];
userNameMap.value = members.reduce((acc, m) => {
const uid = String(m?.userid || '');
if (!uid) return acc;
acc[uid] = String(m?.anotherName || m?.name || m?.userid || '') || uid;
return acc;
}, {});
}
function getSortTimeTitle(templateType) {
2026-02-09 15:40:03 +08:00
const rawType = String(templateType || '');
2026-02-10 15:47:35 +08:00
const ui = normalizeMedicalType(rawType);
// 预问诊记录:列表时间固定使用就诊日期字段,避免被模板配置的 timeTitle 覆盖
if (ui === 'preConsultationRecord') return 'consultationDate';
2026-02-09 15:40:03 +08:00
const t = templateMap.value[rawType] || {};
2026-02-06 17:15:09 +08:00
if (t?.service?.timeTitle) return String(t.service.timeTitle);
2026-02-09 15:40:03 +08:00
if (ui === 'outpatient') return 'visitTime';
if (ui === 'inhospital') return 'inhosDate';
if (ui === 'physicalExaminationTemplate') return 'inspectDate';
2026-01-27 16:46:36 +08:00
return '';
}
function normalizeText(v) {
if (Array.isArray(v)) return v.filter((i) => i !== null && i !== undefined && String(i).trim()).join('');
if (v === 0) return '0';
return v ? String(v) : '';
}
function formatPositiveFind(v, { withOpinion = false } = {}) {
if (Array.isArray(v)) {
const list = v
.map((i) => (i && typeof i === 'object' ? { category: i.category, opinion: i.opinion } : null))
.filter((i) => i && (i.category || i.opinion));
if (!list.length) return '';
if (!withOpinion) return list.map((i) => String(i.category || '').trim()).filter(Boolean).join('');
return list
.map((i) => {
const c = String(i.category || '').trim();
const o = String(i.opinion || '').trim();
if (c && o) return `${c}${o}`;
return c || o;
})
.filter(Boolean)
.join('');
}
return normalizeText(v);
}
function getTemplateName(type) {
2026-02-06 17:15:09 +08:00
const t = templateMap.value[String(type || '')];
return t?.name ? String(t.name) : '';
2026-01-27 16:46:36 +08:00
}
function toDateStr(sortTime) {
2026-02-09 15:40:03 +08:00
return formatAnyDate(sortTime, 'YYYY-MM-DD');
}
function normalizeMedicalType(raw) {
const s = String(raw || '').trim();
if (!s) return '';
const lower = s.toLowerCase();
2026-02-10 15:47:35 +08:00
if (lower.includes('preconsult') || (lower.includes('pre') && lower.includes('consult'))) return 'preConsultationRecord';
2026-02-09 15:40:03 +08:00
if (lower === 'outpatient' || lower === 'out_patient' || lower === 'out-patient') return 'outpatient';
if (lower === 'inhospital' || lower === 'in_hospital' || lower === 'in-hospital' || lower === 'inpatient') return 'inhospital';
2026-02-10 15:47:35 +08:00
if (lower === 'preconsultation' || lower === 'pre_consultation' || lower === 'pre-consultation') return 'preConsultationRecord';
2026-02-09 15:40:03 +08:00
if (lower === 'physicalexaminationtemplate' || lower === 'physicalexamination' || lower === 'physical_examination') return 'physicalExaminationTemplate';
if (s === 'outPatient') return 'outpatient';
if (s === 'inHospital') return 'inhospital';
2026-02-10 15:47:35 +08:00
if (s === 'preConsultation') return 'preConsultationRecord';
if (s === 'preConsultationRecord') return 'preConsultationRecord';
2026-02-09 15:40:03 +08:00
if (s === 'physicalExaminationTemplate') return 'physicalExaminationTemplate';
return s;
}
function parseAnyTimeMs(v) {
if (v === null || v === undefined) return 0;
if (typeof v === 'number') {
// 10位秒级时间戳
if (v > 1e9 && v < 1e12) return v * 1000;
return v;
}
const s = String(v).trim();
if (!s) return 0;
if (/^\d{10,13}$/.test(s)) return Number(s.length === 10 ? `${s}000` : s);
const d = dayjs(s);
return d.isValid() ? d.valueOf() : 0;
}
function formatAnyDate(v, fmt = 'YYYY-MM-DD') {
const ms = parseAnyTimeMs(v);
if (!ms) return '';
const d = dayjs(ms);
return d.isValid() ? d.format(fmt) : '';
2026-01-27 16:46:36 +08:00
}
function toDateTimeStr(ts) {
2026-02-09 15:40:03 +08:00
return formatAnyDate(ts, 'YYYY-MM-DD HH:mm');
2026-01-27 16:46:36 +08:00
}
async function refreshList() {
2026-01-22 15:54:15 +08:00
if (!props.archiveId) return;
2026-01-27 16:46:36 +08:00
const corpId = getCorpId();
if (!corpId) return;
2026-02-06 17:15:09 +08:00
await loadVisitTemplates();
2026-01-27 16:46:36 +08:00
loadTeamMembers();
loading('加载中...');
try {
const params = { memberId: props.archiveId, corpId };
// 添加类型筛选
params.medicalType =
2026-02-06 17:15:09 +08:00
currentType.value.value === 'ALL' ? availableTypes.value : currentType.value.value;
2026-01-27 16:46:36 +08:00
// 添加时间筛选
if (Array.isArray(dateRange.value) && dateRange.value.length === 2 && dateRange.value[0] && dateRange.value[1]) {
params.startTime = dayjs(dateRange.value[0]).startOf('day').valueOf();
params.endTime = dayjs(dateRange.value[1]).endOf('day').valueOf();
2026-01-27 16:46:36 +08:00
}
const res = await api('getCustomerMedicalRecord', params);
hideLoading();
// 接口返回结构兼容:
// - { success, message, list: [] }
// - { success, message, data: { list: [] } }
const list = Array.isArray(res?.list)
? res.list
: Array.isArray(res?.data?.list)
? res.data.list
2026-02-09 15:40:03 +08:00
: Array.isArray(res?.data?.data)
? res.data.data
: Array.isArray(res?.data?.data?.list)
? res.data.data.list
2026-01-27 16:46:36 +08:00
: Array.isArray(res?.data)
? res.data
: [];
if (list.length) {
const mapped = list.map((r) => {
2026-02-09 15:40:03 +08:00
const rawType = String(r?.medicalType || r?.templateType || '') || '';
const uiType = normalizeMedicalType(rawType);
const normalized = normalizeVisitRecordFormData(uiType, r);
const timeTitle = getSortTimeTitle(rawType);
const rawTime = timeTitle ? (normalized?.[timeTitle] ?? r?.[timeTitle]) : '';
const dateStr = rawTime ? formatAnyDate(rawTime, 'YYYY-MM-DD') : '';
2026-01-27 16:46:36 +08:00
return {
...r,
2026-02-09 15:40:03 +08:00
...normalized,
medicalType: rawType,
templateType: uiType,
rawTemplateType: rawType,
2026-01-27 16:46:36 +08:00
dateStr: dateStr || toDateStr(r?.sortTime),
2026-02-09 15:40:03 +08:00
createDateStr: r?.createTime ? formatAnyDate(r.createTime, 'YYYY-MM-DD') : '',
2026-01-27 16:46:36 +08:00
createTimeStr: toDateTimeStr(r?.createTime),
2026-02-09 15:40:03 +08:00
tempName: r?.tempName || getTemplateName(rawType) || '病历',
2026-01-27 16:46:36 +08:00
};
});
// 未互通时,仅展示本团队数据
records.value = !shareAllTeamsForQuery.value && teamId.value
? mapped.filter((i) => String(i?.teamId || '') === String(teamId.value))
: mapped;
} else {
records.value = [];
}
} catch (error) {
hideLoading();
console.error('获取病历记录失败:', error);
records.value = [];
}
2026-01-22 15:54:15 +08:00
}
const tagClass = {
outpatient: 'bg-amber',
inhospital: 'bg-teal',
2026-02-10 15:47:35 +08:00
preConsultationRecord: 'bg-indigo',
2026-01-22 15:54:15 +08:00
physicalExaminationTemplate: 'bg-green',
};
2026-02-09 15:40:03 +08:00
function resolveRecordType(r) {
if (!r) return '';
const direct = normalizeMedicalType(r.uiType || r.templateType || r.rawTemplateType || r.medicalType || '');
if (direct) return direct;
// fallback by known fields
if (r.inspectDate || r.positiveFind || r.inspectSummary) return 'physicalExaminationTemplate';
if (r.inhosDate || r.surgeryName || r.surgeryDate || r.operationDate) return 'inhospital';
if (r.visitTime || r.disposePlan || r.treatmentPlan) return 'outpatient';
2026-02-10 15:47:35 +08:00
if (r.consultationDate || r.consultDate || r.presentIllness || r.presentIllnessHistory || r.pastHistory) return 'preConsultationRecord';
2026-02-09 15:40:03 +08:00
return '';
}
2026-01-22 17:39:23 +08:00
function getDiagnosis(r) {
if (!r) return '--';
2026-02-09 15:40:03 +08:00
const t = resolveRecordType(r);
2026-02-10 15:47:35 +08:00
if (t === 'preConsultationRecord') return normalizeText(r.chiefComplaint) || normalizeText(r.summary) || '--';
2026-01-27 16:46:36 +08:00
if (t === 'physicalExaminationTemplate') return formatPositiveFind(r.positiveFind) || normalizeText(r.summary) || '--';
if (t === 'outpatient' || t === 'inhospital') return normalizeText(r.diagnosisName || r.diagnosis) || normalizeText(r.summary) || '--';
return normalizeText(r.diagnosisName || r.diagnosis || r.summary) || '--';
}
function firstLine(v) {
const s = normalizeText(v);
return s || '--';
}
function getDisplayLines(r) {
2026-02-09 15:40:03 +08:00
const t = resolveRecordType(r);
2026-01-27 16:46:36 +08:00
if (t === 'outpatient') {
return [{ label: '门诊诊断:', value: firstLine(r.diagnosisName || r.diagnosis) }];
}
if (t === 'inhospital') {
const lines = [{ label: '入院诊断:', value: firstLine(r.diagnosisName || r.diagnosis) }];
const surgery = normalizeText(r.surgeryName);
if (surgery) lines.push({ label: '手术名称:', value: surgery });
return lines;
}
if (t === 'physicalExaminationTemplate') {
2026-02-09 15:40:03 +08:00
return [{ label: '体检小结:', value: firstLine(r.summary || r.inspectSummary) }];
2026-01-27 16:46:36 +08:00
}
2026-02-10 15:47:35 +08:00
if (t === 'preConsultationRecord') {
2026-01-27 16:46:36 +08:00
const lines = [
{ label: '主诉:', value: firstLine(r.chiefComplaint) },
2026-02-09 15:40:03 +08:00
{ label: '现病史:', value: firstLine(r.presentIllness || r.presentIllnessHistory) },
2026-01-27 16:46:36 +08:00
];
const past = normalizeText(r.pastHistory);
if (past) lines.push({ label: '既往史:', value: past });
return lines;
}
return [{ label: '摘要:', value: firstLine(r.summary) }];
}
function getCreateFooter(r) {
const time = r?.createTimeStr || r?.createDateStr || '';
const byCustomer = r?.ignore === 'checkIn';
if (byCustomer) return time ? `${time} 患者自建` : '患者自建';
const creatorId = String(r?.creator || '');
if (!creatorId) return time ? `创建时间:${time}` : '';
const name = resolveUserName(creatorId);
return time ? `${time} ${name}代建` : `${name}代建`;
2026-01-22 15:54:15 +08:00
}
function pickType(e) {
currentType.value = typeRange.value[e.detail.value] || { name: '全部', value: 'ALL' };
refreshList();
}
function pickTimeRange(val) {
2026-02-10 15:47:35 +08:00
if (Array.isArray(val)) {
dateRange.value = val;
} else if (val && typeof val === 'object' && Array.isArray(val.value)) {
dateRange.value = val.value;
} else if (val && typeof val === 'object' && val.detail && Array.isArray(val.detail.value)) {
dateRange.value = val.detail.value;
} else {
dateRange.value = [];
}
refreshList();
}
function clearTimeRange() {
dateRange.value = [];
2026-01-22 15:54:15 +08:00
refreshList();
}
2026-01-22 17:39:23 +08:00
function getFiles(r) {
const arr = r?.files;
2026-02-09 15:40:03 +08:00
return Array.isArray(arr)
? arr
.filter((i) => i && i.url)
.map((i) => ({ ...i, url: normalizeFileUrl(i.url) }))
: [];
2026-01-22 17:39:23 +08:00
}
function previewFiles(r, idx) {
const urls = getFiles(r).map((i) => i.url);
if (!urls.length) return;
uni.previewImage({ urls, current: urls[idx] });
2026-01-22 15:54:15 +08:00
}
2026-02-06 18:04:34 +08:00
function goAdd(t) {
if (!t?.templateType) return;
uni.navigateTo({
url: `/pages/case/visit-record-detail?archiveId=${encodeURIComponent(props.archiveId)}&type=${encodeURIComponent(t.templateType)}&name=${encodeURIComponent(props.data?.name || '')}`,
});
}
2026-02-06 17:15:09 +08:00
function pickAddType(e) {
if (!props.archiveId) return toast('缺少档案信息');
const idx = Number(e?.detail?.value ?? -1);
const t = selectableTemplates.value[idx];
2026-02-06 18:04:34 +08:00
if (!t) return;
goAdd(t);
}
function showAddActionSheet() {
if (!props.archiveId) return toast('缺少档案信息');
const list = selectableTemplates.value;
if (!list.length) return toast('暂无可用病历模板');
uni.showActionSheet({
itemList: list.map((i) => i.name),
success: ({ tapIndex }) => {
const t = list[tapIndex];
if (!t) return;
goAdd(t);
},
fail: (e) => {
// 用户取消无需提示;其他错误忽略
const errMsg = String(e?.errMsg || '');
if (errMsg && errMsg.includes('cancel')) return;
if (errMsg) console.warn('[health-profile-tab] showActionSheet fail:', errMsg);
},
2026-01-22 15:54:15 +08:00
});
}
2026-02-06 18:04:34 +08:00
async function onFabTap() {
if (!props.archiveId) return toast('缺少档案信息');
// 模板可能尚未加载:先拉一次
if (!templates.value.length) {
loading('加载模板...');
try {
await loadVisitTemplates();
} finally {
hideLoading();
}
}
// <=6用 actionSheet>6由 picker 自己弹(这里不做额外处理)
if (useActionSheet.value) showAddActionSheet();
}
2026-01-22 15:54:15 +08:00
function edit(record) {
2026-01-27 16:46:36 +08:00
const type = String(record?.medicalType || record?.templateType || '') || '';
2026-01-22 15:54:15 +08:00
uni.navigateTo({
2026-01-27 16:46:36 +08:00
url: `/pages/case/visit-record-view?archiveId=${encodeURIComponent(props.archiveId)}&id=${encodeURIComponent(record._id)}&type=${encodeURIComponent(type)}`,
2026-01-22 15:54:15 +08:00
});
}
onMounted(() => {
uni.$on('archive-detail:visit-record-changed', refreshList);
});
onUnmounted(() => {
uni.$off('archive-detail:visit-record-changed', refreshList);
});
2026-01-27 16:46:36 +08:00
watch(
() => props.archiveId,
(v, old) => {
const next = v ? String(v) : '';
const prev = old ? String(old) : '';
if (next && next !== prev) refreshList();
},
{ immediate: true }
);
2026-01-22 15:54:15 +08:00
</script>
<style scoped>
.wrap {
2026-01-28 20:01:28 +08:00
padding: 24rpx 0 192rpx;
2026-01-22 15:54:15 +08:00
}
.filters {
display: flex;
2026-01-28 20:01:28 +08:00
gap: 20rpx;
padding: 20rpx 28rpx;
2026-01-22 15:54:15 +08:00
background: #f5f6f8;
2026-01-28 20:01:28 +08:00
border-bottom: 2rpx solid #f2f2f2;
2026-01-22 15:54:15 +08:00
}
2026-02-11 15:24:39 +08:00
2026-01-22 15:54:15 +08:00
.filter-pill {
background: #fff;
2026-01-28 20:01:28 +08:00
border: 2rpx solid #e6e6e6;
border-radius: 12rpx;
padding: 20rpx 24rpx;
2026-01-22 15:54:15 +08:00
display: flex;
align-items: center;
justify-content: space-between;
2026-01-28 20:01:28 +08:00
gap: 20rpx;
2026-01-22 17:39:23 +08:00
flex: 1;
2026-01-22 15:54:15 +08:00
}
.pill-text {
2026-01-28 20:01:28 +08:00
font-size: 26rpx;
2026-01-22 15:54:15 +08:00
color: #333;
2026-01-28 20:01:28 +08:00
max-width: 360rpx;
2026-01-22 15:54:15 +08:00
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
2026-02-10 15:47:35 +08:00
.pill-icons {
display: flex;
align-items: center;
gap: 12rpx;
flex-shrink: 0;
}
.pill-clear {
display: flex;
align-items: center;
justify-content: center;
}
2026-01-22 17:39:23 +08:00
.share-tip {
2026-01-28 20:01:28 +08:00
padding: 20rpx 28rpx 0;
2026-01-22 15:54:15 +08:00
}
2026-01-22 17:39:23 +08:00
.share-tip-text {
display: inline-block;
background: #eef2ff;
color: #4338ca;
2026-01-28 20:01:28 +08:00
font-size: 24rpx;
padding: 12rpx 20rpx;
border-radius: 12rpx;
2026-01-22 15:54:15 +08:00
}
.list {
2026-01-28 20:01:28 +08:00
padding: 0 28rpx;
2026-01-22 15:54:15 +08:00
}
2026-02-11 15:24:39 +08:00
2026-01-22 15:54:15 +08:00
.card {
background: #fff;
2026-01-28 20:01:28 +08:00
border-radius: 20rpx;
margin-top: 20rpx;
2026-01-22 15:54:15 +08:00
overflow: hidden;
2026-01-28 20:01:28 +08:00
box-shadow: 0 12rpx 28rpx rgba(0, 0, 0, 0.06);
2026-01-22 15:54:15 +08:00
}
.record {
padding: 0;
}
.record-head {
display: flex;
align-items: center;
2026-01-28 20:01:28 +08:00
padding: 24rpx 24rpx 20rpx;
gap: 16rpx;
2026-01-22 15:54:15 +08:00
}
.record-title {
2026-01-28 20:01:28 +08:00
font-size: 30rpx;
2026-01-22 15:54:15 +08:00
font-weight: 600;
color: #1f1f1f;
}
.record-date {
2026-01-28 20:01:28 +08:00
font-size: 28rpx;
2026-01-22 15:54:15 +08:00
font-weight: 600;
color: #333;
}
.record-body {
2026-01-28 20:01:28 +08:00
padding: 0 24rpx 24rpx;
2026-01-22 15:54:15 +08:00
}
2026-01-22 17:39:23 +08:00
.line {
2026-01-22 15:54:15 +08:00
display: flex;
2026-01-28 20:01:28 +08:00
padding-top: 20rpx;
font-size: 26rpx;
2026-01-22 15:54:15 +08:00
color: #333;
2026-01-28 20:01:28 +08:00
line-height: 36rpx;
2026-01-22 15:54:15 +08:00
}
2026-01-22 17:39:23 +08:00
.line-label {
2026-01-22 15:54:15 +08:00
flex-shrink: 0;
color: #666;
}
2026-01-22 17:39:23 +08:00
.line-value {
2026-01-22 15:54:15 +08:00
flex: 1;
2026-01-22 17:39:23 +08:00
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
2026-01-27 16:46:36 +08:00
white-space: nowrap;
2026-01-22 17:39:23 +08:00
}
.thumbs {
2026-01-28 20:01:28 +08:00
padding-top: 20rpx;
2026-01-22 17:39:23 +08:00
display: flex;
2026-01-28 20:01:28 +08:00
gap: 20rpx;
2026-01-22 17:39:23 +08:00
align-items: center;
flex-wrap: wrap;
}
.thumb {
2026-01-28 20:01:28 +08:00
width: 168rpx;
height: 128rpx;
border-radius: 12rpx;
2026-01-22 17:39:23 +08:00
overflow: hidden;
background: #f3f4f6;
2026-01-28 20:01:28 +08:00
border: 2rpx solid #e5e7eb;
2026-01-22 17:39:23 +08:00
}
.thumb-img {
2026-01-28 20:01:28 +08:00
width: 168rpx;
height: 128rpx;
2026-01-22 17:39:23 +08:00
}
.thumb-more {
2026-01-28 20:01:28 +08:00
font-size: 24rpx;
2026-01-22 17:39:23 +08:00
color: #6b7280;
2026-01-22 15:54:15 +08:00
}
.record-foot {
display: flex;
align-items: center;
justify-content: space-between;
2026-01-28 20:01:28 +08:00
padding: 24rpx 24rpx;
border-top: 2rpx solid #f2f2f2;
font-size: 24rpx;
2026-01-22 15:54:15 +08:00
color: #999;
}
.foot-left {
flex-shrink: 0;
2026-01-28 20:01:28 +08:00
margin-right: 20rpx;
2026-01-22 15:54:15 +08:00
}
.record-tag {
2026-01-28 20:01:28 +08:00
font-size: 24rpx;
2026-01-22 15:54:15 +08:00
color: #fff;
2026-01-28 20:01:28 +08:00
padding: 8rpx 16rpx;
border-radius: 16rpx;
2026-01-22 15:54:15 +08:00
}
.bg-blue {
2026-02-02 15:15:51 +08:00
background: #0877F1;
2026-01-22 15:54:15 +08:00
}
.bg-amber {
background: #d97706;
}
.bg-teal {
background: #0f766e;
}
2026-01-22 17:39:23 +08:00
.bg-indigo {
background: #4f46e5;
}
2026-01-22 15:54:15 +08:00
.bg-green {
background: #16a34a;
}
.bg-rose {
background: #f43f5e;
}
.empty {
2026-01-28 20:01:28 +08:00
padding: 240rpx 0;
2026-01-22 15:54:15 +08:00
text-align: center;
color: #9aa0a6;
2026-01-28 20:01:28 +08:00
font-size: 26rpx;
2026-01-22 15:54:15 +08:00
}
2026-02-06 17:15:09 +08:00
.fab-picker {
2026-01-22 15:54:15 +08:00
position: fixed;
2026-01-28 20:01:28 +08:00
right: 32rpx;
2026-02-06 17:15:09 +08:00
width: 104rpx;
height: 104rpx;
z-index: 20;
}
.fab {
2026-01-28 20:01:28 +08:00
width: 104rpx;
height: 104rpx;
border-radius: 52rpx;
2026-02-02 15:15:51 +08:00
background: #0877F1;
2026-01-22 15:54:15 +08:00
display: flex;
align-items: center;
justify-content: center;
2026-01-28 20:01:28 +08:00
box-shadow: 0 20rpx 36rpx rgba(79, 110, 247, 0.35);
2026-02-06 17:15:09 +08:00
}
.fab--disabled {
opacity: 0.5;
2026-01-22 15:54:15 +08:00
}
</style>