fix:优化病历列表显示

This commit is contained in:
Jafeng 2026-02-06 14:12:56 +08:00
parent ea51cee22a
commit de468c1acb

View File

@ -30,13 +30,8 @@
<view class="tabs-area"> <view class="tabs-area">
<scroll-view scroll-x class="tabs-scroll" :show-scrollbar="false"> <scroll-view scroll-x class="tabs-scroll" :show-scrollbar="false">
<view class="tabs-container"> <view class="tabs-container">
<view <view v-for="tab in tabs" :key="tab.key" class="tab-item" :class="{ active: currentTabKey === tab.key }"
v-for="tab in tabs" @click="onTabClick(tab)">
:key="tab.key"
class="tab-item"
:class="{ active: currentTabKey === tab.key }"
@click="onTabClick(tab)"
>
{{ tab.label }} {{ tab.label }}
</view> </view>
</view> </view>
@ -47,32 +42,24 @@
<!-- Main Content --> <!-- Main Content -->
<view class="content-body"> <view class="content-body">
<!-- Patient List --> <!-- Patient List -->
<scroll-view <scroll-view scroll-y class="patient-list" :scroll-into-view="scrollIntoId" :scroll-with-animation="true"
scroll-y lower-threshold="80" @scrolltolower="loadMore">
class="patient-list"
:scroll-into-view="scrollIntoId"
:scroll-with-animation="true"
lower-threshold="80"
@scrolltolower="loadMore"
>
<view v-for="group in patientList" :key="group.letter" :id="letterToDomId(group.letter)"> <view v-for="group in patientList" :key="group.letter" :id="letterToDomId(group.letter)">
<view class="group-title">{{ group.letter }}</view> <view class="group-title">{{ group.letter }}</view>
<view v-for="(patient, pIndex) in group.data" :key="pIndex" class="patient-card" @click="handlePatientClick(patient)"> <view v-for="(patient, pIndex) in group.data" :key="pIndex" class="patient-card"
@click="handlePatientClick(patient)">
<!-- Checkbox for Batch Mode --> <!-- Checkbox for Batch Mode -->
<view v-if="isBatchMode" class="checkbox-area"> <view v-if="isBatchMode" class="checkbox-area">
<uni-icons <uni-icons :type="selectedItems.includes(getSelectId(patient)) ? 'checkbox-filled' : 'checkbox'" size="24"
:type="selectedItems.includes(getSelectId(patient)) ? 'checkbox-filled' : 'checkbox'" :color="selectedItems.includes(getSelectId(patient)) ? '#007aff' : '#ccc'"></uni-icons>
size="24"
:color="selectedItems.includes(getSelectId(patient)) ? '#007aff' : '#ccc'"
></uni-icons>
</view> </view>
<view class="card-content"> <view class="card-content">
<!-- Row 1 --> <!-- Row 1 -->
<view class="card-row-top"> <view class="card-row-top">
<view class="patient-info"> <view class="patient-info">
<text class="patient-name">{{ patient.name }}</text> <text class="patient-name">{{ patient.name }}</text>
<text class="patient-meta">{{ patient.gender }}/{{ patient.age }}</text> <text class="patient-meta">{{ patient.gender }}{{ patient.age ? '/' + patient.age + '岁' : '' }}</text>
</view> </view>
<view class="patient-tags"> <view class="patient-tags">
<view v-for="(tag, tIndex) in resolveGroupTags(patient)" :key="tIndex" class="tag"> <view v-for="(tag, tIndex) in resolveGroupTags(patient)" :key="tIndex" class="tag">
@ -80,12 +67,21 @@
</view> </view>
</view> </view>
</view> </view>
<!-- Row 2 / 3 --> <!-- Row 2 -->
<view v-if="currentTabKey === 'new'" class="card-row-middle">
<text v-if="patient.record" class="record-text record-ellipsis">
{{ patient.record.type }} / {{ patient.record.date }} / {{ patient.record.diagnosis }}
</text>
<text v-else class="no-record">暂无病历记录</text>
</view>
<!-- Row 3 -->
<view class="card-row-bottom"> <view class="card-row-bottom">
<template v-if="currentTabKey === 'new'"> <!-- New Patient Tab --> <template v-if="currentTabKey === 'new'"> <!-- New Patient Tab -->
<text class="record-text"> <text class="record-text">
{{ patient.createTime || '-' }} / {{ resolveCreatorName(patient) || '-' }} {{ patient.createTime || '-' }} {{ resolveCreatorName(patient) ? resolveCreatorName(patient) +
'创建' : '-' }}
</text> </text>
</template> </template>
<template v-else> <template v-else>
@ -104,12 +100,7 @@
<!-- Sidebar Index --> <!-- Sidebar Index -->
<view v-if="!isBatchMode" class="sidebar-index"> <view v-if="!isBatchMode" class="sidebar-index">
<view <view v-for="letter in indexList" :key="letter" class="index-item" @click="scrollToLetter(letter)">
v-for="letter in indexList"
:key="letter"
class="index-item"
@click="scrollToLetter(letter)"
>
{{ letter }} {{ letter }}
</view> </view>
</view> </view>
@ -118,11 +109,9 @@
<!-- Batch Actions Footer --> <!-- Batch Actions Footer -->
<view v-if="isBatchMode" class="batch-footer"> <view v-if="isBatchMode" class="batch-footer">
<view class="left-action" @click="handleSelectAll"> <view class="left-action" @click="handleSelectAll">
<uni-icons <uni-icons
:type="selectedItems.length > 0 && selectedItems.length === patientList.flatMap(g => g.data).length ? 'checkbox-filled' : 'checkbox'" :type="selectedItems.length > 0 && selectedItems.length === patientList.flatMap(g => g.data).length ? 'checkbox-filled' : 'checkbox'"
size="24" size="24" color="#666"></uni-icons>
color="#666"
></uni-icons>
<text class="footer-text">全选 ({{ selectedItems.length }})</text> <text class="footer-text">全选 ({{ selectedItems.length }})</text>
</view> </view>
<view class="right-actions"> <view class="right-actions">
@ -202,7 +191,7 @@ const accountStore = useAccountStore();
const { account, doctorInfo } = storeToRefs(accountStore); const { account, doctorInfo } = storeToRefs(accountStore);
const { getDoctorInfo } = accountStore; const { getDoctorInfo } = accountStore;
const teamDisplay = computed(() => `${currentTeam.value?.name || ''}(${managedArchiveCountAllTeams.value})`); const teamDisplay = computed(() => `${currentTeam.value?.name || ''}`);
function asArray(value) { function asArray(value) {
return Array.isArray(value) ? value : []; return Array.isArray(value) ? value : [];
@ -246,10 +235,10 @@ async function loadTeamMembers() {
const members = Array.isArray(t.memberList) ? t.memberList : []; const members = Array.isArray(t.memberList) ? t.memberList : [];
// Update map // Update map
members.forEach(m => { members.forEach(m => {
const uid = String(m?.userid || ''); const uid = String(m?.userid || '');
if (uid) { if (uid) {
userNameMap.value[uid] = String(m?.anotherName || m?.name || m?.userid || '') || uid; userNameMap.value[uid] = String(m?.anotherName || m?.name || m?.userid || '') || uid;
} }
}); });
} catch (e) { } catch (e) {
console.error('获取团队成员失败', e); console.error('获取团队成员失败', e);
@ -386,7 +375,7 @@ function formatPatient(raw) {
const rawTags = asArray(raw?.tags).filter((i) => typeof i === 'string' && i.trim()); const rawTags = asArray(raw?.tags).filter((i) => typeof i === 'string' && i.trim());
// 使 tagIds // 使 tagIds
const tagIds = asArray(raw?.tagIds).map(String).filter(Boolean); const tagIds = asArray(raw?.tagIds).map(String).filter(Boolean);
// tagNames > tags > tagIds // tagNames > tags > tagIds
const displayTags = rawTagNames.length ? rawTagNames : (rawTags.length ? rawTags : []); const displayTags = rawTagNames.length ? rawTagNames : (rawTags.length ? rawTags : []);
@ -535,11 +524,11 @@ async function reload(reset = true) {
managedArchiveCountAllTeams.value = managedArchiveCountAllTeams.value =
Number( Number(
payload.totalAllTeams || payload.totalAllTeams ||
payload.totalAllTeam || payload.totalAllTeam ||
payload.totalAllTeamsCount || payload.totalAllTeamsCount ||
managedArchiveCountAllTeams.value || managedArchiveCountAllTeams.value ||
totalFromApi.value || totalFromApi.value ||
0 0
) || (totalFromApi.value || 0); ) || (totalFromApi.value || 0);
} }
@ -579,7 +568,7 @@ const patientList = computed(() => {
.filter((p) => Number(p?.createTimeTs || 0) >= sevenDaysAgo) .filter((p) => Number(p?.createTimeTs || 0) >= sevenDaysAgo)
.slice() .slice()
.sort((a, b) => Number(b?.createTimeTs || 0) - Number(a?.createTimeTs || 0)); .sort((a, b) => Number(b?.createTimeTs || 0) - Number(a?.createTimeTs || 0));
return [{ letter: '最近新增', data: flatList }]; return [{ letter: '最近7天新增', data: flatList }];
} }
return groupByLetter(all); return groupByLetter(all);
@ -587,7 +576,8 @@ const patientList = computed(() => {
const indexList = computed(() => { const indexList = computed(() => {
if (currentTab.value.kind === 'new') return []; // No index bar for new patient if (currentTab.value.kind === 'new') return []; // No index bar for new patient
return 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').filter(l => patientList.value.some(g => g.letter === l)); const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ#'.split('').filter(l => patientList.value.some(g => g.letter === l));
return letters;
}); });
const totalPatients = computed(() => { const totalPatients = computed(() => {
@ -752,7 +742,7 @@ const openCreatePatientEntry = () => {
// Batch Operations // Batch Operations
const toggleSelect = (patient) => { const toggleSelect = (patient) => {
if (!isBatchMode.value) return; // Should not happen if click handler is correct if (!isBatchMode.value) return; // Should not happen if click handler is correct
const id = patient._id || patient.id || patient.phone || patient.mobile; // Prefer server id const id = patient._id || patient.id || patient.phone || patient.mobile; // Prefer server id
const index = selectedItems.value.indexOf(id); const index = selectedItems.value.indexOf(id);
if (index > -1) { if (index > -1) {
@ -861,11 +851,11 @@ onShow(async () => {
height: 100vh; height: 100vh;
background-color: #fff; background-color: #fff;
padding-bottom: 0; // Default padding-bottom: 0; // Default
// Padding for batch footer // Padding for batch footer
/* &.is-batch { /* &.is-batch {
padding-bottom: 100rpx; padding-bottom: 100rpx;
} */ } */
// We can't use &.is-batch because scoped style and root element is tricky depending on uni-app version/style // We can't use &.is-batch because scoped style and root element is tricky depending on uni-app version/style
// Instead we handle it in content-body or separate view // Instead we handle it in content-body or separate view
} }
@ -884,11 +874,11 @@ onShow(async () => {
font-size: 32rpx; font-size: 32rpx;
font-weight: bold; font-weight: bold;
color: #333; color: #333;
.team-name { .team-name {
margin-right: 10rpx; margin-right: 10rpx;
} }
.team-icon { .team-icon {
font-size: 32rpx; font-size: 32rpx;
color: #333; color: #333;
@ -898,13 +888,13 @@ onShow(async () => {
.header-actions { .header-actions {
display: flex; display: flex;
gap: 30rpx; gap: 30rpx;
.action-item { .action-item {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
.action-text { .action-text {
font-size: 20rpx; font-size: 20rpx;
color: #333; color: #333;
@ -925,11 +915,11 @@ onShow(async () => {
flex: 1; flex: 1;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
.tabs-container { .tabs-container {
display: flex; display: flex;
padding: 20rpx 30rpx; padding: 20rpx 30rpx;
.tab-item { .tab-item {
padding: 10rpx 30rpx; padding: 10rpx 30rpx;
margin-right: 20rpx; margin-right: 20rpx;
@ -938,7 +928,7 @@ onShow(async () => {
background-color: #fff; background-color: #fff;
border-radius: 8rpx; border-radius: 8rpx;
flex-shrink: 0; flex-shrink: 0;
&.active { &.active {
color: #5d8aff; color: #5d8aff;
background-color: #e6f0ff; background-color: #e6f0ff;
@ -953,7 +943,7 @@ onShow(async () => {
color: #666; color: #666;
white-space: nowrap; white-space: nowrap;
flex-shrink: 0; flex-shrink: 0;
min-width: 100rpx; min-width: 70rpx;
text-align: right; text-align: right;
} }
} }
@ -1022,7 +1012,7 @@ onShow(async () => {
.patient-tags { .patient-tags {
display: flex; display: flex;
gap: 10rpx; gap: 10rpx;
.tag { .tag {
font-size: 20rpx; font-size: 20rpx;
color: #5d8aff; color: #5d8aff;
@ -1035,13 +1025,14 @@ onShow(async () => {
} }
} }
.card-row-bottom { .card-row-middle {
font-size: 28rpx; font-size: 28rpx;
margin-bottom: 12rpx;
.record-text { .record-text {
color: #666; color: #666;
} }
.record-ellipsis { .record-ellipsis {
display: block; display: block;
white-space: nowrap; white-space: nowrap;
@ -1049,7 +1040,27 @@ onShow(async () => {
text-overflow: ellipsis; text-overflow: ellipsis;
max-width: 100%; max-width: 100%;
} }
.no-record {
color: #bdc3c7;
}
}
.card-row-bottom {
font-size: 28rpx;
.record-text {
color: #666;
}
.record-ellipsis {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
.no-record { .no-record {
color: #bdc3c7; color: #bdc3c7;
} }
@ -1067,12 +1078,13 @@ onShow(async () => {
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 0 30rpx; padding: 0 30rpx;
box-shadow: 0 -4rpx 20rpx rgba(0,0,0,0.05); box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
z-index: 99; z-index: 99;
.left-action { .left-action {
display: flex; display: flex;
align-items: center; align-items: center;
.footer-text { .footer-text {
margin-left: 10rpx; margin-left: 10rpx;
font-size: 28rpx; font-size: 28rpx;
@ -1083,7 +1095,7 @@ onShow(async () => {
.right-actions { .right-actions {
display: flex; display: flex;
gap: 20rpx; gap: 20rpx;
.footer-btn { .footer-btn {
font-size: 28rpx; font-size: 28rpx;
padding: 0 30rpx; padding: 0 30rpx;
@ -1091,13 +1103,13 @@ onShow(async () => {
line-height: 64rpx; line-height: 64rpx;
margin: 0; margin: 0;
border-radius: 8rpx; border-radius: 8rpx;
&.plain { &.plain {
border: 2rpx solid #ddd; border: 2rpx solid #ddd;
background-color: #fff; background-color: #fff;
color: #666; color: #666;
} }
&.primary { &.primary {
background-color: #5d8aff; background-color: #5d8aff;
color: #fff; color: #fff;
@ -1123,7 +1135,7 @@ onShow(async () => {
justify-content: center; justify-content: center;
background-color: transparent; background-color: transparent;
z-index: 10; z-index: 10;
.index-item { .index-item {
font-size: 20rpx; font-size: 20rpx;
color: #555; color: #555;