From 7d474dcf5e34a30a04ccffe8634fd6e30e64811e Mon Sep 17 00:00:00 2001 From: Karlcarlo Date: Thu, 25 Jun 2026 11:06:14 +0800 Subject: [PATCH] Implement code changes to enhance functionality and improve performance --- frontend/src/app/pages/AIAnalysisPage.tsx | 1446 +++++++++++---------- 1 file changed, 726 insertions(+), 720 deletions(-) diff --git a/frontend/src/app/pages/AIAnalysisPage.tsx b/frontend/src/app/pages/AIAnalysisPage.tsx index c360ae0..2847704 100644 --- a/frontend/src/app/pages/AIAnalysisPage.tsx +++ b/frontend/src/app/pages/AIAnalysisPage.tsx @@ -1,721 +1,727 @@ -import { useState, useEffect } from "react"; -import { useNavigate, useParams } from "react-router"; -import { motion, AnimatePresence } from "motion/react"; -import { - ChevronLeft, - Heart, - TrendingUp, - AlertCircle, - CheckCircle2, - Info, - Calendar, - Share2, - Download, - Sparkles, - Activity, - Baby, - MessageCircle, - FileText, - X, - Link2, - Check, - AlertTriangle, - ShieldAlert, - Loader2, - Image as ImageIcon, -} from "lucide-react"; -import { Button } from "../components/ui/button"; -import { Card } from "../components/ui/card"; -import { Badge } from "../components/ui/badge"; -import { generateAnalysis, type AnalysisResponse } from "../services/api"; - -export function AIAnalysisPage() { - const navigate = useNavigate(); - const { reportId } = useParams(); - const [showShareSheet, setShowShareSheet] = useState(false); - const [linkCopied, setLinkCopied] = useState(false); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - const [analysisData, setAnalysisData] = useState(null); - - useEffect(() => { - if (!reportId) return; - setLoading(true); - generateAnalysis(reportId) - .then((data) => setAnalysisData(data)) - .catch((err) => setError(err instanceof Error ? err.message : "加载失败")) - .finally(() => setLoading(false)); - }, [reportId]); - - const handleCopyLink = async () => { - try { - const shareUrl = `${window.location.origin}/share/${reportId}`; - await navigator.clipboard.writeText(shareUrl); - setLinkCopied(true); - setTimeout(() => setLinkCopied(false), 2000); - } catch (error) { - console.error('复制失败:', error); - // 降级方案:使用旧的复制方法 - const textArea = document.createElement('textarea'); - textArea.value = `${window.location.origin}/share/${reportId}`; - textArea.style.position = 'fixed'; - textArea.style.left = '-999999px'; - document.body.appendChild(textArea); - textArea.select(); - try { - document.execCommand('copy'); - setLinkCopied(true); - setTimeout(() => setLinkCopied(false), 2000); - } catch (err) { - alert('复制失败,请手动复制链接'); - } - document.body.removeChild(textArea); - } - }; - - if (loading) { - return ( -
-
- -

AI 正在解读您的报告...

-

这可能需要几秒钟

-
-
- ); - } - - if (error || !analysisData) { - return ( -
-
- -

解读失败

-

{error || "未知错误"}

- -
-
- ); - } - - const getStatusColor = (status: string) => { - switch (status) { - case "normal": - return { - bg: "bg-morandi-sage-100", - border: "border-morandi-sage-200", - text: "text-morandi-sage-600", - badge: "bg-morandi-sage-100 text-morandi-sage-600", - icon: "text-morandi-sage-400", - }; - case "attention": - return { - bg: "bg-morandi-rose-100", - border: "border-morandi-200", - text: "text-morandi-600", - badge: "bg-morandi-rose-100 text-morandi-600", - icon: "text-morandi-rose-400", - }; - case "high": - return { - bg: "bg-red-50", - border: "border-red-200", - text: "text-red-700", - badge: "bg-red-100 text-red-700", - icon: "text-red-500", - }; - default: - return { - bg: "bg-gray-50", - border: "border-gray-200", - text: "text-gray-700", - badge: "bg-gray-100 text-gray-700", - icon: "text-gray-500", - }; - } - }; - - const statusColors = getStatusColor(analysisData.overall_status); - - // 指标状态颜色(紫色系色温区分:冷调sage→中性purple→暖调rose) - const getIndicatorColor = (indicatorStatus: string) => { - switch (indicatorStatus) { - case "normal": - return { - bg: "bg-morandi-sage-100", - border: "border-morandi-sage-200", - text: "text-morandi-sage-600", - badge: "bg-morandi-sage-100 text-morandi-sage-600", - barBg: "bg-morandi-sage-200", - barFill: "bg-morandi-sage-400", - }; - case "borderline": - return { - bg: "bg-morandi-100", - border: "border-morandi-200", - text: "text-morandi-600", - badge: "bg-morandi-200 text-morandi-700", - barBg: "bg-morandi-200", - barFill: "bg-morandi-400", - }; - case "abnormal": - return { - bg: "bg-morandi-rose-100", - border: "border-morandi-rose-300", - text: "text-morandi-700", - badge: "bg-morandi-rose-100 text-morandi-rose-400", - barBg: "bg-morandi-rose-100", - barFill: "bg-morandi-rose-400", - }; - default: - return { - bg: "bg-gray-50", - border: "border-gray-200", - text: "text-gray-700", - badge: "bg-gray-100 text-gray-700", - barBg: "bg-gray-100", - barFill: "bg-gray-400", - }; - } - }; - - const getRiskLabel = (riskLevel: string) => { - switch (riskLevel) { - case "low": - return { label: "低风险", color: "bg-morandi-100 text-morandi-600" }; - case "medium": - return { label: "中风险", color: "bg-morandi-rose-100 text-morandi-rose-400" }; - case "high": - return { label: "高风险", color: "bg-morandi-rose-100 text-morandi-700" }; - default: - return { label: "", color: "" }; - } - }; - - return ( -
- {/* 顶部导航 */} -
-
- -

AI 智能解读

- -
-
- -
- {/* AI 解读标识 */} - - - AI 深度解读 · 个性化定制 - - - {/* 整体评估卡片 */} - - -
-
- -
-
-
-

- 整体评估 -

- - {analysisData.overall_status === "normal" - ? "一切正常" - : analysisData.overall_status === "attention" - ? "需要关注" - : "需要重视"} - -
-

- {analysisData.emotional_summary || analysisData.overall_summary} -

-
-
- - {new Date(analysisData.created_at).toLocaleDateString("zh-CN")} -
-
-
-
- - {/* 风险等级指示器 */} -
-
- - 综合风险评级 - - - {analysisData.risk_level === "low" - ? "低风险" - : analysisData.risk_level === "medium" - ? "中风险" - : "高风险"} - -
-
- -
-
-
-
- - {/* 分类详情 */} - -

详细解读

- -
- {analysisData.categories.map((category, index) => { - const colors = getStatusColor(category.status); - return ( - - - {/* 分类头部 */} -
-
-
- -
-
-

- {category.category} -

-
- - {category.status === "normal" - ? "正常" - : category.status === "attention" - ? "需关注" - : "异常"} - -
-
- - {/* 指标详情 */} -
- {category.indicators.map((item, itemIdx) => { - const indicatorStatus = item.status === "normal" ? "normal" : item.status === "low" || item.status === "high" ? "abnormal" : "borderline"; - const indColors = getIndicatorColor(indicatorStatus); - return ( -
- {/* 指标名称和数值 */} -
-
-

- {item.name} -

- {item.nickname && ( -

- {item.nickname} -

- )} -
- - {item.value} - - - {item.unit} - -
- {item.reference_range && ( -

- 参考范围:{item.reference_range} -

- )} -
-
-
- {indicatorStatus === "normal" ? ( - - ) : ( - - )} - - {item.status === "normal" ? "正常" : item.status === "high" ? "偏高" : "偏低"} - -
-
-
- - {/* 状态指示条 */} -
-
-
-
-
- - {/* 白话解释 */} -
- {item.what_is_it && ( -
-
- -
-

这是什么?

-

{item.what_is_it}

-
-
-
- )} - - {item.baby_impact && ( -
-
- -
-

对宝宝的影响

-

{item.baby_impact}

-
-
-
- )} - - {item.suggestion && ( -
-
- -
-

建议措施

-

{item.suggestion}

-
-
-
- )} - - {item.follow_up && ( -
- - {item.follow_up} -
- )} -
-
- ); - })} -
- - - ); - })} -
-
- - {/* 行动建议 */} - -

- 接下来该做什么? -

- -
- {analysisData.action_items.map((rec, index) => ( - - -
-
-
-

- {rec.title} -

- {rec.priority === "high" && ( - - 重要 - - )} -
-

- {rec.description} -

- {rec.timeframe && ( -
- - {rec.timeframe} -
- )} -
-
-
-
- ))} -
-
- - {/* 深度服务 - 新增 */} - -

- 🎯 获取更多帮助 -

-
- navigate(`/chat/${reportId}`)} - className="bg-morandi-100 rounded-2xl p-4 border-0 cursor-pointer hover:scale-105 transition-transform" - > -
- -
-

