Files

600 lines
39 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>SSA-Pro 智能统计工作台 V11.0 (V9 Visuals + V10 Features)</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
body { font-family: 'Inter', sans-serif; background-color: #f8fafc; color: #334155; overflow: hidden; }
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
.fade-in { animation: fadeIn 0.4s ease-out forwards; opacity: 0; }
.slide-up { animation: slideUp 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards; opacity: 0; transform: translateY(15px); }
.pop-in { animation: popIn 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards; opacity: 0; transform: scale(0.95); }
@keyframes fadeIn { to { opacity: 1; } }
@keyframes slideUp { to { transform: translateY(0); opacity: 1; } }
@keyframes popIn { to { transform: scale(1); opacity: 1; } }
/* 科学表格样式 (V9 风格) */
.sci-table { width: 100%; border-collapse: collapse; font-size: 0.875rem; }
.sci-table th { border-top: 2px solid #1e293b; border-bottom: 1px solid #1e293b; padding: 10px 12px; text-align: left; font-weight: 600; color: #1e293b; }
.sci-table td { border-bottom: 1px solid #e2e8f0; padding: 10px 12px; color: #475569; }
.sci-table tr:last-child td { border-bottom: 2px solid #1e293b; }
.status-dot { height: 8px; width: 8px; border-radius: 50%; display: inline-block; margin-right: 6px; }
.status-success { background-color: #22c55e; box-shadow: 0 0 0 2px #dcfce7; }
pre code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.85rem; line-height: 1.5; }
/* Artifact 阴影 (V9 风格) */
.artifact-shadow { box-shadow: 0 4px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1); }
/* 模态框背景模糊 */
.modal-backdrop { backdrop-filter: blur(4px); }
</style>
</head>
<body class="h-screen flex text-slate-800">
<!-- 全局 Toast 容器 -->
<div id="toast-container" class="fixed top-6 left-1/2 transform -translate-x-1/2 z-50 flex flex-col gap-2 pointer-events-none"></div>
<!-- ========================================== -->
<!-- 左侧Gemini 交互 + V9 视觉的抽屉侧边栏 -->
<!-- ========================================== -->
<aside id="sidebar" class="w-16 bg-slate-50 border-r border-slate-200 flex flex-col z-30 flex-shrink-0 transition-all duration-300 ease-in-out relative">
<!-- 顶部:汉堡菜单与 Logo -->
<div class="h-16 flex items-center px-3 flex-shrink-0 overflow-hidden whitespace-nowrap">
<button onclick="toggleSidebar()" class="w-10 h-10 rounded-lg hover:bg-slate-200 flex items-center justify-center text-slate-600 transition flex-shrink-0">
<i class="fa-solid fa-bars text-lg"></i>
</button>
<div class="sidebar-text hidden ml-3 flex items-center gap-2 fade-in">
<div class="w-7 h-7 bg-slate-800 rounded flex items-center justify-center text-white shadow-sm">
<i class="fa-solid fa-chart-simple text-xs"></i>
</div>
<span class="font-bold text-slate-800 tracking-tight text-lg">SSA-Pro</span>
</div>
</div>
<!-- 核心操作区 -->
<div class="px-3 mt-4 flex flex-col gap-3">
<!-- 新建按钮 -->
<button id="new-chat-btn" class="w-10 h-10 bg-white border border-slate-200 hover:border-blue-300 shadow-sm rounded-lg flex items-center justify-center text-slate-700 transition-all duration-300 overflow-hidden whitespace-nowrap group">
<i class="fa-solid fa-plus text-blue-600"></i>
<span class="sidebar-text hidden ml-3 font-medium text-sm text-slate-700 group-hover:text-blue-600 transition">新建分析</span>
</button>
<!-- 收起时显示的历史图标 -->
<button id="history-icon-only" onclick="toggleSidebar()" class="w-10 h-10 rounded-lg hover:bg-slate-200 flex items-center justify-center text-slate-500 transition-all duration-300" title="查看历史记录">
<i class="fa-solid fa-clock-rotate-left"></i>
</button>
</div>
<!-- 历史记录列表 (展开时显示) -->
<div id="history-list" class="flex-1 overflow-y-auto px-3 mt-6 hidden opacity-0 transition-opacity duration-300 pb-4">
<div class="px-2 py-1 text-[10px] font-bold text-slate-400 uppercase tracking-wider mb-2">近期分析</div>
<div class="space-y-1">
<button class="w-full text-left px-3 py-2.5 rounded-lg bg-white border border-slate-200 text-blue-700 font-medium text-sm truncate flex items-center gap-3 shadow-sm">
<i class="fa-regular fa-message text-blue-500"></i> 心血管药物研究
</button>
<button class="w-full text-left px-3 py-2.5 rounded-lg hover:bg-slate-200 text-slate-600 text-sm truncate flex items-center gap-3 transition">
<i class="fa-regular fa-message text-slate-400"></i> 生存分析方案咨询
</button>
<button class="w-full text-left px-3 py-2.5 rounded-lg hover:bg-slate-200 text-slate-600 text-sm truncate flex items-center gap-3 transition">
<i class="fa-regular fa-message text-slate-400"></i> 基线特征 Table 1
</button>
</div>
</div>
<!-- 底部设置图标 -->
<div class="mt-auto p-3 flex-shrink-0 border-t border-slate-200">
<button id="settings-btn" class="w-10 h-10 rounded-lg hover:bg-slate-200 flex items-center justify-center text-slate-500 transition-all duration-300 overflow-hidden whitespace-nowrap">
<i class="fa-solid fa-gear"></i>
<span class="sidebar-text hidden ml-3 font-medium text-sm">配置中心</span>
</button>
</div>
</aside>
<!-- ========================================== -->
<!-- 动态主容器 (Chat + Workspace) -->
<!-- ========================================== -->
<div class="flex-1 flex overflow-hidden relative bg-white" id="main-container">
<!-- ========================================== -->
<!-- 左半部分 (或全屏):对话区 (Chat Pane) -->
<!-- 采用了 V10 的居中布局V9 的视觉卡片 -->
<!-- ========================================== -->
<section id="chat-pane" class="flex-1 flex flex-col h-full transition-all duration-500 ease-in-out relative bg-[#f8fafc]">
<!-- Chat Header (V9 风格) -->
<header class="h-16 flex items-center justify-between px-6 z-10 border-b border-slate-100 bg-white/90 backdrop-blur flex-shrink-0 absolute top-0 left-0 right-0">
<div class="font-semibold text-slate-800 text-lg">心血管药物疗效研究</div>
<div class="flex items-center gap-2 text-xs font-medium text-slate-500 bg-slate-50 px-3 py-1.5 rounded-full border border-slate-100 shadow-sm">
<span class="status-dot status-success animate-pulse"></span> R Engine Ready
</div>
</header>
<!-- Chat Messages (居中对齐) -->
<div id="chat-container" class="flex-1 overflow-y-auto px-4 md:px-8 pt-24 flex flex-col items-center pb-6">
<!-- 内部定宽容器,保证阅读体验 -->
<div class="w-full max-w-3xl space-y-8 min-h-full pb-32">
<!-- 欢迎语 (V9 风格) -->
<div class="flex gap-4 slide-up">
<div class="w-8 h-8 rounded-full bg-slate-800 flex items-center justify-center flex-shrink-0 mt-0.5"><i class="fa-solid fa-robot text-white text-xs"></i></div>
<div class="bg-white text-slate-700 p-4 rounded-2xl rounded-tl-none text-sm leading-relaxed border border-slate-200 shadow-sm">
你好!我是 SSA-Pro 智能统计助手。<br>你可以直接描述研究目标,我将为你生成分析方案;或者点击下方 📎 <b>上传数据文件</b>,我们将直接开始分析。
</div>
</div>
<!-- User Msg (V9 风格) -->
<div class="flex gap-4 flex-row-reverse slide-up" style="animation-delay: 0.1s">
<div class="w-8 h-8 rounded-full bg-slate-200 flex items-center justify-center flex-shrink-0 mt-0.5"><i class="fa-solid fa-user text-slate-500 text-xs"></i></div>
<div class="bg-blue-600 text-white p-4 rounded-2xl rounded-tr-none text-sm leading-relaxed shadow-md">
我在研究一种新药,收集了实验组和对照组患者的血压下降值数据。我想知道新药是否有效。
</div>
</div>
<!-- AI Msg (无数据纯咨询) -->
<div class="flex gap-4 slide-up" style="animation-delay: 0.2s">
<div class="w-8 h-8 rounded-full bg-slate-800 flex items-center justify-center flex-shrink-0 mt-0.5"><i class="fa-solid fa-robot text-white text-xs"></i></div>
<div class="bg-white border border-slate-200 text-slate-700 p-4 rounded-2xl rounded-tl-none text-sm leading-relaxed shadow-sm">
理解了。针对“两组独立样本疗效对比”,如果是连续数值型变量,通常推荐使用 <b>独立样本 T 检验 (T-Test)</b><br><br>
💡 <b>请上传数据文件</b>,我将根据实际列名生成可执行的方案并展示在右侧工作区。
</div>
</div>
<div id="chat-anchor"></div>
</div>
</div>
<!-- Chat Input (V9 质感的悬浮输入框) -->
<div class="flex-shrink-0 w-full flex justify-center pb-6 px-4 absolute bottom-0 bg-gradient-to-t from-[#f8fafc] via-[#f8fafc] to-transparent pt-12 z-20">
<div class="w-full max-w-3xl relative">
<!-- V9 风格:包裹挂载区和输入框的容器 -->
<div class="shadow-lg rounded-2xl bg-white border border-slate-200 focus-within:border-blue-400 focus-within:ring-4 focus-within:ring-blue-50 transition-all flex flex-col p-2">
<!-- 数据挂载区 (嵌入式) -->
<div id="data-mount-zone" class="hidden items-center mb-1 pl-1 pop-in w-full">
<div class="bg-slate-50 border border-slate-200 rounded-lg px-2 py-1.5 flex items-center gap-3 w-max transition-all hover:border-blue-300">
<div class="w-8 h-8 rounded bg-green-50 flex items-center justify-center border border-green-100">
<i class="fa-solid fa-file-csv text-green-600 text-sm"></i>
</div>
<div class="flex flex-col pr-2">
<span class="text-xs font-bold text-slate-700">BP_Trial_Data.csv</span>
<span class="text-[10px] text-slate-400 mt-0.5 leading-none">120 rows • 45KB</span>
</div>
<div class="h-6 w-px bg-slate-200 mx-1"></div>
<button onclick="removeData(event)" class="text-slate-400 hover:text-red-500 transition px-1" title="移除数据">
<i class="fa-solid fa-xmark text-sm"></i>
</button>
</div>
</div>
<!-- 文本输入行 -->
<div class="relative flex items-end w-full">
<button onclick="simulateUpload()" id="btn-upload" class="absolute left-1 bottom-1 text-slate-400 hover:text-blue-600 transition w-9 h-9 flex items-center justify-center rounded-lg hover:bg-slate-100 z-10" title="上传数据">
<i class="fa-solid fa-paperclip text-lg"></i>
</button>
<textarea class="w-full bg-transparent py-2.5 pl-11 pr-12 focus:outline-none resize-none text-sm text-slate-700 placeholder-slate-400" rows="1" placeholder="发送消息,或点击回形针 📎 上传数据触发分析..." style="min-height: 44px;"></textarea>
<button class="absolute right-1 bottom-1 w-9 h-9 bg-slate-800 text-white rounded-lg hover:bg-slate-700 transition flex items-center justify-center shadow-sm z-10">
<i class="fa-solid fa-arrow-up text-sm"></i>
</button>
</div>
</div>
<div class="text-center mt-2">
<span class="text-[10px] text-slate-400">AI 可能会犯错,请核实生成的统计结论。</span>
</div>
</div>
</div>
</section>
<!-- ========================================== -->
<!-- 右半部分:动态工作区 (Workspace Pane) - 保持 V9 完美体验 -->
<!-- ========================================== -->
<section id="workspace-pane" class="w-0 overflow-hidden bg-slate-50 transition-all duration-500 ease-in-out flex flex-col flex-shrink-0 border-l border-transparent relative">
<div class="w-full min-w-[600px] h-full flex flex-col absolute inset-0">
<!-- 顶部工具栏 -->
<header class="h-14 flex items-center justify-between px-4 border-b border-slate-200 bg-slate-100/50 flex-shrink-0">
<div class="flex items-center gap-2">
<span id="workspace-badge" class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-blue-100 text-blue-700 border border-blue-200">
Planning
</span>
<span id="workspace-title" class="text-sm font-semibold text-slate-700">统计分析计划 (SAP)</span>
</div>
<div class="flex gap-2">
<button id="btn-code" class="hidden w-8 h-8 bg-white border border-slate-200 text-slate-500 rounded-lg hover:text-blue-600 transition shadow-sm flex items-center justify-center" title="查看源代码" onclick="openModal('code-modal')">
<i class="fa-brands fa-r-project"></i>
</button>
<button onclick="closeWorkspace()" class="w-8 h-8 bg-white border border-slate-200 text-slate-500 rounded-lg hover:bg-slate-100 hover:text-slate-800 transition shadow-sm flex items-center justify-center" title="收起工作区">
<i class="fa-solid fa-right-to-bracket"></i>
</button>
</div>
</header>
<!-- 工作区主画布 -->
<div class="flex-1 p-4 md:p-6 overflow-y-auto">
<div class="bg-white rounded-2xl artifact-shadow border border-slate-200/60 min-h-full flex flex-col relative overflow-hidden">
<!-- 视图 1SAP 文档 -->
<div id="view-sap" class="p-8 w-full fade-in">
<h1 class="text-xl font-bold text-slate-900 border-b border-slate-200 pb-4 mb-6">研究课题:新药治疗高血压疗效对比</h1>
<div class="space-y-6">
<section>
<h3 class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-2">1. 推荐统计方法</h3>
<div class="border border-blue-100 rounded-xl overflow-hidden">
<div class="bg-blue-50 px-4 py-3 border-b border-blue-100 font-medium text-blue-800 text-sm flex items-center gap-2">
<i class="fa-solid fa-star text-amber-500"></i> 首选:独立样本 T 检验 (Independent T-Test)
</div>
<div class="p-4 text-sm text-slate-700 bg-white grid grid-cols-2 gap-4">
<div class="bg-slate-50 p-3 rounded-lg border border-slate-100">
<div class="text-xs text-slate-400 mb-1">自变量 (X)</div>
<div class="font-mono bg-slate-200/50 px-1 rounded inline-block">Group</div> <span class="text-xs text-slate-500 ml-1">(分类)</span>
</div>
<div class="bg-slate-50 p-3 rounded-lg border border-slate-100">
<div class="text-xs text-slate-400 mb-1">因变量 (Y)</div>
<div class="font-mono bg-slate-200/50 px-1 rounded inline-block">BP_Change</div> <span class="text-xs text-slate-500 ml-1">(数值)</span>
</div>
</div>
</div>
</section>
<section>
<h3 class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-2">2. 统计护栏与执行策略</h3>
<ul class="text-sm text-slate-700 space-y-3 pl-1">
<li class="flex items-start gap-2 bg-slate-50 p-3 rounded-lg border border-slate-100">
<i class="fa-solid fa-shield text-blue-500 mt-1"></i>
<div>
<b>正态性假设检验 (Shapiro-Wilk)</b><br>
<span class="text-xs text-slate-500 mt-1 block">系统将在核心计算前执行检查。若 P < 0.05将触发降级策略自动改用 <b>Wilcoxon 秩和检验</b> 以保证结果严谨性。</span>
</div>
</li>
</ul>
</section>
<!-- 执行按钮区 -->
<div class="mt-8 pt-6 border-t border-slate-100 flex justify-end">
<button onclick="runAnalysis(this)" id="btn-run" class="bg-blue-600 text-white px-6 py-2.5 rounded-lg font-medium hover:bg-blue-700 transition shadow-md flex items-center gap-2">
<i class="fa-solid fa-play text-xs"></i> 开始执行分析
</button>
</div>
</div>
</div>
<!-- 视图 2动态执行日志 -->
<div id="view-execution" class="absolute inset-0 bg-white p-8 hidden flex-col items-center justify-center">
<div class="w-full max-w-lg">
<div class="flex items-center gap-3 mb-6 justify-center">
<i class="fa-solid fa-circle-notch fa-spin text-blue-600 text-2xl"></i>
<h3 class="font-bold text-slate-800 text-lg">正在调用云端 R 引擎...</h3>
</div>
<!-- 酷炫的终端样式 -->
<div class="bg-[#0f172a] rounded-xl p-5 font-mono text-xs text-slate-300 shadow-2xl relative overflow-hidden h-64 border border-slate-700">
<div class="absolute left-[31px] top-6 bottom-6 w-px bg-slate-700"></div>
<div id="trace-logs" class="space-y-4 relative z-10">
<!-- 动态插入日志 -->
</div>
</div>
</div>
</div>
<!-- 视图 3结果报告 -->
<div id="view-result" class="p-8 w-full hidden">
<!-- 核心结论 -->
<div class="bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-100 p-5 rounded-xl mb-8 flex gap-4 items-start shadow-sm">
<i class="fa-solid fa-lightbulb text-blue-500 text-xl mt-0.5"></i>
<div>
<h4 class="font-bold text-slate-800 text-sm mb-1">AI 统计解读</h4>
<p class="text-sm text-slate-700 leading-relaxed">
结果表明新药组Drug的血压下降幅度显著大于对照组Placebo。差异具有统计学意义 (<span class="text-red-600 font-bold">P < 0.001</span>)。
<br><span class="text-xs text-slate-500 mt-1 inline-block">* 注:因数据未通过正态性检验,本结果由自动降级后的 Wilcoxon 秩和检验得出。</span>
</p>
</div>
</div>
<!-- 表格 -->
<div class="mb-8">
<h4 class="text-xs font-bold text-slate-500 uppercase tracking-wider mb-3">Table 1. 血压下降值比较</h4>
<div class="border border-slate-200 rounded-lg overflow-hidden">
<table class="sci-table bg-white">
<thead>
<tr class="bg-slate-50">
<th>组别</th>
<th>N</th>
<th>下降值 (Median [IQR])</th>
<th>W 统计量</th>
<th>P 值</th>
</tr>
</thead>
<tbody>
<tr class="hover:bg-slate-50">
<td>Drug</td>
<td>60</td>
<td>14.5 [12.1 - 16.8]</td>
<td rowspan="2" class="align-middle border-l border-slate-100">2845.5</td>
<td rowspan="2" class="align-middle border-l border-slate-100 text-red-600 font-bold">< 0.001 **</td>
</tr>
<tr class="hover:bg-slate-50">
<td>Placebo</td>
<td>60</td>
<td>8.2 [6.5 - 10.4]</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 图表 -->
<div>
<h4 class="text-xs font-bold text-slate-500 uppercase tracking-wider mb-3">Figure 1. 分布可视化</h4>
<div class="border border-slate-200 rounded-xl p-6 bg-slate-50 flex justify-center shadow-sm">
<div class="relative w-full max-w-sm h-56 flex items-end justify-center gap-16 pb-8 border-b-2 border-l-2 border-slate-400">
<div class="w-16 h-40 bg-blue-500/80 border-2 border-blue-600 rounded-sm shadow-sm relative"><span class="absolute -bottom-6 w-full text-center text-xs font-bold text-slate-600">Drug</span></div>
<div class="w-16 h-20 bg-slate-300/80 border-2 border-slate-400 rounded-sm shadow-sm relative"><span class="absolute -bottom-6 w-full text-center text-xs font-bold text-slate-600">Placebo</span></div>
<div class="absolute top-6 left-[30%] right-[30%] h-3 border-t-2 border-l-2 border-r-2 border-slate-800"></div>
<div class="absolute top-0 w-full text-center text-sm font-bold text-slate-800">P < 0.001</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
<!-- R代码模态框 (保留 V9 风格) -->
<div id="code-modal" class="fixed inset-0 z-50 hidden flex items-center justify-center">
<div class="absolute inset-0 bg-slate-900/60 backdrop-blur-sm" onclick="closeModal('code-modal')"></div>
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-3xl max-h-[80vh] flex flex-col relative z-10 pop-in m-4 border border-slate-200">
<div class="flex items-center justify-between px-6 py-4 border-b border-slate-100 bg-slate-50 rounded-t-2xl">
<h3 class="font-bold text-slate-800 flex items-center gap-2">
<i class="fa-brands fa-r-project text-blue-600 text-xl"></i> R 源代码交付
</h3>
<button onclick="closeModal('code-modal')" class="text-slate-400 hover:text-slate-700 w-8 h-8 rounded-full hover:bg-slate-200 transition flex items-center justify-center">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
<div class="p-6 overflow-y-auto flex-1 bg-white">
<pre class="bg-slate-900 text-slate-50 p-5 rounded-xl overflow-x-auto shadow-inner text-xs font-mono leading-relaxed"><code><span class="text-slate-500"># ------------------------------------------------</span>
<span class="text-slate-500"># SSA-Pro 生成代码: 独立样本差异分析</span>
<span class="text-slate-500"># ------------------------------------------------</span>
<span class="text-pink-400">library</span>(ggplot2)
<span class="text-slate-500"># 1. 加载数据</span>
df <- <span class="text-blue-300">read.csv</span>(<span class="text-green-300">"BP_Trial_Data.csv"</span>)
<span class="text-slate-500"># 2. 核心计算 (自动降级至 Wilcoxon)</span>
res <- <span class="text-blue-300">wilcox.test</span>(BP_Change ~ Group, data = df)
<span class="text-blue-300">print</span>(res)</code></pre>
</div>
<div class="px-6 py-4 border-t border-slate-100 flex justify-end gap-3 bg-slate-50 rounded-b-2xl">
<button class="px-4 py-2 text-sm font-medium text-white bg-slate-800 rounded-lg hover:bg-slate-700 transition shadow-sm" onclick="showToast('R 脚本已下载', 'success'); closeModal('code-modal');">
<i class="fa-solid fa-download mr-1"></i> 下载 .R 文件
</button>
</div>
</div>
</div>
<script>
const sidebar = document.getElementById('sidebar');
const chatContainer = document.getElementById('chat-container');
const anchor = document.getElementById('chat-anchor');
const workspacePane = document.getElementById('workspace-pane');
let isSidebarExpanded = false;
// 1. 抽屉菜单切换
function toggleSidebar() {
const texts = document.querySelectorAll('.sidebar-text');
const historyList = document.getElementById('history-list');
const newChatBtn = document.getElementById('new-chat-btn');
const historyIconBtn = document.getElementById('history-icon-only');
const settingsBtn = document.getElementById('settings-btn');
isSidebarExpanded = !isSidebarExpanded;
if(isSidebarExpanded) {
// 展开 w-64
sidebar.classList.replace('w-16', 'w-64');
texts.forEach(el => el.classList.remove('hidden'));
newChatBtn.classList.replace('w-10', 'w-full');
newChatBtn.classList.replace('justify-center', 'justify-start');
newChatBtn.classList.add('px-3');
settingsBtn.classList.replace('w-10', 'w-full');
settingsBtn.classList.replace('justify-center', 'justify-start');
settingsBtn.classList.add('px-3');
historyIconBtn.classList.add('hidden');
historyList.classList.remove('hidden');
setTimeout(() => historyList.classList.replace('opacity-0', 'opacity-100'), 150);
} else {
// 收缩 w-16
sidebar.classList.replace('w-64', 'w-16');
texts.forEach(el => el.classList.add('hidden'));
newChatBtn.classList.replace('w-full', 'w-10');
newChatBtn.classList.replace('justify-start', 'justify-center');
newChatBtn.classList.remove('px-3');
settingsBtn.classList.replace('w-full', 'w-10');
settingsBtn.classList.replace('justify-start', 'justify-center');
settingsBtn.classList.remove('px-3');
historyList.classList.add('hidden');
historyList.classList.replace('opacity-100', 'opacity-0');
historyIconBtn.classList.remove('hidden');
}
}
// 2. 工作区拉出与收起
function openWorkspace() {
workspacePane.classList.remove('w-0', 'border-transparent');
workspacePane.classList.add('w-[60%]', 'border-slate-200');
setTimeout(scrollToBottom, 300);
}
function closeWorkspace() {
workspacePane.classList.remove('w-[60%]', 'border-slate-200');
workspacePane.classList.add('w-0', 'border-transparent');
}
// 3. 模拟上传与执行交互
function simulateUpload() {
const mountZone = document.getElementById('data-mount-zone');
mountZone.classList.remove('hidden');
mountZone.classList.add('flex');
document.getElementById('btn-upload').classList.add('opacity-50', 'pointer-events-none');
showToast('数据读取成功,正在分析结构...', 'success');
setTimeout(() => {
const aiResponseHTML = `
<div class="flex gap-4 slide-up w-full">
<div class="w-8 h-8 rounded-full bg-slate-800 flex items-center justify-center flex-shrink-0 mt-0.5">
<i class="fa-solid fa-robot text-white text-xs"></i>
</div>
<div class="bg-white border border-slate-200 text-slate-700 p-4 rounded-2xl rounded-tl-none shadow-sm flex-1 max-w-2xl">
<div class="text-sm leading-relaxed mb-3">
<i class="fa-solid fa-bolt text-amber-500 mr-1"></i> <b>数据已挂载</b>。我已经为您规划好了统计分析计划书 (SAP)。
</div>
<button onclick="openWorkspace()" class="bg-blue-50 text-blue-700 border border-blue-200 px-4 py-2.5 rounded-xl w-full flex items-center justify-between hover:bg-blue-100 transition">
<div class="flex items-center gap-3">
<div class="w-8 h-8 bg-white rounded-lg flex items-center justify-center text-blue-600 shadow-sm"><i class="fa-solid fa-file-signature"></i></div>
<div class="text-left">
<div class="text-sm font-bold">查看分析计划 (SAP)</div>
<div class="text-[10px] text-blue-500">参数映射完成,等待执行</div>
</div>
</div>
<i class="fa-solid fa-arrow-right-long text-blue-400"></i>
</button>
</div>
</div>`;
anchor.insertAdjacentHTML('beforebegin', aiResponseHTML);
scrollToBottom();
openWorkspace();
}, 1000);
}
function removeData(e) {
e.stopPropagation();
document.getElementById('data-mount-zone').classList.add('hidden');
document.getElementById('data-mount-zone').classList.remove('flex');
document.getElementById('btn-upload').classList.remove('opacity-50', 'pointer-events-none');
closeWorkspace();
}
function runAnalysis(btn) {
btn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i> 执行中...';
btn.classList.replace('bg-blue-600', 'bg-slate-400');
btn.classList.replace('hover:bg-blue-700', 'hover:bg-slate-400');
btn.disabled = true;
document.getElementById('view-sap').classList.add('hidden');
document.getElementById('view-execution').classList.remove('hidden');
document.getElementById('view-execution').classList.add('flex');
document.getElementById('workspace-badge').innerText = 'Running';
document.getElementById('workspace-badge').className = 'px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-amber-100 text-amber-700 border border-amber-200';
document.getElementById('workspace-title').innerText = '执行日志 Trace';
const execMsg = `
<div class="flex gap-4 slide-up w-full">
<div class="w-8 h-8 rounded-full bg-slate-800 flex items-center justify-center flex-shrink-0 mt-0.5"><i class="fa-solid fa-robot text-white text-xs"></i></div>
<div class="bg-amber-50 border border-amber-200 text-amber-800 p-3 rounded-2xl rounded-tl-none shadow-sm text-sm">
<i class="fa-solid fa-circle-notch fa-spin mr-1"></i> 正在调用 R 引擎执行计算...
</div>
</div>`;
anchor.insertAdjacentHTML('beforebegin', execMsg);
scrollToBottom();
const logBox = document.getElementById('trace-logs');
logBox.innerHTML = '';
setTimeout(() => { addLog(logBox, 'text-green-400', 'fa-check-circle', '[System] 读取数据 BP_Trial_Data.csv (N=120) 成功'); }, 500);
setTimeout(() => { addLog(logBox, 'text-blue-400', 'fa-spinner fa-spin', '[Guardrail] 正在进行 Shapiro-Wilk 正态性检验...'); }, 1200);
setTimeout(() => {
logBox.lastElementChild.innerHTML = '<i class="fa-solid fa-times-circle text-red-400 bg-[#0f172a] z-10 relative"></i> <span class="ml-2">[Guardrail] 正态性检验未通过 (P=0.002 < 0.05)</span>';
addLog(logBox, 'text-amber-400', 'fa-arrow-turn-down', '[Action] 触发护栏:自动降级为 Wilcoxon 秩和检验');
}, 2500);
setTimeout(() => { addLog(logBox, 'text-blue-400', 'fa-spinner fa-spin', '[Compute] 执行 wilcox.test()...'); }, 3200);
setTimeout(() => {
document.getElementById('view-execution').classList.remove('flex');
document.getElementById('view-execution').classList.add('hidden');
document.getElementById('view-result').classList.remove('hidden');
document.getElementById('view-result').classList.add('fade-in');
document.getElementById('workspace-badge').innerText = 'Completed';
document.getElementById('workspace-badge').className = 'px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-green-100 text-green-700 border border-green-200';
document.getElementById('workspace-title').innerText = '分析结果报告';
document.getElementById('btn-code').classList.remove('hidden');
const finalMsg = `
<div class="flex gap-4 slide-up w-full">
<div class="w-8 h-8 rounded-full bg-slate-800 flex items-center justify-center flex-shrink-0 mt-0.5"><i class="fa-solid fa-robot text-white text-xs"></i></div>
<div class="bg-green-50 border border-green-200 text-green-800 p-3 rounded-2xl rounded-tl-none shadow-sm text-sm">
<i class="fa-solid fa-check-circle mr-1"></i> 分析完成!👉 <b>请在右侧面板查看详细的结果报告和图表。</b>
</div>
</div>`;
anchor.insertAdjacentHTML('beforebegin', finalMsg);
scrollToBottom();
}, 4500);
}
function addLog(container, color, icon, text) {
const div = document.createElement('div');
div.className = 'flex items-center fade-in text-slate-300';
div.innerHTML = `<i class="fa-solid ${icon} ${color} bg-[#0f172a] z-10 relative"></i> <span class="ml-3">${text}</span>`;
container.appendChild(div);
}
function scrollToBottom() {
setTimeout(() => {
const c = document.getElementById('chat-container');
c.scrollTo({ top: c.scrollHeight, behavior: 'smooth' });
}, 50);
}
function openModal(id) { document.getElementById(id).classList.remove('hidden'); }
function closeModal(id) { document.getElementById(id).classList.add('hidden'); }
function showToast(message, type = 'info') {
const container = document.getElementById('toast-container');
const toast = document.createElement('div');
let icon = '<i class="fa-solid fa-info-circle text-blue-500"></i>';
if(type === 'success') icon = '<i class="fa-solid fa-check-circle text-green-500"></i>';
toast.className = `bg-white border border-slate-200 shadow-lg rounded-full px-5 py-3 text-sm font-medium text-slate-700 flex items-center gap-3 slide-up`;
toast.innerHTML = `${icon} <span>${message}</span>`;
container.appendChild(toast);
setTimeout(() => {
toast.style.opacity = '0';
toast.style.transform = 'translateY(-10px)';
toast.style.transition = 'all 0.3s ease';
setTimeout(() => toast.remove(), 300);
}, 2500);
}
</script>
</body>
</html>