Files
his/openhis-ui-vue3/src/views/inpatient/VitalSignsChart.vue
2026-05-26 23:41:14 +08:00

123 lines
4.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import * as echarts from 'echarts'
import { getVitalSigns } from '@/api/inpatient'
const props = defineProps({
patientId: { type: Number, required: true }
})
const chartRef = ref(null)
let chartInstance = null
const tableData = ref([])
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' } },
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' } } }
],
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', 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', yAxisIndex: 1, data: adjustOverlap(pulseData, 'pulse'),
symbol: 'circle', symbolSize: 10, 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
}))
}
const fetchData = async () => {
try {
const res = await getVitalSigns(props.patientId)
renderChart(res.data || [])
} catch (e) {
console.error('获取体征数据失败', e)
}
}
onMounted(() => {
initChart()
fetchData()
})
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%; }
</style>