AI对话追问

-

针对报告提问

-
- - navigate(`/doctor-questions/${reportId}`)} - className="bg-gradient-to-br from-morandi-slate-100 to-morandi-slate-50 rounded-2xl p-4 border-0 cursor-pointer hover:scale-105 transition-transform" - > -
- -
-

问诊清单

-

就医沟通话术

-
- -
-
- - {/* 温馨提示 */} - - -
- -
-

- 温馨提示 -

-

- 以上解读仅供参考,不能替代医生的专业诊断。如有任何疑问或不适,请及时就医咨询专业医生。保持好心情,宝宝能感受到妈妈的情绪哦 💕 -

-
-
-
-
-
- - {/* 底部操作栏 */} -
-
- - -
-
- - {/* 分享弹窗 */} - - {showShareSheet && ( - <> - setShowShareSheet(false)} - className="fixed inset-0 bg-black/40 z-40" - /> - -
- {/* 拖拽指示器 */} -
- -
-

分享报告

- -
- - {/* 分享方式 */} -
- {/* 生成分享长图 */} - - - {/* 复制链接 */} - -
- - {/* 分享预览卡片 */} - -
-
- -
-
-

- {analysisData.categories[0]?.category || "检查报告"} · AI解读 -

-

- {analysisData.overall_summary} -

-
- {new Date(analysisData.created_at).toLocaleDateString("zh-CN")} -
-
-
-
- - {/* 隐私提示 */} -
- - 分享时将自动隐藏个人敏感信息,保护您的隐私 -
-
- - - )} - -
- ); +import { useState, useEffect } from "react"; +import { useNavigate, useParams } from "react-router"; +import { motion, AnimatePresence } from "motion/react"; +import bridge from "../services/bridge"; +import { + ChevronLeft, + Heart, + TrendingUp, + AlertCircle, + CheckCircle2, + Info, + Calendar, + Share2, + Download, + Sparkles, + Activity, + Baby, + MessageCircle, + FileText, + X, + Link2, + Check, + AlertTriangle, + ShieldAlert, + Loader2, + Image as ImageIcon, +} from "lucide-react"; +import { Button } from "../components/ui/button"; +import { Card } from "../components/ui/card"; +import { Badge } from "../components/ui/badge"; +import { generateAnalysis, type AnalysisResponse } from "../services/api"; + +export function AIAnalysisPage() { + const navigate = useNavigate(); + const { reportId } = useParams(); + const [showShareSheet, setShowShareSheet] = useState(false); + const [linkCopied, setLinkCopied] = useState(false); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [analysisData, setAnalysisData] = useState(null); + + useEffect(() => { + if (!reportId) return; + setLoading(true); + generateAnalysis(reportId) + .then((data) => setAnalysisData(data)) + .catch((err) => setError(err instanceof Error ? err.message : "加载失败")) + .finally(() => setLoading(false)); + }, [reportId]); + + const handleCopyLink = async () => { + const shareUrl = `${window.location.origin}/share/${reportId}`; + // 微信 web-view 中 navigator.clipboard 不可用,用 execCommand 替代 + let copied = false; + const textArea = document.createElement('textarea'); + textArea.value = shareUrl; + textArea.style.position = 'fixed'; + textArea.style.left = '-999999px'; + textArea.style.top = '-999999px'; + textArea.style.opacity = '0'; + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + try { + copied = document.execCommand('copy'); + } catch (e) { + console.warn('[copy] execCommand failed:', e); + } + document.body.removeChild(textArea); + + if (copied) { + setLinkCopied(true); + setTimeout(() => setLinkCopied(false), 2000); + } else { + // 兜底:通过小程序桥接(postMessage 在页面离开时触发) + bridge.copyLink(shareUrl); + setLinkCopied(true); + setTimeout(() => setLinkCopied(false), 2000); + } + }; + + if (loading) { + return ( +
+
+ +

AI 正在解读您的报告...

+

这可能需要几秒钟

+
+
+ ); + } + + if (error || !analysisData) { + return ( +
+
+ +

解读失败

+

{error || "未知错误"}

+ +
+
+ ); + } + + const getStatusColor = (status: string) => { + switch (status) { + case "normal": + return { + bg: "bg-morandi-sage-100", + border: "border-morandi-sage-200", + text: "text-morandi-sage-600", + badge: "bg-morandi-sage-100 text-morandi-sage-600", + icon: "text-morandi-sage-400", + }; + case "attention": + return { + bg: "bg-morandi-rose-100", + border: "border-morandi-200", + text: "text-morandi-600", + badge: "bg-morandi-rose-100 text-morandi-600", + icon: "text-morandi-rose-400", + }; + case "high": + return { + bg: "bg-red-50", + border: "border-red-200", + text: "text-red-700", + badge: "bg-red-100 text-red-700", + icon: "text-red-500", + }; + default: + return { + bg: "bg-gray-50", + border: "border-gray-200", + text: "text-gray-700", + badge: "bg-gray-100 text-gray-700", + icon: "text-gray-500", + }; + } + }; + + const statusColors = getStatusColor(analysisData.overall_status); + + // 指标状态颜色(紫色系色温区分:冷调sage→中性purple→暖调rose) + const getIndicatorColor = (indicatorStatus: string) => { + switch (indicatorStatus) { + case "normal": + return { + bg: "bg-morandi-sage-100", + border: "border-morandi-sage-200", + text: "text-morandi-sage-600", + badge: "bg-morandi-sage-100 text-morandi-sage-600", + barBg: "bg-morandi-sage-200", + barFill: "bg-morandi-sage-400", + }; + case "borderline": + return { + bg: "bg-morandi-100", + border: "border-morandi-200", + text: "text-morandi-600", + badge: "bg-morandi-200 text-morandi-700", + barBg: "bg-morandi-200", + barFill: "bg-morandi-400", + }; + case "abnormal": + return { + bg: "bg-morandi-rose-100", + border: "border-morandi-rose-300", + text: "text-morandi-700", + badge: "bg-morandi-rose-100 text-morandi-rose-400", + barBg: "bg-morandi-rose-100", + barFill: "bg-morandi-rose-400", + }; + default: + return { + bg: "bg-gray-50", + border: "border-gray-200", + text: "text-gray-700", + badge: "bg-gray-100 text-gray-700", + barBg: "bg-gray-100", + barFill: "bg-gray-400", + }; + } + }; + + const getRiskLabel = (riskLevel: string) => { + switch (riskLevel) { + case "low": + return { label: "低风险", color: "bg-morandi-100 text-morandi-600" }; + case "medium": + return { label: "中风险", color: "bg-morandi-rose-100 text-morandi-rose-400" }; + case "high": + return { label: "高风险", color: "bg-morandi-rose-100 text-morandi-700" }; + default: + return { label: "", color: "" }; + } + }; + + return ( +
+ {/* 顶部导航 */} +
+
+ +

