- refactor(web): PatientDetail.tsx 拆分为 4 个子组件(737→334行) - refactor(web): 提取 usePaginatedData hook 消除重复分页状态 - feat(db): patient.id_number varchar(20)→varchar(255) 容纳加密值 - test(health): 添加预约模块集成测试(创建/列表/租户隔离) - test(plugin): 添加 6 个 SQL 注入 sanitize 测试 - fix(miniprogram): 7 个 service 文件 URL 构建规范化(params 对象) - fix(miniprogram): 跨平台字段名对齐(birth_date/start_time/end_time)
77 lines
2.6 KiB
TypeScript
77 lines
2.6 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { View, Text } from '@tarojs/components';
|
|
import { useRouter } from '@tarojs/taro';
|
|
import { useHealthStore } from '@/stores/health';
|
|
import TrendChart from '@/components/TrendChart';
|
|
import './index.scss';
|
|
|
|
const RANGE_OPTIONS = [
|
|
{ value: '7d', label: '7天' },
|
|
{ value: '30d', label: '30天' },
|
|
{ value: '90d', label: '90天' },
|
|
];
|
|
|
|
const INDICATOR_META: Record<string, { label: string; unit: string; refMin?: number; refMax?: number }> = {
|
|
blood_pressure_systolic: { label: '收缩压', unit: 'mmHg', refMin: 90, refMax: 140 },
|
|
blood_pressure_diastolic: { label: '舒张压', unit: 'mmHg', refMin: 60, refMax: 90 },
|
|
heart_rate: { label: '心率', unit: 'bpm', refMin: 60, refMax: 100 },
|
|
blood_sugar_fasting: { label: '空腹血糖', unit: 'mmol/L', refMin: 3.9, refMax: 6.1 },
|
|
blood_sugar_postprandial: { label: '餐后血糖', unit: 'mmol/L', refMin: 3.9, refMax: 7.8 },
|
|
weight: { label: '体重', unit: 'kg' },
|
|
};
|
|
|
|
export default function Trend() {
|
|
const router = useRouter();
|
|
const indicator = router.params.indicator || 'heart_rate';
|
|
const [range, setRange] = useState('7d');
|
|
const [points, setPoints] = useState<{ date: string; value: number }[]>([]);
|
|
const { getTrend } = useHealthStore();
|
|
|
|
useEffect(() => {
|
|
getTrend(indicator, range).then(setPoints);
|
|
}, [indicator, range]);
|
|
|
|
const meta = INDICATOR_META[indicator] || { label: indicator, unit: '' };
|
|
|
|
return (
|
|
<View className='trend-page'>
|
|
<View className='trend-header'>
|
|
<Text className='trend-title'>{meta.label} 趋势</Text>
|
|
<View className='trend-tabs'>
|
|
{RANGE_OPTIONS.map((opt) => (
|
|
<View
|
|
key={opt.value}
|
|
className={`trend-tab ${range === opt.value ? 'active' : ''}`}
|
|
onClick={() => setRange(opt.value)}
|
|
>
|
|
<Text className='trend-tab-text'>{opt.label}</Text>
|
|
</View>
|
|
))}
|
|
</View>
|
|
</View>
|
|
|
|
{/* ECharts 折线图 */}
|
|
<View className='trend-chart-container'>
|
|
<TrendChart
|
|
data={points}
|
|
referenceMin={meta.refMin}
|
|
referenceMax={meta.refMax}
|
|
unit={meta.unit}
|
|
/>
|
|
</View>
|
|
|
|
{/* 数据列表 */}
|
|
{points.length > 0 && (
|
|
<View className='trend-list'>
|
|
{points.slice().reverse().map((p, i) => (
|
|
<View className='trend-item' key={i}>
|
|
<Text className='trend-item-date'>{p.date}</Text>
|
|
<Text className='trend-item-value'>{p.value}{meta.unit ? ` ${meta.unit}` : ''}</Text>
|
|
</View>
|
|
))}
|
|
</View>
|
|
)}
|
|
</View>
|
|
);
|
|
}
|