患者列表优化

This commit is contained in:
2026-05-09 11:30:04 +08:00
parent dc1c5d5b17
commit fd9053a290
4 changed files with 163 additions and 21 deletions
+1
View File
@@ -29,3 +29,4 @@ bin-release/
/.claude /.claude
/.claude /.claude
/.claude /.claude
/.kbctx
@@ -1,6 +1,6 @@
<template> <template>
<div class="call-record-panel"> <div class="call-record-panel">
<div class="call-record-toolbar"> <div v-if="!readOnly" class="call-record-toolbar">
<upload <upload
ref="toolbarUploadRef" ref="toolbarUploadRef"
type="video" type="video"
@@ -19,7 +19,11 @@
<div class="call-record-empty"> <div class="call-record-empty">
<div class="call-record-empty__title">暂无通话记录</div> <div class="call-record-empty__title">暂无通话记录</div>
<div class="call-record-empty__desc"> <div class="call-record-empty__desc">
现在可以直接点击上方上传视频系统会自动生成一条默认通话记录来承载回放 {{
readOnly
? '暂无录制回放数据。'
: '现在可以直接点击上方“上传视频”。系统会自动生成一条默认通话记录来承载回放。'
}}
</div> </div>
</div> </div>
</template> </template>
@@ -47,7 +51,7 @@
{{ row.recording_status_text || '—' }} {{ row.recording_status_text || '—' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="上传回放" width="180"> <el-table-column v-if="!readOnly" label="上传回放" width="180">
<template #default="{ row }"> <template #default="{ row }">
<upload <upload
type="video" type="video"
@@ -92,7 +96,7 @@
import Upload from '@/components/upload/index.vue' import Upload from '@/components/upload/index.vue'
import feedback from '@/utils/feedback' import feedback from '@/utils/feedback'
import { attachLocalCallRecording, createManualCallRecord, getCallRecords } from '@/api/tcm' import { attachLocalCallRecording, createManualCallRecord, getCallRecords } from '@/api/tcm'
import { ref, useTemplateRef, watch } from 'vue' import { ref, useTemplateRef, watch, withDefaults } from 'vue'
type CallRecordRow = { type CallRecordRow = {
id: number id: number
@@ -106,9 +110,14 @@ type CallRecordRow = {
duration_text?: string duration_text?: string
} }
const props = defineProps<{ const props = withDefaults(
diagnosisId: number defineProps<{
}>() diagnosisId: number
/** 只读场景(如诊单只读详情):不展示任何上传入口 */
readOnly?: boolean
}>(),
{ readOnly: false }
)
const loading = ref(false) const loading = ref(false)
const rows = ref<CallRecordRow[]>([]) const rows = ref<CallRecordRow[]>([])
@@ -3,7 +3,7 @@
<div class="toolbar"> <div class="toolbar">
<el-button <el-button
type="primary" type="primary"
:disabled="!props.diagnosisId || !!props.readOnly || !hasDailyRecordPermission" :disabled="!props.diagnosisId || !hasDailyRecordPermission"
@click="handleAdd" @click="handleAdd"
v-perms="DAILY_RECORD_PERMS" v-perms="DAILY_RECORD_PERMS"
> >
@@ -226,7 +226,7 @@ const disablePastDate = (date: Date) => {
} }
const handleAdd = () => { const handleAdd = () => {
if (!props.diagnosisId || props.readOnly || !hasDailyRecordPermission.value) { if (!props.diagnosisId || !hasDailyRecordPermission.value) {
feedback.msgWarning('诊单未保存,无法添加待办') feedback.msgWarning('诊单未保存,无法添加待办')
return return
} }
+144 -12
View File
@@ -40,23 +40,110 @@
<PatientCaseCard :apt="apt" :diag="diag" /> <PatientCaseCard :apt="apt" :diag="diag" />
<!-- 日常记录只读 --> <!-- 日常记录只读 -->
<div class="card tracking-card"> <div v-if="hasDailyRecordPermission" class="card tracking-card">
<div class="card-title">日常记录</div> <div class="card-title">日常记录</div>
<DailyMatrix <div class="card-body">
:diagnosis-id="Number(diagnosisIdNum)" <DailyMatrix
:patient-id="Number(diag?.patient_id ?? 0)" :diagnosis-id="diagnosisNumericId"
:age="diag?.age ?? null" :patient-id="patientNumericId"
:patient-name="diag?.patient_name || ''" :age="diag?.age ?? null"
:read-only="true" :patient-name="diag?.patient_name || ''"
/> :read-only="true"
/>
</div>
</div> </div>
<!-- 医生备注复用 reception NoteTimeline只读 --> <!-- 医生备注舌苔照片检查报告只读与编辑页医生备注同源 -->
<div v-if="doctorNotes.length" class="card note-card"> <div class="card note-card">
<div class="card-title">医生备注 & 舌苔照片 & 检查报告</div> <div class="card-title">医生备注 & 舌苔照片 & 检查报告</div>
<NoteTimeline :notes="doctorNotes" :diagnosis-id="diag?.id" /> <div class="card-body card-body--matrix-align">
<el-empty v-if="diagnosisNumericId <= 0" description="诊单数据不完整" />
<el-empty
v-else-if="!doctorNotesLocal.length"
description="暂无医生备注"
/>
<NoteTimeline
v-else
:notes="doctorNotesLocal"
:diagnosis-id="diagnosisNumericId"
:readonly="true"
/>
</div>
</div> </div>
<!-- 业务订单 -->
<div
v-if="hasPermission(['tcm.diagnosis/patientOrders'])"
class="card section-card"
>
<div class="card-title">业务订单</div>
<div class="card-body card-body--matrix-align">
<patient-order-list
v-if="diagnosisNumericId > 0"
:diagnosis-id="diagnosisNumericId"
:patient-id="patientNumericId || 0"
/>
<el-empty v-else description="诊单数据不完整" />
</div>
</div>
<!-- 视频录制回放记录 -->
<div v-if="hasPermission(['tcm.diagnosis/huifang'])" class="card section-card">
<div class="card-title">视频录制回放记录</div>
<div class="card-body card-body--matrix-align">
<call-record-panel
v-if="diagnosisNumericId > 0"
:diagnosis-id="diagnosisNumericId"
:read-only="true"
/>
<el-empty v-else description="诊单数据不完整" />
</div>
</div>
<!-- 聊天记录 -->
<div v-if="hasPermission(['tcm.diagnosis/chat'])" class="card section-card">
<div class="card-title">聊天记录</div>
<div class="card-body card-body--matrix-align">
<im-chat-record-panel
v-if="diagnosisNumericId > 0"
:diagnosis-id="diagnosisNumericId"
/>
<el-empty v-else description="诊单数据不完整" />
</div>
</div>
<!-- 指派医助记录 -->
<div
v-if="
hasPermission(['tcm.diagnosis/assign']) ||
hasPermission(['tcm.diagnosis/detail'])
"
class="card section-card"
>
<div class="card-title">指派医助记录</div>
<div class="card-body card-body--matrix-align">
<assign-log-panel
v-if="diagnosisNumericId > 0"
:diagnosis-id="diagnosisNumericId"
/>
<el-empty v-else description="诊单数据不完整" />
</div>
</div>
<!-- 挂号记录 -->
<div
v-if="hasPermission(['doctor.appointment/lists'])"
class="card section-card"
>
<div class="card-title">挂号记录</div>
<div class="card-body card-body--matrix-align">
<appointment-record-panel
v-if="diagnosisNumericId > 0"
:diagnosis-id="diagnosisNumericId"
/>
<el-empty v-else description="诊单数据不完整" />
</div>
</div>
</template> </template>
</div> </div>
</template> </template>
@@ -66,10 +153,17 @@ import { computed, onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { ArrowLeft } from '@element-plus/icons-vue' import { ArrowLeft } from '@element-plus/icons-vue'
import { diagnosisReadonlyDetail } from '@/api/tcm' import { diagnosisReadonlyDetail } from '@/api/tcm'
import { getDoctorNotes } from '@/api/patient'
import { formatGender } from '@/utils/diag-display' import { formatGender } from '@/utils/diag-display'
import { hasPermission } from '@/utils/perm'
import PatientInfoCard from './components/PatientInfoCard.vue' import PatientInfoCard from './components/PatientInfoCard.vue'
import PatientCaseCard from './components/PatientCaseCard.vue' import PatientCaseCard from './components/PatientCaseCard.vue'
import DailyMatrix from './components/DailyMatrix.vue' import DailyMatrix from './components/DailyMatrix.vue'
import CallRecordPanel from './components/CallRecordPanel.vue'
import ImChatRecordPanel from './components/ImChatRecordPanel.vue'
import AssignLogPanel from './components/AssignLogPanel.vue'
import AppointmentRecordPanel from './components/AppointmentRecordPanel.vue'
import PatientOrderList from './components/PatientOrderList.vue'
import NoteTimeline from '@/views/patient/reception/components/NoteTimeline.vue' import NoteTimeline from '@/views/patient/reception/components/NoteTimeline.vue'
const route = useRoute() const route = useRoute()
@@ -80,10 +174,19 @@ const diagnosisIdNum = computed(() => Number(route.query.id || 0))
const loading = ref(false) const loading = ref(false)
const errorMsg = ref('') const errorMsg = ref('')
const detail = ref<any>(null) const detail = ref<any>(null)
/** 医生备注:readonlyDetail 返回 + getDoctorNotes 对齐接诊台结构 */
const doctorNotesLocal = ref<any[]>([])
const apt = computed<any>(() => detail.value?.appointment || {}) const apt = computed<any>(() => detail.value?.appointment || {})
const diag = computed<any>(() => detail.value?.diagnosis || {}) const diag = computed<any>(() => detail.value?.diagnosis || {})
const doctorNotes = computed<any[]>(() => detail.value?.doctor_notes || [])
const hasDailyRecordPermission = computed(() => hasPermission(['tcm.diagnosis/dailyRecord']))
const diagnosisNumericId = computed(() =>
Number(diag.value?.id ?? diagnosisIdNum.value ?? 0)
)
const patientNumericId = computed(() => Number(diag.value?.patient_id ?? 0))
const unservedDays = computed<number | null>(() => { const unservedDays = computed<number | null>(() => {
const v = detail.value?.unserved_days const v = detail.value?.unserved_days
return v === null || v === undefined ? null : Number(v) return v === null || v === undefined ? null : Number(v)
@@ -103,11 +206,23 @@ const unservedDaysClass = (n: unknown) => {
return 'unserved-green' return 'unserved-green'
} }
const fetchDoctorNotes = async () => {
const id = diagnosisNumericId.value
if (!id) return
try {
const data = (await getDoctorNotes({ diagnosis_id: id })) || []
doctorNotesLocal.value = data
} catch {
/* 失败时保留 readonlyDetail 已下发的 doctor_notes,避免整块备注空白 */
}
}
const fetchDetail = async () => { const fetchDetail = async () => {
const id = diagnosisIdNum.value const id = diagnosisIdNum.value
if (!id || id <= 0) { if (!id || id <= 0) {
errorMsg.value = '诊单 id 缺失' errorMsg.value = '诊单 id 缺失'
detail.value = null detail.value = null
doctorNotesLocal.value = []
return return
} }
loading.value = true loading.value = true
@@ -115,9 +230,12 @@ const fetchDetail = async () => {
try { try {
const res: any = await diagnosisReadonlyDetail({ id }) const res: any = await diagnosisReadonlyDetail({ id })
detail.value = res || null detail.value = res || null
doctorNotesLocal.value = Array.isArray(res?.doctor_notes) ? res.doctor_notes : []
await fetchDoctorNotes()
} catch (e: any) { } catch (e: any) {
errorMsg.value = e?.message || '' errorMsg.value = e?.message || ''
detail.value = null detail.value = null
doctorNotesLocal.value = []
} finally { } finally {
loading.value = false loading.value = false
} }
@@ -276,4 +394,18 @@ onMounted(fetchDetail)
background: linear-gradient(180deg, #4f8cff 0%, #2563eb 100%); background: linear-gradient(180deg, #4f8cff 0%, #2563eb 100%);
} }
} }
.card-body {
min-width: 0;
}
/* 与 DailyMatrix 根节点 .daily-matrix { padding: 16px } 对齐,使下方区块与「日常记录 / 待办」主内容区左右留白一致 */
.card-body--matrix-align {
padding: 16px;
box-sizing: border-box;
}
.section-card .card-title {
margin-bottom: 0;
}
</style> </style>