Files
AIclinicalresearch/docs/03-业务模块/ASL-AI智能文献/03-UI设计/AI智能文献-标题摘要初筛原型.html
HaHafeng e52020409c docs: complete documentation system (250+ files)
- System architecture and design documentation
- Business module docs (ASL/AIA/PKB/RVW/DC/SSA/ST)
- ASL module complete design (quality assurance, tech selection)
- Platform layer and common capabilities docs
- Development standards and API specifications
- Deployment and operations guides
- Project management and milestone tracking
- Architecture implementation reports
- Documentation templates and guides
2025-11-16 15:43:55 +08:00

451 lines
40 KiB
HTML
Raw Permalink 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.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>标题摘要初筛原型 V4</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/heroicons/2.1.3/24/outline/css/heroicons.min.css" rel="stylesheet">
<style>
body { font-family: 'Inter', sans-serif; }
.sidebar { background-color: #f8fafc; }
.sidebar-link.active { background-color: #e0f2fe; color: #0c4a6e; font-weight: 600; }
.main-content { margin-left: 16rem; }
.view { display: none; }
.view.active { display: block; }
.modal-backdrop { background-color: rgba(0, 0, 0, 0.5); }
.table-conflict-row > td:not(.expanded-row-cell) { background-color: #fee2e2; }
.table-cell-judge { cursor: pointer; text-align: center; }
.abstract-highlight { background-color: #fef9c3; }
.nav-parent { font-weight: bold; color: #374151; }
.nav-child { padding-left: 1.5rem; }
.sub-nav-link { color: #6b7280; }
.sub-nav-link.active { color: #0c4a6e; font-weight: 600; background-color: #f0f9ff; }
.sidebar-link.disabled { color: #9ca3af; cursor: not-allowed; background-color: transparent !important; }
.sidebar-link.disabled:hover { background-color: transparent !important; }
.result-tab.active { border-bottom-color: #0ea5e9; color: #0ea5e9; font-weight: 600;}
.expand-row-btn { cursor: pointer; width: 20px; text-align: center; }
.expanded-row { display: none; background-color: #f9fafb; }
.expanded-row.shown { display: table-row; }
.expanded-row-cell { padding: 0.75rem 1rem; border-top: 1px solid #e5e7eb; }
.evidence-grid { display: grid; grid-template-columns: auto 1fr; gap: 0.25rem 1rem; }
.evidence-grid dt { font-weight: 500; color: #4b5563; text-align: right;}
.evidence-grid dd { color: #1f2937; }
.abstract-cell .full-abstract { display: none; }
.abstract-cell.expanded .short-abstract { display: none; }
.abstract-cell.expanded .full-abstract { display: block; }
/* V4 specific style for results table PICS */
.results-pics-cell { font-size: 0.7rem; line-height: 1.2; color: #4b5563; }
.results-pics-cell strong { color: #1f2937; }
</style>
</head>
<body class="bg-gray-100 text-gray-800">
<div class="h-screen flex">
<!-- 模拟的左侧主导航 -->
<aside id="sidebar" class="sidebar w-64 border-r border-gray-200 p-4 flex-shrink-0 flex flex-col fixed h-full">
<div class="text-xl font-bold text-gray-800 mb-8">AI文献平台</div>
<nav class="flex-grow space-y-2">
<a href="#" class="sidebar-link group flex items-center px-3 py-2 text-sm font-medium rounded-md text-gray-700">1. 研究方案生成</a>
<a href="#" class="sidebar-link group flex items-center px-3 py-2 text-sm font-medium rounded-md text-gray-700">2. 智能文献检索</a>
<!-- 标题摘要初筛 Section -->
<div class="space-y-1">
<span class="nav-parent group flex items-center px-3 py-2 text-sm rounded-md text-gray-800">3. 标题摘要初筛</span>
<div class="space-y-1 nav-child">
<a href="#" id="nav-title-settings" data-view="title-settings" class="sidebar-link sub-nav-link active group flex items-center px-3 py-2 text-sm font-medium rounded-md">设置与启动</a>
<a href="#" id="nav-title-workbench" data-view="title-workbench" class="sidebar-link sub-nav-link group flex items-center px-3 py-2 text-sm font-medium rounded-md">审核工作台</a>
<a href="#" id="nav-title-results" data-view="title-results" class="sidebar-link sub-nav-link group flex items-center px-3 py-2 text-sm font-medium rounded-md">初筛结果</a>
</div>
</div>
<!-- Full-text Rescreening Section (Disabled Placeholder) -->
<div class="space-y-1">
<span class="nav-parent group flex items-center px-3 py-2 text-sm rounded-md text-gray-400">4. 全文复筛</span>
<div class="space-y-1 nav-child">
<a href="#" class="sidebar-link sub-nav-link disabled group flex items-center px-3 py-2 text-sm font-medium rounded-md">设置与启动</a>
<a href="#" class="sidebar-link sub-nav-link disabled group flex items-center px-3 py-2 text-sm font-medium rounded-md">审核工作台</a>
<a href="#" class="sidebar-link sub-nav-link disabled group flex items-center px-3 py-2 text-sm font-medium rounded-md">复筛结果</a>
</div>
</div>
<a href="#" class="sidebar-link group flex items-center px-3 py-2 text-sm font-medium rounded-md text-gray-700">5. 全文解析与数据提取</a>
<a href="#" class="sidebar-link group flex items-center px-3 py-2 text-sm font-medium rounded-md text-gray-700">6. 数据综合分析与报告</a>
</nav>
</aside>
<!-- 主内容区 -->
<div class="main-content flex-grow flex flex-col w-full">
<header class="bg-white shadow-sm flex-shrink-0 z-10 p-4 border-b">
<h1 id="header-title" class="text-xl font-bold">标题摘要初筛 / 设置与启动</h1>
</header>
<div id="view-container" class="flex-grow p-6 overflow-auto">
<!-- 标题摘要初筛 - 设置与启动 -->
<div id="title-settings-view" class="view active">
<!-- content from v7, unchanged -->
<h2 class="text-2xl font-bold mb-4">标题摘要初筛 - 设置与启动</h2><div class="mb-6 bg-white p-6 rounded-lg shadow space-y-4"><div class="flex justify-between items-center"><h3 class="text-lg font-semibold">筛选标准 (来自研究方案)</h3><button class="text-sm text-sky-600 hover:underline" onclick="alert('弹出模态框允许临时调整标准')">[调整本次筛选标准]</button></div><div class="text-sm text-gray-600 grid grid-cols-2 gap-4 pt-4 border-t"><div class="space-y-1"><h4 class="font-bold">PICO 标准</h4><p>P: 2型糖尿病成人患者</p><p>I: SGLT2抑制剂</p><p>C: 安慰剂或其他常规降糖疗法</p><p>S: 随机对照试验 (RCT)</p></div><div class="space-y-1"><h4 class="font-bold">纳入/排除标准</h4><ul class="list-disc list-inside"><li class="text-green-600">纳入:英文</li><li class="text-red-600">排除:病例报告, 综述</li></ul></div></div></div><div class="bg-white p-6 rounded-lg shadow"><h3 class="text-lg font-semibold mb-4">导入文献</h3><div class="text-center p-6 border rounded-lg max-w-md mx-auto"><h4 class="font-bold text-lg mb-3">上传 Excel 文件</h4><p class="text-sm text-gray-500 mb-4">请按模板上传包含文献标题和摘要的列表。</p><div class="flex items-center justify-center space-x-2"><button onclick="importLiterature('excel')" class="bg-gray-200 hover:bg-gray-300 text-gray-800 font-bold py-2 px-4 rounded-lg">选择文件</button><a href="#" class="text-sky-600 hover:text-sky-800 font-semibold text-sm">下载模板</a></div></div><div class="mt-8 text-center"><button id="start-title-screening-btn" disabled class="bg-gray-400 text-white font-bold py-3 px-8 rounded-lg cursor-not-allowed">开始AI初筛</button><p id="title-import-hint" class="text-sm text-red-500 mt-2">请先导入文献</p></div></div>
</div>
<!-- 标题摘要初筛 - 审核工作台 -->
<div id="title-workbench-view" class="view">
<!-- content from v7, unchanged -->
<details class="bg-white rounded-lg shadow p-4 mb-6" open> <summary class="font-semibold cursor-pointer text-lg">▼ 查看当前筛选标准</summary><div class="mt-4 pt-4 border-t text-sm text-gray-600 grid grid-cols-2 gap-4"> <div class="space-y-1"><h4 class="font-bold">PICO 标准</h4><p>P: 2型糖尿病成人患者</p><p>I: SGLT2抑制剂</p><p>C: 安慰剂或其他常规降糖疗法</p><p>S: 随机对照试验 (RCT)</p></div><div class="space-y-1"><h4 class="font-bold">纳入/排除标准</h4><ul class="list-disc list-inside"><li class="text-green-600">纳入:英文</li><li class="text-red-600">排除:病例报告, 综述</li></ul><p id="title-criteria-adjustment-notice" class="hidden mt-2 text-orange-600 font-semibold">注意:本次筛选正在使用临时调整的标准。</p></div></div></details><div class="bg-white rounded-lg shadow overflow-x-auto"><table class="min-w-full divide-y divide-gray-200 text-sm"><thead class="bg-gray-50"><tr><th rowspan="2" class="px-2 py-3 w-8"></th><th rowspan="2" class="px-3 py-3 text-left font-medium text-gray-500 uppercase tracking-wider w-24">文献ID</th><th rowspan="2" class="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider w-32">研究ID</th><th rowspan="2" class="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider w-1/4">文献来源</th><th colspan="5" class="px-4 py-2 text-center font-medium text-gray-500 uppercase tracking-wider border-l border-r">DS 判断</th><th colspan="5" class="px-4 py-2 text-center font-medium text-gray-500 uppercase tracking-wider border-r">Q3 判断</th><th rowspan="2" class="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider">冲突状态</th><th rowspan="2" class="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider">最终决策</th></tr><tr><th class="px-2 py-2 font-medium text-gray-500 border-l">P</th><th class="px-2 py-2 font-medium text-gray-500">I</th><th class="px-2 py-2 font-medium text-gray-500">C</th><th class="px-2 py-2 font-medium text-gray-500">S</th><th class="px-2 py-2 font-medium text-gray-500 border-r">结论</th><th class="px-2 py-2 font-medium text-gray-500">P</th><th class="px-2 py-2 font-medium text-gray-500">I</th><th class="px-2 py-2 font-medium text-gray-500">C</th><th class="px-2 py-2 font-medium text-gray-500">S</th><th class="px-2 py-2 font-medium text-gray-500 border-r">结论</th></tr></thead><tbody id="title-screening-table-body" class="bg-white divide-y divide-gray-200"></tbody></table></div><div class="mt-6 flex justify-end"><button id="complete-title-screening-btn" onclick="showView('title-results')" class="bg-green-600 text-white font-bold py-2 px-6 rounded-lg shadow hover:bg-green-700">完成标题摘要初筛 (模拟)</button></div>
</div>
<!-- 标题摘要初筛 - 初筛结果 (V7 Enhanced & V4 Update) -->
<div id="title-results-view" class="view">
<h2 class="text-2xl font-bold mb-6">标题摘要初筛 - 结果</h2>
<div class="grid grid-cols-3 gap-6 mb-8 text-center">
<div class="bg-white p-4 rounded-lg shadow"><div class="text-3xl font-bold" id="title-total-count">1000</div><div class="text-gray-500">总计筛选</div></div>
<div class="bg-green-50 p-4 rounded-lg shadow"><div class="text-3xl font-bold text-green-600" id="title-included-count">120</div><div class="text-gray-500">初步纳入</div></div>
<div class="bg-red-50 p-4 rounded-lg shadow"><div class="text-3xl font-bold text-red-600" id="title-excluded-count">880</div><div class="text-gray-500">排除</div></div>
</div>
<div class="mb-8 bg-white p-6 rounded-lg shadow">
<h3 class="text-lg font-semibold mb-4">排除原因统计 (模拟)</h3>
<ul class="text-sm text-gray-600 space-y-1 list-disc list-inside">
<li>排除文献总数: n=<span id="prisma-total-excluded">880</span></li>
<li>非随机对照研究: n=<span id="prisma-s-excluded">250</span></li>
<li>非目标人群 (P): n=<span id="prisma-p-excluded">180</span></li>
<li>干预措施不符 (I): n=<span id="prisma-i-excluded">100</span></li>
<li>对照措施不符 (C): n=<span id="prisma-c-excluded">90</span></li>
<li>重复研究: n=<span id="prisma-duplicate-excluded">150</span></li>
<li>其他 (综述、病例报告等): n=<span id="prisma-other-excluded">110</span></li>
</ul>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="border-b border-gray-200 mb-4">
<nav id="title-result-tabs" class="-mb-px flex space-x-8" aria-label="Tabs">
<button data-tab="included" class="result-tab whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm border-sky-500 text-sky-600 active" onclick="switchResultTab('title', 'included')">初步纳入 (<span id="title-included-tab-count">120</span>)</button>
<button data-tab="excluded" class="result-tab whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300" onclick="switchResultTab('title', 'excluded')">排除 (<span id="title-excluded-tab-count">880</span>)</button>
</nav>
</div>
<div class="flex justify-between items-center mb-4">
<input type="text" id="title-result-search" placeholder="搜索文献..." class="w-1/3 p-2 border rounded text-sm">
<button class="bg-gray-700 hover:bg-gray-800 text-white font-bold py-2 px-4 rounded-lg text-sm">↓ 导出为Excel</button>
</div>
<div class="overflow-x-auto">
<!-- V4: Updated Results Table Header -->
<table class="min-w-full divide-y divide-gray-200 text-sm">
<thead class="bg-gray-50">
<tr>
<th class="px-3 py-3 text-left font-medium text-gray-500 uppercase tracking-wider">文献ID</th>
<th class="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider">研究ID</th>
<th class="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider">文献来源</th>
<th class="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider">标题</th>
<th class="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider">摘要</th>
<th class="px-2 py-3 text-center font-medium text-gray-500 uppercase tracking-wider">P</th>
<th class="px-2 py-3 text-center font-medium text-gray-500 uppercase tracking-wider">I</th>
<th class="px-2 py-3 text-center font-medium text-gray-500 uppercase tracking-wider">C</th>
<th class="px-2 py-3 text-center font-medium text-gray-500 uppercase tracking-wider">S</th>
<th class="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider">结论</th>
<th class="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider">排除理由</th>
</tr>
</thead>
<tbody id="title-results-table-body" class="bg-white divide-y divide-gray-200">
<!-- V4: Simulated Result Rows -->
</tbody>
</table>
</div>
</div>
</div>
<!-- Placeholder for Fulltext views, not functional in this prototype -->
<div id="fulltext-settings-view" class="view"></div>
<div id="fulltext-workbench-view" class="view"></div>
<div id="fulltext-results-view" class="view"></div>
</div>
</div>
</div>
<!-- 任务状态面板 (模态框) -->
<div id="task-status-modal" class="hidden fixed inset-0 z-50 flex items-center justify-center">
<!-- content from v7, unchanged -->
<div class="modal-backdrop absolute inset-0"></div><div class="bg-white rounded-lg shadow-xl p-8 w-full max-w-lg z-10"><h2 id="task-modal-title" class="text-2xl font-bold text-center mb-4">AI初筛进行中...</h2><div id="progress-bar" class="w-full bg-gray-200 rounded-full h-4 mb-4"><div id="progress-bar-inner" class="bg-sky-500 h-4 rounded-full" style="width: 0%"></div></div><p id="progress-text" class="text-center text-gray-500 mb-6">已处理 0 / 1000</p><button id="show-workbench-btn" class="hidden w-full bg-green-500 hover:bg-green-600 text-white font-bold py-3 px-6 rounded-lg">处理完成!进入审核工作台</button></div>
</div>
<!-- 双视图原文审查 (模态框 V7) -->
<div id="review-modal" class="hidden fixed inset-0 z-50 flex items-center justify-center p-8">
<!-- content from v7, unchanged -->
<div class="modal-backdrop absolute inset-0" onclick="closeReviewModal()"></div><div class="bg-white rounded-lg shadow-xl w-full max-w-6xl h-full flex flex-col z-10"><div class="flex justify-between items-center p-4 border-b"><h2 id="modal-title" class="text-xl font-bold">原文审查</h2><button onclick="closeReviewModal()" class="text-gray-500 hover:text-gray-800 text-2xl">&times;</button></div><div class="flex-grow flex overflow-hidden"><div class="w-1/2 p-6 overflow-y-auto border-r"><div id="modal-meta" class="mb-4 border-b pb-4"><h3 id="modal-doc-title" class="font-bold text-lg"></h3><p id="modal-doc-authors" class="text-sm text-gray-600"></p><p id="modal-doc-journal" class="text-sm text-gray-500"></p></div><h3 class="font-bold mb-2">摘要</h3><p id="modal-abstract" class="text-gray-700 leading-relaxed text-sm"></p></div><div class="w-1/2 p-6 overflow-y-auto bg-gray-50"><h3 id="modal-dimension-title" class="font-bold mb-4 text-lg">PICO(S) 判断依据</h3><div id="modal-reasoning" class="space-y-6"></div></div></div></div>
</div>
<script>
// --- STATE MANAGEMENT ---
let literatureData = [];
let simulatedResultsData = [];
let currentViewId = 'title-settings';
// --- DOM ELEMENTS ---
// (Simplified for brevity - assuming all elements defined in V7 are captured)
const views = {
'title-settings': document.getElementById('title-settings-view'),
'title-workbench': document.getElementById('title-workbench-view'),
'title-results': document.getElementById('title-results-view'),
};
const navLinks = {
'title-settings': document.getElementById('nav-title-settings'),
'title-workbench': document.getElementById('nav-title-workbench'),
'title-results': document.getElementById('nav-title-results'),
};
const headerTitle = document.getElementById('header-title');
const startTitleScreeningBtn = document.getElementById('start-title-screening-btn');
const titleImportHint = document.getElementById('title-import-hint');
const taskStatusModal = document.getElementById('task-status-modal');
const screeningTableBody = document.getElementById('title-screening-table-body');
const reviewModal = document.getElementById('review-modal');
const completeTitleScreeningBtn = document.getElementById('complete-title-screening-btn');
// Result page elements
const titleIncludedCount = document.getElementById('title-included-count');
const titleExcludedCount = document.getElementById('title-excluded-count');
const titleIncludedTabCount = document.getElementById('title-included-tab-count');
const titleExcludedTabCount = document.getElementById('title-excluded-tab-count');
const titleResultsTableBody = document.getElementById('title-results-table-body');
const titleTotalCount = document.getElementById('title-total-count');
const prismaTotalExcluded = document.getElementById('prisma-total-excluded');
const prismaSExcluded = document.getElementById('prisma-s-excluded');
const prismaPExcluded = document.getElementById('prisma-p-excluded');
const prismaIExcluded = document.getElementById('prisma-i-excluded');
const prismaCExcluded = document.getElementById('prisma-c-excluded');
const prismaDuplicateExcluded = document.getElementById('prisma-duplicate-excluded');
const prismaOtherExcluded = document.getElementById('prisma-other-excluded');
// --- DATA SIMULATION ---
function generateData() { /* ... unchanged V7 version ... */ literatureData = [];for (let i = 1; i <= 10; i++) { const conflict = Math.random() < 0.3; const model_a_p = Math.random() > 0.2 ? '✓' : '✗'; const model_a_i = Math.random() > 0.1 ? '✓' : '✗'; const model_a_c = Math.random() > 0.3 ? '✓' : '?'; const model_a_s = Math.random() > 0.1 ? '✓' : '✗'; let model_b_s = model_a_s; if (conflict && model_a_s === '✓') model_b_s = '✗'; else if (conflict && model_a_s === '✗') model_b_s = '✓'; const model_a_conclusion = [model_a_p, model_a_i, model_a_s].includes('✗') ? '排除' : '纳入'; const model_b_conclusion = [model_a_p, model_a_i, model_b_s].includes('✗') ? '排除' : '纳入'; const evidence_a = { p: "2型糖尿病成人患者", i: "SGLT2药物", c: "安慰剂", s: "RCT" }; const evidence_b = { p: "成人T2DM患者", i: "SGLT2抑制剂", c: "安慰剂组", s: "随机对照" }; let exclusion_reason = null; if (model_a_conclusion === '排除' || model_b_conclusion === '排除') { const reasons = ["非RCT", "非目标人群", "干预不符", "对照不符", "综述", "重复研究"]; exclusion_reason = reasons[Math.floor(Math.random() * reasons.length)]; } literatureData.push({ id: i, pmid: `PMID${1234500 + i}`, study_id: `AuthorA${i} ${2020 + i}`, source: `J Med ${2020 + i};${10 + i}(${i}):e${123 + i}`, title: `${i}篇研究: SGLT2抑制剂对2型糖尿病心血管结局的影响`, authors: `Author A${i}, Author B${i}`, journal: `Journal of Medicine ${2020 + i}`, abstract: `背景: SGLT2抑制剂是一类新型降糖药物。本研究旨在评估其在<span class='p-source'>2型糖尿病成人患者</span>中的心血管保护作用。方法: 我们进行了一项多中心、双盲、<span class='s-source'>随机对照试验(RCT)</span>纳入了7020名患有心血管疾病的2型糖尿病患者。患者被随机分配接受每日10mg或25mg的<span class='i-source'>SGLT2药物</span>或<span class='c-source'>安慰剂</span>治疗。主要终点为主要心血管不良事件(MACE)。结论: 与安慰剂相比SGLT2抑制剂显著降低了MACE风险。`, model_a: { p: model_a_p, i: model_a_i, c: model_a_c, s: model_a_s, conclusion: model_a_conclusion, p_reason: "摘要明确提到研究对象为'2型糖尿病成人患者'。", i_reason: "干预措施为'SGLT2药物',符合要求。", c_reason: "对照组为'安慰剂',符合标准。", s_reason: "研究设计明确说明为'随机对照试验(RCT)'。", evidence: evidence_a }, model_b: { p: model_a_p, i: model_a_i, c: model_a_c, s: model_b_s, conclusion: model_b_conclusion, p_reason: "文中确认患者群体为'2型糖尿病成人'。", i_reason: "治疗方案包含'SGLT2药物'。", c_reason: "对照组为'安慰剂'。", s_reason: model_b_s === '✓' ? "方法部分描述了'随机对照试验'。" : "研究方法非RCT。", evidence: evidence_b }, final_decision: '待定', decision_method: '待定', exclusion_reason: exclusion_reason });}}
// V3: Generate separate simulated data for the results page
function generateSimulatedResultsData() {
simulatedResultsData = [];
const totalSimulated = 1000;
const includedSimulated = 120;
for (let i = 1; i <= totalSimulated; i++) {
const isIncluded = i <= includedSimulated;
const final_decision = isIncluded ? '纳入' : '排除';
const decision_method = Math.random() > 0.7 ? '人工审核' : (isIncluded ? 'AI自动纳入' : 'AI自动排除'); // More specific method
let exclusion_reason = null;
if (!isIncluded) {
const reasons = ["非RCT", "非目标人群", "干预不符", "对照不符", "综述", "重复研究", "语言不符"];
exclusion_reason = reasons[Math.floor(Math.random() * reasons.length)];
}
// Simulate PICS content based on decision (more realistic)
const sim_p = (isIncluded || Math.random() > 0.2) ? "2型糖尿病成人患者" : "1型糖尿病";
const sim_i = (isIncluded || Math.random() > 0.1) ? "SGLT2抑制剂" : "二甲双胍";
const sim_c = (isIncluded || Math.random() > 0.3) ? "安慰剂" : "生活方式干预";
const sim_s = (isIncluded || Math.random() > 0.1) ? "RCT" : "观察性研究";
simulatedResultsData.push({
id: i + 1000,
pmid: `PMID${2234500 + i}`,
study_id: `SimAuthor${i} ${2021}`,
source: `Sim J ${2021};${i % 10 + 1}:s${i}`,
title: `模拟文献 ${i}: 关于SGLT2的研究`,
abstract: `这是一个模拟的摘要内容,长度可能会比较长,用于演示结果页面的摘要展示和展开收起功能...模拟摘要结束。`,
final_decision: final_decision,
decision_method: decision_method,
exclusion_reason: exclusion_reason,
// V4: Store simulated PICS content
sim_p: sim_p, sim_i: sim_i, sim_c: sim_c, sim_s: sim_s
});
}
}
// --- INITIALIZATION ---
document.addEventListener('DOMContentLoaded', () => {
generateData(); // For workbench
generateSimulatedResultsData(); // V3: For results page
showView('title-settings');
});
// --- NAVIGATION & VIEW MANAGEMENT ---
Object.keys(navLinks).forEach(key => { /* ... unchanged ... */ navLinks[key].addEventListener('click', (e) => { e.preventDefault(); const viewId = navLinks[key].getAttribute('data-view'); showView(viewId); }); });
function showView(viewId) {
currentViewId = viewId;
Object.values(views).forEach(v => v.classList.remove('active'));
if (views[viewId]) {
views[viewId].classList.add('active');
} else {
console.error("View not found:", viewId);
views['title-settings'].classList.add('active');
viewId = 'title-settings';
}
const stage = 'title';
const subView = viewId.split('-')[1];
const stageTitles = { 'title': '标题摘要初筛' };
const subViewTitles = { 'settings': '设置与启动', 'workbench': '审核工作台', 'results': '初筛结果' };
headerTitle.textContent = `${stageTitles[stage]} / ${subViewTitles[subView]}`;
Object.values(navLinks).forEach(link => link.classList.remove('active'));
if (navLinks[viewId]) navLinks[viewId].classList.add('active');
// V3: Always render results page with simulated data when viewed
if (viewId === 'title-results') {
renderResultsPage('title');
}
}
// --- TITLE SCREENING ---
function importLiterature(source) { /* ... unchanged ... */ startTitleScreeningBtn.disabled = false; startTitleScreeningBtn.classList.remove('bg-gray-400', 'cursor-not-allowed'); startTitleScreeningBtn.classList.add('bg-green-600', 'hover:bg-green-700'); titleImportHint.classList.add('hidden'); alert("模拟导入10篇文献成功"); literatureData.forEach(d=>d.final_decision='待定'); }
startTitleScreeningBtn.addEventListener('click', () => { /* ... unchanged ... */ taskStatusModal.classList.remove('hidden'); document.getElementById('task-modal-title').textContent = 'AI标题摘要初筛进行中...'; let progress = 0; const interval = setInterval(() => { progress += 10; document.getElementById('progress-bar-inner').style.width = `${progress}%`; document.getElementById('progress-text').textContent = `已处理 ${progress * (literatureData.length / 10)} / ${literatureData.length}`; if (progress >= 100) { clearInterval(interval); document.getElementById('show-workbench-btn').classList.remove('hidden'); } }, 100); });
document.getElementById('show-workbench-btn').addEventListener('click', () => {
taskStatusModal.classList.add('hidden');
showView('title-workbench');
renderTable('title');
});
// V7: Render Table with Original Header Structure
function renderTable(stage) {
const tableBodyId = 'title-screening-table-body'; // Only title stage
const tableBody = document.getElementById(tableBodyId);
if (!tableBody) return;
const dataToRender = literatureData; // Workbench uses the interactive data
tableBody.innerHTML = dataToRender.map(doc => {
const isConflict = doc.model_a.conclusion !== doc.model_b.conclusion;
const rowId = `${stage}-row-${doc.id}`;
const expandedRowId = `${stage}-expanded-row-${doc.id}`;
const judgeCell = (model, dim) => `<td class="table-cell-judge px-2 py-3 border-l" onclick="openReviewModal(${doc.id}, '${model}', '${dim}', '${stage}')">${{ '✓': '<span class="text-green-600 font-bold">✓</span>', '✗': '<span class="text-red-600 font-bold">✗</span>', '?': '<span class="text-yellow-600 font-bold">?</span>' }[doc[model][dim]]}</td>`;
return `
<tr id="${rowId}" class="${isConflict ? 'table-conflict-row' : ''}">
<td class="px-2 py-3 expand-row-btn" onclick="toggleRow('${expandedRowId}', this)">+</td>
<td class="px-3 py-3 font-mono text-xs text-gray-500">${doc.pmid}</td>
<td class="px-4 py-3 font-semibold text-xs">${doc.study_id}</td>
<td class="px-4 py-3 text-xs text-gray-500 truncate" title="${doc.source}">${doc.source.split('.')[0]}...</td>
<!-- DS Judgements -->
${judgeCell('model_a', 'p')} ${judgeCell('model_a', 'i')} ${judgeCell('model_a', 'c')} ${judgeCell('model_a', 's')}
<td class="px-2 py-3 font-semibold border-r ${doc.model_a.conclusion === '纳入' ? 'text-green-700' : 'text-red-700'}">${doc.model_a.conclusion}</td>
<!-- Q3 Judgements -->
${judgeCell('model_b', 'p')} ${judgeCell('model_b', 'i')} ${judgeCell('model_b', 'c')} ${judgeCell('model_b', 's')}
<td class="px-2 py-3 font-semibold border-r ${doc.model_b.conclusion === '纳入' ? 'text-green-700' : 'text-red-700'}">${doc.model_b.conclusion}</td>
<!-- Status & Decision -->
<td class="px-4 py-3 border-l">${isConflict ? '<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">冲突</span>' : '<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">一致</span>'}</td>
<td class="px-4 py-3 border-l"><select class="rounded-md border-gray-300 text-xs" data-id="${doc.id}" data-stage="${stage}" onchange="updateDecision(this)"><option value="待定" ${doc.final_decision === '待定' ? 'selected' : ''}>待定</option><option value="纳入" ${doc.final_decision === '纳入' ? 'selected' : ''}>纳入</option><option value="排除" ${doc.final_decision === '排除' ? 'selected' : ''}>排除</option></select></td>
</tr>
<tr id="${expandedRowId}" class="expanded-row">
<td colspan="15" class="expanded-row-cell text-xs">
<div class="grid grid-cols-2 gap-4">
<div><strong class="text-gray-600">DS 证据:</strong><dl class="mt-1 evidence-grid"><dt>P:</dt><dd>${doc.model_a.evidence.p}</dd><dt>I:</dt><dd>${doc.model_a.evidence.i}</dd><dt>C:</dt><dd>${doc.model_a.evidence.c}</dd><dt>S:</dt><dd>${doc.model_a.evidence.s}</dd></dl></div>
<div><strong class="text-gray-600">Q3 证据:</strong><dl class="mt-1 evidence-grid"><dt>P:</dt><dd>${doc.model_b.evidence.p}</dd><dt>I:</dt><dd>${doc.model_b.evidence.i}</dd><dt>C:</dt><dd>${doc.model_b.evidence.c}</dd><dt>S:</dt><dd>${doc.model_b.evidence.s}</dd></dl></div>
</div>
</td>
</tr>
`;
}).join('');
}
function toggleRow(rowId, btn) { /* ... unchanged ... */ const row = document.getElementById(rowId); const isShown = row.classList.toggle('shown'); btn.textContent = isShown ? '-' : '+'; }
function updateDecision(selectElement) { /* ... unchanged ... */ const docId = parseInt(selectElement.dataset.id); const stage = selectElement.dataset.stage; const dataSet = literatureData; /* Only title data here */ const doc = dataSet.find(d => d.id === docId); if (doc) { doc.final_decision = selectElement.value; if (doc.final_decision !== '待定') { doc.decision_method = '人工审核'; } else { doc.decision_method = '待定'; } } }
function openReviewModal(docId, modelKey, dimensionKey, stage) { /* ... unchanged V7 version ... */ const dataSet = literatureData; /* Only title data */ const doc = dataSet.find(d => d.id === docId); if (!doc) return; const dimensionMap = { p: '人群 (P)', i: '干预 (I)', c: '对照 (C)', s: '研究设计 (S)' }; document.getElementById('modal-title').textContent = `审查: ${doc.title}`; document.getElementById('modal-doc-title').textContent = doc.title; document.getElementById('modal-doc-authors').textContent = doc.authors; document.getElementById('modal-doc-journal').textContent = `${doc.journal} (${doc.pmid})`; const abstractEl = document.getElementById('modal-abstract'); let abstractContent = doc.abstract; const sourceSpan = doc.abstract.match(new RegExp(`<span class='${dimensionKey.toLowerCase()}-source'>(.*?)</span>`)); let sourceText = sourceSpan ? sourceSpan[1] : ""; abstractContent = abstractContent.replace(/<span class='.-source'>/g, '').replace(/<\/span>/g, ''); if (sourceText) { abstractContent = abstractContent.replace(sourceText, `<mark class="abstract-highlight">${sourceText}</mark>`); } abstractEl.innerHTML = abstractContent; setTimeout(() => { const mark = abstractEl.querySelector('mark'); if (mark) { mark.scrollIntoView({ behavior: 'smooth', block: 'center' }); } }, 100); document.getElementById('modal-dimension-title').textContent = `PICO(S) 判断依据`; const reasoningContainer = document.getElementById('modal-reasoning'); let reasoningHTML = `<div class="grid grid-cols-2 gap-6">`; ['model_a', 'model_b'].forEach(mKey => { const modelName = mKey === 'model_a' ? 'DeepSeek' : 'Qwen3'; reasoningHTML += `<div class="border rounded-lg p-4 bg-white"><h4 class="font-bold text-gray-700 mb-3">${modelName} 判断</h4><div class="space-y-2 text-sm mb-3">`; ['p', 'i', 'c', 's'].forEach(dim => { const icon = { '✓': '<span class="text-green-600 font-bold">✓ 符合</span>', '✗': '<span class="text-red-600 font-bold">✗ 不符</span>', '?': '<span class="text-yellow-600 font-bold">? 不确定</span>' }[doc[mKey][dim]]; reasoningHTML += `<p><strong>${dimensionMap[dim]}:</strong> ${icon}</p>`; }); reasoningHTML += `</div><p class="text-sm mb-2"><strong>总览结论:</strong> <span class="font-semibold ${doc[mKey].conclusion === '纳入' ? 'text-green-700' : 'text-red-700'}">${doc[mKey].conclusion}</span></p><p class="text-sm text-gray-600"><strong>"${dimensionMap[dimensionKey]}" 判断理由:</strong> ${doc[mKey][`${dimensionKey}_reason`]}</p></div>`; }); reasoningHTML += `</div>`; reasoningContainer.innerHTML = reasoningHTML; reviewModal.classList.remove('hidden'); }
function closeReviewModal() { reviewModal.classList.add('hidden'); }
// V3: Simplified button action - just go to results view
completeTitleScreeningBtn.addEventListener('click', () => {
showView('title-results');
});
// --- TITLE RESULTS PAGE (V7 Enhanced & V3 Independent Data) ---
function renderResultsPage(stage) {
if (stage !== 'title') return;
// V3: Use simulated data for counts and table
const included = simulatedResultsData.filter(doc => doc.final_decision === '纳入');
const excluded = simulatedResultsData.filter(doc => doc.final_decision === '排除');
const totalSimulated = simulatedResultsData.length;
titleTotalCount.textContent = totalSimulated;
titleIncludedCount.textContent = included.length;
titleExcludedCount.textContent = excluded.length;
titleIncludedTabCount.textContent = included.length;
titleExcludedTabCount.textContent = excluded.length;
// V7: Prisma Summary Simulation based on simulated excluded data
prismaTotalExcluded.textContent = excluded.length;
prismaSExcluded.textContent = excluded.filter(d => d.exclusion_reason === '非RCT').length;
prismaPExcluded.textContent = excluded.filter(d => d.exclusion_reason === '非目标人群').length;
prismaIExcluded.textContent = excluded.filter(d => d.exclusion_reason === '干预不符').length;
prismaCExcluded.textContent = excluded.filter(d => d.exclusion_reason === '对照不符').length;
prismaDuplicateExcluded.textContent = excluded.filter(d => d.exclusion_reason === '重复研究').length;
prismaOtherExcluded.textContent = excluded.filter(d => !["非RCT", "非目标人群", "干预不符", "对照不符", "重复研究"].includes(d.exclusion_reason)).length;
renderResultsTable('title', 'included'); // Default view uses simulated data now
}
function switchResultTab(stage, tab) {
if (stage !== 'title') return;
const tabContainerId = 'title-result-tabs';
document.querySelectorAll(`#${tabContainerId} button`).forEach(btn => { btn.classList.remove('active', 'border-sky-500', 'text-sky-600'); btn.classList.add('border-transparent', 'text-gray-500'); });
const activeBtn = document.querySelector(`#${tabContainerId} button[data-tab="${tab}"]`);
activeBtn.classList.add('active', 'border-sky-500', 'text-sky-600');
activeBtn.classList.remove('border-transparent', 'text-gray-500');
renderResultsTable('title', tab); // Will use simulated data
}
// V7: Render Enhanced Results Table (Now uses simulatedResultsData)
// V4 Update: Display content instead of icons for PICS
function renderResultsTable(stage, tab) {
if (stage !== 'title') return;
const tableBodyId = 'title-results-table-body';
const tableBody = document.getElementById(tableBodyId);
if (!tableBody) return;
const dataSet = simulatedResultsData;
const filteredData = dataSet.filter(doc => doc.final_decision === (tab === 'included' ? '纳入' : '排除'));
tableBody.innerHTML = filteredData.map(doc => {
// V4: Get content for PICS instead of icons
const pContent = doc.sim_p === '✓' ? "2型糖尿病成人患者" : "非目标人群";
const iContent = doc.sim_i === '✓' ? "SGLT2抑制剂" : "非SGLT2";
const cContent = doc.sim_c === '✓' ? "安慰剂" : (doc.sim_c === '?' ? "不适用/未报告" : "非安慰剂");
const sContent = doc.sim_s === '✓' ? "RCT" : "非RCT";
const shortAbstract = doc.abstract.substring(0, 100) + '...';
const fullAbstract = doc.abstract;
return `<tr>
<td class="px-3 py-3 font-mono text-xs">${doc.pmid}</td>
<td class="px-4 py-3 text-xs">${doc.study_id}</td>
<td class="px-4 py-3 text-xs truncate" title="${doc.source}">${doc.source}</td>
<td class="px-4 py-3 text-xs font-medium">${doc.title}</td>
<td class="px-4 py-3 text-xs text-gray-500 abstract-cell">
<span class="short-abstract">${shortAbstract}</span>
<span class="full-abstract">${fullAbstract}</span>
<a href="#" class="text-sky-600 text-[10px] block mt-1" onclick="toggleAbstract(this, event)">显示更多</a>
</td>
<td class="px-2 py-3 text-center results-pics-cell">${pContent}</td>
<td class="px-2 py-3 text-center results-pics-cell">${iContent}</td>
<td class="px-2 py-3 text-center results-pics-cell">${cContent}</td>
<td class="px-2 py-3 text-center results-pics-cell">${sContent}</td>
<td class="px-4 py-3">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${doc.final_decision === '纳入' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}">${doc.final_decision}</span>
</td>
<td class="px-4 py-3 text-xs text-gray-500">${doc.final_decision === '排除' ? (doc.exclusion_reason || '未指定') : 'N/A'}</td>
</tr>`;
}).join('');
}
// V7: Toggle Abstract in results table
function toggleAbstract(link, event) { /* ... unchanged ... */ event.preventDefault(); const cell = link.closest('.abstract-cell'); const isExpanded = cell.classList.toggle('expanded'); link.textContent = isExpanded ? '收起' : '显示更多'; }
// Remove fulltext related functions as they are not needed in this prototype
</script>
</body>
</html>