import { useState, useEffect } from "react"; import { useNavigate } from "react-router"; import { motion } from "motion/react"; import { ChevronLeft, TrendingUp, Info, Download, Share2, Loader2, Heart, } from "lucide-react"; import { Button } from "../components/ui/button"; import { Card } from "../components/ui/card"; import { Badge } from "../components/ui/badge"; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, Area, AreaChart, } from "recharts"; import { getGrowthCurve } from "../services/api"; import { useUser } from "../context/UserContext"; // 宝宝预期大小数据(按孕周,基于WHO标准) // 4-19周:身长为头臀长(CRL);20周起:身长为头到脚全身长 const BABY_EXPECTED_DATA: Record = { 4: { emoji: "🫘", fruit: "罂粟种子", length: "0.1cm", weight: "<1g", highlight: "心脏开始跳动" }, 5: { emoji: "🫘", fruit: "芝麻粒", length: "0.2cm", weight: "<1g", highlight: "神经管开始发育" }, 6: { emoji: "🫘", fruit: "小扁豆", length: "0.6cm", weight: "<1g", highlight: "小手小脚在萌芽" }, 7: { emoji: "🫐", fruit: "蓝莓", length: "1.3cm", weight: "<1g", highlight: "五官开始形成" }, 8: { emoji: "🫐", fruit: "覆盆子", length: "1.6cm", weight: "1g", highlight: "手指脚趾开始分化" }, 9: { emoji: "🍇", fruit: "葡萄", length: "2.3cm", weight: "2g", highlight: "可以微微活动了" }, 10: { emoji: "🍊", fruit: "金橘", length: "3.1cm", weight: "4g", highlight: "所有器官已初步形成" }, 11: { emoji: "🫒", fruit: "无花果", length: "4.1cm", weight: "7g", highlight: "能做出吞咽动作" }, 12: { emoji: "🍋", fruit: "青柠", length: "5.4cm", weight: "14g", highlight: "进入稳定的孕中期" }, 13: { emoji: "🍋", fruit: "柠檬", length: "7.4cm", weight: "23g", highlight: "指纹开始形成" }, 14: { emoji: "🍑", fruit: "桃子", length: "8.7cm", weight: "43g", highlight: "能做出表情了" }, 16: { emoji: "🥑", fruit: "牛油果", length: "11.6cm", weight: "100g", highlight: "开始能听到声音" }, 18: { emoji: "🫑", fruit: "甜椒", length: "14.2cm", weight: "190g", highlight: "妈妈可能感到胎动了" }, 20: { emoji: "🍌", fruit: "香蕉", length: "25.6cm", weight: "300g", highlight: "味觉开始发育" }, 22: { emoji: "🥭", fruit: "木瓜", length: "27.8cm", weight: "430g", highlight: "眉毛和眼睫毛长出" }, 24: { emoji: "🌽", fruit: "玉米", length: "30cm", weight: "600g", highlight: "肺部开始发育" }, 26: { emoji: "🥦", fruit: "菜花", length: "35.6cm", weight: "760g", highlight: "能睁开眼睛了" }, 28: { emoji: "🍆", fruit: "茄子", length: "37.6cm", weight: "1000g", highlight: "开始有规律的作息" }, 30: { emoji: "🥬", fruit: "卷心菜", length: "39.9cm", weight: "1320g", highlight: "大脑快速发育中" }, 32: { emoji: "🥥", fruit: "椰子", length: "42.4cm", weight: "1700g", highlight: "皮下脂肪增多" }, 34: { emoji: "🍈", fruit: "哈密瓜", length: "45cm", weight: "2150g", highlight: "肺部接近成熟" }, 36: { emoji: "🍈", fruit: "蜜瓜", length: "47.4cm", weight: "2620g", highlight: "头部开始入盆" }, 38: { emoji: "🎃", fruit: "南瓜", length: "49.8cm", weight: "3080g", highlight: "各器官发育成熟" }, 40: { emoji: "🍉", fruit: "小西瓜", length: "51.2cm", weight: "3460g", highlight: "随时准备来到这个世界" }, }; function getBabyExpectedData(week: number) { const weeks = Object.keys(BABY_EXPECTED_DATA).map(Number).sort((a, b) => a - b); let closest = weeks[0]; for (const w of weeks) { if (w <= week) closest = w; else break; } return BABY_EXPECTED_DATA[closest]; } // 生成孕周里程碑时间线数据 function getGrowthMilestones(currentWeek: number) { const milestones = [ { week: 8, label: "心跳可检测", emoji: "💓" }, { week: 12, label: "首次B超", emoji: "🔍" }, { week: 16, label: "开始感知声音", emoji: "👂" }, { week: 20, label: "大排畸检查", emoji: "🏥" }, { week: 24, label: "肺部发育", emoji: "🫁" }, { week: 28, label: "眼睛能睁开", emoji: "👀" }, { week: 32, label: "快速增重", emoji: "📈" }, { week: 36, label: "接近足月", emoji: "🎯" }, { week: 40, label: "预产期到来", emoji: "🎉" }, ]; return milestones.map(m => ({ ...m, passed: currentWeek >= m.week })); } export function GrowthCurvePage() { const navigate = useNavigate(); const { user } = useUser(); const currentWeek = user.currentWeek || 0; const [selectedMetric, setSelectedMetric] = useState("bpd"); const [loading, setLoading] = useState(true); // Standard reference data (no fake values) const standardData: Record = { bpd: { name: "双顶径 (BPD)", unit: "mm", description: "胎儿头部左右两侧之间最宽部位的长度", data: [ { week: 12, standard: 20, lower: 18, upper: 22 }, { week: 16, standard: 35, lower: 32, upper: 38 }, { week: 20, standard: 47, lower: 43, upper: 51 }, { week: 24, standard: 60, lower: 56, upper: 64 }, { week: 28, standard: 71, lower: 67, upper: 75 }, { week: 32, standard: 81, lower: 77, upper: 85 }, { week: 36, standard: 89, lower: 85, upper: 93 }, ], }, ac: { name: "腹围 (AC)", unit: "mm", description: "胎儿腹部一周的长度", data: [ { week: 12, standard: 60, lower: 54, upper: 66 }, { week: 16, standard: 102, lower: 94, upper: 110 }, { week: 20, standard: 148, lower: 138, upper: 158 }, { week: 24, standard: 192, lower: 180, upper: 204 }, { week: 28, standard: 235, lower: 221, upper: 249 }, { week: 32, standard: 279, lower: 263, upper: 295 }, { week: 36, standard: 318, lower: 300, upper: 336 }, ], }, fl: { name: "股骨长 (FL)", unit: "mm", description: "胎儿大腿骨的长度", data: [ { week: 12, standard: 8, lower: 7, upper: 9 }, { week: 16, standard: 22, lower: 20, upper: 24 }, { week: 20, standard: 34, lower: 31, upper: 37 }, { week: 24, standard: 45, lower: 42, upper: 48 }, { week: 28, standard: 54, lower: 51, upper: 57 }, { week: 32, standard: 63, lower: 60, upper: 66 }, { week: 36, standard: 70, lower: 67, upper: 73 }, ], }, }; const [growthData, setGrowthData] = useState(standardData); useEffect(() => { getGrowthCurve() .then((data) => { if (data.data_points && data.data_points.length > 0) { // Merge API data points into standard data const updated = { ...standardData }; for (const metric of ["bpd", "ac", "fl"] as const) { const apiPoints = data.data_points.filter((p) => p[metric] != null); if (apiPoints.length > 0) { updated[metric] = { ...updated[metric], data: updated[metric].data.map((d) => { const match = apiPoints.find((p) => p.week === d.week); return match && match[metric] != null ? { ...d, value: match[metric]! } : d; }), }; } } setGrowthData(updated); } }) .catch(() => {/* keep standard data */}) .finally(() => setLoading(false)); }, []); const currentData = growthData[selectedMetric]; // 检查是否有真实数据 const hasRealData = currentData.data.some(d => d.value != null); // 生成动态评估结果 const generateAssessment = () => { if (!hasRealData) { return "暂无生长数据。上传B超报告后,系统会自动记录宝宝的生长数据并生成评估结果。"; } const realDataPoints = currentData.data.filter(d => d.value != null); const latestPoint = realDataPoints[realDataPoints.length - 1]; if (!latestPoint) { return "暂无生长数据。上传B超报告后,系统会自动记录宝宝的生长数据并生成评估结果。"; } const { value, lower, upper, week, standard } = latestPoint; const metricName = currentData.name.split(" ")[0]; if (value! >= lower && value! <= upper) { const deviation = Math.abs(value! - standard); const deviationPercent = ((deviation / standard) * 100).toFixed(1); if (deviation < standard * 0.05) { return `宝宝的${metricName}发育非常好!孕${week}周的测量值为${value}${currentData.unit},与标准值${standard}${currentData.unit}非常接近,在正常范围内(${lower}-${upper}${currentData.unit})。继续保持健康的生活方式,按时产检。`; } else { return `宝宝的${metricName}发育良好!孕${week}周的测量值为${value}${currentData.unit},在正常范围内(${lower}-${upper}${currentData.unit}),与标准值${standard}${currentData.unit}相差约${deviationPercent}%。继续保持健康的生活方式,按时产检。`; } } else if (value! < lower) { const gap = lower - value!; return `宝宝的${metricName}略偏小。孕${week}周的测量值为${value}${currentData.unit},低于正常范围下限(${lower}${currentData.unit})约${gap}${currentData.unit}。建议加强营养摄入,多吃富含蛋白质的食物,并及时咨询产科医生,进行进一步评估。`; } else { const gap = value! - upper; return `宝宝的${metricName}略偏大。孕${week}周的测量值为${value}${currentData.unit},高于正常范围上限(${upper}${currentData.unit})约${gap}${currentData.unit}。建议控制饮食,避免高糖高脂食物,并及时咨询产科医生,进行进一步评估。`; } }; const CustomTooltip = ({ active, payload }: any) => { if (active && payload && payload.length) { return (

