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

363 lines
8.9 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-01-22 17:39:23 +08:00
<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" />
2026-01-22 15:54:15 +08:00
</view>
2026-01-22 17:39:23 +08:00
</picker>
</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-22 17:39:23 +08:00
<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>
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-22 17:39:23 +08:00
<view class="foot-left">创建时间{{ r.createDateStr || '' }}</view>
<view class="foot-right">创建人{{ r.creatorName || '—' }}</view>
2026-01-22 15:54:15 +08:00
</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';
2026-01-22 17:39:23 +08:00
import { ensureSeed, getCurrentTeamId, getVisitRecordTemplates, queryVisitRecords } from './mock';
2026-01-22 15:54:15 +08:00
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([]);
2026-01-22 17:39:23 +08:00
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;
});
2026-01-22 15:54:15 +08:00
function refreshList() {
if (!props.archiveId) return;
records.value = queryVisitRecords({
archiveId: props.archiveId,
medicalType: currentType.value.value,
2026-01-22 17:39:23 +08:00
timeRange: currentTimeRange.value.value,
teamId: teamId.value,
shareAllTeams: shareAllTeamsForQuery.value,
});
2026-01-22 15:54:15 +08:00
}
const tagClass = {
outpatient: 'bg-amber',
inhospital: 'bg-teal',
2026-01-22 17:39:23 +08:00
preConsultation: 'bg-indigo',
2026-01-22 15:54:15 +08:00
physicalExaminationTemplate: 'bg-green',
};
2026-01-22 17:39:23 +08:00
function getDiagnosis(r) {
if (!r) return '--';
if (r.templateType === 'preConsultation') return r.chiefComplaint || r.summary || '--';
return r.diagnosisName || r.summary || '--';
2026-01-22 15:54:15 +08:00
}
function pickType(e) {
currentType.value = typeRange.value[e.detail.value] || { name: '全部', value: 'ALL' };
refreshList();
}
2026-01-22 17:39:23 +08:00
function pickTimeRange(e) {
currentTimeRange.value = timeRangeOptions[e.detail.value] || timeRangeOptions[0];
2026-01-22 15:54:15 +08:00
refreshList();
}
2026-01-22 17:39:23 +08:00
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] });
2026-01-22 15:54:15 +08:00
}
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({
2026-01-22 17:39:23 +08:00
url: `/pages/case/visit-record-view?archiveId=${encodeURIComponent(props.archiveId)}&id=${encodeURIComponent(record._id)}`,
2026-01-22 15:54:15 +08:00
});
}
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;
2026-01-22 17:39:23 +08:00
padding: 10px 12px;
2026-01-22 15:54:15 +08:00
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
2026-01-22 17:39:23 +08:00
flex: 1;
2026-01-22 15:54:15 +08:00
}
.pill-text {
font-size: 13px;
color: #333;
2026-01-22 17:39:23 +08:00
max-width: 180px;
2026-01-22 15:54:15 +08:00
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
2026-01-22 17:39:23 +08:00
.share-tip {
padding: 10px 14px 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;
font-size: 12px;
padding: 6px 10px;
border-radius: 6px;
2026-01-22 15:54:15 +08:00
}
.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;
}
2026-01-22 17:39:23 +08:00
.line {
2026-01-22 15:54:15 +08:00
display: flex;
padding-top: 10px;
font-size: 13px;
color: #333;
line-height: 18px;
}
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;
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;
2026-01-22 15:54:15 +08:00
}
.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;
}
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 {
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>