fix:
No SOFA trend chart or organ-system timeline No GCS trend chart or component history No qSOFA history view
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
<script setup>
|
||||
import { computed, shallowRef, markRaw } from 'vue'
|
||||
import { Chart } from 'vue-chartjs'
|
||||
import { Chart as ChartJS, registerables } from 'chart.js'
|
||||
import { formatTime } from '@/composables/chartFormat'
|
||||
|
||||
ChartJS.register(...registerables)
|
||||
|
||||
const props = defineProps({ history: { type: Array, required: true } })
|
||||
|
||||
const ORGAN_DATASETS = [
|
||||
{ label: 'Respiratory', key: 'respiratoryScore', color: '#3b82f6' },
|
||||
{ label: 'Coagulation', key: 'coagulationScore', color: '#8b5cf6' },
|
||||
{ label: 'Liver', key: 'liverScore', color: '#f59e0b' },
|
||||
{ label: 'Cardiovascular', key: 'cardiovascularScore', color: '#ef4444' },
|
||||
{ label: 'CNS', key: 'cnsScore', color: '#ec4899' },
|
||||
{ label: 'Renal', key: 'renalScore', color: '#06b6d4' },
|
||||
]
|
||||
|
||||
function sofaRiskBorder(score) {
|
||||
if (score >= 10) return '#dc2626'
|
||||
if (score >= 6) return '#f59e0b'
|
||||
return '#22c55e'
|
||||
}
|
||||
|
||||
const sortedHistory = computed(() =>
|
||||
[...props.history].sort((a, b) => new Date(a.calculatedAt) - new Date(b.calculatedAt)),
|
||||
)
|
||||
|
||||
const chartData = computed(() => {
|
||||
const sorted = sortedHistory.value
|
||||
const labels = sorted.map(h => formatTime(h.calculatedAt))
|
||||
|
||||
const organDatasets = ORGAN_DATASETS.map(({ label, key, color }) => ({
|
||||
type: 'line',
|
||||
label,
|
||||
data: sorted.map(h => h[key] ?? 0),
|
||||
backgroundColor: `${color}66`,
|
||||
borderColor: color,
|
||||
borderWidth: 1,
|
||||
fill: true,
|
||||
stack: 'organs',
|
||||
pointRadius: 0,
|
||||
tension: 0.2,
|
||||
order: 2,
|
||||
}))
|
||||
|
||||
return {
|
||||
labels,
|
||||
datasets: [
|
||||
...organDatasets,
|
||||
{
|
||||
type: 'line',
|
||||
label: 'SOFA Total',
|
||||
data: sorted.map(h => h.totalScore),
|
||||
borderColor: '#111827',
|
||||
backgroundColor: 'transparent',
|
||||
pointBackgroundColor: sorted.map(h => sofaRiskBorder(h.totalScore)),
|
||||
pointBorderColor: sorted.map(h => sofaRiskBorder(h.totalScore)),
|
||||
pointRadius: 4,
|
||||
borderWidth: 2,
|
||||
fill: false,
|
||||
tension: 0.3,
|
||||
order: 1,
|
||||
},
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
const chartOptions = shallowRef(markRaw({
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
animation: {
|
||||
duration: window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 0 : 400,
|
||||
},
|
||||
interaction: { mode: 'index', intersect: false },
|
||||
scales: {
|
||||
x: { stacked: true },
|
||||
y: {
|
||||
stacked: true,
|
||||
min: 0,
|
||||
max: 24,
|
||||
title: { display: true, text: 'SOFA Score' },
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'bottom',
|
||||
labels: { boxWidth: 12, font: { size: 11 } },
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
footer(items) {
|
||||
const total = items.find(i => i.dataset.label === 'SOFA Total')
|
||||
if (!total) return ''
|
||||
const score = total.parsed.y
|
||||
if (score >= 10) return 'Risk: High (≥10)'
|
||||
if (score >= 6) return 'Risk: Moderate (6–9)'
|
||||
return 'Risk: Low (0–5)'
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full min-w-0 rounded-lg border border-gray-200 bg-white p-4 dark:border-gray-700 dark:bg-gray-900">
|
||||
<h3 class="mb-1 text-sm font-medium text-gray-700 dark:text-gray-300">SOFA Score Over Time</h3>
|
||||
<p class="mb-4 text-xs text-gray-500 dark:text-gray-400">
|
||||
Stacked areas show per-organ contributions; line shows total score.
|
||||
<span class="text-green-600 dark:text-green-400">0–5</span>,
|
||||
<span class="text-amber-600 dark:text-amber-400">6–9</span>,
|
||||
<span class="text-red-600 dark:text-red-400">10+</span>.
|
||||
</p>
|
||||
<div class="aspect-video w-full min-h-0 overflow-hidden">
|
||||
<Chart type="line" :data="chartData" :options="chartOptions" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user