# 全文复筛前端开发计? > **文档版本?* v1.0 > **创建日期?* 2025-11-23 > **最后更新:** 2025-11-23 > **预计工期?* 2.5? > **开发阶段:** 全文复筛模块 - 前端UI实现 --- ## 📋 目录 - [1. 开发目标](#1-开发目? - [2. 页面架构](#2-页面架构) - [3. 详细设计](#3-详细设计) - [4. 组件清单](#4-组件清单) - [5. 开发排期](#5-开发排? - [6. 技术实现](#6-技术实? - [7. 测试计划](#7-测试计划) --- ## 1. 开发目? ### 1.1 核心目标 实现全文复筛模块的完整前端UI,包括: - 4个核心页? - 支持独立运行和衔接标题摘要初筛两种模? - 实时任务进度监控 - 双模型判断对? - 简单PDF全文预览 - Excel结果导出 ### 1.2 设计原则 1. **灵活?* - 既能独立运行,也能衔接标题摘要初? 2. **实时?* - 长时间LLM任务需要实时进度反? 3. **可视?* - 冲突检测、PICS符合性清晰展? 4. **易用?* - 简化操作流程,降低学习成本 5. **一致?* - 与标题摘要初筛保持UI风格一? --- ## 2. 页面架构 ### 2.1 路由设计 ``` /asl/fulltext-screening ├── /settings - 设置与启动页? ├── /progress/:taskId - 任务进度监控页面 ├── /workbench/:taskId - 审核工作台页? └── /results/:taskId - 结果展示页面 ``` ### 2.2 页面流转 ``` 设置页面 (Settings) ?点击"开始全文复? ?POST /api/v1/asl/fulltext-screening/tasks ? 进度监控页面 (Progress) ?轮询 GET /tasks/:taskId/progress (?? ?status === 'completed' ? 审核工作?(Workbench) ?人工审核冲突和待定文? ?PUT /results/:resultId/decision ?点击"完成审核" ? 结果展示页面 (Results) ?GET /tasks/:taskId/results ?GET /tasks/:taskId/export (下载Excel) ``` ### 2.3 灵活导航 用户可以通过左侧导航随时?个页面之间跳转: - 进度页面未完成时,工作台显示"部分结果" - 任何阶段都可以导出当前状态的Excel - 支持返回设置页面重新开? --- ## 3. 详细设计 ### 3.1 设置与启动页?(Settings) #### 3.1.1 页面结构 ``` ┌─────────────────────────────────────────────? ?全文复筛 / 设置与启? ? ├─────────────────────────────────────────────? ? ? ?📋 PICOS标准 [调整标准] ? ?┌─────────────────────────────────────────?? ??P: 2型糖尿病成人患? ?? ??I: SGLT2抑制? ?? ??C: 安慰剂或常规降糖疗法 ?? ??O: 心血管事件、死亡率 ?? ??S: 随机对照试验 (RCT) ?? ?└─────────────────────────────────────────?? ? ? ?⚙️ 模型配置 ? ?┌─────────────────────────────────────────?? ??Model A: [DeepSeek-V3 ▼] ?? ??Model B: [Qwen-Max ▼] ?? ??并发? [3 ] ?? ?└─────────────────────────────────────────?? ? ? ?📚 全文文献管理 (5? [📤 上传PDF] ? ?┌─────────────────────────────────────────?? ??? 文献A.pdf ?已上?[查看][删除] ?? ??? 文献B.pdf ?已上?[查看][删除] ?? ??? 文献C.pdf ?上传?.. 45% ?? ??? 文献D.pdf ?上传失败 [重试] ?? ?└─────────────────────────────────────────?? ? ? ? [开始全文复筛] ?大绿色按? ? └─────────────────────────────────────────────? ``` #### 3.1.2 功能模块 **?)PICOS标准卡片** - 显示当前PICOS标准(来自项目或手动编辑? - "[调整标准]" 按钮 ?打开编辑弹窗 - 支持临时调整本次筛选标? **?)模型配?* - Model A 下拉选择:DeepSeek-V3 / Qwen-Max / GPT-4o / Claude-Sonnet-4 - Model B 下拉选择:同? - 并发数输入:1-10,默? **?)文献管理表?* - 显示已上传的PDF列表 - 显示上传状态:已上?上传?上传失败 - 操作列:查看、删除、重? - "[📤 上传PDF]" 按钮 ?打开上传弹窗 **?)开始复筛按?* - 当所有PDF上传成功后,按钮变为可用(绿色) - 点击后跳转到进度监控页面 #### 3.1.3 PICOS编辑弹窗 **触发**: 点击 "[调整标准]" 按钮 **设计**: ``` ┌─────────────────────────────────────────────? ?📋 编辑PICOS标准 [×] ? ├─────────────────────────────────────────────? ? ? ?Population (人群) * ? ?┌─────────────────────────────────────────?? ??2型糖尿病成人患? ?? ?└─────────────────────────────────────────?? ? ? ?Intervention (干预) * ? ?┌─────────────────────────────────────────?? ??SGLT2抑制? ?? ?└─────────────────────────────────────────?? ? ? ?Comparison (对照) ? ?┌─────────────────────────────────────────?? ??安慰剂或其他常规降糖疗法 ?? ?└─────────────────────────────────────────?? ? ? ?Outcome (结局) * ? ?┌─────────────────────────────────────────?? ??心血管事件、全因死亡、卒中复? ?? ?└─────────────────────────────────────────?? ? ? ?Study Design (研究设计) * ? ?┌─────────────────────────────────────────?? ??随机对照试验 (RCT) ?? ?└─────────────────────────────────────────?? ? ? ?⚠️ 注意:修改PICOS标准会影响AI筛选结? ? ? ? ? [取消] [保存修改] ? └─────────────────────────────────────────────? ``` **字段说明**: - 所有字段支持多行文本输? - P/I/O/S 为必填项?标记? - 保存后自动关闭弹窗,刷新PICOS卡片 #### 3.1.4 上传PDF弹窗 **触发**: 点击 "[📤 上传PDF]" 按钮 **设计**: ``` ┌─────────────────────────────────────────────? ?📤 上传PDF文件 [×] ? ├─────────────────────────────────────────────? ? ? ?┌─────────────────────────────────────────?? ?? ?? ?? 📁 拖拽文件到此? ?? ?? 或点击选择文件 ?? ?? ?? ?? ?支持格式:PDF ?? ?? ?单个文件最大:50MB ?? ?? ?支持批量上传(最?0篇) ?? ?? ?? ?└─────────────────────────────────────────?? ? ? ?上传列表? ? ?┌─────────────────────────────────────────?? ???文献A.pdf (2.3MB) ?? ???文献B.pdf (5.1MB) - 上传?.. 67% ?? ???文献C.pdf (120MB) - 文件过大 ?? ?└─────────────────────────────────────────?? ? ? ? [取消] [开始上传] ? └─────────────────────────────────────────────? ``` **功能说明**: - 使用 Ant Design `Upload.Dragger` 组件 - 支持拖拽和点击选择 - 自动验证文件格式和大? - 显示实时上传进度 - 上传完成后自动关闭弹窗,刷新文献列表 --- ### 3.2 任务进度监控页面 (Progress) #### 3.2.1 页面结构 ``` ┌─────────────────────────────────────────────? ?全文复筛 / 任务进度 ? ├─────────────────────────────────────────────? ? ? ?🤖 AI全文复筛进行?.. ? ? ? ?████████████░░░░░░░░ 60% ? ? ? ?📊 实时统计 ? ?┌─────────────────────────────────────────?? ??当前进度? / 5 ? ?? ???成功?? ?? ???失败?? ?? ??⚠️ 降级模式?? ?? ?? ?? ??💰 Token消耗:45,234 ?? ??💵 成本:?.1523 ?? ?? ?? ??⏱️ 已用时:3?5? ?? ??📈 预计剩余??0? ?? ?└─────────────────────────────────────────?? ? ? ?📝 处理日志 (自动滚动) ? ?┌─────────────────────────────────────────?? ??10:24:30 ?PMID123 处理完成 ?? ?? DeepSeek: ?Qwen: ? ?? ?? Token: 12,345 成本: ¥0.0412 ?? ?? ?? ??10:25:15 ?PMID456 正在处理... ?? ?? DeepSeek: 处理?.. ?? ?? ?? ??10:26:02 ?PMID789 处理失败 ?? ?? 错误: PDF提取失败 ?? ?└─────────────────────────────────────────?? ? ? ?[取消任务] [查看已完成结果] ? └─────────────────────────────────────────────? ``` #### 3.2.2 功能说明 **?)进度条** - 显示整体进度百分? - 使用 Ant Design `Progress` 组件 - 根据 `processedCount / totalCount` 计算 **?)实时统计卡?* - 当前进度、成功数、失败数、降级数 - Token消耗和成本统计 - 已用时间和预计剩余时间(基于平均处理速度? **?)处理日?* - 实时显示每篇文献的处理状? - 自动滚动到最新日? - 显示详细的错误信? **?)操作按?* - "取消任务" - 中断当前任务 - "查看已完成结? - 跳转到审核工作台(显示部分结果) #### 3.2.3 技术要? **轮询机制**: ```typescript const { data: task, refetch } = useQuery({ queryKey: ['fulltextTask', taskId], queryFn: () => api.getTaskProgress(taskId), refetchInterval: (data) => { // 任务进行中:?秒轮询一? // 任务完成或失败:停止轮询 return data?.status === 'processing' ? 3000 : false; }, }); // 任务完成后自动跳? useEffect(() => { if (task?.status === 'completed') { setTimeout(() => { navigate(`/asl/fulltext-screening/workbench/${taskId}`); }, 2000); // 延迟2秒,让用户看到完成状? } }, [task?.status]); ``` --- ### 3.3 审核工作台页?(Workbench) #### 3.3.1 页面结构 ``` ┌──────────────────────────────────────────────────────────────? ?全文复筛 / 审核工作? ? ├──────────────────────────────────────────────────────────────? ? ? ?📋 PICOS标准 (点击展开) ? ? ?┌────────────────────────────────────────────────────────? ? ??P: 2型糖尿病成人患?| I: SGLT2抑制?| ... ? ? ?└────────────────────────────────────────────────────────? ? ? ? ?📊 统计概览 ? ?┌────────────────────────────────────────────────────────? ? ??总数: 5 | 一? 3 | 冲突: 2 | 待定: 0 ? ? ?└────────────────────────────────────────────────────────? ? ? ? ?🔍 筛? [全部▼] [冲突▼] [一致▼] 🔎 搜索: [____]? ? ? ?┌────────────────────────────────────────────────────────? ? ??文献 | DS判断(PICS) | Q3判断(PICS) | 冲突 | 决策 | 操作? ? ?├────────────────────────────────────────────────────────? ? ??[+] ? ? ? ? ? ? ? ??PMID ?????纳入 ?????纳入?一?│纳? │详? ? ? ??123 ? ? ? ? ? ? ? ?├────────────────────────────────────────────────────────? ? ??[+] ? ? ? ? ? ? ? ??PMID ?????排除 ?????纳入│❗冲突│[▼] │详? ? ? ??456 ? ? ? ? ? ? ? ?? ? ├──────────────────────────────────────────────────? ? ?? └─> ?💡 冲突原因:对照组(C)判断不一? ? ? ?? ??DS: C不符合(未提及对照组? ? ? ?? ??Q3: C符合(安慰剂对照? ? ? ?? └──────────────────────────────────────────────────? ? ?└────────────────────────────────────────────────────────? ? ? ? ? [完成审核,进入结果页面] ? └──────────────────────────────────────────────────────────────? ``` #### 3.3.2 功能模块 **?)PICOS标准折叠面板** - 默认折叠,节省空? - 点击展开显示完整PICOS标准 - 供审核时参? **?)统计概览卡?* - 显示总数、一致数、冲突数、待定数 - 快速了解整体情? **?)筛选和搜索** - 按冲突状态筛选:全部/仅冲?仅一? - 按决策筛选:全部/纳入/排除/待定 - 搜索:支持PMID、标题关键词 **?)双模型判断表格** - 显示PICS四维度符合性(???? - 显示双模型结论(纳入/排除? - 冲突行自动高亮(红色背景? - 展开行显示冲突详? **?)最终决策列** - 一致文献:自动采用AI决策 - 冲突文献:显示下拉框,需人工决策 - 支持三个选项:纳?排除/待定 **?)操作列** - "详情" 按钮 ?打开详情抽屉 #### 3.3.3 详情抽屉设计 **布局**: 右侧滑出(宽?00px? ``` ┌────────────────────────────────────────? ?文献详情 - PMID456 [×] ? ├────────────────────────────────────────? ?[AI判断对比] [PDF全文] [12字段详情] ??Tab切换 ├────────────────────────────────────────? ? ? ?📄 基本信息 ? ??标题:Effect of SGLT2 inhibitors... ? ??作者:Zhang W, Li H, Wang Y ? ??期刊:JAMA 2023;15(3):e124 ? ??年份?023 ? ? ? ?───────────────────────────────────── ? ? ? ?🤖 AI判断对比 ? ? ? ?┌──────────────┬──────────────? ? ??DeepSeek-V3 ? Qwen-Max ? ? ?├──────────────┼──────────────? ? ??P: ?符合 ? P: ?符合 ? ? ?? 成人T2DM?? 成人T2DM ? ? ?? 者,年龄... ? 患? ? ? ?? ? ? ? ??I: ?符合 ? I: ?符合 ? ? ?? SGLT2抑制剂│ 达格列净 ? ? ?? 达格列净... ? ? ? ?? ? ? ? ??C: ?不符? ? C: ?符合 ??冲突? ?? 未明确对?? 安慰剂对 ? ? ?? 组,仅提?? 照,双盲 ? ? ?? "常规治疗" ? ? ? ?? ? ? ? ??S: ?符合 ? S: ?符合 ? ? ?? 随机对照?? RCT,多 ? ? ?? 验,多中?? 中心研究 ? ? ?? ? ? ? ??结论:排? ? 结论:纳? ? ? ?└──────────────┴──────────────? ? ? ? ?⚠️ 冲突提示:两个模型在对照?C)判断 ? ? 上存在分歧,建议查看原文验证 ? ? ? ?───────────────────────────────────── ? ? ? ?✏️ 人工决策 ? ?最终决策:[纳入 ▼] ? ?决策理由? ? ?┌──────────────────────────────────? ? ??虽然DS判断对照组不符合,但查看 ? ? ??原文后确认为安慰剂对照,符合纳入 ? ? ??标准? ? ? ?└──────────────────────────────────? ? ? ? ? [取消] [保存决策] ? └────────────────────────────────────────? ``` **Tab 2: PDF全文预览** ``` ┌────────────────────────────────────────? ?文献详情 - PMID456 [×] ? ├────────────────────────────────────────? ?[AI判断对比] [PDF全文] [12字段详情] ? ├────────────────────────────────────────? ? ? ?📄 PDF预览 ? ?┌────────────────────────────────────?? ?? ?? ?? [PDF内容渲染区域] ?? ?? ?? ?? 使用 react-pdf 渲染 ?? ?? ?? ?? 支持? ?? ?? ?翻页(上一?下一页) ?? ?? ?缩放(放?缩小/适应? ?? ?? ?跳页(输入页码直接跳转) ?? ?? ?? ?└────────────────────────────────────?? ? ? ?页码? / 15 [<] [>] [跳转__] ? ?缩放:[100% ▼] [适应宽度] [适应高度] ? ? ? └────────────────────────────────────────? ``` **Tab 3: 12字段详情** ``` ┌────────────────────────────────────────? ?文献详情 - PMID456 [×] ? ├────────────────────────────────────────? ?[AI判断对比] [PDF全文] [12字段详情] ? ├────────────────────────────────────────? ? ? ?📋 12字段完整性评? ? ? ? ??1. 文献来源 ? ? 存在? ?完整 ? ? 第一作? Zhang W ? ? 年份: 2023 ? ? 期刊: JAMA ? ? ? ??2. 研究类型 ? ? 存在? ?完整 ? ? 类型: RCT ? ? ? ??3. 研究设计细节 (点击展开) ? ? ? ??4. 疾病诊断标准 ? ? ? ??5. 人群特征 ?(关键字段) ? ? ? ?... (?2个字? ? ? ? └────────────────────────────────────────? ``` --- ### 3.4 结果展示页面 (Results) #### 3.4.1 页面结构 ``` ┌─────────────────────────────────────────────? ?全文复筛 / 结果展示 ? ├─────────────────────────────────────────────? ? ? ?📊 总体统计 ? ?┌───────────┬───────────┬───────────? ? ??总计复筛 ? 最终纳?? 排除 ? ? ?? 100 ? 55 ? 45 ? ? ?└───────────┴───────────┴───────────? ? ? ? ?📈 PRISMA流程图统? ? ?┌─────────────────────────────────────────?? ??排除原因统计? ?? ???排除文献总数: 45? ?? ???非随机对照研?S): 5? ?? ???非目标人?P): 7? ?? ???干预/对照不符(I/C): 18? ?? ???结局指标不符(O): 9? ?? ???其他原因: 6? ?? ?└─────────────────────────────────────────?? ? ? ?💰 成本统计 ? ?┌─────────────────────────────────────────?? ???DeepSeek-V3: Token 245K, 成本 ¥0.82 ?? ???Qwen-Max: Token 198K, 成本 ¥1.35 ?? ???总计: Token 443K, 成本 ¥2.17 ?? ???平均单篇成本: ¥0.0217 ?? ?└─────────────────────────────────────────?? ? ? ?📋 文献列表 [导出Excel]? ?┌─────────────────────────────────────────?? ??[最终纳?(55)] [排除 (45)] ?Tab ?? ?? ?? ??🔎 搜索: [________] [筛选▼] ?? ?? ?? ??PMID | 研究ID | 来源 | 决策 | 方式 ?? ??123 | A1 2021?... | 纳入 | AI纳入 ?? ??456 | B2 2022?... | 纳入 | 人工审核 ?? ??... ?? ?└─────────────────────────────────────────?? └─────────────────────────────────────────────? ``` #### 3.4.2 功能说明 **?)总体统计卡片** - 显示总数、纳入数、排除数 - 大数字突出显? - 绿色(纳入)、红色(排除)配? **?)PRISMA统计卡片** - 按排除原因分类统? - 符合PRISMA流程图要? - 便于撰写系统评价报告 **?)成本统计卡?* - 双模型Token和成本对? - 显示平均单篇成本 - 便于成本控制和优? **?)文献列?* - Tab切换:最终纳?/ 排除 - 显示文献基本信息 - 显示决策方式:AI纳入/AI排除/人工审核 - 支持搜索和筛? **?)导出Excel按钮** - 下载4-Sheet Excel报告 - 包含:纳入文献、排除文献、PRISMA统计、成本统? --- ## 4. 组件清单 ### 4.1 页面组件(Page Components? | 组件?| 文件路径 | 功能描述 | 预计工作?| |--------|----------|----------|------------| | `FulltextScreeningSettings` | `pages/FulltextScreeningSettings.tsx` | 设置与启动页?| 4小时 | | `FulltextScreeningProgress` | `pages/FulltextScreeningProgress.tsx` | 任务进度监控页面 | 3小时 | | `FulltextScreeningWorkbench` | `pages/FulltextScreeningWorkbench.tsx` | 审核工作台页?| 6小时 | | `FulltextScreeningResults` | `pages/FulltextScreeningResults.tsx` | 结果展示页面 | 3小时 | ### 4.2 功能组件(Feature Components? | 组件?| 文件路径 | 功能描述 | 预计工作?| |--------|----------|----------|------------| | `PICOSCard` | `components/PICOSCard.tsx` | PICOS标准展示卡片(可折叠?| 1小时 | | `PICOSEditModal` | `components/PICOSEditModal.tsx` | PICOS编辑弹窗 | 2小时 | | `PDFUploadModal` | `components/PDFUploadModal.tsx` | PDF上传弹窗 | 2小时 | | `LiteratureTable` | `components/LiteratureTable.tsx` | 文献管理表格 | 2小时 | | `ProgressMonitor` | `components/ProgressMonitor.tsx` | 进度监控组件 | 2小时 | | `DualModelJudgmentTable` | `components/DualModelJudgmentTable.tsx` | 双模型判断表?| 4小时 | | `LiteratureDetailDrawer` | `components/LiteratureDetailDrawer.tsx` | 文献详情抽屉 | 4小时 | | `PDFViewer` | `components/PDFViewer.tsx` | PDF预览组件 | 2小时 | | `FieldsCollapse` | `components/FieldsCollapse.tsx` | 12字段折叠面板 | 2小时 | | `PRISMAStatistics` | `components/PRISMAStatistics.tsx` | PRISMA统计卡片 | 1小时 | ### 4.3 复用组件(Reused Components? | 组件?| 来源 | 功能描述 | |--------|------|----------| | `ASLLayout` | 标题摘要初筛 | 左侧导航布局 | | `JudgmentBadge` | 标题摘要初筛 | PICOS判断标签(✓/???| | `ConclusionTag` | 标题摘要初筛 | 决策标签(纳?排除?| ### 4.4 Hooks(自定义钩子? | Hook?| 文件路径 | 功能描述 | 预计工作?| |--------|----------|----------|------------| | `useFulltextTask` | `hooks/useFulltextTask.ts` | 管理全文复筛任务状?| 1小时 | | `useTaskProgress` | `hooks/useTaskProgress.ts` | 轮询任务进度 | 1小时 | | `useTaskResults` | `hooks/useTaskResults.ts` | 获取任务结果 | 1小时 | --- ## 5. 开发排? ### 5.1 Day 6 - 基础页面?小时? #### 上午?小时? **5.1.1 设置与启动页面基础**?小时? - [ ] 创建页面文件和路? - [ ] 实现PICOS展示卡片 - [ ] 实现模型配置表单 - [ ] 实现文献列表表格(基础版) **5.1.2 PICOS编辑和PDF上传弹窗**?小时? - [ ] 实现PICOS编辑弹窗 - [ ] 实现PDF上传弹窗(Ant Design Upload? - [ ] 集成后端PDF上传API - [ ] 实时显示上传进度 #### 下午?小时? **5.1.3 任务进度监控页面**?小时? - [ ] 创建页面文件和路? - [ ] 实现进度条和统计卡片 - [ ] 实现轮询机制(React Query? - [ ] 实现处理日志滚动显示 - [ ] 任务完成后自动跳? --- ### 5.2 Day 7 - 核心功能?0小时? #### 上午?小时? **5.2.1 审核工作台页面基础**?小时? - [ ] 创建页面文件和路? - [ ] 实现PICOS折叠面板 - [ ] 实现统计概览卡片 - [ ] 实现筛选和搜索功能 - [ ] 实现双模型判断表格(基础版) **5.2.2 冲突可视?*?小时? - [ ] 冲突行高亮显? - [ ] 展开行显示冲突详? - [ ] 冲突原因文字说明 - [ ] 最终决策下拉框(含验证? #### 下午?小时? **5.2.3 文献详情抽屉 - AI判断对比Tab**?小时? - [ ] 创建抽屉组件(Ant Design Drawer? - [ ] 实现基本信息展示 - [ ] 实现双模型判断对比(左右分栏? - [ ] 高亮冲突字段 - [ ] 人工决策表单(含提交? **5.2.4 PDF预览Tab**?小时? - [ ] 集成react-pdf? - [ ] 实现基础PDF渲染 - [ ] 实现翻页功能(上一?下一?跳页? - [ ] 实现缩放功能(放?缩小/适应? - [ ] 优化加载性能 **5.2.5 12字段详情Tab**?小时? - [ ] 实现12字段折叠面板(Ant Design Collapse? - [ ] 显示每个字段的存在性、完整? - [ ] 支持展开/折叠单个字段 --- ### 5.3 Day 8 - 结果页面与联调(6小时? #### 上午?小时? **5.3.1 结果展示页面**?小时? - [ ] 创建页面文件和路? - [ ] 实现总体统计卡片 - [ ] 实现PRISMA统计卡片 - [ ] 实现成本统计卡片 - [ ] 实现文献列表(Tab切换? - [ ] 实现Excel导出功能 #### 下午?小时? **5.3.2 前后端联?*?小时? - [ ] 测试完整流程(设置→进度→工作台→结果) - [ ] 测试PDF上传 - [ ] 测试实时进度轮询 - [ ] 测试人工决策提交 - [ ] 测试Excel导出下载 **5.3.3 Bug修复和优?*?小时? - [ ] 修复发现的Bug - [ ] 优化UI细节 - [ ] 优化性能(懒加载、虚拟滚动等? - [ ] 补充错误处理和提? --- ## 6. 技术实? ### 6.1 技术栈 - **框架**: React 18 + TypeScript 5 - **路由**: React Router DOM v6 - **状态管?*: @tanstack/react-query (React Query v5) - **UI组件**: Ant Design v5 - **PDF预览**: react-pdf v7 - **文件上传**: Ant Design Upload + axios - **样式**: TailwindCSS v3 ### 6.2 关键技术实? #### 6.2.1 PDF上传 ```typescript import { Upload, message } from 'antd'; import type { UploadFile } from 'antd/es/upload/interface'; const PDFUploadModal = () => { const [fileList, setFileList] = useState([]); const customRequest = async ({ file, onProgress, onSuccess, onError }: any) => { try { const formData = new FormData(); formData.append('file', file); const response = await axios.post( '/api/v1/asl/literatures/upload-pdf', formData, { onUploadProgress: (e) => { const percent = Math.round((e.loaded / e.total!) * 100); onProgress({ percent }); }, } ); onSuccess(response.data); message.success(`${file.name} 上传成功`); } catch (error) { onError(error); message.error(`${file.name} 上传失败`); } }; return ( setFileList(fileList)} beforeUpload={(file) => { // 验证文件大小(最?0MB? const isLt50M = file.size / 1024 / 1024 < 50; if (!isLt50M) { message.error('文件大小不能超过50MB!'); } return isLt50M; }} >

