Features: - Backend statistics API (cloud-native Prisma aggregation) - Results page with hybrid solution (AI consensus + human final decision) - Excel export (frontend generation, zero disk write, cloud-native) - PRISMA-style exclusion reason analysis with bar chart - Batch selection and export (3 export methods) - Fixed logic contradiction (inclusion does not show exclusion reason) - Optimized table width (870px, no horizontal scroll) Components: - Backend: screeningController.ts - add getProjectStatistics API - Frontend: ScreeningResults.tsx - complete results page (hybrid solution) - Frontend: excelExport.ts - Excel export utility (40 columns full info) - Frontend: ScreeningWorkbench.tsx - add navigation button - Utils: get-test-projects.mjs - quick test tool Architecture: - Cloud-native: backend aggregation reduces network transfer - Cloud-native: frontend Excel generation (zero file persistence) - Reuse platform: global prisma instance, logger - Performance: statistics API < 500ms, Excel export < 3s (1000 records) Documentation: - Update module status guide (add Week 4 features) - Update task breakdown (mark Week 4 completed) - Update API design spec (add statistics API) - Update database design (add field usage notes) - Create Week 4 development plan - Create Week 4 completion report - Create technical debt list Test: - End-to-end flow test passed - All features verified - Performance test passed - Cloud-native compliance verified Ref: Week 4 Development Plan Scope: ASL Module MVP - Title Abstract Screening Results Cloud-Native: Backend aggregation + Frontend Excel generation
304 lines
19 KiB
HTML
304 lines
19 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>数据综合分析模块原型 V1</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; transition: width 0.3s ease; }
|
||
.sidebar-link.active { background-color: #e0f2fe; color: #0c4a6e; font-weight: 600; }
|
||
.main-content { transition: margin-left 0.3s ease; margin-left: 16rem; }
|
||
.modal-backdrop { background-color: rgba(0, 0, 0, 0.5); }
|
||
.wizard-step { display: none; }
|
||
.wizard-step.active { display: block; }
|
||
.droppable { background-color: #f9fafb; border: 2px dashed #d1d5db; }
|
||
.draggable { cursor: grab; }
|
||
.bubble { transition: all 0.3s ease; }
|
||
.bubble-chart-cell:hover .bubble { transform: scale(1.2); }
|
||
.tooltip { visibility: hidden; opacity: 0; transition: opacity 0.2s; }
|
||
.has-tooltip:hover .tooltip { visibility: visible; opacity: 1; }
|
||
</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 flex items-center space-x-2">
|
||
<svg class="h-8 w-8 text-sky-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 0 0 6 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 1 6 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 0 1 6-2.292c1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 0 18 18a8.967 8.967 0 0 0-6 2.292m0-14.25v14.25" /></svg>
|
||
<span class="logo-text">AI文献平台</span>
|
||
</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 space-x-3"><i class="h-5 w-5 hi-outline hi-home"></i><span class="sidebar-text">项目概览</span></a>
|
||
<a href="#" class="sidebar-link group flex items-center px-3 py-2 text-sm font-medium rounded-md text-gray-700 space-x-3"><i class="h-5 w-5 hi-outline hi-magnifying-glass"></i><span class="sidebar-text">1. 智能文献检索</span></a>
|
||
<a href="#" class="sidebar-link group flex items-center px-3 py-2 text-sm font-medium rounded-md text-gray-700 space-x-3"><i class="h-5 w-5 hi-outline hi-beaker"></i><span class="sidebar-text">2. AI辅助初筛</span></a>
|
||
<a href="#" class="sidebar-link group flex items-center px-3 py-2 text-sm font-medium rounded-md text-gray-700 space-x-3"><i class="h-5 w-5 hi-outline hi-document-text"></i><span class="sidebar-text">3. 全文解析与数据提取</span></a>
|
||
<a href="#" id="nav-analysis-main" class="sidebar-link active group flex items-center px-3 py-2 text-sm font-medium rounded-md text-gray-700 space-x-3"><i class="h-5 w-5 hi-outline hi-chart-pie"></i><span class="sidebar-text">4. 数据综合分析与报告</span></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">
|
||
<!-- 视图1: 应用选择中心 -->
|
||
<div id="hub-view">
|
||
<h2 class="text-3xl font-bold mb-6">应用选择中心</h2>
|
||
<p class="text-gray-600 mb-8">请选择您希望进行的分析应用。数据将自动从“全文解析与数据提取”模块导入。</p>
|
||
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||
<div class="bg-white p-8 rounded-lg shadow hover:shadow-xl transition-shadow cursor-pointer" onclick="startWizard()">
|
||
<h3 class="font-bold text-xl mb-3 text-sky-700">证据图谱生成</h3>
|
||
<p class="text-sm text-gray-600 mb-4">通过可视化矩阵,直观展示研究领域的证据分布,快速识别研究热点与证据空白。</p>
|
||
<span class="font-semibold text-sky-600">开始分析 →</span>
|
||
</div>
|
||
<div class="bg-white p-8 rounded-lg shadow hover:shadow-xl transition-shadow cursor-pointer opacity-50">
|
||
<h3 class="font-bold text-xl mb-3">Meta分析数据准备</h3>
|
||
<p class="text-sm text-gray-600 mb-4">为RevMan, Stata等专业统计软件,准备和导出格式化、可直接使用的数据文件。</p>
|
||
<span class="font-semibold text-gray-500">即将推出</span>
|
||
</div>
|
||
<div class="bg-white p-8 rounded-lg shadow hover:shadow-xl transition-shadow cursor-pointer opacity-50">
|
||
<h3 class="font-bold text-xl mb-3">药物综合评价报告</h3>
|
||
<p class="text-sm text-gray-600 mb-4">基于模板,一键生成包含有效性、安全性等多维度的综合评价报告初稿。</p>
|
||
<span class="font-semibold text-gray-500">即将推出</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 视图2: 配置向导 -->
|
||
<div id="wizard-view" class="hidden">
|
||
<div class="max-w-4xl mx-auto bg-white p-8 rounded-lg shadow">
|
||
<h2 class="text-2xl font-bold mb-2">证据图谱框架配置向导</h2>
|
||
<p class="text-gray-500 mb-6">请按照步骤定义图谱的框架,以便系统为您生成可视化结果。</p>
|
||
|
||
<!-- Steps -->
|
||
<div id="wizard-step-1" class="wizard-step active">
|
||
<h3 class="text-lg font-semibold mb-4">步骤 1/3: 定义Y轴 (干预措施)</h3>
|
||
<p class="text-sm text-gray-600 mb-4">请将左侧提取的干预措施拖拽或分配到右侧的分组中。</p>
|
||
<div class="grid grid-cols-2 gap-4 h-96">
|
||
<div class="border rounded p-4">
|
||
<h4 class="font-semibold mb-2">可用的干预措施</h4>
|
||
<div id="available-interventions" class="space-y-2 text-sm">
|
||
<div class="draggable bg-gray-100 p-2 rounded">Drug-X</div><div class="draggable bg-gray-100 p-2 rounded">Drug-Y</div><div class="draggable bg-gray-100 p-2 rounded">安慰剂</div>
|
||
</div>
|
||
</div>
|
||
<div class="droppable rounded p-4">
|
||
<h4 class="font-semibold mb-2">干预措施分组</h4>
|
||
<div class="p-2 bg-white border rounded mb-2"><strong>分组1:</strong> Drug-X, Drug-Y <button class="text-xs text-sky-600 ml-2">重命名</button></div>
|
||
<div class="p-2 bg-white border rounded"><strong>分组2:</strong> 安慰剂 <button class="text-xs text-sky-600 ml-2">重命名</button></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="wizard-step-2" class="wizard-step">
|
||
<h3 class="text-lg font-semibold mb-4">步骤 2/3: 定义X轴 (结局指标)</h3>
|
||
<p class="text-sm text-gray-600 mb-4">请将左侧提取的结局指标拖拽或分配到右侧的分组中。</p>
|
||
<!-- Simplified for demo -->
|
||
<p class="p-4 bg-gray-50 rounded">此步骤与上一步类似,用户可对“糖化血红蛋白”、“不良事件”等结局指标进行分组。</p>
|
||
</div>
|
||
<div id="wizard-step-3" class="wizard-step">
|
||
<h3 class="text-lg font-semibold mb-4">步骤 3/3: 配置气泡含义</h3>
|
||
<p class="text-sm text-gray-600 mb-4">请选择气泡颜色所代表的维度。气泡大小固定代表研究数量。</p>
|
||
<div class="space-y-3">
|
||
<label class="flex items-center p-4 border rounded-lg cursor-pointer"><input type="radio" name="bubble-color" class="mr-3" checked>研究质量/偏倚风险</label>
|
||
<label class="flex items-center p-4 border rounded-lg cursor-pointer"><input type="radio" name="bubble-color" class="mr-3">研究设计</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Navigation -->
|
||
<div class="mt-8 flex justify-between">
|
||
<button id="wizard-prev" onclick="navigateWizard(-1)" class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded">上一步</button>
|
||
<button id="wizard-next" onclick="navigateWizard(1)" class="bg-sky-600 hover:bg-sky-700 text-white font-bold py-2 px-4 rounded">下一步</button>
|
||
<button id="wizard-finish" onclick="finishWizard()" class="hidden bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded">生成证据图谱</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 视图3: 分析仪表盘 -->
|
||
<div id="dashboard-view" class="hidden h-full flex flex-col">
|
||
<div class="flex-shrink-0 flex justify-between items-center mb-4">
|
||
<h2 class="text-2xl font-bold">交互式分析仪表盘</h2>
|
||
<button onclick="showView('report')" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-6 rounded-lg shadow">生成完整报告</button>
|
||
</div>
|
||
<div class="flex-grow flex space-x-6 overflow-hidden">
|
||
<!-- Left Panel -->
|
||
<aside class="w-1/4 bg-white rounded-lg shadow p-6 overflow-y-auto">
|
||
<h3 class="text-lg font-bold mb-4">动态筛选器</h3>
|
||
<!-- Filters -->
|
||
<div class="space-y-4 text-sm">
|
||
<div><label class="font-semibold">发表年份:</label><input type="range" class="w-full mt-1"></div>
|
||
<div><label class="font-semibold">研究设计:</label><div class="mt-1 space-y-1"><label class="flex items-center"><input type="checkbox" checked class="mr-2">RCT</label><label class="flex items-center"><input type="checkbox" checked class="mr-2">队列研究</label></div></div>
|
||
</div>
|
||
<hr class="my-6">
|
||
<h3 class="text-lg font-bold mb-4">AI洞察摘要</h3>
|
||
<div class="text-sm text-gray-700 space-y-3 bg-gray-50 p-4 rounded-lg">
|
||
<p><strong>证据热点:</strong> 研究证据主要集中在‘Drug-X’对‘糖化血红蛋白’的影响上。</p>
|
||
<p><strong>证据空白:</strong> 在‘Drug-Y’与‘不良事件’交叉领域缺少高质量研究。</p>
|
||
</div>
|
||
</aside>
|
||
<!-- Main Panel -->
|
||
<main class="w-3/4 flex flex-col space-y-6 overflow-y-auto">
|
||
<div class="bg-white rounded-lg shadow p-6">
|
||
<h3 class="text-xl font-bold mb-4">证据图谱</h3>
|
||
<div id="bubble-chart-container" class="border rounded-lg p-4"></div>
|
||
</div>
|
||
<div class="bg-white rounded-lg shadow p-6">
|
||
<h3 class="text-xl font-bold mb-4">描述性统计</h3>
|
||
<p class="text-sm text-gray-600">本次分析共纳入50项研究,其中60% (30项) 为随机对照试验 (RCT)...</p>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 视图4: 报告编辑器 -->
|
||
<div id="report-view" class="hidden">
|
||
<div class="flex justify-between items-center mb-4">
|
||
<h2 class="text-2xl font-bold">智能报告编辑器</h2>
|
||
<div>
|
||
<button onclick="showView('dashboard')" class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded mr-2">返回仪表盘</button>
|
||
<button class="bg-sky-600 hover:bg-sky-700 text-white font-bold py-2 px-4 rounded">导出为 Word</button>
|
||
</div>
|
||
</div>
|
||
<div class="bg-white p-12 rounded-lg shadow prose max-w-none">
|
||
<h1>XX药物治疗XX疾病的证据图谱分析报告</h1>
|
||
<p class="text-gray-500">生成日期: 2025-10-22</p>
|
||
<h2>摘要</h2>
|
||
<p>本报告旨在通过证据图谱方法,系统性梳理XX药物治疗XX疾病的现有研究证据。分析共纳入50项研究,结果表明,研究热点主要集中于... 同时,我们发现...领域存在显著的证据空白,提示这是未来需要重点投入的研究方向。</p>
|
||
<h2>方法</h2>
|
||
<p>我们遵循系统评价流程... 最终纳入50篇文献进行分析。证据图谱的干预措施维度包括... 结局指标维度包括...</p>
|
||
<h2>结果</h2>
|
||
<h3>纳入研究的描述性统计</h3>
|
||
<p>如图1所示,本次分析共纳入50项研究。其中,随机对照试验(RCT)是主要的研究类型,占总数的60%(30项),其次是队列研究(25%,12项)...</p>
|
||
<h3>证据图谱可视化与解读</h3>
|
||
<p>证据图谱(图2)显示,研究证据主要集中在‘抗血小板药物’对‘主要心血管不良事件(MACE)’的影响上...</p>
|
||
<figure>
|
||
<div class="border rounded-lg p-4 bg-gray-50 text-center text-gray-500">[此处为证据图谱图片]</div>
|
||
<figcaption>图2: 证据图谱可视化</figcaption>
|
||
</figure>
|
||
<h4>证据空白分析</h4>
|
||
<p>图谱也揭示了显著的证据空白。特别是在‘新一代抗凝药’与‘出血风险’这一关键领域的直接对比研究非常稀少...</p>
|
||
<h2>结论</h2>
|
||
<p>...</p>
|
||
<h2>附录</h2>
|
||
<p>...</p>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// --- STATE MANAGEMENT ---
|
||
let currentView = 'hub';
|
||
let wizardStep = 1;
|
||
|
||
// --- DOM ELEMENTS ---
|
||
const views = {
|
||
hub: document.getElementById('hub-view'),
|
||
wizard: document.getElementById('wizard-view'),
|
||
dashboard: document.getElementById('dashboard-view'),
|
||
report: document.getElementById('report-view')
|
||
};
|
||
const headerTitle = document.getElementById('header-title');
|
||
|
||
// --- NAVIGATION ---
|
||
function showView(view) {
|
||
currentView = view;
|
||
Object.values(views).forEach(v => v.classList.add('hidden'));
|
||
views[view].classList.remove('hidden');
|
||
|
||
const titles = {
|
||
hub: '数据综合分析与报告生成',
|
||
wizard: '证据图谱配置向导',
|
||
dashboard: '交互式分析仪表盘',
|
||
report: '智能报告编辑器'
|
||
};
|
||
headerTitle.textContent = titles[view];
|
||
}
|
||
|
||
function startWizard() {
|
||
wizardStep = 1;
|
||
updateWizardView();
|
||
showView('wizard');
|
||
}
|
||
|
||
function navigateWizard(direction) {
|
||
wizardStep += direction;
|
||
updateWizardView();
|
||
}
|
||
|
||
function updateWizardView() {
|
||
document.querySelectorAll('.wizard-step').forEach(step => step.classList.remove('active'));
|
||
document.getElementById(`wizard-step-${wizardStep}`).classList.add('active');
|
||
|
||
document.getElementById('wizard-prev').style.visibility = wizardStep === 1 ? 'hidden' : 'visible';
|
||
document.getElementById('wizard-next').style.display = wizardStep === 3 ? 'none' : 'block';
|
||
document.getElementById('wizard-finish').style.display = wizardStep === 3 ? 'block' : 'none';
|
||
}
|
||
|
||
function finishWizard() {
|
||
renderBubbleChart();
|
||
showView('dashboard');
|
||
}
|
||
|
||
|
||
// --- DASHBOARD: BUBBLE CHART ---
|
||
function renderBubbleChart() {
|
||
const container = document.getElementById('bubble-chart-container');
|
||
const interventions = ['Drug-X', 'Drug-Y', '安慰剂'];
|
||
const outcomes = ['糖化血红蛋白', '不良事件', 'MACE'];
|
||
|
||
// Simulated data
|
||
const data = [
|
||
[15, 2, 8],
|
||
[5, 1, 3],
|
||
[12, 10, 11]
|
||
];
|
||
const quality = [
|
||
['green', 'red', 'yellow'],
|
||
['yellow', 'red', 'green'],
|
||
['green', 'green', 'yellow']
|
||
];
|
||
|
||
let tableHTML = `<table class="w-full border-collapse"><thead><tr><th class="border p-2"></th>`;
|
||
outcomes.forEach(o => tableHTML += `<th class="border p-2 text-sm font-semibold">${o}</th>`);
|
||
tableHTML += `</tr></thead><tbody>`;
|
||
|
||
interventions.forEach((inter, i) => {
|
||
tableHTML += `<tr><td class="border p-2 text-sm font-semibold text-right">${inter}</td>`;
|
||
outcomes.forEach((outc, j) => {
|
||
const count = data[i][j];
|
||
const size = count * 3 + 10; // Simple size mapping
|
||
const color = quality[i][j];
|
||
const colorClass = {green: 'bg-green-500', yellow: 'bg-yellow-400', red: 'bg-red-500'}[color];
|
||
|
||
tableHTML += `
|
||
<td class="border p-2 h-24 w-24 text-center bubble-chart-cell has-tooltip relative">
|
||
${count > 0 ? `
|
||
<div class="bubble w-12 h-12 rounded-full mx-auto flex items-center justify-center text-white font-bold ${colorClass}" style="width:${size}px; height:${size}px;">
|
||
${count}
|
||
</div>
|
||
<div class="tooltip absolute z-10 -mt-24 w-48 bg-gray-800 text-white text-xs rounded py-1 px-2 text-left">
|
||
<strong>${inter} vs ${outc}</strong><br>
|
||
研究数量: ${count}<br>
|
||
质量: ${color === 'green' ? '高' : '中'}
|
||
</div>` : ''}
|
||
</td>`;
|
||
});
|
||
tableHTML += `</tr>`;
|
||
});
|
||
|
||
tableHTML += `</tbody></table>`;
|
||
container.innerHTML = tableHTML;
|
||
}
|
||
|
||
// Initial view
|
||
showView('hub');
|
||
</script>
|
||
</body>
|
||
</html>
|