Merge branch 'long-consultation-desk'

This commit is contained in:
2026-04-30 10:36:25 +08:00
148 changed files with 2944 additions and 22718 deletions
@@ -0,0 +1,116 @@
<template>
<div v-if="notes.length" class="note-timeline">
<div v-for="note in notes" :key="note.id" class="timeline-node">
<div class="timeline-dot"></div>
<div class="timeline-date">{{ note.note_date }}</div>
<div class="timeline-body">
<div v-if="note.content" class="timeline-content">
<div v-for="(line, idx) in splitLines(note.content)" :key="idx" class="content-line">
{{ line }}
</div>
</div>
<div v-if="note.tongue_images?.length" class="timeline-images">
<el-image
v-for="(url, idx) in note.tongue_images"
:key="idx"
:src="url"
:preview-src-list="note.tongue_images"
:initial-index="idx"
fit="cover"
class="timeline-thumb"
preview-teleported
/>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
export interface DoctorNoteItem {
id: number
note_date: string
content: string | null
tongue_images: string[]
}
defineProps<{ notes: DoctorNoteItem[] }>()
const splitLines = (content: string | null): string[] => {
if (!content) return []
return content.split('\n').filter(Boolean)
}
</script>
<style scoped lang="scss">
.note-timeline {
position: relative;
padding-left: 22px;
}
.note-timeline::before {
content: '';
position: absolute;
left: 6px;
top: 4px;
bottom: 4px;
width: 2px;
background: #ebeef5;
border-radius: 1px;
}
.timeline-node {
position: relative;
padding-bottom: 16px;
}
.timeline-node:last-child {
padding-bottom: 0;
}
.timeline-dot {
position: absolute;
left: -19px;
top: 5px;
width: 10px;
height: 10px;
border-radius: 50%;
background: #409eff;
border: 2px solid #ecf5ff;
}
.timeline-date {
font-size: 13px;
font-weight: 600;
color: #303133;
margin-bottom: 6px;
font-family: 'IBM Plex Mono', Consolas, monospace;
}
.timeline-body {
display: flex;
flex-direction: column;
gap: 6px;
}
.timeline-content {
display: flex;
flex-direction: column;
gap: 2px;
}
.content-line {
font-size: 12.5px;
color: #606266;
line-height: 1.6;
word-break: break-word;
}
.timeline-images {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.timeline-thumb {
width: 64px;
height: 64px;
border-radius: 6px;
border: 1px solid #ebeef5;
cursor: pointer;
transition: transform 0.15s ease;
}
.timeline-thumb:hover {
transform: scale(1.05);
}
</style>
File diff suppressed because it is too large Load Diff
+20 -1
View File
@@ -527,7 +527,13 @@
</fieldset>
</el-form>
</el-tab-pane>
<el-tab-pane label="医生备注" name="doctorNote" :disabled="!formData.id" lazy>
<div v-if="formData.id" class="p-4">
<NoteTimeline v-if="diagNotes.length" :notes="diagNotes" />
<el-empty v-else description="暂无备注记录" :image-size="48" />
</div>
<el-empty v-else description="请先保存诊单后查看" />
</el-tab-pane>
<!-- 血糖血压记录标签页 -->
<el-tab-pane label="血糖血压记录" name="blood" :disabled="!formData.id">
<blood-record-list
@@ -662,6 +668,7 @@
/>
<el-empty v-else description="请先保存诊单后再查看挂号记录" />
</el-tab-pane>
</el-tabs>
<!-- 处方详情(查看病历) -->
@@ -702,7 +709,9 @@ 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 NoteTimeline from '@/views/patient/reception/components/NoteTimeline.vue'
import TcmPrescription from '@/components/tcm-prescription/index.vue'
import { getDoctorNotes } from '@/api/patient'
import {
DIAGNOSIS_TONGUE_IMAGES_UPDATED,
type DiagnosisTongueImagesUpdatedDetail
@@ -733,6 +742,16 @@ const drawerTitle = computed(() => {
return mode.value === 'add' ? '新增诊单' : '编辑诊单'
})
const diagNotes = ref<any[]>([])
watch(() => activeTab.value, async (tab) => {
if (tab === 'doctorNote' && formData.value.id) {
try {
diagNotes.value = await getDoctorNotes({ diagnosis_id: Number(formData.value.id) }) || []
} catch { diagNotes.value = [] }
}
})
watch([visible, activeTab], () => {
if (!visible.value) return
if (activeTab.value === 'chat' && !hasPermission(['tcm.diagnosis/chat'])) {