📁

拖拽文件到此处,或点击选择文件

支持格式:PDF | 单个文件最大:50MB | 支持批量上传(最?0篇)

); }; ``` #### 6.2.2 任务进度轮询 ```typescript import { useQuery } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom'; const useTaskProgress = (taskId: string) => { const navigate = useNavigate(); const { data: task, isLoading } = useQuery({ queryKey: ['fulltextTask', taskId], queryFn: async () => { const res = await axios.get(`/api/v1/asl/fulltext-screening/tasks/${taskId}/progress`); return res.data.data; }, refetchInterval: (data) => { // 任务进行中:?秒轮? // 任务完成或失败:停止轮询 return data?.status === 'processing' ? 3000 : false; }, refetchOnWindowFocus: false, }); // 任务完成后自动跳? useEffect(() => { if (task?.status === 'completed') { setTimeout(() => { navigate(`/asl/fulltext-screening/workbench/${taskId}`); }, 2000); } }, [task?.status, taskId, navigate]); return { task, isLoading }; }; ``` #### 6.2.3 PDF预览组件 ```typescript import { useState } from 'react'; import { Document, Page, pdfjs } from 'react-pdf'; import 'react-pdf/dist/esm/Page/AnnotationLayer.css'; import 'react-pdf/dist/esm/Page/TextLayer.css'; // 配置PDF.js worker pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`; interface PDFViewerProps { url: string; } const PDFViewer: React.FC = ({ url }) => { const [numPages, setNumPages] = useState(0); const [pageNumber, setPageNumber] = useState(1); const [scale, setScale] = useState(1.0); const onDocumentLoadSuccess = ({ numPages }: { numPages: number }) => { setNumPages(numPages); setPageNumber(1); }; const changePage = (offset: number) => { setPageNumber((prev) => Math.min(Math.max(1, prev + offset), numPages)); }; const changeScale = (newScale: number) => { setScale(Math.min(Math.max(0.5, newScale), 2.0)); }; return (
加载?..
} error={
PDF加载失败
} >
页码:{pageNumber} / {numPages}
{Math.round(scale * 100)}%
); }; export default PDFViewer; ``` #### 6.2.4 双模型判断表? ```typescript interface DualModelJudgmentTableProps { results: FulltextScreeningResult[]; onUpdateDecision: (resultId: string, decision: string) => void; onOpenDetail: (result: FulltextScreeningResult) => void; } const DualModelJudgmentTable: React.FC = ({ results, onUpdateDecision, onOpenDetail, }) => { const [expandedKeys, setExpandedKeys] = useState([]); const columns: ColumnsType = [ { title: '文献', dataIndex: 'literatureId', key: 'literatureId', render: (_, record) => record.literature.pmid, }, { title: 'DS判断(PICS)', key: 'modelA', render: (_, record) => { const { modelAFields, modelAOverall } = record; return (
); }, }, { title: 'Q3判断(PICS)', key: 'modelB', render: (_, record) => { const { modelBFields, modelBOverall } = record; return (
); }, }, { title: '冲突', key: 'conflict', render: (_, record) => { const hasConflict = record.conflictSeverity !== null; return hasConflict ? ( 冲突 ) : ( 一?/Tag> ); }, }, { title: '决策', key: 'decision', render: (_, record) => { const hasConflict = record.conflictSeverity !== null; if (!hasConflict && record.finalDecision) { return ; } return ( ); }, }, { title: '操作', key: 'action', render: (_, record) => ( ), }, ]; return ( { if (expanded) { setExpandedKeys([...expandedKeys, record.id]); } else { setExpandedKeys(expandedKeys.filter((k) => k !== record.id)); } }, expandedRowRender: (record) => { if (!record.conflictFields || record.conflictFields.length === 0) { return null; } return (

💡 冲突详情?/h4>
    {record.conflictFields.map((field) => (
  • {field}: Model A vs Model B 判断不一?
  • ))}

); }, rowExpandable: (record) => record.conflictFields && record.conflictFields.length > 0, }} rowClassName={(record) => record.conflictSeverity ? 'bg-red-50' : '' } /> ); }; ``` #### 6.2.5 Excel导出 ```typescript const handleExportExcel = async (taskId: string) => { try { message.loading({ content: '正在生成Excel...', key: 'export' }); const response = await axios.get( `/api/v1/asl/fulltext-screening/tasks/${taskId}/export`, { responseType: 'blob', // 重要:指定响应类型为blob } ); // 创建下载链接 const url = window.URL.createObjectURL(new Blob([response.data])); const link = document.createElement('a'); link.href = url; // 从响应头获取文件? const contentDisposition = response.headers['content-disposition']; const filename = contentDisposition ? contentDisposition.split('filename=')[1].replace(/"/g, '') : `全文复筛结果_${taskId}.xlsx`; link.setAttribute('download', filename); document.body.appendChild(link); link.click(); // 清理 link.remove(); window.URL.revokeObjectURL(url); message.success({ content: '导出成功?, key: 'export' }); } catch (error) { message.error({ content: '导出失败', key: 'export' }); console.error('Export error:', error); } }; ``` --- ## 7. 测试计划 ### 7.1 功能测试 **测试用例**: | 编号 | 测试?| 测试步骤 | 预期结果 | |------|--------|----------|----------| | T1 | PDF上传 | 上传单个PDF | 上传成功,显示在文献列表 | | T2 | PDF批量上传 | 上传5个PDF | 全部上传成功 | | T3 | PDF大小验证 | 上传>50MB的PDF | 提示文件过大,上传失?| | T4 | PICOS编辑 | 修改P字段并保?| PICOS卡片更新 | | T5 | 创建任务 | 点击"开始全文复? | 跳转到进度页?| | T6 | 进度轮询 | 停留在进度页?| ?秒更新一次进?| | T7 | 自动跳转 | 等待任务完成 | 自动跳转到工作台 | | T8 | 冲突可视?| 查看工作台冲突行 | 红色高亮,显示冲突详?| | T9 | 人工决策 | 修改冲突文献决策 | 保存成功,刷新列?| | T10 | PDF预览 | 打开详情抽屉查看PDF | PDF正常渲染,可翻页缩放 | | T11 | Excel导出 | 点击"导出Excel" | 下载4-Sheet Excel文件 | ### 7.2 性能测试 | 测试?| 测试条件 | 性能指标 | |--------|----------|----------| | 页面加载 | 首次访问 | < 2?| | PDF上传 | 10MB文件 | < 5?| | 进度轮询 | 轮询100?| 无内存泄?| | PDF渲染 | 15页PDF | < 3?| | 表格渲染 | 100条数?| < 1?| ### 7.3 兼容性测? | 浏览?| 版本 | 测试结果 | |--------|------|----------| | Chrome | 最新版 | ?通过 | | Firefox | 最新版 | ?通过 | | Edge | 最新版 | ?通过 | | Safari | 最新版 | ⚠️ PDF预览需额外测试 | --- ## 📝 附录 ### A. API接口清单 | 接口 | 方法 | 路径 | 功能 | |------|------|------|------| | 上传PDF | POST | `/api/v1/asl/literatures/upload-pdf` | 上传单个PDF文件 | | 创建任务 | POST | `/api/v1/asl/fulltext-screening/tasks` | 创建全文复筛任务 | | 获取进度 | GET | `/api/v1/asl/fulltext-screening/tasks/:taskId/progress` | 获取任务进度 | | 获取结果 | GET | `/api/v1/asl/fulltext-screening/tasks/:taskId/results` | 获取任务结果列表 | | 更新决策 | PUT | `/api/v1/asl/fulltext-screening/results/:resultId/decision` | 人工更新决策 | | 导出Excel | GET | `/api/v1/asl/fulltext-screening/tasks/:taskId/export` | 导出Excel报告 | ### B. 数据结构 **FulltextScreeningTask**: ```typescript interface FulltextScreeningTask { id: string; projectId: string; status: 'pending' | 'processing' | 'completed' | 'failed'; totalCount: number; processedCount: number; successCount: number; failedCount: number; degradedCount: number; totalTokens: number; totalCost: number; createdAt: Date; startedAt: Date | null; completedAt: Date | null; } ``` **FulltextScreeningResult**: ```typescript interface FulltextScreeningResult { id: string; taskId: string; literatureId: string; literature: { pmid: string; title: string; authors: string; journal: string; publicationYear: number; }; modelAName: string; modelAStatus: 'success' | 'failed'; modelAFields: any; // 12字段 modelAOverall: { overall_decision: 'include' | 'exclude'; overall_reasoning: string; }; modelBName: string; modelBStatus: 'success' | 'failed'; modelBFields: any; modelBOverall: any; conflictFields: string[]; conflictSeverity: 'high' | 'medium' | 'low' | null; finalDecision: 'include' | 'exclude' | 'pending' | null; reviewedBy: string | null; reviewedAt: Date | null; } ``` --- **文档维护**? - 开发过程中如有调整,及时更新本文档 - 每个页面开发完成后,更新完成状? - 遇到技术难点,记录在文档中 **相关文档**? - [全文复筛开发计划](./04-全文复筛开发计?md) - [API设计规范](../02-技术设?02-API设计规范.md) - [数据库设计](../02-技术设?01-数据库设?md) - [技术债务清单](../06-技术债务/技术债务清单.md) --- **文档版本历史**? - v1.0 (2025-11-23) - 初始版本,Day 5完成后创?