diff --git a/apps/miniprogram/src/pages/ai-report/detail/index.scss b/apps/miniprogram/src/pages/ai-report/detail/index.scss
index e3c0acf..b39fb22 100644
--- a/apps/miniprogram/src/pages/ai-report/detail/index.scss
+++ b/apps/miniprogram/src/pages/ai-report/detail/index.scss
@@ -99,3 +99,32 @@
color: $tx3;
font-size: 28px;
}
+
+.auto-badge {
+ margin-top: 16px;
+ display: inline-block;
+}
+
+.auto-badge-text {
+ display: inline-block;
+ padding: 4px 16px;
+ border-radius: 8px;
+ font-size: 20px;
+ font-weight: 500;
+ background: #f0e6ff;
+ color: #7c3aed;
+}
+
+.trend-tip-card {
+ background: #fffbeb;
+ border: 1px solid #fde68a;
+ border-radius: $r;
+ padding: 20px 24px;
+ margin-bottom: 20px;
+}
+
+.trend-tip-text {
+ font-size: 22px;
+ color: #92400e;
+ line-height: 1.6;
+}
diff --git a/apps/miniprogram/src/pages/ai-report/detail/index.tsx b/apps/miniprogram/src/pages/ai-report/detail/index.tsx
index 42a2e53..1e86e27 100644
--- a/apps/miniprogram/src/pages/ai-report/detail/index.tsx
+++ b/apps/miniprogram/src/pages/ai-report/detail/index.tsx
@@ -55,6 +55,9 @@ export default function AiReportDetail() {
? markdownToHtml(analysis.result_content)
: '
暂无分析结果
';
+ const isTrendAnalysis = analysis.analysis_type === 'trend';
+ const isAutoAnalysis = (analysis.result_metadata as Record)?.auto_analysis === true;
+
return (
@@ -63,8 +66,21 @@ export default function AiReportDetail() {
模型: {analysis.model_used}
{new Date(analysis.created_at).toLocaleString('zh-CN')}
+ {isAutoAnalysis && (
+
+ 系统自动分析
+
+ )}
+ {isTrendAnalysis && (
+
+
+ 趋势分析基于最小二乘法线性回归和 2 倍标准差异常检测。R² 越接近 1 表示趋势拟合越好。斜率为正表示上升趋势,斜率为负表示下降趋势。
+
+
+ )}
+
diff --git a/apps/miniprogram/src/services/ai-analysis.ts b/apps/miniprogram/src/services/ai-analysis.ts
index a6f2627..73f3c81 100644
--- a/apps/miniprogram/src/services/ai-analysis.ts
+++ b/apps/miniprogram/src/services/ai-analysis.ts
@@ -7,6 +7,7 @@ export interface AiAnalysisItem {
model_used: string;
status: string;
result_content: string | null;
+ result_metadata: Record | null;
error_message: string | null;
created_at: string;
}
diff --git a/apps/web/src/pages/health/AiAnalysisList.tsx b/apps/web/src/pages/health/AiAnalysisList.tsx
index ce4a9bd..f5ecc99 100644
--- a/apps/web/src/pages/health/AiAnalysisList.tsx
+++ b/apps/web/src/pages/health/AiAnalysisList.tsx
@@ -1,8 +1,13 @@
import { useEffect, useState, useCallback, useMemo } from 'react';
import { Table, Select, Tag, Space, message, Typography } from 'antd';
+import {
+ RobotOutlined,
+} from '@ant-design/icons';
import { useThemeMode } from '../../hooks/useThemeMode';
import { analysisApi, type AnalysisItem } from '../../api/ai/analysis';
+const { Text } = Typography;
+
const ANALYSIS_TYPE_MAP: Record = {
lab_report_interpretation: '化验单解读',
health_trend_analysis: '趋势分析',
@@ -22,6 +27,88 @@ const TYPE_OPTIONS = Object.entries(ANALYSIS_TYPE_MAP).map(([value, label]) => (
label,
}));
+// ---------------------------------------------------------------------------
+// 分析结果渲染(Markdown 风格)
+// ---------------------------------------------------------------------------
+
+function AnalysisContent({ content, isDark }: { content: string; isDark: boolean }) {
+ // 简单的 Markdown 风格渲染
+ const lines = content.split('\n');
+ const rendered = lines.map((line, i) => {
+ // 标题行
+ if (line.startsWith('### ')) {
+ return (
+
+ {line.slice(4)}
+
+ );
+ }
+ if (line.startsWith('## ')) {
+ return (
+
+ {line.slice(3)}
+
+ );
+ }
+ // 列表项
+ if (line.startsWith('- ') || line.startsWith('* ')) {
+ return (
+
+ •
+ {renderInlineStyles(line.slice(2))}
+
+ );
+ }
+ // 有序列表
+ const orderedMatch = line.match(/^(\d+)\.\s/);
+ if (orderedMatch) {
+ return (
+
+ {orderedMatch[1]}.
+ {renderInlineStyles(line.slice(orderedMatch[0].length))}
+
+ );
+ }
+ // 空行
+ if (line.trim() === '') {
+ return ;
+ }
+ // 普通段落
+ return {renderInlineStyles(line)}
;
+ });
+
+ return {rendered}
;
+}
+
+/** 简单行内样式渲染(粗体、代码) */
+function renderInlineStyles(text: string) {
+ // 拆分 **bold** 和 `code` 模式
+ const parts = text.split(/(\*\*[^*]+\*\*|`[^`]+`)/g);
+ return parts.map((part, i) => {
+ if (part.startsWith('**') && part.endsWith('**')) {
+ return {part.slice(2, -2)};
+ }
+ if (part.startsWith('`') && part.endsWith('`')) {
+ return (
+
+ {part.slice(1, -1)}
+
+ );
+ }
+ return {part};
+ });
+}
+
+// ---------------------------------------------------------------------------
+// 主组件
+// ---------------------------------------------------------------------------
+
export default function AiAnalysisList() {
const [data, setData] = useState([]);
const [total, setTotal] = useState(0);
@@ -69,6 +156,15 @@ export default function AiAnalysisList() {
}
};
+ // 解析 metadata 中的趋势统计信息
+ const trendMetrics = useMemo(() => {
+ if (!detail?.result_metadata) return null;
+ const meta = detail.result_metadata as Record;
+ // 自动分析结果中可能包含 metrics 统计
+ // 也从 sanitized_input 中解析(如果有的话)
+ return meta;
+ }, [detail]);
+
const columns = useMemo(() => [
{
title: '分析类型',
@@ -151,36 +247,55 @@ export default function AiAnalysisList() {
onExpand: handleExpand,
expandedRowRender: () => {
if (!detail) return null;
+ const isTrendAnalysis = detail.analysis_type === 'trend';
+
return (
+ {/* 自动分析标记 */}
+ {trendMetrics && (trendMetrics as Record
).auto_analysis === true && (
+
+
+
+ 系统自动分析
+
+
+ )}
+
{detail.error_message && (
- 错误: {detail.error_message}
+ 错误: {detail.error_message}
)}
{detail.result_content && (
-
+
分析结果
-
+
- {detail.result_content}
+
+
+ {/* 趋势分析类型显示统计摘要提示 */}
+ {isTrendAnalysis && (
+
+
+ 提示:趋势分析基于最小二乘法线性回归和 2 倍标准差异常检测。R² 越接近 1 表示趋势拟合越好。
+ 斜率为正表示上升趋势,斜率为负表示下降趋势。
+
+
+ )}
)}
{!detail.result_content && !detail.error_message && (
- 暂无结果内容
+ 暂无结果内容
)}
);