Files
hms/apps/miniprogram/src/pages/pkg-health/trend/index.tsx
iven 37327a4da4 refactor(mp): 迁移医护+健康页面 — 使用 PageShell + ContentCard 统一组件库
行动收件箱、医护工作台、健康趋势、患者告警、体征录入、
日常监测、设备同步共 7 个页面迁移:
- 最外层容器 → PageShell
- 卡片元素 → ContentCard
- SCSS 删除 min-height/background/box-shadow 通用样式
2026-05-16 01:33:24 +08:00

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>
);
}