Files
hms/apps/miniprogram/src/pages/ai-report/detail/index.tsx
iven 1fd2c7a533 refactor(mp): 架构重构 — usePageData 统一数据加载 + Store 解耦 + 大页面拆分
新增 usePageData hook(useDidShow 节流 + usePullDownRefresh + loadingRef 防重入 + enabled 条件守卫),
44/58 页面迁移接入,消灭 4 种数据加载模式并存。

- 新增 hooks/usePageData.ts — 统一页面数据加载生命周期
- 新增 stores/index.ts — resetAllStores() 解耦 auth↔health store 依赖
- 新增 pages/index/useHomeData.ts — 首页数据 hook(424→282 行)
- 新增 pages/health/useHealthData.ts — 健康页数据 hook(422→254 行)
- 44 个页面迁移到 usePageData(9 患者端 + 15 医生端 + 20 子包)
- auth store logout 不再直接导入 health store

构建通过,测试 74/75(1 个预存失败)。
2026-05-15 01:13:01 +08:00

99 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useCallback } from 'react';
import { View, Text, RichText } from '@tarojs/components';
import Taro, { useRouter } from '@tarojs/taro';
import { usePageData } from '@/hooks/usePageData';
import { getAiAnalysisDetail, type AiAnalysisItem } from '@/services/ai-analysis';
import Loading from '@/components/Loading';
import { sanitizeHtml } from '@/utils/sanitize-html';
import { useElderClass } from '../../../hooks/useElderClass';
import './index.scss';
const TYPE_LABELS: Record<string, string> = {
lab_report_interpretation: '化验单解读',
health_trend_analysis: '趋势分析',
personalized_checkup_plan: '体检方案',
report_summary_generation: '报告摘要',
};
function markdownToHtml(md: string): string {
const escaped = sanitizeHtml(md);
return escaped
.replace(/^(#{1,3}) (.+)$/gm, (_, h: string, t: string) => `<h${h.length}>${t}</h${h.length}>`)
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.+?)\*/g, '<em>$1</em>')
.replace(/^- (.+)$/gm, '<li>$1</li>')
.replace(/(<li>[\s\S]*?<\/li>)/g, '<ul>$1</ul>')
.replace(/\n\n/g, '<br/><br/>')
.replace(/\n/g, '<br/>');
}
export default function AiReportDetail() {
const modeClass = useElderClass();
const router = useRouter();
const id = router.params.id || '';
const [analysis, setAnalysis] = useState<AiAnalysisItem | null>(null);
const [loading, setLoading] = useState(true);
const fetchDetail = useCallback(async () => {
if (!id) return;
setLoading(true);
try {
const data = await getAiAnalysisDetail(id);
setAnalysis(data);
} catch {
Taro.showToast({ title: '加载失败', icon: 'none' });
} finally {
setLoading(false);
}
}, [id]);
usePageData(fetchDetail, { throttleMs: 60000 });
if (loading) return <Loading />;
if (!analysis) {
return (
<View className={`detail-page ${modeClass}`}>
<Text className='empty-text'></Text>
</View>
);
}
const htmlContent = analysis.result_content
? markdownToHtml(analysis.result_content)
: '<p>暂无分析结果</p>';
const isTrendAnalysis = analysis.analysis_type === 'trend';
const isAutoAnalysis = (analysis.result_metadata as Record<string, unknown>)?.auto_analysis === true;
return (
<View className={`detail-page ${modeClass}`}>
<View className='detail-card'>
<Text className='detail-type'>{TYPE_LABELS[analysis.analysis_type] || analysis.analysis_type}</Text>
<View className='detail-meta'>
<Text className='meta-item'>: {analysis.model_used}</Text>
<Text className='meta-item'>{new Date(analysis.created_at).toLocaleString('zh-CN')}</Text>
</View>
{isAutoAnalysis && (
<View className='auto-badge'>
<Text className='auto-badge-text'></Text>
</View>
)}
</View>
{isTrendAnalysis && (
<View className='trend-tip-card'>
<Text className='trend-tip-text'>
线 2 R² 1
</Text>
</View>
)}
<View className='content-card'>
<RichText className='report-content' nodes={htmlContent} />
</View>
</View>
);
}