AI 智能解读

+ +
+
+ +
+ {/* AI 解读标识 */} + + + AI 深度解读 · 个性化定制 + + + {/* 整体评估卡片 */} + + +
+
+ +
+
+
+

+ 整体评估 +

+ + {analysisData.overall_status === "normal" + ? "一切正常" + : analysisData.overall_status === "attention" + ? "需要关注" + : "需要重视"} + +
+

+ {analysisData.emotional_summary || analysisData.overall_summary} +

+
+
+ + {new Date(analysisData.created_at).toLocaleDateString("zh-CN")} +
+
+
+
+ + {/* 风险等级指示器 */} +
+
+ + 综合风险评级 + + + {analysisData.risk_level === "low" + ? "低风险" + : analysisData.risk_level === "medium" + ? "中风险" + : "高风险"} + +
+
+ +
+
+
+
+ + {/* 分类详情 */} + +

详细解读

+ +
+ {analysisData.categories.map((category, index) => { + const colors = getStatusColor(category.status); + return ( + + + {/* 分类头部 */} +
+
+
+ +
+
+

+ {category.category} +

+
+ + {category.status === "normal" + ? "正常" + : category.status === "attention" + ? "需关注" + : "异常"} + +
+
+ + {/* 指标详情 */} +
+ {category.indicators.map((item, itemIdx) => { + const indicatorStatus = item.status === "normal" ? "normal" : item.status === "low" || item.status === "high" ? "abnormal" : "borderline"; + const indColors = getIndicatorColor(indicatorStatus); + return ( +
+ {/* 指标名称和数值 */} +
+
+

+ {item.name} +

+ {item.nickname && ( +

+ {item.nickname} +

+ )} +
+ + {item.value} + + + {item.unit} + +
+ {item.reference_range && ( +

+ 参考范围:{item.reference_range} +

+ )} +
+
+
+ {indicatorStatus === "normal" ? ( + + ) : ( + + )} + + {item.status === "normal" ? "正常" : item.status === "high" ? "偏高" : "偏低"} + +
+
+
+ + {/* 状态指示条 */} +
+
+
+
+
+ + {/* 白话解释 */} +
+ {item.what_is_it && ( +
+
+ +
+

这是什么?

+

{item.what_is_it}

+
+
+
+ )} + + {item.baby_impact && ( +
+
+ +
+

对宝宝的影响

+

{item.baby_impact}

+
+
+
+ )} + + {item.suggestion && ( +
+
+ +
+

建议措施

+

{item.suggestion}

+
+
+
+ )} + + {item.follow_up && ( +
+ + {item.follow_up} +
+ )} +
+
+ ); + })} +
+ + + ); + })} +
+
+ + {/* 行动建议 */} + +