孕{payload[0].payload.week}周

标准值: {payload[0].payload.standard} {currentData.unit}

正常范围: {payload[0].payload.lower}-{payload[0].payload.upper}{" "} {currentData.unit}

); } return null; }; return (
{/* 顶部导航 */}

胎儿生长曲线

基于B超数据自动生成

{/* 无B超数据时展示宝宝预期大小 */} {!hasRealData && currentWeek > 0 && (() => { const babyData = getBabyExpectedData(currentWeek); const milestones = getGrowthMilestones(currentWeek); const progress = Math.min(100, Math.max(0, ((currentWeek - 4) / 36) * 100)); if (!babyData) return null; return ( <> {/* 宝宝当前大小 */}

宝宝现在有多大?

{babyData.emoji} 孕{currentWeek}周,宝宝像一个{babyData.fruit}

{babyData.highlight}

{/* 预期数据 */}

预期身长

{babyData.length}

预期体重

{babyData.weight}

{/* 孕期进度 */}
孕期进度 {Math.round(progress)}%
4周 40周
{/* 成长里程碑时间线 */}

成长里程碑

{milestones.map((m, i) => (
{/* 时间线连接线 */}
= m.week - 2 ? "bg-morandi-100 text-morandi-500 ring-2 ring-morandi-300" : "bg-gray-100 text-gray-400" }`}> {m.emoji}
{i < milestones.length - 1 && (
)}

{m.label}

孕{m.week}周

))}
{/* 上传B超提示 */}

查看精准发育数据

上传B超报告后,系统会自动提取胎儿双顶径、腹围、股骨长等数据,生成个性化的生长曲线,让你更直观地了解宝宝发育情况。

); })()} {/* 指标选择 - 只在有B超数据时显示 */} {hasRealData && (
)} {/* 有B超数据时显示指标详情、曲线图、数据表格 */} {hasRealData && ( <> {/* 指标说明 */}

{currentData.name}

{currentData.description}

{/* 生长曲线图 */}

生长趋势

标准值
正常范围
} /> {/* 完全移除实际值线条 */}
{/* 数据表格 */}

详细数据

{currentData.data.map((row, index) => { const isNormal = row.value != null && row.value >= row.lower && row.value <= row.upper; return ( ); })}
孕周 实际值 标准值 正常范围 状态
{row.week}周 {row.value != null ? `${row.value} ${currentData.unit}` : '-'} {row.standard} {currentData.unit} {row.lower}-{row.upper} {currentData.unit} {row.value != null ? ( {isNormal ? "正常" : "关注"} ) : ( - )}
{/* 医生建议 */}

评估结果

{generateAssessment()}

)}
); }