ykt-wxapp/pages/case/visit-record-detail.vue

329 lines
8.6 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="header">
<view class="header-title">{{ template?.templateName || '健康档案' }}</view>
</view>
<view class="form-wrap">
<FormTemplate v-if="template" ref="formRef" :items="showItems" :form="forms" @change="onChange" />
</view>
<!-- 附件上传FormTemplate 不支持 files单独实现 -->
<view v-if="hasFilesField" class="upload-wrap">
<view class="upload-title">文件上传</view>
<view class="upload-grid">
<view v-for="(f, idx) in fileList" :key="idx" class="upload-item" @click="previewFile(idx)">
<image class="upload-thumb" :src="f.url" mode="aspectFill" />
<view class="upload-remove" @click.stop="removeFile(idx)">×</view>
</view>
<view class="upload-add" @click="addFiles">
<view class="plus">+</view>
</view>
</view>
</view>
<view style="height: 120px;"></view>
</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 FormTemplate from '@/components/form-template/index.vue';
import { ensureSeed, getCurrentTeamId, getVisitRecord, getVisitRecordTemplate, removeVisitRecord, upsertVisitRecord } from '@/components/archive-detail/mock';
const archiveId = ref('');
const recordId = ref('');
const templateType = ref('');
const template = computed(() => getVisitRecordTemplate(templateType.value));
const detail = ref({});
const form = reactive({});
const forms = computed(() => ({ ...detail.value, ...form }));
const showItems = computed(() => {
const list = template.value?.templateList || [];
// referenceField 兼容(与 mobile 一致)
return list.filter((i) => {
if (i?.type === 'files') return false;
if (i && typeof i.referenceField === 'string') {
return forms.value[i.referenceField] === i.referenceValue;
}
return true;
});
});
const hasFilesField = computed(() => {
const list = template.value?.templateList || [];
return list.some((i) => i && (i.type === 'files' || i.title === 'files'));
});
const formRef = ref(null);
const fileList = computed(() => {
const arr = forms.value?.files;
return Array.isArray(arr) ? arr.filter((i) => i && i.url) : [];
});
function ensureFilesField() {
if (form.files !== undefined) return;
if (detail.value && detail.value.files !== undefined) return;
form.files = [];
}
onLoad((options) => {
archiveId.value = options?.archiveId ? String(options.archiveId) : '';
recordId.value = options?.id ? String(options.id) : '';
templateType.value = options?.type ? String(options.type) : '';
ensureSeed(archiveId.value, { name: options?.name ? String(options.name) : '' });
if (recordId.value) {
const record = getVisitRecord({ archiveId: archiveId.value, id: recordId.value });
if (record) {
templateType.value = record.templateType || record.medicalType || templateType.value;
detail.value = record;
ensureFilesField();
return;
}
}
if (!templateType.value) templateType.value = 'outpatient';
ensureFilesField();
});
function onChange({ title, value }) {
form[title] = value;
const item = showItems.value.find((i) => i.title === title);
if (!item) return;
// 关联字段变化时清理被关联字段(与 mobile 行为一致)
const relat = (template.value?.templateList || []).filter((i) => i.referenceField === title);
relat.forEach((i) => (form[i.title] = ''));
}
function cancel() {
uni.navigateBack();
}
function save() {
if (!archiveId.value) {
uni.showToast({ title: '缺少 archiveId', icon: 'none' });
return;
}
if (formRef.value?.verify && !formRef.value.verify()) return;
// sortTime尽量用模板中的日期字段
const timeTitle =
templateType.value === 'outpatient'
? 'visitTime'
: templateType.value === 'inhospital'
? 'inhosDate'
: templateType.value === 'preConsultation'
? 'consultDate'
: templateType.value === 'physicalExaminationTemplate'
? 'inspectDate'
: '';
const timeValue = timeTitle ? forms.value[timeTitle] : '';
const sortTime = timeValue && dayjs(timeValue).isValid() ? dayjs(timeValue).valueOf() : Date.now();
upsertVisitRecord({
archiveId: archiveId.value,
record: {
_id: recordId.value || '',
medicalType: templateType.value,
templateType: templateType.value,
tempName: template.value?.templateName || '健康档案',
sortTime,
teamId: detail.value?.teamId || getCurrentTeamId(),
...form,
files: fileList.value,
createTime: detail.value?.createTime || Date.now(),
creatorName: detail.value?.creatorName || '我',
},
});
uni.$emit('archive-detail:visit-record-changed');
uni.showToast({ title: '保存成功', icon: 'success' });
setTimeout(() => uni.navigateBack(), 300);
}
function remove() {
uni.showModal({
title: '提示',
content: '确定删除当前记录?',
success: (res) => {
if (!res.confirm) return;
removeVisitRecord({ archiveId: archiveId.value, id: recordId.value });
uni.$emit('archive-detail:visit-record-changed');
uni.showToast({ title: '已删除', icon: 'success' });
setTimeout(() => uni.navigateBack(), 300);
},
});
}
function addFiles() {
uni.chooseImage({
count: 9,
success: (res) => {
const paths = Array.isArray(res.tempFilePaths) ? res.tempFilePaths : [];
const next = paths.map((p) => ({ url: p, name: '' }));
const cur = Array.isArray(forms.value.files) ? forms.value.files : [];
form.files = [...cur, ...next];
},
});
}
function removeFile(idx) {
const cur = Array.isArray(forms.value.files) ? forms.value.files : [];
form.files = cur.filter((_, i) => i !== idx);
}
function previewFile(idx) {
const urls = fileList.value.map((i) => i.url);
uni.previewImage({ urls, current: urls[idx] });
}
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f5f6f8;
padding-bottom: calc(76px + env(safe-area-inset-bottom));
}
.body {
height: 100vh;
display: flex;
flex-direction: column;
}
.scroll {
flex: 1;
}
.header {
background: #fff;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06);
}
.header-title {
padding: 14px 14px;
font-size: 16px;
font-weight: 600;
color: #333;
}
.form-wrap {
background: #fff;
margin-top: 10px;
padding: 4px 0;
}
.upload-wrap {
background: #fff;
margin: 10px 14px 0;
border-radius: 8px;
padding: 14px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06);
}
.upload-title {
font-size: 14px;
font-weight: 700;
color: #111827;
margin-bottom: 10px;
}
.upload-grid {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.upload-item {
width: 90px;
height: 70px;
position: relative;
border: 1px solid #d1d5db;
background: #f9fafb;
}
.upload-thumb {
width: 90px;
height: 70px;
}
.upload-remove {
position: absolute;
right: 0;
top: 0;
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
background: rgba(0, 0, 0, 0.55);
color: #fff;
font-size: 14px;
}
.upload-add {
width: 90px;
height: 70px;
border: 1px dashed #c7c7c7;
display: flex;
align-items: center;
justify-content: center;
color: #666;
}
.plus {
font-size: 26px;
line-height: 26px;
}
.footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #fff;
padding: 12px 14px calc(12px + env(safe-area-inset-bottom));
display: flex;
gap: 12px;
box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.06);
}
.btn {
flex: 1;
height: 44px;
line-height: 44px;
border-radius: 6px;
font-size: 15px;
}
.btn::after {
border: none;
}
.btn.plain {
background: #fff;
color: #4f6ef7;
border: 1px solid #4f6ef7;
}
.btn.primary {
background: #4f6ef7;
color: #fff;
}
.delete-fab {
position: fixed;
right: 16px;
bottom: calc(96px + env(safe-area-inset-bottom));
width: 52px;
height: 52px;
border-radius: 26px;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 10px 18px rgba(0, 0, 0, 0.12);
z-index: 30;
}
</style>