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

363 lines
8.9 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>
<!-- 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">
<view class="pill-text">{{ currentType.value === 'ALL' ? '全部病历' : currentType.name }}</view>
<uni-icons type="arrowdown" size="12" color="#666" />
</view>
</picker>
<picker mode="selector" :range="timeRangeOptions" range-key="label" @change="pickTimeRange">
<view class="filter-pill">
<view class="pill-text">{{ currentTimeRange.label }}</view>
<uni-icons type="arrowdown" size="12" color="#666" />
</view>
</picker>
</view>
<view v-if="showShareTip" class="share-tip">
<text class="share-tip-text">患者已授权其他团队病历信息互通</text>
</view>
<view class="list">
<view v-for="r in records" :key="r._id" class="card record" @click="edit(r)">
<view class="record-head">
<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>
</view>
<view class="record-body">
<view class="line">
<text class="line-label">诊断</text>
<text class="line-value">{{ getDiagnosis(r) }}</text>
</view>
<view v-if="r.templateType === 'inhospital' && r.surgeryName" class="line">
<text class="line-label">手术</text>
<text class="line-value">{{ r.surgeryName }}</text>
</view>
<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>
</view>
</view>
<view class="record-foot">
<view class="foot-left">创建时间{{ r.createDateStr || '' }}</view>
<view class="foot-right">创建人{{ r.creatorName || '—' }}</view>
</view>
</view>
<view v-if="records.length === 0" class="empty">暂无数据</view>
</view>
<view class="fab" :style="{ bottom: `${floatingBottom}px` }" @click="add">
<uni-icons type="plusempty" size="24" color="#fff" />
</view>
</view>
</template>
<script setup>
import { computed, onMounted, onUnmounted, ref } from 'vue';
import { ensureSeed, getCurrentTeamId, getVisitRecordTemplates, queryVisitRecords } from './mock';
const props = defineProps({
data: { type: Object, default: () => ({}) },
archiveId: { type: String, default: '' },
floatingBottom: { type: Number, default: 16 },
});
const templates = ref(getVisitRecordTemplates());
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 timeRangeOptions = [
{ label: '全部时间', value: 'ALL' },
{ label: '今天', value: 'today' },
{ label: '近7天', value: '7d' },
{ label: '近30天', value: '30d' },
];
const currentTimeRange = ref(timeRangeOptions[0]);
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;
});
function refreshList() {
if (!props.archiveId) return;
records.value = queryVisitRecords({
archiveId: props.archiveId,
medicalType: currentType.value.value,
timeRange: currentTimeRange.value.value,
teamId: teamId.value,
shareAllTeams: shareAllTeamsForQuery.value,
});
}
const tagClass = {
outpatient: 'bg-amber',
inhospital: 'bg-teal',
preConsultation: 'bg-indigo',
physicalExaminationTemplate: 'bg-green',
};
function getDiagnosis(r) {
if (!r) return '--';
if (r.templateType === 'preConsultation') return r.chiefComplaint || r.summary || '--';
return r.diagnosisName || r.summary || '--';
}
function pickType(e) {
currentType.value = typeRange.value[e.detail.value] || { name: '全部', value: 'ALL' };
refreshList();
}
function pickTimeRange(e) {
currentTimeRange.value = timeRangeOptions[e.detail.value] || timeRangeOptions[0];
refreshList();
}
function getFiles(r) {
const arr = r?.files;
return Array.isArray(arr) ? arr.filter((i) => i && i.url) : [];
}
function previewFiles(r, idx) {
const urls = getFiles(r).map((i) => i.url);
if (!urls.length) return;
uni.previewImage({ urls, current: urls[idx] });
}
function add() {
uni.showActionSheet({
itemList: templates.value.map((i) => i.name),
success: ({ tapIndex }) => {
const t = templates.value[tapIndex];
uni.navigateTo({
url: `/pages/case/visit-record-detail?archiveId=${encodeURIComponent(props.archiveId)}&type=${encodeURIComponent(t.templateType)}&name=${encodeURIComponent(props.data?.name || '')}`,
});
},
});
}
function edit(record) {
uni.navigateTo({
url: `/pages/case/visit-record-view?archiveId=${encodeURIComponent(props.archiveId)}&id=${encodeURIComponent(record._id)}`,
});
}
onMounted(() => {
ensureSeed(props.archiveId, props.data);
refreshList();
uni.$on('archive-detail:visit-record-changed', refreshList);
});
onUnmounted(() => {
uni.$off('archive-detail:visit-record-changed', refreshList);
});
</script>
<style scoped>
.wrap {
padding: 12px 0 96px;
}
.filters {
display: flex;
gap: 10px;
padding: 10px 14px;
background: #f5f6f8;
border-bottom: 1px solid #f2f2f2;
}
.filter-pill {
background: #fff;
border: 1px solid #e6e6e6;
border-radius: 6px;
padding: 10px 12px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
flex: 1;
}
.pill-text {
font-size: 13px;
color: #333;
max-width: 180px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.share-tip {
padding: 10px 14px 0;
}
.share-tip-text {
display: inline-block;
background: #eef2ff;
color: #4338ca;
font-size: 12px;
padding: 6px 10px;
border-radius: 6px;
}
.list {
padding: 0 14px;
}
.card {
background: #fff;
border-radius: 10px;
margin-top: 10px;
overflow: hidden;
box-shadow: 0 6px 14px rgba(0, 0, 0, 0.06);
}
.record {
padding: 0;
}
.record-head {
display: flex;
align-items: center;
padding: 12px 12px 10px;
gap: 8px;
}
.record-title {
font-size: 15px;
font-weight: 600;
color: #1f1f1f;
}
.record-date {
font-size: 14px;
font-weight: 600;
color: #333;
}
.record-body {
padding: 0 12px 12px;
}
.line {
display: flex;
padding-top: 10px;
font-size: 13px;
color: #333;
line-height: 18px;
}
.line-label {
flex-shrink: 0;
color: #666;
}
.line-value {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.thumbs {
padding-top: 10px;
display: flex;
gap: 10px;
align-items: center;
flex-wrap: wrap;
}
.thumb {
width: 84px;
height: 64px;
border-radius: 6px;
overflow: hidden;
background: #f3f4f6;
border: 1px solid #e5e7eb;
}
.thumb-img {
width: 84px;
height: 64px;
}
.thumb-more {
font-size: 12px;
color: #6b7280;
}
.record-foot {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 12px;
border-top: 1px solid #f2f2f2;
font-size: 12px;
color: #999;
}
.foot-left {
flex-shrink: 0;
margin-right: 10px;
}
.foot-right {
flex: 1;
text-align: right;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.record-tag {
font-size: 12px;
color: #fff;
padding: 4px 8px;
border-radius: 8px;
}
.bg-blue {
background: #4f6ef7;
}
.bg-amber {
background: #d97706;
}
.bg-teal {
background: #0f766e;
}
.bg-indigo {
background: #4f46e5;
}
.bg-green {
background: #16a34a;
}
.bg-rose {
background: #f43f5e;
}
.empty {
padding: 120px 0;
text-align: center;
color: #9aa0a6;
font-size: 13px;
}
.fab {
position: fixed;
right: 16px;
width: 52px;
height: 52px;
border-radius: 26px;
background: #4f6ef7;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 10px 18px rgba(79, 110, 247, 0.35);
z-index: 20;
}
</style>