- 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
451 lines
40 KiB
HTML
451 lines
40 KiB
HTML
<!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">×</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>
|