Files
HaHafeng 4088275290 fix(pkb): fix create KB and upload issues - remove simulated upload, fix department mapping, add upload modal
Fixed issues:
- Remove simulateUpload function from DashboardPage Step 3
- Map department to description field when creating KB
- Add upload modal in WorkspacePage knowledge assets tab
- Fix DocumentUpload import path (../../stores to ../stores)

Known issue: Dify API validation error during document upload (file uploaded but DB record failed, needs investigation)

Testing: KB creation works, upload dialog opens correctly
2026-01-13 13:17:20 +08:00

665 lines
40 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI智能问答 V3.0 - 完整版</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Noto+Sans+SC:wght@400;500;700&display=swap');
body {
font-family: 'Noto Sans SC', 'Inter', sans-serif;
background-color: #F8FAFC;
}
/* --- 品牌色体系 (与V11保持一致) --- */
:root {
--brand-blue: #4F6EF2;
--brand-teal: #0D9488;
--brand-purple: #9333EA;
--brand-yellow: #CA8A04;
}
/* 自定义滚动条 */
.scrollbar-hide::-webkit-scrollbar { display: none; }
.custom-scroll::-webkit-scrollbar { width: 6px; }
.custom-scroll::-webkit-scrollbar-track { background: transparent; }
.custom-scroll::-webkit-scrollbar-thumb { background-color: #CBD5E1; border-radius: 3px; }
/* 思考过程折叠动画 */
.thinking-box {
transition: all 0.3s ease-in-out;
overflow: hidden;
}
.thinking-box.collapsed { max-height: 48px; }
.thinking-box.expanded { max-height: 800px; }
/* 打字机光标 */
.cursor-blink {
display: inline-block;
width: 2px;
height: 1.2em;
background-color: currentColor;
animation: blink 1s step-end infinite;
vertical-align: middle;
}
@keyframes blink { 50% { opacity: 0; } }
/* 卡片基础样式 */
.card-box {
transition: all 0.2s ease-in-out;
position: relative;
overflow: hidden;
}
.card-box:hover {
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
/* 水印序号 */
.num-watermark {
font-family: 'Inter', sans-serif;
font-weight: 800;
font-size: 1.5rem;
opacity: 0.3;
transition: all 0.3s;
}
.card-box:hover .num-watermark {
opacity: 0.8;
transform: scale(1.1);
}
/* --- 主题配色类 --- */
/* Blue (Phase 1 & 2) */
.theme-blue .agent-icon { background-color: #EFF6FF; color: #4F6EF2; border: 1px solid #DBEAFE; }
.theme-blue:hover { border-color: #4F6EF2; background-color: #EEF4FF; }
.theme-blue .num-watermark { color: #BFDBFE; }
/* Yellow (Phase 3) */
.theme-yellow .agent-icon { background-color: #FEFCE8; color: #CA8A04; border: 1px solid #FEF08A; }
.theme-yellow:hover { border-color: #CA8A04; background-color: #FEF9C3; }
.theme-yellow .num-watermark { color: #FDE047; }
/* Teal (Phase 4 - Tools) */
.theme-teal { background-color: #F0FDFA; }
.theme-teal .agent-icon { background-color: #FFFFFF; color: #0D9488; border: 1px solid #CCFBF1; }
.theme-teal:hover { border-color: #0D9488; background-color: #E0F2F1; }
.theme-teal .num-watermark { color: #99F6E4; }
/* Purple (Phase 5) */
.theme-purple .agent-icon { background-color: #FAF5FF; color: #9333EA; border: 1px solid #E9D5FF; }
.theme-purple:hover { border-color: #9333EA; background-color: #F3E8FF; }
.theme-purple .num-watermark { color: #D8B4FE; }
/* Markdown 样式微调 */
.prose p { margin-bottom: 0.5em; }
.prose ul { list-style-type: disc; padding-left: 1.2em; margin-bottom: 0.5em; }
/* 移动端侧边栏遮罩 */
.drawer-overlay { background-color: rgba(0,0,0,0.5); backdrop-filter: blur(2px); transition: opacity 0.3s; }
</style>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
brand: {
blue: '#4F6EF2',
teal: '#0D9488',
purple: '#9333EA',
yellow: '#CA8A04'
}
},
animation: {
'fade-in': 'fadeIn 0.3s ease-out',
'fade-in-up': 'fadeInUp 0.5s ease-out'
},
keyframes: {
fadeIn: { '0%': { opacity: '0' }, '100%': { opacity: '1' } },
fadeInUp: { '0%': { opacity: '0', transform: 'translateY(10px)' }, '100%': { opacity: '1', transform: 'translateY(0)' } }
}
}
}
}
</script>
</head>
<body class="text-slate-600 h-screen overflow-hidden flex flex-col">
<!-- ================= 应用容器 ================= -->
<div id="app" class="flex-1 flex flex-col relative overflow-hidden">
<!-- ================= 视图 1: 智能体大厅 (Dashboard) ================= -->
<div id="view-dashboard" class="flex-1 overflow-y-auto bg-white transition-opacity duration-300">
<!-- Navbar -->
<nav class="sticky top-0 z-40 bg-white/90 backdrop-blur border-b border-slate-100 h-16 flex items-center justify-between px-4 md:px-8">
<div class="flex items-center gap-2">
<div class="w-8 h-8 bg-brand-blue rounded-lg flex items-center justify-center text-white shadow-sm">
<i data-lucide="bot" class="w-5 h-5"></i>
</div>
<span class="font-bold text-lg text-slate-800 tracking-tight">AI 临床研究平台</span>
</div>
<div class="flex items-center gap-4">
<div class="w-8 h-8 rounded-full bg-slate-100 border border-slate-200 overflow-hidden cursor-pointer">
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Felix" alt="User">
</div>
</div>
</nav>
<!-- Hero Section -->
<div class="max-w-3xl mx-auto pt-10 pb-8 px-4 text-center">
<div class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-blue-50 text-brand-blue text-xs font-semibold mb-4 border border-blue-100 animate-fade-in-up">
<i data-lucide="sparkles" class="w-3 h-3"></i> 已接入 DeepSeek-V3
</div>
<h1 class="text-3xl md:text-4xl font-bold text-slate-900 mb-6 tracking-tight">想研究什么?</h1>
<div class="relative group max-w-2xl mx-auto">
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
<i data-lucide="search" class="w-5 h-5 text-slate-400 group-focus-within:text-brand-blue transition-colors"></i>
</div>
<input type="text"
class="w-full pl-11 pr-14 py-4 rounded-xl border border-slate-200 shadow-sm focus:outline-none focus:ring-2 focus:ring-brand-blue/20 focus:border-brand-blue transition-all text-base bg-white"
placeholder="输入想法,自动匹配智能体(如:肺癌的回顾性研究..."
onkeydown="if(event.key === 'Enter') enterChat('TOPIC_01', this.value)"
>
<button class="absolute right-2 top-2 bottom-2 aspect-square bg-brand-blue text-white rounded-lg hover:bg-blue-600 transition-colors shadow-sm flex items-center justify-center" onclick="enterChat('TOPIC_01')">
<i data-lucide="arrow-right" class="w-5 h-5"></i>
</button>
</div>
</div>
<!-- Agent Pipeline (12 Modules) -->
<div class="max-w-5xl mx-auto px-4 pb-20 space-y-8">
<!-- Stage 1: 选题优化 (Blue) -->
<div class="relative pl-8 border-l-2 border-slate-100">
<div class="absolute -left-[9px] top-0 w-4 h-4 rounded-full bg-brand-blue border-2 border-white shadow-sm ring-2 ring-blue-50"></div>
<h2 class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-4">Phase 1: 选题优化</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<!-- 01 -->
<div onclick="enterChat('TOPIC_01')" class="card-box bg-white border border-slate-200 rounded-xl p-4 cursor-pointer theme-blue group">
<span class="absolute top-3 right-3 num-watermark">01</span>
<div class="agent-icon w-10 h-10 rounded-lg flex items-center justify-center mb-3 transition-colors">
<i data-lucide="list-tree" class="w-5 h-5"></i>
</div>
<h3 class="font-bold text-slate-800 mb-1">科学问题梳理</h3>
<p class="text-xs text-slate-500 line-clamp-2">从科学问题的清晰度、系统性、可验证性等角度使用科学理论对您的科学问题进行全面的评价。</p>
</div>
<!-- 02 -->
<div onclick="enterChat('TOPIC_02')" class="card-box bg-white border border-slate-200 rounded-xl p-4 cursor-pointer theme-blue group">
<span class="absolute top-3 right-3 num-watermark">02</span>
<div class="agent-icon w-10 h-10 rounded-lg flex items-center justify-center mb-3 transition-colors">
<i data-lucide="target" class="w-5 h-5"></i>
</div>
<h3 class="font-bold text-slate-800 mb-1">PICO 梳理</h3>
<p class="text-xs text-slate-500 line-clamp-2">基于科学问题梳理研究对象、干预(暴露)、对照和结局指标,并评价并给出合理化的建议。</p>
</div>
<!-- 03 -->
<div onclick="enterChat('TOPIC_03')" class="card-box bg-white border border-slate-200 rounded-xl p-4 cursor-pointer theme-blue group">
<span class="absolute top-3 right-3 num-watermark">03</span>
<div class="agent-icon w-10 h-10 rounded-lg flex items-center justify-center mb-3 transition-colors">
<i data-lucide="clipboard-check" class="w-5 h-5"></i>
</div>
<h3 class="font-bold text-slate-800 mb-1">选题评价</h3>
<p class="text-xs text-slate-500 line-clamp-2">从创新性、临床价值、科学性和可行性等方面使用科学理论对您的临床问题进行全面的评价。</p>
</div>
</div>
</div>
<!-- Stage 2: 方案设计 (Blue) -->
<div class="relative pl-8 border-l-2 border-slate-100">
<div class="absolute -left-[9px] top-0 w-4 h-4 rounded-full bg-brand-blue border-2 border-white shadow-sm ring-2 ring-blue-50"></div>
<h2 class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-4">Phase 2: 方案设计</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<!-- 04 -->
<div onclick="enterChat('DESIGN_04')" class="card-box bg-white border border-slate-200 rounded-xl p-4 cursor-pointer theme-blue group">
<span class="absolute top-3 right-3 num-watermark">04</span>
<div class="agent-icon w-10 h-10 rounded-lg flex items-center justify-center mb-3 transition-colors">
<i data-lucide="eye" class="w-5 h-5"></i>
</div>
<h3 class="font-bold text-slate-800 mb-1">观察指标设计</h3>
<p class="text-xs text-slate-500 line-clamp-2">基于研究设计和因果关系提供可能的观察指标集。</p>
</div>
<!-- 05 -->
<div onclick="enterChat('DESIGN_05')" class="card-box bg-white border border-slate-200 rounded-xl p-4 cursor-pointer theme-blue group">
<span class="absolute top-3 right-3 num-watermark">05</span>
<div class="agent-icon w-10 h-10 rounded-lg flex items-center justify-center mb-3 transition-colors">
<i data-lucide="table" class="w-5 h-5"></i>
</div>
<h3 class="font-bold text-slate-800 mb-1">病例报告表设计</h3>
<p class="text-xs text-slate-500 line-clamp-2">基于研究方案设计梳理观察指标集并给出建议的病例报告表框架。</p>
</div>
<!-- 06 -->
<div onclick="enterChat('DESIGN_06')" class="card-box bg-white border border-slate-200 rounded-xl p-4 cursor-pointer theme-blue group">
<span class="absolute top-3 right-3 num-watermark">06</span>
<div class="agent-icon w-10 h-10 rounded-lg flex items-center justify-center mb-3 transition-colors">
<i data-lucide="calculator" class="w-5 h-5"></i>
</div>
<h3 class="font-bold text-slate-800 mb-1">样本量计算</h3>
<p class="text-xs text-slate-500 line-clamp-2">基于研究阶段和研究设计为研究提供科学合理的样本量估算结果。</p>
</div>
<!-- 07 -->
<div onclick="enterChat('DESIGN_07')" class="card-box bg-white border border-slate-200 rounded-xl p-4 cursor-pointer theme-blue group">
<span class="absolute top-3 right-3 num-watermark">07</span>
<div class="agent-icon w-10 h-10 rounded-lg flex items-center justify-center mb-3 transition-colors">
<i data-lucide="file-text" class="w-5 h-5"></i>
</div>
<h3 class="font-bold text-slate-800 mb-1">方案撰写 (Pro)</h3>
<p class="text-xs text-slate-500 line-clamp-2">基于科学问题、PICOS等信息给出一个初步的临床研究设计方案。</p>
</div>
</div>
</div>
<!-- Stage 3: 方案预评审 (Yellow) -->
<div class="relative pl-8 border-l-2 border-slate-100">
<div class="absolute -left-[9px] top-0 w-4 h-4 rounded-full bg-brand-yellow border-2 border-white shadow-sm ring-2 ring-yellow-50"></div>
<h2 class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-4">Phase 3: 方案预评审</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- 08 -->
<div onclick="enterChat('REVIEW_08')" class="card-box bg-white border border-slate-200 rounded-xl p-4 cursor-pointer theme-yellow group">
<span class="absolute top-3 right-3 num-watermark">08</span>
<div class="agent-icon w-10 h-10 rounded-lg flex items-center justify-center mb-3 transition-colors">
<i data-lucide="shield-check" class="w-5 h-5"></i>
</div>
<h3 class="font-bold text-slate-800 mb-1">方法学评审</h3>
<p class="text-xs text-slate-500 line-clamp-2">从研究问题、研究方案和临床意义方面,对研究进行临床研究方法学的全面评价。</p>
</div>
</div>
</div>
<!-- Stage 4: 数据与统计 (Teal - Tools) -->
<div class="relative pl-8 border-l-2 border-slate-100">
<div class="absolute -left-[9px] top-0 w-4 h-4 rounded-full bg-brand-teal border-2 border-white shadow-sm ring-2 ring-teal-50"></div>
<h2 class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-4">Phase 4: 数据与统计 (工具)</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- 09 -->
<div class="card-box border border-slate-200 rounded-xl p-4 cursor-pointer theme-teal group">
<i data-lucide="external-link" class="absolute top-3 right-3 w-4 h-4 text-slate-300 group-hover:text-brand-teal transition-colors"></i>
<span class="absolute bottom-3 right-3 num-watermark">09</span>
<div class="agent-icon w-10 h-10 rounded-lg flex items-center justify-center mb-3 transition-colors">
<i data-lucide="database" class="w-5 h-5"></i>
</div>
<h3 class="font-bold text-slate-800 mb-1">数据评价与预处理</h3>
<p class="text-xs text-slate-500 line-clamp-2">对现有数据的质量进行自动评价,并给出数据质量报告,包括缺失值、异常值、定量分析等。</p>
</div>
<!-- 10 -->
<div class="card-box border border-slate-200 rounded-xl p-4 cursor-pointer theme-teal group">
<i data-lucide="external-link" class="absolute top-3 right-3 w-4 h-4 text-slate-300 group-hover:text-brand-teal transition-colors"></i>
<span class="absolute bottom-3 right-3 num-watermark">10</span>
<div class="agent-icon w-10 h-10 rounded-lg flex items-center justify-center mb-3 transition-colors">
<i data-lucide="bar-chart-2" class="w-5 h-5"></i>
</div>
<h3 class="font-bold text-slate-800 mb-1">智能统计分析</h3>
<p class="text-xs text-slate-500 line-clamp-2">内置3条智能研究路径队列研究、预测模型、RCT研究以及近百种统计分析工具。</p>
</div>
</div>
</div>
<!-- Stage 5: 写作助手 (Purple) -->
<div class="relative pl-8 border-l-2 border-transparent">
<div class="absolute -left-[9px] top-0 w-4 h-4 rounded-full bg-brand-purple border-2 border-white shadow-sm ring-2 ring-purple-50"></div>
<h2 class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-4">Phase 5: 写作助手</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- 11 -->
<div onclick="enterChat('WRITING_11')" class="card-box bg-white border border-slate-200 rounded-xl p-4 cursor-pointer theme-purple group">
<span class="absolute top-3 right-3 num-watermark">11</span>
<div class="agent-icon w-10 h-10 rounded-lg flex items-center justify-center mb-3 transition-colors">
<i data-lucide="pen-tool" class="w-5 h-5"></i>
</div>
<h3 class="font-bold text-slate-800 mb-1">论文润色</h3>
<p class="text-xs text-slate-500 line-clamp-2">结合目标杂志,提供专业化的润色服务,给出合理化的修改建议。</p>
</div>
<!-- 12 -->
<div onclick="enterChat('WRITING_12')" class="card-box bg-white border border-slate-200 rounded-xl p-4 cursor-pointer theme-purple group">
<span class="absolute top-3 right-3 num-watermark">12</span>
<div class="agent-icon w-10 h-10 rounded-lg flex items-center justify-center mb-3 transition-colors">
<i data-lucide="languages" class="w-5 h-5"></i>
</div>
<h3 class="font-bold text-slate-800 mb-1">论文翻译</h3>
<p class="text-xs text-slate-500 line-clamp-2">结合目标杂志,提供专业化的翻译并进行润色,给出合理化的修改建议。</p>
</div>
</div>
</div>
</div>
</div>
<!-- ================= 视图 2: 沉浸式工作台 (Chat Workspace) ================= -->
<div id="view-chat" class="flex-1 flex bg-white hidden">
<!-- Mobile Drawer Overlay -->
<div id="mobile-overlay" class="absolute inset-0 z-40 bg-black/20 backdrop-blur-sm hidden lg:hidden" onclick="toggleSidebar()"></div>
<!-- Sidebar (Desktop & Mobile Drawer) -->
<aside id="chat-sidebar" class="absolute lg:relative z-50 w-64 h-full bg-slate-50 border-r border-slate-200 transform -translate-x-full lg:translate-x-0 transition-transform duration-300 flex flex-col">
<div class="p-4 border-b border-slate-100 flex items-center justify-between">
<button onclick="exitChat()" class="flex items-center gap-2 text-slate-500 hover:text-slate-800 transition-colors">
<i data-lucide="chevron-left" class="w-5 h-5"></i>
<span class="font-bold text-sm">返回大厅</span>
</button>
<button class="lg:hidden p-1 text-slate-400" onclick="toggleSidebar()">
<i data-lucide="x" class="w-5 h-5"></i>
</button>
</div>
<!-- New Chat Button -->
<div class="p-4">
<button onclick="clearChat()" class="w-full py-2.5 px-4 bg-white border border-slate-200 hover:border-brand-blue hover:text-brand-blue rounded-lg shadow-sm text-sm font-medium transition-all flex items-center justify-center gap-2 group">
<i data-lucide="plus" class="w-4 h-4 text-slate-400 group-hover:text-brand-blue"></i> 新建对话
</button>
</div>
<!-- History List -->
<div class="flex-1 overflow-y-auto px-2 pb-4 custom-scroll space-y-6">
<div>
<div class="px-2 text-[10px] font-bold text-slate-400 uppercase mb-2">Today</div>
<div class="space-y-1">
<button class="w-full text-left px-3 py-2 rounded-lg bg-slate-200/50 text-slate-800 text-xs font-medium truncate border-l-2 border-brand-blue">
关于肺癌的PICO梳理
</button>
<button class="w-full text-left px-3 py-2 rounded-lg hover:bg-slate-100 text-slate-600 text-xs truncate border-l-2 border-transparent">
样本量计算咨询
</button>
</div>
</div>
</div>
<!-- User Profile Bottom -->
<div class="p-4 border-t border-slate-200 bg-slate-100/50">
<div class="flex items-center gap-2">
<div class="w-7 h-7 rounded-full bg-brand-blue text-white flex items-center justify-center text-xs">U</div>
<div class="flex-1 min-w-0">
<div class="text-xs font-medium text-slate-700 truncate">Dr. Wang</div>
<div class="text-[10px] text-slate-400">专业版会员</div>
</div>
</div>
</div>
</aside>
<!-- Main Chat Area -->
<main class="flex-1 flex flex-col h-full relative w-full">
<!-- Chat Header -->
<header class="h-14 border-b border-slate-100 flex items-center justify-between px-4 bg-white/80 backdrop-blur z-10 sticky top-0">
<div class="flex items-center gap-3">
<button onclick="toggleSidebar()" class="lg:hidden p-2 -ml-2 text-slate-500 hover:bg-slate-100 rounded-md">
<i data-lucide="menu" class="w-5 h-5"></i>
</button>
<div id="current-agent-icon" class="w-8 h-8 rounded-lg bg-brand-blue text-white flex items-center justify-center">
<i data-lucide="bot" class="w-5 h-5"></i>
</div>
<div>
<h2 id="current-agent-name" class="text-sm font-bold text-slate-800">智能体名称</h2>
<div class="flex items-center gap-1.5">
<span class="w-1.5 h-1.5 rounded-full bg-green-500"></span>
<span class="text-[10px] text-slate-500">Online</span>
</div>
</div>
</div>
<div class="flex items-center gap-2">
<button class="p-2 text-slate-400 hover:text-brand-blue rounded-md transition-colors" title="导出对话">
<i data-lucide="download" class="w-4 h-4"></i>
</button>
</div>
</header>
<!-- Messages Container -->
<div id="messages-container" class="flex-1 overflow-y-auto p-4 md:p-6 space-y-6 custom-scroll bg-white">
<!-- 欢迎语 (动态生成) -->
</div>
<!-- Input Area -->
<div class="p-4 border-t border-slate-100 bg-white relative">
<!-- Attachments Preview (Hidden) -->
<div id="attachment-preview" class="hidden absolute -top-12 left-4 bg-white border border-slate-200 rounded-lg shadow-sm px-3 py-1.5 flex items-center gap-2 text-xs">
<i data-lucide="file-text" class="w-4 h-4 text-slate-400"></i>
<span class="text-slate-700 font-medium">protocol_draft_v1.pdf</span>
<button onclick="removeAttachment()" class="hover:text-red-500"><i data-lucide="x" class="w-3 h-3"></i></button>
</div>
<div class="max-w-4xl mx-auto relative bg-slate-50 border border-slate-200 rounded-2xl focus-within:ring-2 focus-within:ring-brand-blue/10 focus-within:border-brand-blue transition-all shadow-sm">
<textarea id="chat-input" rows="1"
class="w-full py-3.5 pl-4 pr-12 bg-transparent border-none focus:ring-0 text-sm resize-none max-h-32"
placeholder="输入问题,或使用 / 呼出快捷指令..."
oninput="this.style.height = ''; this.style.height = this.scrollHeight + 'px'"
onkeydown="if(event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); sendMessage(); }"
></textarea>
<!-- Toolbar -->
<div class="absolute bottom-2 right-2 flex items-center gap-1">
<button class="p-2 text-slate-400 hover:text-slate-600 hover:bg-slate-200 rounded-lg transition-colors" onclick="document.getElementById('file-upload').click()">
<i data-lucide="paperclip" class="w-4 h-4"></i>
</button>
<input type="file" id="file-upload" class="hidden" onchange="handleFileUpload(this)">
<button id="send-btn" class="p-2 bg-slate-900 text-white rounded-lg hover:bg-slate-700 transition-colors shadow-sm disabled:opacity-50 disabled:cursor-not-allowed" onclick="sendMessage()">
<i data-lucide="send-horizontal" class="w-4 h-4"></i>
</button>
</div>
</div>
<div class="text-center mt-2">
<p class="text-[10px] text-slate-400">内容由 AI 生成,需经过专业人员核实。支持 Markdown 公式与表格。</p>
</div>
</div>
</main>
</div>
</div>
<!-- ================= 逻辑脚本 ================= -->
<script>
// --- 1. 智能体配置 (12 Modules) ---
const AGENTS = {
'TOPIC_01': { name: '科学问题梳理', icon: 'list-tree', color: 'blue', prompt: '你好!我是**科学问题梳理助手**。请告诉我你感兴趣的研究方向,我将从科学性、创新性等维度帮你梳理。' },
'TOPIC_02': { name: 'PICO 梳理', icon: 'target', color: 'blue', prompt: '你好!我是**PICO梳理助手**。请描述你的研究想法,我来帮你拆解为 P (人群)、I (干预)、C (对照)、O (结局)。' },
'TOPIC_03': { name: '选题评价', icon: 'clipboard-check', color: 'blue', prompt: '你好!我是**选题评价助手**。请提供你的初步选题,我将从创新性、临床价值等方面进行评估。' },
'DESIGN_04': { name: '观察指标设计', icon: 'eye', color: 'blue', prompt: '你好!我是**观察指标设计助手**。请告诉我你的研究目的,我来帮你推荐主要和次要观察指标。' },
'DESIGN_05': { name: '病例报告表设计', icon: 'table', color: 'blue', prompt: '你好!我是**CRF设计助手**。我可以帮你构建病例报告表的框架。' },
'DESIGN_06': { name: '样本量计算', icon: 'calculator', color: 'blue', prompt: '你好需要计算样本量吗请告诉我研究类型如RCT、队列研究和主要指标的预期值。' },
'DESIGN_07': { name: '方案撰写 (Pro)', icon: 'file-text', color: 'blue', prompt: '你好!欢迎来到**方案设计工作台**。我们将基于科学问题和PICOS信息共同撰写一份完整的临床研究方案。' },
'REVIEW_08': { name: '方法学评审', icon: 'shield-check', color: 'yellow', prompt: '你好!我是**方法学评审助手**。请上传你的方案草稿,我将模拟审稿人视角进行方法学审查。' },
'WRITING_11': { name: '论文润色', icon: 'pen-tool', color: 'purple', prompt: 'Please paste the text you want to polish. I will check grammar, flow, and academic tone.' },
'WRITING_12': { name: '论文翻译', icon: 'languages', color: 'purple', prompt: '你好!请贴入需要翻译的段落,我将为你提供地道的学术英语翻译。' }
};
let currentAgentId = null;
// --- 2. 视图切换逻辑 ---
function enterChat(agentId, initialQuery = '') {
const agent = AGENTS[agentId];
if (!agent) {
// 处理工具类卡片 (09, 10) 的逻辑
// 在真实场景中跳转路由,这里模拟提示
alert(`即将跳转至工具模块:${agentId}`);
return;
}
currentAgentId = agentId;
// UI Update
document.getElementById('view-dashboard').style.display = 'none';
document.getElementById('view-chat').classList.remove('hidden');
// Set Header Info
document.getElementById('current-agent-name').innerText = agent.name;
const iconContainer = document.getElementById('current-agent-icon');
iconContainer.innerHTML = `<i data-lucide="${agent.icon}" class="w-5 h-5"></i>`;
// Set Color Theme (Update Header Icon Bg)
iconContainer.className = `w-8 h-8 rounded-lg flex items-center justify-center text-white`;
if (agent.color === 'purple') iconContainer.classList.add('bg-purple-600');
else if (agent.color === 'yellow') iconContainer.classList.add('bg-yellow-600');
else iconContainer.classList.add('bg-brand-blue');
// Init Chat
const container = document.getElementById('messages-container');
container.innerHTML = '';
// Add Welcome Message
addMessage('ai', agent.prompt);
// If there's an initial query from dashboard search
if (initialQuery) {
setTimeout(() => {
document.getElementById('chat-input').value = initialQuery;
sendMessage();
}, 500);
}
lucide.createIcons();
}
function exitChat() {
document.getElementById('view-chat').classList.add('hidden');
document.getElementById('view-dashboard').style.display = 'block';
}
function toggleSidebar() {
const sidebar = document.getElementById('chat-sidebar');
const overlay = document.getElementById('mobile-overlay');
if (sidebar.classList.contains('-translate-x-full')) {
sidebar.classList.remove('-translate-x-full'); // Open
overlay.classList.remove('hidden');
} else {
sidebar.classList.add('-translate-x-full'); // Close
overlay.classList.add('hidden');
}
}
function clearChat() {
document.getElementById('messages-container').innerHTML = '';
if (currentAgentId) addMessage('ai', AGENTS[currentAgentId].prompt);
}
// --- 3. 消息发送与深度思考模拟 ---
function sendMessage() {
const input = document.getElementById('chat-input');
const text = input.value.trim();
if (!text) return;
addMessage('user', text);
input.value = '';
input.style.height = 'auto';
// Simulate AI
const msgId = Date.now();
const container = document.getElementById('messages-container');
const aiWrapper = document.createElement('div');
aiWrapper.className = 'flex gap-3 animate-fade-in';
aiWrapper.innerHTML = `
<div class="w-8 h-8 rounded-lg bg-gradient-to-br from-indigo-500 to-brand-blue text-white flex-shrink-0 flex items-center justify-center shadow-sm">
<i data-lucide="bot" class="w-5 h-5"></i>
</div>
<div class="flex-1 space-y-2 max-w-[85%]">
<div id="think-${msgId}" class="thinking-box expanded bg-slate-50 border border-slate-200 rounded-lg text-xs text-slate-500 overflow-hidden">
<div class="px-3 py-2 border-b border-slate-100 flex items-center justify-between cursor-pointer hover:bg-slate-100" onclick="toggleThinking('${msgId}')">
<div class="flex items-center gap-2">
<i data-lucide="brain-circuit" class="w-3.5 h-3.5 animate-pulse text-brand-blue"></i>
<span class="font-medium" id="think-status-${msgId}">深度思考中...</span>
</div>
<i id="chevron-${msgId}" data-lucide="chevron-down" class="w-3 h-3 transition-transform"></i>
</div>
<div class="p-3 font-mono text-slate-400 bg-slate-50/50" id="think-content-${msgId}"></div>
</div>
<div id="answer-${msgId}" class="prose prose-sm prose-slate max-w-none bg-white p-3 rounded-xl border border-transparent"><span class="cursor-blink"></span></div>
</div>
`;
container.appendChild(aiWrapper);
container.scrollTop = container.scrollHeight;
lucide.createIcons();
simulateDeepSeekStream(msgId);
}
function addMessage(role, content) {
const container = document.getElementById('messages-container');
const isUser = role === 'user';
const div = document.createElement('div');
div.className = `flex gap-3 ${isUser ? 'flex-row-reverse' : ''} animate-fade-in`;
const avatar = isUser
? `<div class="w-8 h-8 rounded-lg bg-slate-200 overflow-hidden flex-shrink-0"><img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Felix"></div>`
: `<div class="w-8 h-8 rounded-lg bg-gradient-to-br from-indigo-500 to-brand-blue text-white flex-shrink-0 flex items-center justify-center shadow-sm"><i data-lucide="bot" class="w-5 h-5"></i></div>`;
const bubble = isUser
? `<div class="bg-brand-blue text-white px-4 py-2.5 rounded-2xl rounded-tr-none shadow-md text-sm max-w-[85%]">${content}</div>`
: `<div class="prose prose-sm prose-slate max-w-[85%] bg-white border border-slate-100 px-4 py-3 rounded-2xl rounded-tl-none shadow-sm">${marked.parse(content)}</div>`;
div.innerHTML = avatar + bubble;
container.appendChild(div);
container.scrollTop = container.scrollHeight;
lucide.createIcons();
}
function simulateDeepSeekStream(msgId) {
const thoughts = ["识别上下文...", "检索知识库...", "组织回答..."];
const thinkContent = document.getElementById(`think-content-${msgId}`);
const thinkStatus = document.getElementById(`think-status-${msgId}`);
const answerBox = document.getElementById(`answer-${msgId}`);
let tIndex = 0;
const thinkInterval = setInterval(() => {
if (tIndex >= thoughts.length) {
clearInterval(thinkInterval);
thinkStatus.innerText = `已深度思考 (耗时 1.2s)`;
document.getElementById(`think-${msgId}`).classList.add('collapsed');
document.getElementById(`think-${msgId}`).classList.remove('expanded');
startAnswering(answerBox);
return;
}
const p = document.createElement('div');
p.innerText = `> ${thoughts[tIndex]}`;
thinkContent.appendChild(p);
tIndex++;
}, 500);
}
function startAnswering(element) {
const fullText = "收到,我明白了。这是一个很好的研究方向...";
let i = 0;
element.innerHTML = '<span class="cursor-blink"></span>';
const interval = setInterval(() => {
if (i >= fullText.length) {
clearInterval(interval);
element.innerHTML = marked.parse(fullText);
return;
}
element.innerHTML = marked.parse(fullText.substring(0, i+1)) + '<span class="cursor-blink"></span>';
i++;
document.getElementById('messages-container').scrollTop = document.getElementById('messages-container').scrollHeight;
}, 30);
}
function toggleThinking(id) {
const box = document.getElementById(`think-${id}`);
if (box.classList.contains('collapsed')) {
box.classList.remove('collapsed');
box.classList.add('expanded');
document.getElementById(`chevron-${id}`).style.transform = 'rotate(180deg)';
} else {
box.classList.add('collapsed');
box.classList.remove('expanded');
document.getElementById(`chevron-${id}`).style.transform = 'rotate(0deg)';
}
}
function handleFileUpload(input) {
if (input.files && input.files[0]) {
const preview = document.getElementById('attachment-preview');
preview.querySelector('span').innerText = input.files[0].name;
preview.classList.remove('hidden');
}
}
function removeAttachment() {
document.getElementById('file-upload').value = '';
document.getElementById('attachment-preview').classList.add('hidden');
}
lucide.createIcons();
</script>
</body>
</html>