+ 接下来该做什么? +

+ +
+ {analysisData.action_items.map((rec, index) => ( + + +
+
+
+

+ {rec.title} +

+ {rec.priority === "high" && ( + + 重要 + + )} +
+

+ {rec.description} +

+ {rec.timeframe && ( +
+ + {rec.timeframe} +
+ )} +
+
+
+
+ ))} +
+
+ + {/* 深度服务 - 新增 */} + +

+ 🎯 获取更多帮助 +

+
+ navigate(`/chat/${reportId}`)} + className="bg-morandi-100 rounded-2xl p-4 border-0 cursor-pointer hover:scale-105 transition-transform" + > +
+ +
+

AI对话追问

+

针对报告提问

+
+ + navigate(`/doctor-questions/${reportId}`)} + className="bg-gradient-to-br from-morandi-slate-100 to-morandi-slate-50 rounded-2xl p-4 border-0 cursor-pointer hover:scale-105 transition-transform" + > +
+ +
+

问诊清单

+

就医沟通话术

+
+ +
+
+ + {/* 温馨提示 */} + + +
+ +
+

+ 温馨提示 +

+

+ 以上解读仅供参考,不能替代医生的专业诊断。如有任何疑问或不适,请及时就医咨询专业医生。保持好心情,宝宝能感受到妈妈的情绪哦 💕 +

+
+
+
+
+
+ + {/* 底部操作栏 */} +
+
+ + +
+
+ + {/* 分享弹窗 */} + + {showShareSheet && ( + <> + setShowShareSheet(false)} + className="fixed inset-0 bg-black/40 z-40" + /> + +
+ {/* 拖拽指示器 */} +
+ +
+

分享报告

+ +
+ + {/* 分享方式 */} +
+ {/* 生成分享长图 */} + + + {/* 复制链接 */} + +
+ + {/* 分享预览卡片 */} + +
+
+ +
+
+

+ {analysisData.categories[0]?.category || "检查报告"} · AI解读 +

+

+ {analysisData.overall_summary} +

+
+ {new Date(analysisData.created_at).toLocaleDateString("zh-CN")} +
+
+
+
+ + {/* 隐私提示 */} +
+ + 分享时将自动隐藏个人敏感信息,保护您的隐私 +
+
+ + + )} + +
+ ); } \ No newline at end of file