- 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
525 lines
45 KiB
HTML
525 lines
45 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>全文复筛原型 V2</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; }
|
|
.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>
|
|
|
|
<!-- Title Screening Section (Not active) -->
|
|
<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="#" class="sidebar-link sub-nav-link group flex items-center px-3 py-2 text-sm font-medium rounded-md">设置与启动</a>
|
|
<a href="#" class="sidebar-link sub-nav-link group flex items-center px-3 py-2 text-sm font-medium rounded-md">审核工作台</a>
|
|
<a href="#" 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 (Active) -->
|
|
<div class="space-y-1">
|
|
<span class="nav-parent group flex items-center px-3 py-2 text-sm rounded-md text-gray-800">4. 全文复筛</span>
|
|
<div class="space-y-1 nav-child">
|
|
<a href="#" id="nav-fulltext-settings" data-view="fulltext-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-fulltext-workbench" data-view="fulltext-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-fulltext-results" data-view="fulltext-results" class="sidebar-link sub-nav-link 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">
|
|
<!-- Placeholder for Title views, not functional -->
|
|
<div id="title-settings-view" class="view"></div>
|
|
<div id="title-workbench-view" class="view"></div>
|
|
<div id="title-results-view" class="view"></div>
|
|
|
|
<!-- 全文复筛 - 设置与启动 -->
|
|
<div id="fulltext-settings-view" class="view active">
|
|
<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><p id="fulltext-criteria-adjustment-notice" class="hidden mt-2 text-orange-600 font-semibold">注意:本次筛选正在使用临时调整的标准。</p></div></div>
|
|
</div>
|
|
|
|
<div id="fulltext-independent-start" class="hidden bg-white p-6 rounded-lg shadow mb-6">
|
|
<h3 class="text-lg font-semibold mb-4">导入待复筛文献</h3>
|
|
<div class="grid grid-cols-2 gap-6">
|
|
<div class="text-center p-6 border rounded-lg">
|
|
<h4 class="font-bold text-lg mb-3">上传 Excel 文件</h4>
|
|
<p class="text-sm text-gray-500 mb-4">上传包含文献ID和标题的列表。</p>
|
|
<div class="flex items-center justify-center space-x-2"><button 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="text-center p-6 border rounded-lg">
|
|
<h4 class="font-bold text-lg mb-3">从知识库添加</h4>
|
|
<p class="text-sm text-gray-500 mb-4">从您已有的知识库中选择文献。</p>
|
|
<button onclick="addFromKB()" class="bg-gray-200 hover:bg-gray-300 text-gray-800 font-bold py-2 px-4 rounded-lg">选择文献</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="fulltext-acquisition-step">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h3 class="text-lg font-semibold">全文获取与管理 (<span id="fulltext-count">0</span>篇)</h3>
|
|
<button onclick="addFromKB()" class="bg-blue-100 text-blue-700 font-semibold py-2 px-4 rounded-lg text-sm hover:bg-blue-200">+ 从知识库添加</button>
|
|
</div>
|
|
<div class="bg-white rounded-lg shadow overflow-hidden mb-6">
|
|
<table class="min-w-full divide-y divide-gray-200 text-sm">
|
|
<thead class="bg-gray-50"><tr><th class="px-4 py-3 text-left font-medium text-gray-500 uppercase">文献ID</th><th class="px-4 py-3 text-left font-medium text-gray-500 uppercase">文献标题</th><th class="px-4 py-3 text-left font-medium text-gray-500 uppercase">获取状态</th><th class="px-4 py-3 text-left font-medium text-gray-500 uppercase">操作</th></tr></thead>
|
|
<tbody id="fulltext-list-body"></tbody>
|
|
</table>
|
|
</div>
|
|
<div class="text-center">
|
|
<!-- V2: Button always enabled for prototype demonstration -->
|
|
<button id="start-fulltext-screening-btn" class="bg-green-600 hover:bg-green-700 text-white font-bold py-3 px-8 rounded-lg shadow">开始全文复筛</button>
|
|
<p id="fulltext-ready-hint" class="hidden text-sm text-red-500 mt-2">(实际应用中需确保所有文献全文均已就绪)</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 全文复筛 - 审核工作台 -->
|
|
<div id="fulltext-workbench-view" class="view">
|
|
<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="fulltext-criteria-adjustment-notice-workbench" 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="fulltext-screening-table-body" class="bg-white divide-y divide-gray-200"></tbody>
|
|
</table>
|
|
</div>
|
|
<div class="mt-6 flex justify-end">
|
|
<button onclick="completeFulltextScreening()" class="bg-green-600 text-white font-bold py-2 px-6 rounded-lg shadow hover:bg-green-700">完成全文复筛 (模拟)</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 全文复筛 - 复筛结果 -->
|
|
<div id="fulltext-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="fulltext-reviewed-count">100</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="fulltext-included-count">55</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="fulltext-excluded-count">45</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="fulltext-prisma-total-excluded">45</span></li>
|
|
<li>非随机对照研究: n=<span id="fulltext-prisma-s-excluded">5</span></li>
|
|
<li>非目标人群 (P): n=<span id="fulltext-prisma-p-excluded">7</span></li>
|
|
<li>干预/对照不符 (I/C): n=<span id="fulltext-prisma-ic-excluded">18</span></li>
|
|
<li>结局指标不符 (O): n=<span id="fulltext-prisma-o-excluded">9</span></li>
|
|
<li>其他原因 (如数据不全): n=<span id="fulltext-prisma-other-excluded">6</span></li>
|
|
</ul>
|
|
</div>
|
|
<div class="bg-white rounded-lg shadow p-6">
|
|
<div class="border-b border-gray-200 mb-4">
|
|
<nav id="fulltext-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('fulltext', 'included')">最终纳入 (<span id="fulltext-included-tab-count">55</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('fulltext', 'excluded')">排除 (<span id="fulltext-excluded-tab-count">45</span>)</button>
|
|
</nav>
|
|
</div>
|
|
<div class="flex justify-between items-center mb-4">
|
|
<input type="text" id="fulltext-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">
|
|
<table id="fulltext-results-table" class="min-w-full divide-y divide-gray-200 text-sm">
|
|
<!-- Fix: Add ID to thead -->
|
|
<thead id="fulltext-results-table-head" class="bg-gray-50">
|
|
<!-- Header row dynamically generated -->
|
|
</thead>
|
|
<tbody id="fulltext-results-table-body" class="bg-white divide-y divide-gray-200">
|
|
<!-- V2: Simulated Result Rows -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 任务状态面板 (模态框) -->
|
|
<div id="task-status-modal" class="hidden fixed inset-0 z-50 flex items-center justify-center">
|
|
<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 / 0</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">
|
|
<div class="modal-backdrop absolute inset-0" onclick="closeReviewModal()"></div><div class="bg-white rounded-lg shadow-xl w-full max-w-6xl h-[90vh] 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 id="modal-text-source-title" class="font-bold mb-2">摘要/全文</h3><div id="modal-text-content" class="text-gray-700 leading-relaxed text-sm prose max-w-none"></div></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 ---
|
|
// Using placeholder data assuming title screening produced some results
|
|
let fulltextDocs = [
|
|
{ id: 1, pmid: `PMID1234501`, study_id: `AuthorA1 2021`, source: `J Med 2021;11(1):e124`, title: `第1篇研究...`, final_decision: '待定', decision_method: '待定', pdf_status: undefined, model_a: {p: '✓', i: '✓', c:'✓', s:'✓', conclusion: '纳入', evidence: {p:'成人',i:'药物A',c:'安慰剂',s:'RCT'}, p_reason:"...", i_reason:"...", c_reason:"...", s_reason:"..."}, model_b: {p: '✓', i: '✓', c:'✓', s:'✓', conclusion: '纳入', evidence: {p:'成人',i:'药物A',c:'安慰剂',s:'RCT'}, p_reason:"...", i_reason:"...", c_reason:"...", s_reason:"..."}, exclusion_reason: null},
|
|
{ id: 3, pmid: `PMID1234503`, study_id: `AuthorA3 2023`, source: `Lancet 2023;3(1):e125`, title: `第3篇研究...`, final_decision: '待定', decision_method: '待定', pdf_status: undefined, model_a: {p: '✓', i: '✓', c:'?', s:'✓', conclusion: '纳入', evidence: {p:'成人',i:'药物A',c:'常规疗法',s:'RCT'}, p_reason:"...", i_reason:"...", c_reason:"...", s_reason:"..."}, model_b: {p: '✓', i: '✓', c:'?', s:'✓', conclusion: '纳入', evidence: {p:'成人',i:'药物A',c:'常规疗法',s:'RCT'}, p_reason:"...", i_reason:"...", c_reason:"...", s_reason:"..."} , exclusion_reason: null},
|
|
{ id: 5, pmid: `PMID1234505`, study_id: `AuthorA5 2025`, source: `BMJ 2025;5(1):e126`, title: `第5篇研究...`, final_decision: '待定', decision_method: '待定', pdf_status: undefined, model_a: {p: '✓', i: '✓', c:'✓', s:'✗', conclusion: '排除', evidence: {p:'成人',i:'药物A',c:'安慰剂',s:'观察性'}, p_reason:"...", i_reason:"...", c_reason:"...", s_reason:"..."}, model_b: {p: '✓', i: '✓', c:'✓', s:'✓', conclusion: '纳入', evidence: {p:'成人',i:'药物A',c:'安慰剂',s:'RCT'}, p_reason:"...", i_reason:"...", c_reason:"...", s_reason:"..."} , exclusion_reason: "非RCT"},
|
|
{ id: 7, pmid: `PMID1234507`, study_id: `AuthorA7 2022`, source: `Diabetes Care 2022;45(2):e127`, title: `第7篇研究...`, final_decision: '待定', decision_method: '待定', pdf_status: undefined, model_a: {p: '✓', i: '✓', c:'✓', s:'✓', conclusion: '纳入', evidence: {p:'成人T2DM',i:'药物A',c:'安慰剂',s:'RCT'}}, model_b: {p: '✓', i: '✓', c:'✓', s:'✓', conclusion: '纳入', evidence: {p:'成人T2DM',i:'药物A',c:'安慰剂',s:'RCT'}} , exclusion_reason: null},
|
|
{ id: 9, pmid: `PMID1234509`, study_id: `AuthorA9 2024`, source: `JAMA 2024;331(3):e128`, title: `第9篇研究...`, final_decision: '待定', decision_method: '待定', pdf_status: undefined, model_a: {p: '✗', i: '✓', c:'✓', s:'✓', conclusion: '排除', evidence: {p:'青少年',i:'药物A',c:'安慰剂',s:'RCT'}}, model_b: {p: '✗', i: '✓', c:'✓', s:'✓', conclusion: '排除', evidence: {p:'青少年',i:'药物A',c:'安慰剂',s:'RCT'}} , exclusion_reason: "非目标人群"},
|
|
];
|
|
let currentViewId = 'fulltext-settings'; // Start at fulltext settings
|
|
let simulatedFulltextResults = []; // For results page simulation
|
|
|
|
// --- DOM ELEMENTS ---
|
|
const views = {};
|
|
const navLinks = {};
|
|
document.querySelectorAll('.view').forEach(v => {
|
|
if(v.id.includes('fulltext')) { views[v.id.replace('-view','')] = v; }
|
|
});
|
|
document.querySelectorAll('.sub-nav-link').forEach(l => {
|
|
if(l.id.includes('fulltext')) { navLinks[l.id.replace('nav-','')] = l; }
|
|
});
|
|
const headerTitle = document.getElementById('header-title');
|
|
const taskStatusModal = document.getElementById('task-status-modal');
|
|
const reviewModal = document.getElementById('review-modal');
|
|
const fulltextAcquisitionStep = document.getElementById('fulltext-acquisition-step');
|
|
const fulltextListBody = document.getElementById('fulltext-list-body');
|
|
const startFulltextScreeningBtn = document.getElementById('start-fulltext-screening-btn');
|
|
const fulltextIndependentStart = document.getElementById('fulltext-independent-start');
|
|
const fulltextReadyHint = document.getElementById('fulltext-ready-hint');
|
|
const fulltextScreeningTableBody = document.getElementById('fulltext-screening-table-body');
|
|
const fulltextReviewedCount = document.getElementById('fulltext-reviewed-count');
|
|
const fulltextIncludedCount = document.getElementById('fulltext-included-count');
|
|
const fulltextExcludedCount = document.getElementById('fulltext-excluded-count');
|
|
const fulltextIncludedTabCount = document.getElementById('fulltext-included-tab-count');
|
|
const fulltextExcludedTabCount = document.getElementById('fulltext-excluded-tab-count');
|
|
const fulltextResultsTableBody = document.getElementById('fulltext-results-table-body');
|
|
const ftPrismaTotalExcluded = document.getElementById('fulltext-prisma-total-excluded');
|
|
const ftPrismaSExcluded = document.getElementById('fulltext-prisma-s-excluded');
|
|
const ftPrismaPExcluded = document.getElementById('fulltext-prisma-p-excluded');
|
|
const ftPrismaICExcluded = document.getElementById('fulltext-prisma-ic-excluded');
|
|
const ftPrismaOExcluded = document.getElementById('fulltext-prisma-o-excluded');
|
|
const ftPrismaOtherExcluded = document.getElementById('fulltext-prisma-other-excluded');
|
|
|
|
// --- SIMULATED DATA ---
|
|
function generateSimulatedFulltextResultsData() { /* ... unchanged V2 version ... */ simulatedFulltextResults = []; const totalSimulated = 100; const includedSimulated = 55; for (let i = 1; i <= totalSimulated; i++) { const isIncluded = i <= includedSimulated; const final_decision = isIncluded ? '纳入' : '排除'; const decision_method = Math.random() > 0.6 ? '人工审核' : (isIncluded ? 'AI全文纳入' : 'AI全文排除'); let exclusion_reason = null; if (!isIncluded) { const reasons = ["非RCT", "非目标人群", "干预/对照不符", "结局指标不符", "数据不全"]; exclusion_reason = reasons[Math.floor(Math.random() * reasons.length)]; } simulatedFulltextResults.push({ id: i + 3000, pmid: `PMID${3234500 + i}`, study_id: `FullSim${i} ${2022}`, source: `Full J ${2022};${i % 10 + 1}:f${i}`, title: `模拟全文文献 ${i}: 进一步研究`, final_decision: final_decision, decision_method: decision_method, exclusion_reason: exclusion_reason, }); } }
|
|
|
|
// --- INITIALIZATION ---
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
document.getElementById('fulltext-count').textContent = fulltextDocs.length;
|
|
renderFulltextList();
|
|
generateSimulatedFulltextResultsData();
|
|
showView('fulltext-settings');
|
|
});
|
|
|
|
// --- NAVIGATION & VIEW MANAGEMENT ---
|
|
Object.keys(navLinks).forEach(key => {
|
|
navLinks[key].addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
const viewId = navLinks[key].getAttribute('data-view');
|
|
// V2: Allow navigating to results anytime for demo
|
|
// if(navLinks[key].classList.contains('disabled')) return;
|
|
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['fulltext-settings'].classList.add('active'); // Fallback
|
|
viewId = 'fulltext-settings';
|
|
}
|
|
|
|
const stage = 'fulltext';
|
|
const subView = viewId.split('-')[1];
|
|
|
|
const stageTitles = { 'fulltext': '全文复筛' };
|
|
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');
|
|
|
|
if (viewId === 'fulltext-settings') {
|
|
renderFulltextList();
|
|
}
|
|
|
|
// V2: Render results page whenever navigated to
|
|
if (viewId === 'fulltext-results') renderResultsPage('fulltext');
|
|
}
|
|
|
|
// --- FULLTEXT SCREENING ---
|
|
function renderFulltextList() {
|
|
fulltextListBody.innerHTML = fulltextDocs.map((doc, index) => {
|
|
let status, action;
|
|
if (!doc.pdf_status) {
|
|
const statusRand = Math.random();
|
|
if (statusRand < 0.6) doc.pdf_status = 'ready';
|
|
else if (statusRand < 0.8) doc.pdf_status = 'fetching';
|
|
else doc.pdf_status = 'failed';
|
|
}
|
|
switch(doc.pdf_status) {
|
|
case 'ready': status = `<span class="text-green-600 font-semibold">获取成功</span>`; action = `<span class="text-gray-400">已就绪</span>`; break;
|
|
case 'fetching': status = `<span class="text-blue-500">获取中...</span>`; action = `<span class="text-gray-400">请稍候</span>`; break;
|
|
default: status = `<span class="text-red-600 font-semibold">获取失败</span>`; action = `<button class="text-sky-600 hover:underline" onclick="uploadFulltext(${doc.id})">上传全文</button>`;
|
|
}
|
|
return `<tr><td class="px-4 py-3">${doc.pmid}</td><td class="px-4 py-3">${doc.title}</td><td class="px-4 py-3">${status}</td><td class="px-4 py-3">${action}</td></tr>`;
|
|
}).join('');
|
|
checkFulltextReady();
|
|
}
|
|
|
|
function uploadFulltext(docId) {
|
|
const doc = fulltextDocs.find(d => d.id === docId);
|
|
if(doc) {
|
|
doc.pdf_status = 'ready';
|
|
alert(`模拟为文献 ${doc.pmid} 上传全文成功!`);
|
|
}
|
|
renderFulltextList();
|
|
}
|
|
|
|
function addFromKB() { /* ... unchanged V1 version ... */ const newDocIdStart = 2000; for(let i=1; i<=3; i++){ fulltextDocs.push({ id: newDocIdStart + i, pmid: `KB${newDocIdStart+i}`, study_id: `KB_Author ${2022+i}`, source: `Know Base J ${2022+i}`, title: `知识库文献 ${i}`, final_decision: '待定', decision_method: '待定', pdf_status: 'ready', model_a: {p: '✓', i: '✓', c:'?', s:'✓', conclusion: '纳入', evidence:{p:'...',i:'...',c:'...',s:'...'}}, model_b: {p: '✓', i: '✓', c:'?', s:'✓', conclusion: '纳入', evidence:{p:'...',i:'...',c:'...',s:'...'}} }); } document.getElementById('fulltext-count').textContent = fulltextDocs.length; renderFulltextList(); alert("已从知识库添加3篇文献。"); }
|
|
|
|
function checkFulltextReady() {
|
|
const allReady = fulltextDocs.every(doc => doc.pdf_status === 'ready');
|
|
// V2: Logic removed, button always enabled for demo
|
|
startFulltextScreeningBtn.disabled = false; // Always enable for demo
|
|
if(allReady) {
|
|
startFulltextScreeningBtn.classList.add('bg-green-600', 'hover:bg-green-700');
|
|
fulltextReadyHint.classList.add('hidden');
|
|
} else {
|
|
startFulltextScreeningBtn.classList.add('bg-gray-400', 'cursor-not-allowed'); // Keep visual cue
|
|
startFulltextScreeningBtn.classList.remove('bg-green-600', 'hover:bg-green-700');
|
|
fulltextReadyHint.classList.remove('hidden');
|
|
}
|
|
}
|
|
|
|
startFulltextScreeningBtn.addEventListener('click', () => {
|
|
taskStatusModal.classList.remove('hidden');
|
|
document.getElementById('task-modal-title').textContent = 'AI全文复筛进行中...';
|
|
let progress = 0;
|
|
const totalToProcess = fulltextDocs.length;
|
|
document.getElementById('progress-text').textContent = `已处理 0 / ${totalToProcess}`;
|
|
document.getElementById('show-workbench-btn').onclick = () => {
|
|
taskStatusModal.classList.add('hidden');
|
|
showView('fulltext-workbench');
|
|
renderTable('fulltext');
|
|
};
|
|
|
|
const interval = setInterval(() => {
|
|
progress += Math.ceil(100 / (totalToProcess || 1));
|
|
if (progress > 100) progress = 100;
|
|
document.getElementById('progress-bar-inner').style.width = `${progress}%`;
|
|
document.getElementById('progress-text').textContent = `已处理 ${Math.ceil(progress/100 * totalToProcess)} / ${totalToProcess}`;
|
|
|
|
if (progress >= 100) {
|
|
clearInterval(interval);
|
|
fulltextDocs.forEach(doc => {
|
|
doc.final_decision = '待定';
|
|
doc.model_a.conclusion = Math.random() > 0.3 ? '纳入' : '排除';
|
|
doc.model_b.conclusion = Math.random() > 0.25 ? '纳入' : '排除';
|
|
doc.model_a.s = Math.random() > 0.1 ? '✓' : '✗';
|
|
doc.model_b.s = Math.random() > 0.15 ? '✓' : '✗';
|
|
if (doc.model_a.conclusion === '排除' || doc.model_b.conclusion === '排除') {
|
|
const reasons = ["非RCT", "非目标人群", "干预/对照不符", "结局指标不符", "数据不全"];
|
|
doc.exclusion_reason = reasons[Math.floor(Math.random() * reasons.length)];
|
|
} else { doc.exclusion_reason = null; }
|
|
});
|
|
document.getElementById('show-workbench-btn').classList.remove('hidden');
|
|
}
|
|
}, 200);
|
|
});
|
|
|
|
|
|
// V7: Render Table with Original Header Structure (Applied to Fulltext)
|
|
function renderTable(stage) {
|
|
if (stage !== 'fulltext') return; // Only render for fulltext stage
|
|
const tableBodyId = 'fulltext-screening-table-body';
|
|
const tableBody = document.getElementById(tableBodyId);
|
|
if (!tableBody) return;
|
|
|
|
const dataToRender = fulltextDocs; // Use fulltext 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 V7 version ... */ const docId = parseInt(selectElement.dataset.id); const stage = selectElement.dataset.stage; const dataSet = stage === 'title' ? literatureData : fulltextDocs; 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 = stage === 'title' ? literatureData : fulltextDocs; 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 textContentEl = document.getElementById('modal-text-content'); const textSourceTitle = document.getElementById('modal-text-source-title'); let textContent = ""; if (stage === 'fulltext') { textSourceTitle.textContent = "全文片段"; textContent = `<h4 class='font-semibold'>方法部分:</h4><p>...<span class='s-source'>随机对照试验(RCT)</span>...招募了<span class='p-source'>2型糖尿病成人患者</span>...</p> <h4 class='font-semibold mt-4'>干预部分:</h4><p>...患者接受<span class='i-source'>SGLT2药物</span>或<span class='c-source'>安慰剂</span>治疗...</p>`; } else { textSourceTitle.textContent = "摘要"; textContent = doc.abstract; } const sourceSpan = textContent.match(new RegExp(`<span class='${dimensionKey.toLowerCase()}-source'>(.*?)</span>`)); let sourceText = sourceSpan ? sourceSpan[1] : ""; textContent = textContent.replace(/<span class='.-source'>/g, '').replace(/<\/span>/g, ''); if (sourceText) { textContent = textContent.replace(sourceText, `<mark class="abstract-highlight">${sourceText}</mark>`); } textContentEl.innerHTML = textContent; setTimeout(() => { const mark = textContentEl.querySelector('mark'); if (mark) { mark.scrollIntoView({ behavior: 'smooth', block: 'center' }); } }, 100); document.getElementById('modal-dimension-title').textContent = `PICO(S) 判断依据 (基于${stage==='title'?'摘要':'全文'})`; 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'); }
|
|
|
|
// --- RESULTS PAGES (V2 Independent Data) ---
|
|
function renderResultsPage(stage) {
|
|
// V2: Use simulated data for counts and table FOR FULLTEXT as well
|
|
const dataSet = stage === 'title' ? simulatedResultsData : simulatedFulltextResults;
|
|
const included = dataSet.filter(doc => doc.final_decision === '纳入');
|
|
const excluded = dataSet.filter(doc => doc.final_decision === '排除');
|
|
const totalDocsInStage = dataSet.length;
|
|
|
|
if (stage === 'title') {
|
|
// ... (title result logic using simulatedResultsData - unchanged from V1 of this specific prototype) ...
|
|
} else { // fulltext
|
|
fulltextReviewedCount.textContent = totalDocsInStage;
|
|
fulltextIncludedCount.textContent = included.length;
|
|
fulltextExcludedCount.textContent = excluded.length;
|
|
fulltextIncludedTabCount.textContent = included.length;
|
|
fulltextExcludedTabCount.textContent = excluded.length;
|
|
|
|
// Simulate Prisma for fulltext results
|
|
ftPrismaTotalExcluded.textContent = excluded.length;
|
|
ftPrismaSExcluded.textContent = excluded.filter(d => d.exclusion_reason === '非RCT').length;
|
|
ftPrismaPExcluded.textContent = excluded.filter(d => d.exclusion_reason === '非目标人群').length;
|
|
ftPrismaICExcluded.textContent = excluded.filter(d => d.exclusion_reason === '干预/对照不符').length;
|
|
ftPrismaOExcluded.textContent = excluded.filter(d => d.exclusion_reason === '结局指标不符').length;
|
|
ftPrismaOtherExcluded.textContent = excluded.filter(d => !["非RCT", "非目标人群", "干预/对照不符", "结局指标不符"].includes(d.exclusion_reason)).length;
|
|
|
|
renderResultsTable('fulltext', document.querySelector('#fulltext-result-tabs .result-tab.active')?.dataset.tab || 'included'); // V2: Added fallback
|
|
}
|
|
}
|
|
|
|
|
|
function switchResultTab(stage, tab) {
|
|
if (stage !== 'fulltext') return; // Only handle fulltext stage
|
|
const tabContainerId = 'fulltext-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}"]`);
|
|
if (activeBtn) { // V2: Check if button exists
|
|
activeBtn.classList.add('active', 'border-sky-500', 'text-sky-600');
|
|
activeBtn.classList.remove('border-transparent', 'text-gray-500');
|
|
renderResultsTable('fulltext', tab);
|
|
}
|
|
}
|
|
|
|
// V7: Render Enhanced Results Table (Now uses appropriate data source)
|
|
function renderResultsTable(stage, tab) {
|
|
if (stage !== 'fulltext') return; // Only handle fulltext stage
|
|
const tableBodyId = 'fulltext-results-table-body';
|
|
const tableBody = document.getElementById(tableBodyId);
|
|
const tableHead = document.getElementById('fulltext-results-table-head'); // V2: Get head by ID
|
|
if (!tableBody || !tableHead) return;
|
|
|
|
// V2 Logic: Use simulated data
|
|
const dataSet = simulatedFulltextResults;
|
|
const filteredData = dataSet.filter(doc => doc.final_decision === (tab === 'included' ? '纳入' : '排除'));
|
|
|
|
// Simplified header for fulltext results
|
|
let headerHTML = `<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-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>`;
|
|
tableHead.innerHTML = headerHTML;
|
|
|
|
tableBody.innerHTML = filteredData.map(doc => {
|
|
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"><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.decision_method || 'N/A'}</td>
|
|
<td class="px-4 py-3 text-xs text-gray-500">${doc.final_decision === '排除' ? (doc.exclusion_reason || '全文排除-未指定') : 'N/A'}</td></tr>`;
|
|
}).join('');
|
|
}
|
|
|
|
|
|
function toggleAbstract(link, event) { /* ... unchanged ... */ event.preventDefault(); const cell = link.closest('.abstract-cell'); const isExpanded = cell.classList.toggle('expanded'); link.textContent = isExpanded ? '收起' : '显示更多'; }
|
|
|
|
// V2: Simplified completion logic for demo purposes
|
|
function completeFulltextScreening() {
|
|
// Just navigate to results page which uses simulated data
|
|
showView('fulltext-results');
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
|