行动收件箱、医护工作台、健康趋势、患者告警、体征录入、 日常监测、设备同步共 7 个页面迁移: - 最外层容器 → PageShell - 卡片元素 → ContentCard - SCSS 删除 min-height/background/box-shadow 通用样式
143 lines
5.1 KiB
TypeScript
143 lines
5.1 KiB
TypeScript
import { useState, useEffect, useCallback } from 'react';
|
|
import { View, Text } from '@tarojs/components';
|
|
import { useRouter } from '@tarojs/taro';
|
|
import { usePageData } from '@/hooks/usePageData';
|
|
import { useHealthStore } from '@/stores/health';
|
|
import TrendChart from '@/components/TrendChart';
|
|
import Loading from '@/components/Loading';
|
|
import ErrorState from '@/components/ErrorState';
|
|
import EmptyState from '@/components/EmptyState';
|
|
import SegmentTabs from '@/components/SegmentTabs';
|
|
import PageShell from '@/components/ui/PageShell';
|
|
import ContentCard from '@/components/ui/ContentCard';
|
|
import { useElderClass } from '../../../hooks/useElderClass';
|
|
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 }> = {
|
|
systolic_bp_morning: { label: '收缩压(晨)', unit: 'mmHg', refMin: 90, refMax: 140 },
|
|
diastolic_bp_morning: { label: '舒张压(晨)', unit: 'mmHg', refMin: 60, refMax: 90 },
|
|
systolic_bp_evening: { label: '收缩压(晚)', unit: 'mmHg', refMin: 90, refMax: 140 },
|
|
diastolic_bp_evening: { label: '舒张压(晚)', unit: 'mmHg', refMin: 60, refMax: 90 },
|
|
heart_rate: { label: '心率', unit: 'bpm', refMin: 60, refMax: 100 },
|
|
blood_sugar: { label: '血糖', unit: 'mmol/L', refMin: 3.9, refMax: 6.1 },
|
|
weight: { label: '体重', unit: 'kg' },
|
|
};
|
|
|
|
export default function Trend() {
|
|
const modeClass = useElderClass();
|
|
const router = useRouter();
|
|
const indicator = router.params.indicator || 'heart_rate';
|
|
const [range, setRange] = useState('7d');
|
|
const [points, setPoints] = useState<{ date: string; value: number }[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState(false);
|
|
const getTrend = useHealthStore((s) => s.getTrend);
|
|
|
|
const fetchTrend = useCallback(async () => {
|
|
setLoading(true);
|
|
setError(false);
|
|
try {
|
|
const data = await getTrend(indicator, range);
|
|
setPoints(data);
|
|
} catch {
|
|
setError(true);
|
|
setPoints([]);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [getTrend, indicator, range]);
|
|
|
|
usePageData(fetchTrend, { throttleMs: 60000, enablePullDown: true });
|
|
|
|
// range 切换时手动触发
|
|
useEffect(() => {
|
|
fetchTrend();
|
|
}, [fetchTrend]);
|
|
|
|
const meta = INDICATOR_META[indicator] || { label: indicator, unit: '' };
|
|
|
|
const isOutOfRange = (val: number) => {
|
|
if (meta.refMin !== undefined && val < meta.refMin) return true;
|
|
if (meta.refMax !== undefined && val > meta.refMax) return true;
|
|
return false;
|
|
};
|
|
|
|
return (
|
|
<PageShell padding="none" className={modeClass}>
|
|
{/* 页面标题 */}
|
|
<View className='trend-hero'>
|
|
<View className='trend-hero-icon'>
|
|
<Text className='trend-hero-icon-text'>T</Text>
|
|
</View>
|
|
<Text className='trend-hero-title'>{meta.label}趋势</Text>
|
|
</View>
|
|
|
|
{/* 时间范围切换 */}
|
|
<SegmentTabs tabs={RANGE_OPTIONS.map(o => ({ key: o.value, label: o.label }))} activeKey={range} onChange={setRange} variant="pill" />
|
|
|
|
{/* ECharts 折线图 */}
|
|
{loading ? (
|
|
<ContentCard className='trend-chart-card'>
|
|
<Loading />
|
|
</ContentCard>
|
|
) : error ? (
|
|
<ContentCard className='trend-chart-card'>
|
|
<ErrorState onRetry={fetchTrend} />
|
|
</ContentCard>
|
|
) : points.length === 0 ? (
|
|
<ContentCard className='trend-chart-card'>
|
|
<EmptyState text='暂无趋势数据' />
|
|
</ContentCard>
|
|
) : (
|
|
<ContentCard className='trend-chart-card'>
|
|
<TrendChart
|
|
data={points}
|
|
referenceMin={meta.refMin}
|
|
referenceMax={meta.refMax}
|
|
unit={meta.unit}
|
|
/>
|
|
</ContentCard>
|
|
)}
|
|
|
|
{/* 参考区间 */}
|
|
{!loading && points.length > 0 && meta.refMin !== undefined && meta.refMax !== undefined && (
|
|
<ContentCard variant="outlined" className='trend-ref-card'>
|
|
<Text className='trend-ref-label'>参考区间</Text>
|
|
<Text className='trend-ref-value'>
|
|
{meta.refMin} ~ {meta.refMax} {meta.unit}
|
|
</Text>
|
|
</ContentCard>
|
|
)}
|
|
|
|
{/* 数据列表 */}
|
|
{points.length > 0 && (
|
|
<View className='trend-list'>
|
|
<Text className='trend-list-title'>历史记录</Text>
|
|
{points.slice().reverse().map((p, i) => {
|
|
const abnormal = isOutOfRange(p.value);
|
|
return (
|
|
<View className={`trend-item ${abnormal ? 'trend-item-warn' : ''}`} key={i}>
|
|
<View className='trend-item-left'>
|
|
<Text className='trend-item-date'>{p.date}</Text>
|
|
{abnormal && (
|
|
<Text className='trend-item-tag'>偏高</Text>
|
|
)}
|
|
</View>
|
|
<Text className={`trend-item-value ${abnormal ? 'trend-item-value-warn' : ''}`}>
|
|
{p.value}{meta.unit ? ` ${meta.unit}` : ''}
|
|
</Text>
|
|
</View>
|
|
);
|
|
})}
|
|
</View>
|
|
)}
|
|
</PageShell>
|
|
);
|
|
}
|