跟踪备注
This commit is contained in:
@@ -19,6 +19,19 @@ export function diagnosisTrackingWindow(params: {
|
||||
return request.get({ url: '/tcm.diagnosis/trackingWindow', params })
|
||||
}
|
||||
|
||||
/** 新增跟踪备注(按天合并追加,仅文字) */
|
||||
export function diagnosisAddTrackingNote(params: {
|
||||
diagnosis_id: number
|
||||
tracking_content: string
|
||||
}) {
|
||||
return request.post({ url: '/tcm.diagnosis/addTrackingNote', params })
|
||||
}
|
||||
|
||||
/** 拉取跟踪备注列表(note_date DESC) */
|
||||
export function diagnosisTrackingNotes(params: { diagnosis_id: number }) {
|
||||
return request.get({ url: '/tcm.diagnosis/trackingNotes', params })
|
||||
}
|
||||
|
||||
// 医助理诊单统计(按部门、按人)
|
||||
export function assistantDiagnosisStats(params?: {
|
||||
start_time?: string
|
||||
|
||||
@@ -126,6 +126,12 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 跟踪备注(只读时间轴;新增入口在「编辑诊单」内) -->
|
||||
<div class="card tracking-note-card">
|
||||
<div class="card-title">跟踪备注</div>
|
||||
<TrackingNoteTimeline :notes="trackingNotes" readonly />
|
||||
</div>
|
||||
|
||||
<!-- 跟踪信息(与只读病例页同口径,自治 lazy load) -->
|
||||
<div class="card metric-card">
|
||||
<div class="card-title">跟踪信息</div>
|
||||
@@ -191,6 +197,7 @@ import NoteTimeline from './components/NoteTimeline.vue'
|
||||
import PatientInfoCard from '@/views/tcm/diagnosis/components/PatientInfoCard.vue'
|
||||
import PatientCaseCard from '@/views/tcm/diagnosis/components/PatientCaseCard.vue'
|
||||
import TrackingMatrix from '@/views/tcm/diagnosis/components/TrackingMatrix.vue'
|
||||
import TrackingNoteTimeline from '@/views/tcm/diagnosis/components/TrackingNoteTimeline.vue'
|
||||
import { completeAppointment } from '@/api/doctor'
|
||||
import { getCallSignature } from '@/api/tcm'
|
||||
import feedback from '@/utils/feedback'
|
||||
@@ -266,6 +273,7 @@ const noteSaving = ref(false)
|
||||
const apt = computed<any>(() => detail.value?.appointment || {})
|
||||
const diag = computed<any>(() => detail.value?.diagnosis || {})
|
||||
const doctorNotes = computed<any[]>(() => detail.value?.doctor_notes || [])
|
||||
const trackingNotes = computed<any[]>(() => detail.value?.tracking_notes || [])
|
||||
|
||||
const todayMeta = computed(() => {
|
||||
const d = new Date()
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<div class="tracking-timeline-wrap">
|
||||
<!-- 编辑模式:输入新备注 -->
|
||||
<div v-if="!readonly && diagnosisId" class="timeline-input">
|
||||
<el-input
|
||||
v-model="draft"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="输入跟踪备注,回车换行;保存后将以「[HH:MM] 内容」追加到当天记录"
|
||||
maxlength="1000"
|
||||
show-word-limit
|
||||
resize="none"
|
||||
:disabled="saving"
|
||||
/>
|
||||
<div class="input-actions">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
:loading="saving"
|
||||
:disabled="!draft.trim()"
|
||||
@click="handleSave"
|
||||
>
|
||||
添加
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 时间轴 -->
|
||||
<div v-if="notes.length" class="tracking-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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-empty v-if="!notes.length && readonly" description="暂无跟踪备注" :image-size="48" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { diagnosisAddTrackingNote } from '@/api/tcm'
|
||||
import feedback from '@/utils/feedback'
|
||||
|
||||
export interface TrackingNoteItem {
|
||||
id: number
|
||||
note_date: string
|
||||
content: string | null
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
notes: TrackingNoteItem[]
|
||||
diagnosisId?: number
|
||||
readonly?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{ refresh: [] }>()
|
||||
|
||||
const draft = ref('')
|
||||
const saving = ref(false)
|
||||
|
||||
const splitLines = (content: string | null): string[] => {
|
||||
if (!content) return []
|
||||
return content.split('\n').filter(Boolean)
|
||||
}
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!props.diagnosisId) return
|
||||
const content = draft.value.trim()
|
||||
if (!content) return
|
||||
saving.value = true
|
||||
try {
|
||||
await diagnosisAddTrackingNote({
|
||||
diagnosis_id: props.diagnosisId,
|
||||
tracking_content: content,
|
||||
})
|
||||
feedback.msgSuccess('已添加')
|
||||
draft.value = ''
|
||||
emit('refresh')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.tracking-timeline-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.timeline-input {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px dashed #ebeef5;
|
||||
}
|
||||
|
||||
.input-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.tracking-timeline {
|
||||
position: relative;
|
||||
padding-left: 22px;
|
||||
}
|
||||
|
||||
.tracking-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: #16a34a;
|
||||
border: 2px solid #dcfce7;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
</style>
|
||||
@@ -562,6 +562,17 @@
|
||||
</div>
|
||||
<el-empty v-else description="请先保存诊单后查看" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="跟踪备注" name="trackingNote" :disabled="!formData.id" lazy>
|
||||
<div v-if="formData.id" class="p-4">
|
||||
<TrackingNoteTimeline
|
||||
:notes="trackingNotes"
|
||||
:diagnosis-id="Number(formData.id)"
|
||||
:readonly="viewOnly"
|
||||
@refresh="fetchTrackingNotes"
|
||||
/>
|
||||
</div>
|
||||
<el-empty v-else description="请先保存诊单后查看" />
|
||||
</el-tab-pane>
|
||||
<!-- 血糖血压记录标签页 -->
|
||||
<el-tab-pane label="血糖血压记录" name="blood" :disabled="!formData.id">
|
||||
<blood-record-list
|
||||
@@ -711,7 +722,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { tcmDiagnosisAdd, tcmDiagnosisEdit, tcmDiagnosisDetail, checkPhone, checkIdCard } from '@/api/tcm'
|
||||
import { tcmDiagnosisAdd, tcmDiagnosisEdit, tcmDiagnosisDetail, checkPhone, checkIdCard, diagnosisTrackingNotes } from '@/api/tcm'
|
||||
import { getDictData } from '@/api/app'
|
||||
import feedback from '@/utils/feedback'
|
||||
import { hasPermission } from '@/utils/perm'
|
||||
@@ -729,6 +740,7 @@ 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 TrackingNoteTimeline from './components/TrackingNoteTimeline.vue'
|
||||
import TcmPrescription from '@/components/tcm-prescription/index.vue'
|
||||
import { getDoctorNotes } from '@/api/patient'
|
||||
|
||||
@@ -766,10 +778,22 @@ const fetchDiagNotes = async () => {
|
||||
} catch { diagNotes.value = [] }
|
||||
}
|
||||
|
||||
const trackingNotes = ref<any[]>([])
|
||||
|
||||
const fetchTrackingNotes = async () => {
|
||||
if (!formData.value.id) return
|
||||
try {
|
||||
trackingNotes.value = await diagnosisTrackingNotes({ diagnosis_id: Number(formData.value.id) }) || []
|
||||
} catch { trackingNotes.value = [] }
|
||||
}
|
||||
|
||||
watch(() => activeTab.value, async (tab) => {
|
||||
if (tab === 'doctorNote' && formData.value.id) {
|
||||
await fetchDiagNotes()
|
||||
}
|
||||
if (tab === 'trackingNote' && formData.value.id) {
|
||||
await fetchTrackingNotes()
|
||||
}
|
||||
})
|
||||
|
||||
watch([visible, activeTab], () => {
|
||||
|
||||
@@ -53,6 +53,12 @@
|
||||
<div class="card-title">医生备注 & 舌苔照片 & 检查报告</div>
|
||||
<NoteTimeline :notes="doctorNotes" :diagnosis-id="diag?.id" />
|
||||
</div>
|
||||
|
||||
<!-- 跟踪备注(只读时间轴) -->
|
||||
<div class="card tracking-note-card">
|
||||
<div class="card-title">跟踪备注</div>
|
||||
<TrackingNoteTimeline :notes="trackingNotes" readonly />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@@ -66,6 +72,7 @@ import { formatGender } from '@/utils/diag-display'
|
||||
import PatientInfoCard from './components/PatientInfoCard.vue'
|
||||
import PatientCaseCard from './components/PatientCaseCard.vue'
|
||||
import TrackingMatrix from './components/TrackingMatrix.vue'
|
||||
import TrackingNoteTimeline from './components/TrackingNoteTimeline.vue'
|
||||
import NoteTimeline from '@/views/patient/reception/components/NoteTimeline.vue'
|
||||
|
||||
const route = useRoute()
|
||||
@@ -80,6 +87,7 @@ const detail = ref<any>(null)
|
||||
const apt = computed<any>(() => detail.value?.appointment || {})
|
||||
const diag = computed<any>(() => detail.value?.diagnosis || {})
|
||||
const doctorNotes = computed<any[]>(() => detail.value?.doctor_notes || [])
|
||||
const trackingNotes = computed<any[]>(() => detail.value?.tracking_notes || [])
|
||||
const unservedDays = computed<number | null>(() => {
|
||||
const v = detail.value?.unserved_days
|
||||
return v === null || v === undefined ? null : Number(v)
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user