fix(mobile): 优化患者列表 - 分页加载+搜索+数据格式适配

This commit is contained in:
2026-06-20 14:41:38 +08:00
parent f5ae4f3c64
commit 9e428cbd0f

View File

@@ -1,52 +1,79 @@
<template>
<div class="patient-list">
<div class="search-bar"><input v-model="searchText" placeholder="搜索患者姓名/床号..." class="search-input" /></div>
<div v-if="loading" class="loading">加载中...</div>
<div v-for="p in displayPatients" :key="p.id" class="patient-card" @click="$router.push(`/mobile/patient-detail/${p.id}`)">
<div class="patient-avatar" :class="'level-' + p.nursingLevel">{{ p.name?.charAt(0) }}</div>
<div class="search-bar">
<input v-model="searchText" placeholder="搜索患者姓名/床号..." class="search-input" @input="onSearch" />
</div>
<div v-if="loading" class="loading">
<div class="loading-spinner"></div>
<span>加载中...</span>
</div>
<div v-for="p in patients" :key="p.patientId || p.id" class="patient-card" @click="goDetail(p)">
<div class="patient-avatar" :class="'level-' + (p.nursingLevel || 3)">{{ (p.patientName || p.name || '?').charAt(0) }}</div>
<div class="patient-info">
<div class="patient-name">{{ p.name }} <span class="bed">{{ p.bedNo }}</span></div>
<div class="patient-diag">{{ p.diagnosis || '暂无诊断' }}</div>
<div class="patient-tags"><span class="tag" :class="'level-' + p.nursingLevel">{{ p.nursingLevel }}级护理</span><span v-if="p.gender" class="tag">{{ p.gender }}</span></div>
<div class="patient-name">{{ p.patientName || p.name || '未知患者' }} <span class="bed">{{ p.bedNo || p.locationName || '' }}</span></div>
<div class="patient-diag">{{ p.primaryDiagnosisName || p.diagnosis || '暂无诊断' }}</div>
<div class="patient-tags">
<span class="tag" :class="'level-' + (p.nursingLevel || 3)">{{ (p.nursingLevel || 3) }}级护理</span>
<span v-if="p.gender" class="tag">{{ p.gender }}</span>
<span v-if="p.age" class="tag">{{ p.age }}</span>
</div>
</div>
</div>
<div v-if="!loading && displayPatients.length === 0" class="empty">暂无患者</div>
<div v-if="!loading && patients.length === 0" class="empty">暂无患者</div>
<div v-if="!loading && hasMore" class="load-more" @click="loadMore">加载更多</div>
</div>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue'
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { nursingApi } from '../api'
const router = useRouter()
const patients = ref([])
const loading = ref(false)
const searchText = ref('')
const displayPatients = computed(() => searchText.value ? patients.value.filter(p => p.name?.includes(searchText.value) || p.bedNo?.includes(searchText.value)) : patients.value)
const pageNo = ref(1)
const pageSize = 20
const hasMore = ref(true)
const loadPatients = async () => {
const loadPatients = async (reset = false) => {
if (reset) { pageNo.value = 1; patients.value = []; hasMore.value = true }
loading.value = true
try {
const res = await nursingApi.getPatientList({})
patients.value = res.data?.list || res.data?.records || res.data?.rows || res.data || []
} catch (e) { ElMessage.error('加载患者列表失败') } finally { loading.value = false }
const params = { pageNo: pageNo.value, pageSize: pageSize }
if (searchText.value) params.searchKey = searchText.value
const res = await nursingApi.getPatientList(params)
const list = res.data?.list || res.data?.records || res.data?.rows || res.data || []
if (reset) { patients.value = list } else { patients.value.push(...list) }
hasMore.value = list.length >= pageSize
} catch (e) { console.error('加载失败:', e) } finally { loading.value = false }
}
onMounted(loadPatients)
const onSearch = () => { loadPatients(true) }
const loadMore = () => { pageNo.value++; loadPatients(false) }
const goDetail = (p) => { router.push(`/mobile/patient-detail/${p.patientId || p.id}`) }
onMounted(() => loadPatients(true))
</script>
<style scoped>
.search-bar { padding: 8px 0; }
.search-input { width: 100%; padding: 10px 16px; border: 1px solid #ddd; border-radius: 20px; font-size: 15px; outline: none; background: #fff; }
.search-input:focus { border-color: #1890ff; }
.loading { text-align: center; padding: 20px; color: #999; }
.loading { text-align: center; padding: 20px; color: #999; display: flex; align-items: center; justify-content: center; gap: 8px; }
.loading-spinner { width: 20px; height: 20px; border: 2px solid #1890ff; border-top-color: transparent; border-radius: 50%; animation: spin 0.8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.patient-card { background: #fff; border-radius: 8px; padding: 12px; margin-bottom: 8px; display: flex; align-items: center; gap: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
.patient-avatar { width: 44px; height: 44px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 18px; font-weight: 600; color: #fff; }
.patient-avatar { width: 44px; height: 44px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 18px; font-weight: 600; color: #fff; flex-shrink: 0; }
.level-1 { background: #f5222d; } .level-2 { background: #fa8c16; } .level-3 { background: #52c41a; }
.patient-name { font-weight: 600; font-size: 15px; }
.bed { color: #999; font-size: 13px; }
.patient-diag { color: #666; font-size: 13px; margin: 2px 0; }
.patient-tags { display: flex; gap: 6px; }
.tag { font-size: 11px; padding: 2px 6px; border-radius: 4px; }
.patient-info { flex: 1; min-width: 0; }
.patient-name { font-weight: 600; font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bed { color: #999; font-size: 13px; margin-left: 4px; }
.patient-diag { color: #666; font-size: 13px; margin: 2px 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.patient-tags { display: flex; gap: 6px; flex-wrap: wrap; }
.tag { font-size: 11px; padding: 2px 6px; border-radius: 4px; background: #f5f5f5; }
.load-more { text-align: center; padding: 12px; color: #1890ff; font-size: 14px; cursor: pointer; }
.empty { text-align: center; padding: 40px; color: #999; }
</style>