From 6c60be0047d2b953862da3dfbc795d5bb1c563f8 Mon Sep 17 00:00:00 2001 From: iven Date: Sun, 26 Apr 2026 08:08:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20=E4=BD=93=E5=BE=81=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E9=A1=B5=E9=9D=A2=20UI/UX=20=E4=BC=98=E5=8C=96=20?= =?UTF-8?q?=E2=80=94=20=E6=B6=88=E9=99=A4=E7=A9=BA=E7=99=BD+=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E5=AF=86=E5=BA=A6=E6=8F=90=E5=8D=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VitalSignsChart: - 空状态改为带图标的虚线提示区域(替代 Empty 组件大片空白) - 图表高度约束 180px,加载状态居中显示 - 添加最新值摘要显示 - 头部选择器与摘要信息并排布局 VitalSignsTab: - 添加最新记录摘要条(体征数据一览) - 表格上方显示记录总数 - 表头列名带 Tooltip 说明 - 录入按钮改为 small size,节省空间 - 表格添加 scroll.x 防止列溢出 --- .../health/components/VitalSignsChart.tsx | 102 ++++++++++++-- .../pages/health/components/VitalSignsTab.tsx | 127 ++++++++++++++++-- 2 files changed, 201 insertions(+), 28 deletions(-) diff --git a/apps/web/src/pages/health/components/VitalSignsChart.tsx b/apps/web/src/pages/health/components/VitalSignsChart.tsx index 141a7c7..3d054b1 100644 --- a/apps/web/src/pages/health/components/VitalSignsChart.tsx +++ b/apps/web/src/pages/health/components/VitalSignsChart.tsx @@ -1,8 +1,11 @@ import { useEffect, useState } from 'react'; import { Line } from '@ant-design/charts'; -import { Spin, Empty, Select, Space, Alert } from 'antd'; +import { Spin, Select, Alert, Typography } from 'antd'; +import { LineChartOutlined } from '@ant-design/icons'; import { healthDataApi } from '../../../api/health/healthData'; +const { Text } = Typography; + interface Props { patientId: string; indicator?: string; @@ -16,6 +19,14 @@ const INDICATORS = [ { value: 'blood_sugar', label: '血糖' }, ]; +const INDICATOR_UNITS: Record = { + systolic_bp_morning: 'mmHg', + diastolic_bp_morning: 'mmHg', + heart_rate: 'bpm', + weight: 'kg', + blood_sugar: 'mmol/L', +}; + export function VitalSignsChart({ patientId, indicator: initialIndicator }: Props) { const [indicator, setIndicator] = useState(initialIndicator ?? 'systolic_bp_morning'); const [data, setData] = useState<{ date: string; value: number }[]>([]); @@ -33,30 +44,93 @@ export function VitalSignsChart({ patientId, indicator: initialIndicator }: Prop .finally(() => setLoading(false)); }, [patientId, indicator]); - if (loading) return ; - if (error) return ; - if (data.length === 0) return ; + const unit = INDICATOR_UNITS[indicator] ?? ''; + + // 头部:选择器 + 最新值摘要 + const latestValue = data.length > 0 ? data[data.length - 1].value : null; + + const renderHeader = () => ( +
+ +
+ {renderHeader()}
- +
); } diff --git a/apps/web/src/pages/health/components/VitalSignsTab.tsx b/apps/web/src/pages/health/components/VitalSignsTab.tsx index 707bc7b..91504a6 100644 --- a/apps/web/src/pages/health/components/VitalSignsTab.tsx +++ b/apps/web/src/pages/health/components/VitalSignsTab.tsx @@ -1,23 +1,69 @@ import { useCallback, useState } from 'react'; -import { Table, Button, Modal, Form, InputNumber, DatePicker, Input, message } from 'antd'; -import { PlusOutlined } from '@ant-design/icons'; +import { Table, Button, Modal, Form, InputNumber, DatePicker, Input, message, Typography, Tooltip } from 'antd'; +import { PlusOutlined, InfoCircleOutlined } from '@ant-design/icons'; import type { Dayjs } from 'dayjs'; import { healthDataApi } from '../../../api/health/healthData'; import type { VitalSigns } from '../../../api/health/healthData'; import { VitalSignsChart } from './VitalSignsChart'; import { usePaginatedData } from '../../../hooks/usePaginatedData'; +const { Text } = Typography; + interface Props { patientId: string; } const columns = [ - { title: '记录日期', dataIndex: 'record_date', key: 'record_date', width: 120 }, - { title: '收缩压(晨)', dataIndex: 'systolic_bp_morning', key: 'systolic_bp_morning', width: 110, render: (v?: number) => (v != null ? `${v} mmHg` : '-') }, - { title: '舒张压(晨)', dataIndex: 'diastolic_bp_morning', key: 'diastolic_bp_morning', width: 110, render: (v?: number) => (v != null ? `${v} mmHg` : '-') }, - { title: '心率', dataIndex: 'heart_rate', key: 'heart_rate', width: 80, render: (v?: number) => (v != null ? `${v} bpm` : '-') }, - { title: '体重', dataIndex: 'weight', key: 'weight', width: 80, render: (v?: number) => (v != null ? `${v} kg` : '-') }, - { title: '血糖', dataIndex: 'blood_sugar', key: 'blood_sugar', width: 80, render: (v?: number) => (v != null ? `${v} mmol/L` : '-') }, + { + title: '记录日期', + dataIndex: 'record_date', + key: 'record_date', + width: 110, + fixed: 'left' as const, + }, + { + title: () => ( + + 收缩压(晨) + + ), + dataIndex: 'systolic_bp_morning', + key: 'systolic_bp_morning', + width: 110, + render: (v?: number) => (v != null ? `${v} mmHg` : '-'), + }, + { + title: () => ( + + 舒张压(晨) + + ), + dataIndex: 'diastolic_bp_morning', + key: 'diastolic_bp_morning', + width: 110, + render: (v?: number) => (v != null ? `${v} mmHg` : '-'), + }, + { + title: '心率', + dataIndex: 'heart_rate', + key: 'heart_rate', + width: 80, + render: (v?: number) => (v != null ? `${v} bpm` : '-'), + }, + { + title: '体重', + dataIndex: 'weight', + key: 'weight', + width: 80, + render: (v?: number) => (v != null ? `${v} kg` : '-'), + }, + { + title: '血糖', + dataIndex: 'blood_sugar', + key: 'blood_sugar', + width: 90, + render: (v?: number) => (v != null ? `${v} mmol/L` : '-'), + }, ]; export function VitalSignsTab({ patientId }: Props) { @@ -69,16 +115,64 @@ export function VitalSignsTab({ patientId }: Props) { } }; + // 最近一次数据摘要 + const latest = data.length > 0 ? data[0] : null; + return (
-
- -
+ {/* 趋势图 */}
+ + {/* 最新记录摘要条 */} + {latest && ( +
+ + + 最新记录({latest.record_date}) + + {latest.systolic_bp_morning != null && ( + 收缩压 {latest.systolic_bp_morning} mmHg + )} + {latest.diastolic_bp_morning != null && ( + 舒张压 {latest.diastolic_bp_morning} mmHg + )} + {latest.heart_rate != null && ( + 心率 {latest.heart_rate} bpm + )} + {latest.weight != null && ( + 体重 {latest.weight} kg + )} + {latest.blood_sugar != null && ( + 血糖 {latest.blood_sugar} mmol/L + )} +
+ )} + + {/* 操作栏 + 表格 */} +
+ + 历史记录(共 {total} 条) + + +
+ refresh(p), showTotal: (t) => `共 ${t} 条`, + size: 'small', style: { margin: 0 }, }} + scroll={{ x: 600 }} /> +