diff --git a/admin/src/views/patient/reception/index.vue b/admin/src/views/patient/reception/index.vue
index 3f70c49d..aab5a29a 100644
--- a/admin/src/views/patient/reception/index.vue
+++ b/admin/src/views/patient/reception/index.vue
@@ -154,13 +154,11 @@
{{ row.western_medicine || '—' }}
@@ -303,18 +311,19 @@
备注
-
+
编辑病历
{
+ const empty = props.value === undefined || props.value === null || props.value === ''
+ return h(
+ 'div',
+ {
+ class: ['kv-item', 'metric-kv-item', empty ? 'empty' : ''],
+ style: props.span ? `grid-column: span ${props.span}` : ''
+ },
+ [
+ h('span', { class: 'kv-label' }, props.label),
+ h(
+ 'span',
+ { class: ['kv-value', 'metric-kv-value', props.high ? 'high' : ''] },
+ empty
+ ? '—'
+ : [
+ `${props.value}${props.unit || ''}`,
+ props.high ? h('span', { class: 'metric-up' }, '↑') : null
+ ]
+ )
+ ]
+ )
+}
+
const POLL_MS = 5_000
const PAGE_SIZE = 15
@@ -422,6 +461,8 @@ let countdownTimer: ReturnType | null = null
let queueInflight = false
let detailInflight = false
let scrollObserver: IntersectionObserver | null = null
+let queueRequestSeq = 0
+let detailRequestSeq = 0
const queueScrollRef = ref(null)
const sentinelRef = ref(null)
@@ -499,19 +540,67 @@ const formatBp = (row: any) => {
return `${sys ?? '—'} / ${dia ?? '—'}`
}
+const toNumeric = (value: any) => {
+ if (value === null || value === undefined || value === '') return null
+ const num = Number(value)
+ return Number.isFinite(num) ? num : null
+}
+
+const getBloodSugarThresholds = (ageValue: any) => {
+ const age = toNumeric(ageValue)
+ if (age === null) return null
+ if (age < 50) return { fasting: 7, postprandial: 9 }
+ if (age >= 50) return { fasting: 8, postprandial: 10 }
+ return null
+}
+
+const isHighFastingBloodSugar = (value: any, ageValue: any) => {
+ const num = toNumeric(value)
+ const thresholds = getBloodSugarThresholds(ageValue)
+ return num !== null && thresholds !== null && num >= thresholds.fasting
+}
+
+const isHighPostprandialBloodSugar = (value: any, ageValue: any) => {
+ const num = toNumeric(value)
+ const thresholds = getBloodSugarThresholds(ageValue)
+ return num !== null && thresholds !== null && num >= thresholds.postprandial
+}
+
+const isHighOtherBloodSugar = (value: any, ageValue: any) => {
+ const num = toNumeric(value)
+ const thresholds = getBloodSugarThresholds(ageValue)
+ return num !== null && thresholds !== null && num >= thresholds.postprandial
+}
+
+const isHighBloodPressure = (row: any) => {
+ const sys = toNumeric(row?.systolic_pressure)
+ const dia = toNumeric(row?.diastolic_pressure)
+ return (sys !== null && sys > 140) || (dia !== null && dia > 90)
+}
+
+const isHighSystolicPressure = (value: any) => {
+ const num = toNumeric(value)
+ return num !== null && num > 140
+}
+
+const isHighDiastolicPressure = (value: any) => {
+ const num = toNumeric(value)
+ return num !== null && num > 90
+}
+
const joinArray = (arr: any): string => {
if (!arr) return ''
if (Array.isArray(arr)) return arr.filter(Boolean).join('、')
return String(arr)
}
-const fetchQueuePage = async (pageNo: number, status?: number) => {
+const fetchQueuePage = async (pageNo: number, status?: number, patientName?: string) => {
const today = todayStr()
const res: any = await receptionQueue({
status: status ?? activeStatus.value,
start_date: today,
end_date: today,
- patient_name: searchName.value || undefined,
+ patient_name: (patientName ?? searchName.value) || undefined,
page_no: pageNo,
page_size: PAGE_SIZE
})
@@ -520,54 +609,63 @@ const fetchQueuePage = async (pageNo: number, status?: number) => {
return { list, count }
}
-const fetchOtherCount = async () => {
- const otherStatus = activeStatus.value === 1 ? 4 : 1
+const fetchOtherCount = async (baseStatus: 1 | 4, patientName: string, requestSeq: number) => {
+ const otherStatus = baseStatus === 1 ? 4 : 1
try {
- const { count } = await fetchQueuePage(1, otherStatus)
+ const { count } = await fetchQueuePage(1, otherStatus, patientName || undefined)
+ if (requestSeq !== queueRequestSeq || activeStatus.value !== baseStatus || searchName.value !== patientName) return
if (otherStatus === 1) waitingTotal.value = count
else passedTotal.value = count
} catch { /* 统计计数失败不阻塞 */ }
}
-const loadQueue = async (opts?: { silent?: boolean; keepSelection?: boolean }) => {
+const loadQueue = async (opts?: { silent?: boolean; autoSelect?: boolean }) => {
const silent = opts?.silent === true
if (silent && queueInflight) return
+ const requestSeq = ++queueRequestSeq
+ const statusSnapshot = activeStatus.value
+ const searchSnapshot = searchName.value
+ const autoSelect = opts?.autoSelect ?? !silent
queueInflight = true
if (!silent) queueLoading.value = true
try {
page.value = 1
- const { list, count } = await fetchQueuePage(1)
+ const { list, count } = await fetchQueuePage(1, statusSnapshot, searchSnapshot || undefined)
+ if (requestSeq !== queueRequestSeq || activeStatus.value !== statusSnapshot || searchName.value !== searchSnapshot) return
queue.value = list
total.value = count || list.length
- if (activeStatus.value === 1) waitingTotal.value = count
+ if (statusSnapshot === 1) waitingTotal.value = count
else passedTotal.value = count
hasMore.value = list.length >= PAGE_SIZE && queue.value.length < (count || 0)
- fetchOtherCount()
+ void fetchOtherCount(statusSnapshot, searchSnapshot, requestSeq)
if (selectedId.value && !list.some((r) => r.id === selectedId.value)) {
- if (opts?.keepSelection === false) {
- selectedId.value = null
- detail.value = null
- }
+ selectedId.value = null
+ detail.value = null
}
- if (!selectedId.value && list.length > 0) {
+ if (!selectedId.value && autoSelect && list.length > 0) {
await selectRow(list[0])
}
} catch (e) {
if (!silent) console.error('加载队列失败:', e)
} finally {
- queueLoading.value = false
- queueInflight = false
+ if (requestSeq === queueRequestSeq) {
+ queueLoading.value = false
+ queueInflight = false
+ }
}
}
const loadMore = async () => {
if (loadingMore.value || !hasMore.value) return
+ const statusSnapshot = activeStatus.value
+ const searchSnapshot = searchName.value
loadingMore.value = true
try {
const nextPage = page.value + 1
- const { list, count } = await fetchQueuePage(nextPage)
+ const { list, count } = await fetchQueuePage(nextPage, statusSnapshot, searchSnapshot || undefined)
+ if (activeStatus.value !== statusSnapshot || searchName.value !== searchSnapshot) return
// 去重合并(防止轮询时上方有新插入导致重复)
const existing = new Set(queue.value.map((r) => r.id))
const appended = list.filter((r) => !existing.has(r.id))
@@ -584,33 +682,41 @@ const loadMore = async () => {
const refreshDetailSilently = async () => {
if (!selectedId.value || detailInflight) return
+ const currentId = selectedId.value
+ const requestSeq = ++detailRequestSeq
detailInflight = true
try {
- const res: any = await receptionDetail({ id: selectedId.value })
+ const res: any = await receptionDetail({ id: currentId })
+ if (requestSeq !== detailRequestSeq || selectedId.value !== currentId) return
detail.value = res || detail.value
} catch (e) {
/* 静默失败 */
} finally {
- detailInflight = false
+ if (requestSeq === detailRequestSeq) detailInflight = false
}
}
const selectRow = async (row: QueueRow) => {
if (selectedId.value === row.id) return
+ const currentId = row.id
+ const requestSeq = ++detailRequestSeq
selectedId.value = row.id
detail.value = null
detailLoading.value = true
detailInflight = true
- resetCountdown()
+ restartPolling()
try {
- const res: any = await receptionDetail({ id: row.id })
+ const res: any = await receptionDetail({ id: currentId })
+ if (requestSeq !== detailRequestSeq || selectedId.value !== currentId) return
detail.value = res || null
} catch (e) {
console.error('加载详情失败:', e)
feedback.msgError('加载患者详情失败')
} finally {
- detailLoading.value = false
- detailInflight = false
+ if (requestSeq === detailRequestSeq) {
+ detailLoading.value = false
+ detailInflight = false
+ }
}
}
@@ -620,12 +726,14 @@ const switchStatus = async (status: 1 | 4) => {
selectedId.value = null
detail.value = null
await loadQueue()
+ restartPolling()
}
const handleSearch = async () => {
selectedId.value = null
detail.value = null
await loadQueue()
+ restartPolling()
}
const handleCall = async (row: QueueRow) => {
@@ -656,6 +764,7 @@ const handleNotify = async (row: QueueRow) => {
try {
await notifyAssistant({ id: row.id })
feedback.msgSuccess(`已通知医助 ${row.assistant_name || ''}`)
+ await handleDeskOperationRefresh()
} catch (e: any) {
feedback.msgError(e?.message || '通知失败')
} finally {
@@ -669,7 +778,16 @@ const handleEditDiagnosis = () => {
}
const onDiagnosisEditSuccess = async () => {
- await refreshDetailSilently()
+ await handleDeskOperationRefresh()
+}
+
+const handleDeskOperationRefresh = async () => {
+ const currentId = selectedId.value
+ await loadQueue({ silent: true, autoSelect: false })
+ if (currentId && selectedId.value === currentId) {
+ await refreshDetailSilently()
+ }
+ restartPolling()
}
const handleFinish = async () => {
@@ -681,21 +799,12 @@ const handleFinish = async () => {
}
finishLoading.value = true
try {
- const removedId = apt.value.id
- await completeAppointment({ id: removedId })
+ await completeAppointment({ id: apt.value.id })
feedback.msgSuccess('已完成接诊')
- queue.value = queue.value.filter((r) => r.id !== removedId)
- total.value = Math.max(0, total.value - 1)
- if (activeStatus.value === 1) waitingTotal.value = Math.max(0, waitingTotal.value - 1)
- else passedTotal.value = Math.max(0, passedTotal.value - 1)
selectedId.value = null
detail.value = null
- resetCountdown()
- if (queue.value.length > 0) {
- await selectRow(queue.value[0])
- } else {
- await loadQueue()
- }
+ await loadQueue({ autoSelect: true })
+ restartPolling()
} catch (e: any) {
feedback.msgError(e?.message || '操作失败')
} finally {
@@ -720,7 +829,7 @@ const handleSaveNote = async () => {
feedback.msgSuccess('备注保存成功')
noteContent.value = ''
showNoteDialog.value = false
- await refreshDetailSilently()
+ await handleDeskOperationRefresh()
} catch (e: any) {
feedback.msgError(e?.message || '保存失败')
} finally {
@@ -732,31 +841,40 @@ const resetCountdown = () => {
countdown.value = POLL_MS / 1000
}
-const startPolling = () => {
- stopPolling()
+const startCountdown = () => {
+ if (countdownTimer) clearInterval(countdownTimer)
resetCountdown()
- // 1s 倒计时
countdownTimer = setInterval(() => {
if (pollPaused.value) return
if (countdown.value > 1) {
countdown.value -= 1
} else {
- countdown.value = POLL_MS / 1000
+ countdown.value = 1
}
}, 1000)
- // 5s 实际轮询
- pollTimer = setInterval(async () => {
- if (pollPaused.value) return
- await loadQueue({ silent: true, keepSelection: true })
- if (!detailLoading.value) {
- await refreshDetailSilently()
- }
+}
+
+const scheduleNextPoll = () => {
+ if (pollTimer) clearTimeout(pollTimer)
+ if (pollPaused.value) return
+ startCountdown()
+ pollTimer = setTimeout(async () => {
+ await handleDeskOperationRefresh()
}, POLL_MS)
}
+const restartPolling = () => {
+ scheduleNextPoll()
+}
+
+const startPolling = () => {
+ stopPolling()
+ scheduleNextPoll()
+}
+
const stopPolling = () => {
if (pollTimer) {
- clearInterval(pollTimer)
+ clearTimeout(pollTimer)
pollTimer = null
}
if (countdownTimer) {
@@ -768,9 +886,9 @@ const stopPolling = () => {
const handleVisibilityChange = () => {
pollPaused.value = document.hidden
if (!document.hidden) {
- resetCountdown()
- loadQueue({ silent: true, keepSelection: true })
- refreshDetailSilently()
+ void handleDeskOperationRefresh()
+ } else {
+ stopPolling()
}
}
@@ -820,20 +938,30 @@ onUnmounted(() => {