Fix Bug #566: AI修复
This commit is contained in:
@@ -1,122 +1,132 @@
|
||||
<template>
|
||||
<div class="vital-signs-container">
|
||||
<div ref="chartRef" class="vital-signs-chart" style="height: 400px; width: 100%;"></div>
|
||||
<el-table :data="tableData" class="vital-signs-table" border style="margin-top: 12px;">
|
||||
<el-table-column prop="time" label="时间" width="160" align="center" />
|
||||
<el-table-column prop="temperature" label="体温(℃)" align="center" />
|
||||
<el-table-column prop="heartRate" label="心率(次/分)" align="center" />
|
||||
<el-table-column prop="pulse" label="脉搏(次/分)" align="center" />
|
||||
</el-table>
|
||||
<el-card shadow="never" class="chart-card">
|
||||
<div ref="chartRef" class="chart-container" data-connect-nulls="false"></div>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="table-card">
|
||||
<el-table :data="tableData" border stripe style="width: 100%">
|
||||
<el-table-column prop="recordTime" label="时间" width="180" align="center" />
|
||||
<el-table-column prop="temperature" label="体温(℃)" align="center">
|
||||
<template #default="{ row }">{{ row.temperature ?? '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="heartRate" label="心率(次/分)" align="center">
|
||||
<template #default="{ row }">{{ row.heartRate ?? '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="pulse" label="脉搏(次/分)" align="center">
|
||||
<template #default="{ row }">{{ row.pulse ?? '-' }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { ref, reactive, onMounted, nextTick, watch } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { getVitalSigns } from '@/api/inpatient'
|
||||
import { getVitalSignsList } from '@/api/inpatient'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const props = defineProps({
|
||||
patientId: { type: Number, required: true }
|
||||
patientId: { type: [String, Number], required: true },
|
||||
refreshTrigger: { type: Boolean, default: false }
|
||||
})
|
||||
|
||||
const chartRef = ref(null)
|
||||
let chartInstance = null
|
||||
const tableData = ref([])
|
||||
const chartData = reactive({
|
||||
xAxis: [],
|
||||
series: {
|
||||
temperature: [],
|
||||
heartRate: [],
|
||||
pulse: []
|
||||
}
|
||||
})
|
||||
|
||||
const initChart = () => {
|
||||
if (!chartRef.value) return
|
||||
chartInstance = echarts.init(chartRef.value)
|
||||
window.addEventListener('resize', handleResize)
|
||||
}
|
||||
|
||||
const handleResize = () => chartInstance?.resize()
|
||||
|
||||
const renderChart = (rawData) => {
|
||||
if (!chartInstance) return
|
||||
|
||||
// 1. 按时间升序排序
|
||||
const sorted = [...rawData].sort((a, b) => new Date(a.recordTime) - new Date(b.recordTime))
|
||||
const timeAxis = sorted.map(d => d.timeStr)
|
||||
|
||||
// 2. 提取指标序列,缺失值填 null 触发 ECharts 断线逻辑
|
||||
const tempData = sorted.map(d => d.temperature != null ? Number(d.temperature) : null)
|
||||
const hrData = sorted.map(d => d.heartRate != null ? Number(d.heartRate) : null)
|
||||
const pulseData = sorted.map(d => d.pulse != null ? Number(d.pulse) : null)
|
||||
|
||||
// 3. 重叠处理:同一时间点三值完全一致时,Y轴轻微偏移避免视觉重合
|
||||
const adjustOverlap = (series, type) => {
|
||||
return series.map((val, idx) => {
|
||||
if (val === null) return null
|
||||
const t = tempData[idx], h = hrData[idx], p = pulseData[idx]
|
||||
if (t !== null && h !== null && p !== null && t === h && h === p) {
|
||||
return type === 'temp' ? val + 0.15 : type === 'hr' ? val + 0.08 : val
|
||||
}
|
||||
return val
|
||||
})
|
||||
}
|
||||
|
||||
const option = {
|
||||
tooltip: { trigger: 'axis', axisPointer: { type: 'cross' } },
|
||||
tooltip: { trigger: 'axis' },
|
||||
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
|
||||
xAxis: { type: 'category', data: timeAxis, axisLabel: { rotate: 30, interval: 0 } },
|
||||
yAxis: [
|
||||
{ type: 'value', name: '体温(℃)', min: 35, max: 42, splitNumber: 7, axisLine: { lineStyle: { color: '#1890ff' } } },
|
||||
{ type: 'value', name: '心率/脉搏', min: 40, max: 160, splitNumber: 12, axisLine: { lineStyle: { color: '#f5222d' } } }
|
||||
],
|
||||
xAxis: { type: 'category', data: chartData.xAxis, axisLabel: { rotate: 30 } },
|
||||
yAxis: { type: 'value', min: 35, max: 42, splitNumber: 7 },
|
||||
series: [
|
||||
{
|
||||
name: '体温', type: 'line', yAxisIndex: 0, data: adjustOverlap(tempData, 'temp'),
|
||||
symbol: 'path://M0,0 L10,10 M10,0 L0,10', symbolSize: 10,
|
||||
lineStyle: { color: '#1890ff', width: 2 }, connectNulls: false
|
||||
name: '体温',
|
||||
type: 'line',
|
||||
data: chartData.series.temperature,
|
||||
symbol: 'x',
|
||||
symbolSize: 8,
|
||||
itemStyle: { color: '#1890ff' },
|
||||
lineStyle: { color: '#1890ff', width: 2 },
|
||||
connectNulls: false
|
||||
},
|
||||
{
|
||||
name: '心率', type: 'line', yAxisIndex: 1, data: adjustOverlap(hrData, 'hr'),
|
||||
symbol: 'circle', symbolSize: 10, itemStyle: { color: '#fff', borderColor: '#f5222d', borderWidth: 2 },
|
||||
lineStyle: { color: '#f5222d', width: 2 }, connectNulls: false
|
||||
name: '心率',
|
||||
type: 'line',
|
||||
data: chartData.series.heartRate,
|
||||
symbol: 'emptyCircle',
|
||||
symbolSize: 8,
|
||||
itemStyle: { color: '#f5222d' },
|
||||
lineStyle: { color: '#f5222d', width: 2 },
|
||||
connectNulls: false
|
||||
},
|
||||
{
|
||||
name: '脉搏', type: 'line', yAxisIndex: 1, data: adjustOverlap(pulseData, 'pulse'),
|
||||
symbol: 'circle', symbolSize: 10, itemStyle: { color: '#f5222d' },
|
||||
lineStyle: { color: '#f5222d', width: 2 }, connectNulls: false
|
||||
name: '脉搏',
|
||||
type: 'line',
|
||||
data: chartData.series.pulse,
|
||||
symbol: 'circle',
|
||||
symbolSize: 8,
|
||||
itemStyle: { color: '#f5222d' },
|
||||
lineStyle: { color: '#f5222d', width: 2 },
|
||||
connectNulls: false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
chartInstance.setOption(option, true)
|
||||
|
||||
// 同步下方表格数据
|
||||
tableData.value = sorted.map(d => ({
|
||||
time: d.timeStr,
|
||||
temperature: d.temperature,
|
||||
heartRate: d.heartRate,
|
||||
pulse: d.pulse
|
||||
}))
|
||||
chartInstance.setOption(option)
|
||||
}
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res = await getVitalSigns(props.patientId)
|
||||
renderChart(res.data || [])
|
||||
} catch (e) {
|
||||
console.error('获取体征数据失败', e)
|
||||
const res = await getVitalSignsList({ patientId: props.patientId })
|
||||
if (res.code === 200) {
|
||||
const sortedData = (res.data || []).sort((a, b) => new Date(a.recordTime) - new Date(b.recordTime))
|
||||
chartData.xAxis = sortedData.map(d => d.recordTime)
|
||||
chartData.series.temperature = sortedData.map(d => d.temperature ?? null)
|
||||
chartData.series.heartRate = sortedData.map(d => d.heartRate ?? null)
|
||||
chartData.series.pulse = sortedData.map(d => d.pulse ?? null)
|
||||
tableData.value = sortedData
|
||||
await nextTick()
|
||||
if (chartInstance) {
|
||||
chartInstance.setOption({
|
||||
xAxis: { data: chartData.xAxis },
|
||||
series: [
|
||||
{ data: chartData.series.temperature },
|
||||
{ data: chartData.series.heartRate },
|
||||
{ data: chartData.series.pulse }
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
ElMessage.error('获取体征数据失败')
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.refreshTrigger, (val) => {
|
||||
if (val) fetchData()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
initChart()
|
||||
fetchData()
|
||||
initChart()
|
||||
window.addEventListener('resize', () => chartInstance?.resize())
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', handleResize)
|
||||
chartInstance?.dispose()
|
||||
})
|
||||
|
||||
// 暴露刷新方法供父组件在保存成功后调用
|
||||
defineExpose({ refresh: fetchData })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.vital-signs-container { padding: 10px; background: #fff; border-radius: 4px; }
|
||||
.vital-signs-chart { width: 100%; }
|
||||
.vital-signs-container { padding: 16px; display: flex; flex-direction: column; gap: 16px; }
|
||||
.chart-card { height: 400px; }
|
||||
.chart-container { width: 100%; height: 100%; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user