import { useEffect, useState } from 'react'; import { Activity } from 'lucide-react'; import { statsApi, type InpatientClinicalResponse } from '@/services/api'; import { Card, LoadingState, EmptyState } from '@/components/ui'; import { ErrorBanner } from '@/components/ErrorBanner'; import { TESTIDS } from '@/utils/testids'; import { ClinicalKpiRow } from '@/components/clinical/ClinicalKpiRow'; import { HistogramChart } from '@/components/clinical/HistogramChart'; import { BoxPlotRows, type BoxRow } from '@/components/clinical/BoxPlotRows'; import { DonutChart, type DonutSlice } from '@/components/clinical/DonutChart'; import { CLINICAL_COLORS } from '@/components/clinical/chartColors'; /** 数据是否完全为空(KPI 0 人次且各序列均空)。 */ function isEmpty(d: InpatientClinicalResponse): boolean { return ( (!d.kpis || d.kpis.total_admissions === 0) && (d.los_histogram?.length ?? 0) === 0 && (d.outcome_counts?.length ?? 0) === 0 ); } export function ClinicalAnalysis() { const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { let cancelled = false; const fetchData = async () => { setIsLoading(true); setError(null); try { const res = await statsApi.getInpatientClinical(); if (cancelled) return; setData(res); } catch { if (cancelled) return; setError('住院临床数据加载失败'); } finally { if (!cancelled) setIsLoading(false); } }; fetchData(); return () => { cancelled = true; }; }, []); const header = (

住院临床分析

住院天数、出院结局、入院途径与年龄别 BMI 等临床特征分析

); if (isLoading) { return (
{header}
); } if (error || !data) { return (
{header} window.location.reload()} onDismiss={() => setError(null)} />
); } if (isEmpty(data)) { return (
{header}
); } // 出院结局:治愈/好转在前(按严重程度排序展示更直观)。 const outcomeSlices: DonutSlice[] = (data.outcome_counts ?? []).map((o) => ({ name: o.outcome, value: o.count, })); const routeSlices: DonutSlice[] = (data.admission_route_counts ?? []).map((r) => ({ name: r.route, value: r.count, })); const losBox: BoxRow[] = (data.los_by_disease ?? []).map((d) => ({ label: d.diagnosis, p25: d.p25, median: d.median, p75: d.p75, n: d.n, })); const bmiBox: BoxRow[] = (data.bmi_by_age_band ?? []).map((d) => ({ label: d.age_band, p25: d.p25, median: d.median, p75: d.p75, n: d.n, })); return (
{header} {/* KPI 行 */} {/* 住院天数:分布 + 各病种箱线 */}
{/* 出院结局 + 入院途径 双环 */}
{/* 年龄别 BMI 箱线 */}
); }