ykt-wxapp/pages/case/visit-record-detail.vue
2026-02-10 15:47:35 +08:00

386 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="page">
<!-- Mobile 来源: ykt-management-mobile/src/pages/customer/visit-record-detail/visit-record-detail.vue -->
<view class="body">
<scroll-view scroll-y class="scroll">
<view class="form-wrap">
<FormTemplate v-if="temp" ref="formRef" :items="showItems" :form="forms" @change="onChange" />
</view>
<view class="scroll-gap" />
</scroll-view>
<view class="footer">
<button class="btn plain" @click="cancel">取消</button>
<button class="btn primary" @click="save">保存</button>
</view>
</view>
<view v-if="recordId" class="delete-fab" @click="remove">
<uni-icons type="trash" size="22" color="#ff4d4f" />
</view>
</view>
</template>
<script setup>
import { computed, reactive, ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import dayjs from 'dayjs';
import { storeToRefs } from 'pinia';
import FormTemplate from '@/components/form-template/index.vue';
import api from '@/utils/api';
import useAccountStore from '@/store/account';
import { toast, confirm, loading as uniLoading, hideLoading } from '@/utils/widget';
import { normalizeVisitRecordFormData } from './utils/visit-record';
import { normalizeTemplate, unwrapTemplateResponse } from './utils/template';
const accountStore = useAccountStore();
const { account, doctorInfo } = storeToRefs(accountStore);
const { getDoctorInfo } = accountStore;
async function ensureDoctor() {
if (doctorInfo.value) return;
if (!account.value?.openid) return;
try {
await getDoctorInfo();
} catch {
// ignore
}
}
function getUserId() {
const d = doctorInfo.value || {};
const a = account.value || {};
return String(d.userid || d.userId || d.corpUserId || a.userid || a.userId || '') || '';
}
function getCorpId() {
const d = doctorInfo.value || {};
const a = account.value || {};
const t = uni.getStorageSync('ykt_case_current_team') || {};
return String(d.corpId || a.corpId || t.corpId || '') || '';
}
const memberId = ref('');
const recordId = ref('');
const templateType = ref('');
const temp = ref(null);
const titleText = computed(() => {
const t = temp.value || {};
return String(t.name || t.templateName || t.templateTypeName || '').trim();
});
const detail = ref({});
const form = reactive({});
const forms = computed(() => ({ ...detail.value, ...form }));
const HIDDEN_FIELD_NAMES = {
outpatient: ['就诊机构', '就诊科室', '机构名称', '责任医生'],
inhospital: ['就诊机构', '机构名称'],
physicalExaminationTemplate: ['体检套餐名称'],
};
const HIDDEN_FIELD_TITLES = {
// 对应 systemFieldName/title来自模板
// - 就诊机构: corp
// - 就诊科室: deptName
// - 机构名称: corpName
// - 责任医生: doctor
outpatient: ['corp', 'deptName', 'corpName', 'doctor'],
// - 就诊机构: corp
// - 机构名称: corpName
inhospital: ['corp', 'corpName'],
// - 体检套餐名称: inspectPakageName
physicalExaminationTemplate: ['inspectPakageName'],
};
function shouldHideField(item) {
const t = String(templateType.value || '');
const name = item?.name ? String(item.name).trim() : '';
const title = item?.title ? String(item.title).trim() : '';
const hiddenNames = HIDDEN_FIELD_NAMES[t] || [];
const hiddenTitles = HIDDEN_FIELD_TITLES[t] || [];
return (name && hiddenNames.includes(name)) || (title && hiddenTitles.includes(title));
}
const showItems = computed(() => {
const list = temp.value?.templateList || [];
// referenceField 兼容(与 mobile 一致)
return list.filter((i) => {
if (shouldHideField(i)) return false;
if (i && typeof i.referenceField === 'string') {
return forms.value[i.referenceField] === i.referenceValue;
}
return true;
});
});
const formRef = ref(null);
function ensureFilesField() {
if (form.files !== undefined) return;
if (detail.value && detail.value.files !== undefined) return;
form.files = [];
}
async function loadTemplate(t) {
const corpId = getCorpId();
if (!corpId) return null;
try {
const res = await api('getCurrentTemplate', { corpId, templateType: t });
if (!res?.success) {
toast(res?.message || '获取模板失败');
return null;
}
const raw = unwrapTemplateResponse(res);
return normalizeTemplate(raw);
} catch (e) {
console.error('loadTemplate error:', e);
toast('获取模板失败');
return null;
}
}
onLoad(async (options) => {
memberId.value = options?.memberId || options?.archiveId || '';
recordId.value = options?.id || '';
templateType.value = options?.type || '';
if (recordId.value) {
await getDetail();
} else {
if (!templateType.value) templateType.value = 'outpatient';
temp.value = await loadTemplate(templateType.value);
if (temp.value?.templateType) templateType.value = String(temp.value.templateType);
ensureFilesField();
// 默认填充模板时间字段
const timeKey = temp.value?.service?.timeTitle || '';
if (timeKey && !form[timeKey]) form[timeKey] = dayjs().format('YYYY-MM-DD');
}
if (titleText.value) uni.setNavigationBarTitle({ title: titleText.value });
});
async function getDetail() {
if (!recordId.value || !memberId.value) return;
await ensureDoctor();
const corpId = getCorpId();
if (!corpId) return;
uniLoading('加载中...');
try {
const res = await api('getMedicalRecordById', {
_id: recordId.value,
corpId,
memberId: memberId.value,
medicalType: templateType.value,
});
hideLoading();
const record = res?.record || res?.data?.record || null;
if (res?.success && record) {
templateType.value = record.templateType || record.medicalType || templateType.value;
detail.value = normalizeVisitRecordFormData(templateType.value, record);
ensureFilesField();
// 详情可能返回真实 templateType与模板保持一致
if (!temp.value || temp.value?.templateType !== templateType.value) {
temp.value = await loadTemplate(templateType.value);
if (temp.value?.templateType) templateType.value = String(temp.value.templateType);
if (titleText.value) uni.setNavigationBarTitle({ title: titleText.value });
}
} else {
toast(res.message || '加载失败');
}
} catch (error) {
hideLoading();
console.error('getDetail error:', error);
toast('加载失败');
}
}
function onChange({ title, value }) {
form[title] = value;
const item = showItems.value.find((i) => i.title === title);
if (!item) return;
// 关联字段变化时清理被关联字段(与 mobile 行为一致)
const relat = (temp.value?.templateList || []).filter((i) => i.referenceField === title);
relat.forEach((i) => (form[i.title] = ''));
}
function cancel() {
uni.navigateBack();
}
async function save() {
if (!memberId.value) {
toast('缺少患者信息');
return;
}
await ensureDoctor();
const corpId = getCorpId();
const userId = getUserId();
if (!corpId || !userId) return toast('缺少用户信息');
if (formRef.value?.verify && !formRef.value.verify()) return;
const params = {
...form,
corpId,
memberId: memberId.value,
medicalType: templateType.value,
};
// 门诊/住院:与 mobile 字段对齐diagnosisName
if ((templateType.value === 'outpatient' || templateType.value === 'inhospital') && form.diagnosis && !form.diagnosisName) {
params.diagnosisName = form.diagnosis;
}
if (recordId.value) {
params._id = recordId.value;
params.userId = userId;
} else {
params.creator = userId;
}
// sortTime使用模板中的时间字段
const sortTimeKey = temp.value?.service?.timeTitle || '';
if (sortTimeKey && form[sortTimeKey] && dayjs(form[sortTimeKey]).isValid()) {
params.sortTime = dayjs(form[sortTimeKey]).valueOf();
} else {
params.sortTime = Date.now();
}
uniLoading('保存中...');
try {
const res = await api(
recordId.value ? 'updateMedicalRecord' : 'addMedicalRecord',
params
);
hideLoading();
if (res.success) {
uni.$emit('archive-detail:visit-record-changed');
toast(res.message || '保存成功');
setTimeout(() => uni.navigateBack(), 300);
} else {
toast(res.message || '保存失败');
}
} catch (error) {
hideLoading();
console.error('save error:', error);
toast('保存失败');
}
}
async function remove() {
try {
await confirm('确定删除当前记录?');
} catch {
return;
}
if (!memberId.value || !recordId.value) return toast('缺少必要信息');
await ensureDoctor();
const corpId = getCorpId();
if (!corpId) return toast('缺少必要信息');
uniLoading('删除中...');
try {
const res = await api('removeMedicalRecord', {
corpId,
memberId: memberId.value,
medicalType: templateType.value,
_id: recordId.value,
});
hideLoading();
if (res.success) {
uni.$emit('archive-detail:visit-record-changed');
toast(res.message || '已删除');
setTimeout(() => uni.navigateBack(), 300);
} else {
toast(res.message || '删除失败');
}
} catch (error) {
hideLoading();
console.error('remove error:', error);
toast('删除失败');
}
}
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f5f6f8;
}
.body {
height: 100vh;
display: flex;
flex-direction: column;
position: relative;
}
.scroll {
flex: 1;
min-height: 0;
position: relative;
z-index: 1;
}
.scroll-gap {
height: calc(240rpx + env(safe-area-inset-bottom));
}
.header {
background: #fff;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.06);
}
.header-title {
padding: 28rpx 28rpx;
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.form-wrap {
background: #fff;
margin-top: 20rpx;
padding: 8rpx 0;
}
.footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #fff;
padding: 24rpx 28rpx calc(24rpx + env(safe-area-inset-bottom));
display: flex;
gap: 24rpx;
box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.06);
z-index: 50;
}
.btn {
flex: 1;
height: 88rpx;
line-height: 88rpx;
border-radius: 12rpx;
font-size: 30rpx;
}
.btn::after {
border: none;
}
.btn.plain {
background: #fff;
color: #0877F1;
border: 2rpx solid #0877F1;
}
.btn.primary {
background: #0877F1;
color: #fff;
}
.delete-fab {
position: fixed;
right: 32rpx;
bottom: calc(192rpx + env(safe-area-inset-bottom));
width: 104rpx;
height: 104rpx;
border-radius: 52rpx;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 20rpx 36rpx rgba(0, 0, 0, 0.12);
z-index: 30;
}
</style>