Files
soft/frontend-patient/src/pages/home/HomePage.tsx
MingNian 435af55c4a Initial commit: HealthManager full-stack health management platform
Backend: .NET 10 + PostgreSQL + EF Core + JWT + SignalR
Frontend patient: React 19 + TypeScript + Vite (mobile H5)
Frontend doctor: React 19 + TypeScript + Vite (desktop web)
2026-05-20 16:18:56 +08:00

119 lines
4.6 KiB
TypeScript

import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Card } from '@/components/common/Card';
import { Empty } from '@/components/common/Empty';
import { Badge } from '@/components/common/Badge';
import { PageHeader } from '@/components/layout/PageHeader';
import { useAuth } from '@/hooks/useAuth';
import { useNotificationStore } from '@/stores/notification.store';
import * as healthService from '@/services/health.service';
import { MEASUREMENT_TYPES, HEALTH_TIPS } from '@/utils/constants';
import { getBPRiskLevel } from '@/utils/format';
import type { HealthStats } from '@/types';
import styles from './HomePage.module.css';
const QUICK_ACTIONS = [
{ label: '测血压', icon: '💓', path: '/health/records?type=blood_pressure' },
{ label: '记用药', icon: '💊', path: '/health/medications' },
{ label: '在线问诊', icon: '👨‍⚕️', path: '/services/consultation' },
{ label: '报告解读', icon: '📋', path: '/services/reports' },
{ label: '健康日历', icon: '📅', path: '/health/calendar' },
{ label: '运动饮食', icon: '🏃', path: '/health/exercise-diet' },
];
export function HomePage() {
const navigate = useNavigate();
const { user } = useAuth();
const { unreadCount, fetchNotifications } = useNotificationStore();
const [stats, setStats] = useState<HealthStats[]>([]);
const [tipIndex, setTipIndex] = useState(0);
useEffect(() => {
healthService.getLatestStats().then(setStats);
fetchNotifications();
}, [fetchNotifications]);
const bpStats = stats.find((s) => s.type === 'blood_pressure');
const hrStats = stats.find((s) => s.type === 'heart_rate');
const bpValue = bpStats?.latest?.value;
const systolic = typeof bpValue === 'object' ? bpValue.systolic : null;
const diastolic = typeof bpValue === 'object' ? bpValue.diastolic : null;
const riskLevel = systolic && diastolic ? getBPRiskLevel(systolic, diastolic) : null;
return (
<div className="page">
<PageHeader
title={`你好,${user?.nickname || '用户'}`}
showBack={false}
rightAction={
<button className={styles.notifyBtn} onClick={() => navigate('/notifications')}>
🔔
{unreadCount > 0 && <Badge count={unreadCount} />}
</button>
}
/>
{/* Health Overview */}
{bpStats?.latest && hrStats?.latest ? (
<Card className={styles.overviewCard}>
<div className={styles.overviewHeader}>
<span className={styles.overviewTitle}></span>
<span className={styles.overviewTime}></span>
</div>
<div className={styles.overviewData}>
<div className={styles.bpSection}>
<span className={styles.dataLabel}></span>
<div className={styles.bpValues}>
<span className={`${styles.bpNum} ${styles[`risk_${riskLevel}`] || ''}`}>
{systolic}
</span>
<span className={styles.bpSep}>/</span>
<span className={`${styles.bpNum} ${styles[`risk_${riskLevel}`] || ''}`}>
{diastolic}
</span>
</div>
<span className={styles.unit}>mmHg</span>
</div>
<div className={styles.divider} />
<div className={styles.hrSection}>
<span className={styles.dataLabel}></span>
<span className={styles.hrNum}>{Number(hrStats.latest.value)}</span>
<span className={styles.unit}>bpm</span>
</div>
</div>
</Card>
) : (
<Empty icon="💓" message="暂无健康数据" />
)}
{/* Quick Actions */}
<div className={styles.quickActions}>
{QUICK_ACTIONS.map((action) => (
<button
key={action.label}
className={styles.quickAction}
onClick={() => navigate(action.path)}
>
<span className={styles.quickIcon}>{action.icon}</span>
<span className={styles.quickLabel}>{action.label}</span>
</button>
))}
</div>
{/* Health Tip */}
<Card
className={styles.tipCard}
onClick={() => setTipIndex((prev) => (prev + 1) % HEALTH_TIPS.length)}
>
<div className={styles.tipHeader}>
<span>💡</span>
<span className={styles.tipTitle}></span>
<span className={styles.tipHint}></span>
</div>
<p className={styles.tipContent}>{HEALTH_TIPS[tipIndex]}</p>
</Card>
</div>
);
}