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)
This commit is contained in:
MingNian
2026-05-20 16:18:56 +08:00
commit 435af55c4a
215 changed files with 18595 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
import { useState, useMemo } from 'react';
import { PageHeader } from '@/components/layout/PageHeader';
import { Card } from '@/components/common/Card';
import type { CalendarDay } from '@/types';
import dayjs from 'dayjs';
import styles from './HealthCalendarPage.module.css';
const MARKER_COLORS: Record<string, string> = {
medication_taken: '#10B981',
medication_missed: '#EF4444',
follow_up: '#F59E0B',
measurement: '#2563EB',
};
export function HealthCalendarPage() {
const [currentDate, setCurrentDate] = useState(dayjs());
const calendarDays = useMemo(() => {
const startOfMonth = currentDate.startOf('month');
const endOfMonth = currentDate.endOf('month');
const startDay = startOfMonth.day();
const days: CalendarDay[] = [];
const today = dayjs().format('YYYY-MM-DD');
for (let i = startDay - 1; i >= 0; i--) {
const d = startOfMonth.subtract(i + 1, 'day');
days.push({
date: d.format('YYYY-MM-DD'),
year: d.year(),
month: d.month() + 1,
day: d.date(),
isCurrentMonth: false,
isToday: d.format('YYYY-MM-DD') === today,
markers: [],
});
}
for (let d = startOfMonth; d.isBefore(endOfMonth) || d.isSame(endOfMonth, 'day'); d = d.add(1, 'day')) {
const dateStr = d.format('YYYY-MM-DD');
const markers: CalendarDay['markers'] = [];
// Calendar markers would be populated from real API data
days.push({
date: dateStr,
year: d.year(),
month: d.month() + 1,
day: d.date(),
isCurrentMonth: true,
isToday: dateStr === today,
markers,
});
}
return days;
}, [currentDate]);
const weeks: CalendarDay[][] = [];
for (let i = 0; i < calendarDays.length; i += 7) {
weeks.push(calendarDays.slice(i, i + 7));
}
return (
<div className="page--no-tab">
<PageHeader title="健康日历" />
<div className={styles.monthNav}>
<button onClick={() => setCurrentDate((d) => d.subtract(1, 'month'))}></button>
<span className={styles.monthTitle}>{currentDate.format('YYYY年 M月')}</span>
<button onClick={() => setCurrentDate((d) => d.add(1, 'month'))}></button>
</div>
<div className={styles.weekdays}>
{['日', '一', '二', '三', '四', '五', '六'].map((w) => (
<span key={w} className={styles.weekday}>{w}</span>
))}
</div>
{weeks.map((week, wi) => (
<div key={wi} className={styles.week}>
{week.map((day) => (
<div
key={day.date}
className={`${styles.day} ${!day.isCurrentMonth ? styles.outside : ''} ${day.isToday ? styles.today : ''}`}
>
<span className={styles.dayNum}>{day.day}</span>
<div className={styles.markers}>
{day.markers.slice(0, 3).map((m, i) => (
<span
key={i}
className={styles.dot}
style={{ background: m.color }}
/>
))}
</div>
</div>
))}
</div>
))}
<Card className={styles.legend}>
<div className={styles.legendTitle}></div>
<div className={styles.legendItems}>
<span className={styles.legendItem}><span className={styles.dot} style={{ background: '#2563EB' }} /> </span>
<span className={styles.legendItem}><span className={styles.dot} style={{ background: '#10B981' }} /> </span>
<span className={styles.legendItem}><span className={styles.dot} style={{ background: '#EF4444' }} /> </span>
<span className={styles.legendItem}><span className={styles.dot} style={{ background: '#F59E0B' }} /> </span>
</div>
</Card>
</div>
);
}