Initial commit: HealthCarePregnant project documentation and platform
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
import { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
Apple,
|
||||
ArrowRight,
|
||||
BarChart3,
|
||||
CalendarCheck,
|
||||
ClipboardCheck,
|
||||
Droplet,
|
||||
FileText,
|
||||
HeartHandshake,
|
||||
Leaf,
|
||||
LineChart,
|
||||
PencilLine,
|
||||
Sparkles,
|
||||
Sprout,
|
||||
Heart,
|
||||
Smile,
|
||||
CheckCircle2,
|
||||
type LucideIcon,
|
||||
} from 'lucide-react';
|
||||
import { api } from '../api/client';
|
||||
import { useToast } from '../components/Toast';
|
||||
import { usePatient } from '../lib/usePatient';
|
||||
import { BuildArchive } from '../components/BuildArchive';
|
||||
import { greeting, riskBadgeClass, riskLabel } from '../lib/format';
|
||||
import './HomePage.css';
|
||||
|
||||
type Tone = 'peach' | 'rose' | 'mint' | 'lilac';
|
||||
|
||||
interface Intent {
|
||||
label: string;
|
||||
q?: string;
|
||||
to?: string;
|
||||
icon: LucideIcon;
|
||||
tone: Tone;
|
||||
}
|
||||
|
||||
const INTENTS: Intent[] = [
|
||||
{ label: '我能吃这个吗?', q: '孕期可以吃西瓜吗', icon: Apple, tone: 'peach' },
|
||||
{ label: '记录今天的血糖', to: '/data', icon: Droplet, tone: 'rose' },
|
||||
{ label: '今天该注意什么?', q: '孕期日常需要注意什么', icon: Leaf, tone: 'mint' },
|
||||
{ label: '孕期不适怎么办', q: '孕期恶心想吐怎么办', icon: HeartHandshake, tone: 'lilac' },
|
||||
];
|
||||
|
||||
const QUICK: { label: string; to: string; icon: LucideIcon; tone: Tone }[] = [
|
||||
{ label: '今日打卡', to: '/data', icon: ClipboardCheck, tone: 'peach' },
|
||||
{ label: '血糖趋势', to: '/data', icon: LineChart, tone: 'rose' },
|
||||
{ label: '产检提醒', to: '/tasks', icon: CalendarCheck, tone: 'mint' },
|
||||
];
|
||||
|
||||
export function HomePage(): JSX.Element {
|
||||
const navigate = useNavigate();
|
||||
const { patient, loading, reload } = usePatient();
|
||||
const { show } = useToast();
|
||||
|
||||
const [emotionScore, setEmotionScore] = useState<number | null>(null);
|
||||
const [emotionNote, setEmotionNote] = useState('');
|
||||
const [hasSubmittedEmotion, setHasSubmittedEmotion] = useState(false);
|
||||
const [submittingEmotion, setSubmittingEmotion] = useState(false);
|
||||
|
||||
async function submitEmotion(): Promise<void> {
|
||||
if (!emotionScore) {
|
||||
show('请选择一个情绪分值哦~');
|
||||
return;
|
||||
}
|
||||
setSubmittingEmotion(true);
|
||||
try {
|
||||
await api.createEmotion({ score: emotionScore, note: emotionNote || '今日情绪状况良好' });
|
||||
show('打卡成功!AI 已过滤异常信号并同步至管理师,祝您好心情🌸');
|
||||
setHasSubmittedEmotion(true);
|
||||
setEmotionNote('');
|
||||
} catch (err) {
|
||||
show('心情记录失败,请稍后重试');
|
||||
} finally {
|
||||
setSubmittingEmotion(false);
|
||||
}
|
||||
}
|
||||
|
||||
function goChat(q?: string): void {
|
||||
navigate('/chat', q ? { state: { initialQuestion: q } } : undefined);
|
||||
}
|
||||
|
||||
if (!patient && !loading) {
|
||||
return (
|
||||
<div className="page">
|
||||
<h1 className="section-title" style={{ marginTop: 0 }}>
|
||||
你好,欢迎~
|
||||
</h1>
|
||||
<BuildArchive onDone={reload} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="page home">
|
||||
<header className="home__greet">
|
||||
<p className="home__hello">
|
||||
{greeting()},{patient?.name ?? ''}
|
||||
</p>
|
||||
{patient && (
|
||||
<p className="home__week">
|
||||
孕 {patient.gestationalWeeks} 周
|
||||
{patient.gestationalDays ? ` ${patient.gestationalDays} 天` : ''}
|
||||
<span className={`badge ${riskBadgeClass(patient.initialRiskLevel)}`} style={{ marginLeft: 8 }}>
|
||||
{riskLabel(patient.initialRiskLevel)}
|
||||
</span>
|
||||
</p>
|
||||
)}
|
||||
</header>
|
||||
|
||||
{/* 渐变 Hero 聊天主入口(AI-first) */}
|
||||
<button className="home__hero" onClick={() => goChat()} type="button">
|
||||
<div className="home__hero-glow" aria-hidden />
|
||||
<div className="home__hero-body">
|
||||
<span className="home__hero-eyebrow">
|
||||
<Sparkles size={14} strokeWidth={2} /> 孕期 AI 助手
|
||||
</span>
|
||||
<span className="home__hero-title">和孕期助手聊聊</span>
|
||||
<span className="home__hero-sub">饮食、监测、不适,随时问我~</span>
|
||||
</div>
|
||||
<span className="home__hero-cta" aria-hidden>
|
||||
<ArrowRight size={22} strokeWidth={2.2} />
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{/* 暖色暖系低焦虑今日心情自评打卡(T-D.9) */}
|
||||
<div className="home__emotion-card">
|
||||
<h3 className="emotion-card__title">
|
||||
<Heart size={16} fill="var(--color-destructive)" stroke="none" />
|
||||
记录今日心情,倾听身体信号
|
||||
</h3>
|
||||
{!hasSubmittedEmotion ? (
|
||||
<div className="emotion-card__box">
|
||||
<p className="emotion-card__desc">你今天的心情几分?(1-10分,分低代表压抑或焦虑)</p>
|
||||
<div className="emotion-card__selectors">
|
||||
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((score) => (
|
||||
<button
|
||||
key={score}
|
||||
type="button"
|
||||
className={`emotion-score-btn ${emotionScore === score ? 'active' : ''}`}
|
||||
onClick={() => setEmotionScore(score)}
|
||||
>
|
||||
{score === 10 ? <Smile size={14} /> : score}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<textarea
|
||||
className="emotion-card__textarea"
|
||||
placeholder="今天有什么烦心事,或者身体上有什么不舒服吗?我们会实时保护您的私密信息,若有剧烈焦虑,个案管理师将主动为您致电疏导..."
|
||||
value={emotionNote}
|
||||
onChange={(e) => setEmotionNote(e.target.value)}
|
||||
rows={2}
|
||||
/>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="emotion-card__submit-btn"
|
||||
onClick={submitEmotion}
|
||||
disabled={submittingEmotion}
|
||||
>
|
||||
{submittingEmotion ? '心情记录中…' : '提交今日自评'}
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="emotion-card__result animate-fade-in">
|
||||
<CheckCircle2 size={18} style={{ color: 'var(--color-calm)' }} />
|
||||
<span>今日心情已打卡:<strong>{emotionScore}分</strong> · 感谢您的分享,AI 助手已自动保护并为您分析身心信号,祝您孕期好心情🌸</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 今日健康 */}
|
||||
<h2 className="section-title">今日健康</h2>
|
||||
<div className="home__quick">
|
||||
{QUICK.map((q) => (
|
||||
<button
|
||||
key={q.label}
|
||||
className="home__quick-card"
|
||||
data-tone={q.tone}
|
||||
onClick={() => navigate(q.to)}
|
||||
type="button"
|
||||
>
|
||||
<span className="home__quick-chip">
|
||||
<q.icon size={22} strokeWidth={1.9} />
|
||||
</span>
|
||||
{q.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 意图卡片 */}
|
||||
<h2 className="section-title">你可能想问 / 想做</h2>
|
||||
<div className="home__intents">
|
||||
{INTENTS.map((it) => (
|
||||
<button
|
||||
key={it.label}
|
||||
className="home__intent"
|
||||
data-tone={it.tone}
|
||||
onClick={() => (it.to ? navigate(it.to) : goChat(it.q))}
|
||||
type="button"
|
||||
>
|
||||
<span className="home__intent-chip">
|
||||
<it.icon size={20} strokeWidth={1.9} />
|
||||
</span>
|
||||
<span className="home__intent-label">{it.label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 更多功能 */}
|
||||
<h2 className="section-title">更多功能</h2>
|
||||
<div className="home__more">
|
||||
<button onClick={() => navigate('/data')} type="button">
|
||||
<span className="home__more-chip">
|
||||
<PencilLine size={22} strokeWidth={1.8} />
|
||||
</span>
|
||||
手动录入
|
||||
</button>
|
||||
<button onClick={() => navigate('/me')} type="button">
|
||||
<span className="home__more-chip">
|
||||
<FileText size={22} strokeWidth={1.8} />
|
||||
</span>
|
||||
健康档案
|
||||
</button>
|
||||
<button onClick={() => navigate('/data')} type="button">
|
||||
<span className="home__more-chip">
|
||||
<BarChart3 size={22} strokeWidth={1.8} />
|
||||
</span>
|
||||
我的报告
|
||||
</button>
|
||||
<button onClick={() => goChat('孕期可以做哪些调养')} type="button">
|
||||
<span className="home__more-chip">
|
||||
<Sprout size={22} strokeWidth={1.8} />
|
||||
</span>
|
||||
调养建议
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user