geopro/docs/superpowers/mockups/2026-06-16-three-column-lay...

496 lines
26 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>三栏结构重构 · 高保真原型对比</title>
<style>
/* ===== 真实 app 深色令牌src/app/Theme.cpp dark 列) ===== */
:root{
--bg-app:#0E1116; --bg-panel:#161A20; --bg-panel-subtle:#161B22; --bg-header:#12161C;
--bg-hover:#1B2129; --bg-selected:#16243F;
--border:#262C35; --border-strong:#333B45;
--text:#E6E9EF; --text-2:#A4ADBB; --text-3:#7A8494; --text-dis:#5A626F;
--accent:#5E8DF5; --accent-h:#93B4FA;
--canvas-bg:#0B1320; --canvas-soft:#111B2D; --canvas-grid:#1E2A3D;
--canvas-text:#E6ECF5; --canvas-dim:#8A97AC;
--danger:#FF6166; --warn:#F5A623; --ok:#46C07A;
--r:6px;
}
*{box-sizing:border-box;margin:0;padding:0;}
html,body{height:100%;}
body{
font-family:"Microsoft YaHei UI","Segoe UI",system-ui,sans-serif;
background:#05080d;color:var(--text);font-size:13px;
display:flex;flex-direction:column;height:100vh;overflow:hidden;
}
/* ===== 顶部方案切换条(原型控制,非 app 一部分) ===== */
.meta{
display:flex;align-items:center;gap:14px;padding:10px 16px;
background:#11151c;border-bottom:1px solid var(--border);flex:0 0 auto;
}
.meta .lbl{color:var(--text-3);font-size:12px;}
.meta .opts{display:flex;gap:8px;}
.meta button{
font:inherit;font-size:12.5px;color:var(--text-2);background:var(--bg-panel);
border:1px solid var(--border);border-radius:20px;padding:6px 16px;cursor:pointer;
transition:.12s;
}
.meta button:hover{border-color:var(--accent);color:var(--text);}
.meta button.on{background:var(--accent);border-color:var(--accent);color:#fff;font-weight:600;}
.meta .tag{margin-left:auto;font-size:11.5px;color:var(--text-3);}
.meta .tag b{color:var(--ok);}
/* ===== app 外壳 ===== */
.app{flex:1 1 auto;display:flex;flex-direction:column;min-height:0;background:var(--bg-app);}
/* TopBar */
.topbar{
height:46px;flex:0 0 auto;display:flex;align-items:center;gap:14px;padding:0 14px;
background:var(--bg-header);border-bottom:1px solid var(--border);
}
.topbar .logo{width:24px;height:24px;border-radius:6px;background:linear-gradient(135deg,var(--accent),#3B73EC);}
.topbar .ws{font-weight:600;color:var(--text);}
.topbar .ws small{color:var(--text-3);font-weight:400;margin-left:6px;}
.topbar .spacer{flex:1;}
.topbar .ico{width:30px;height:30px;border-radius:6px;display:grid;place-items:center;color:var(--text-2);}
.topbar .ico:hover{background:var(--bg-hover);}
.topbar .avatar{width:30px;height:30px;border-radius:50%;background:var(--accent);color:#fff;display:grid;place-items:center;font-size:12px;font-weight:700;}
/* dock 网格 */
.dockgrid{flex:1 1 auto;display:grid;gap:6px;padding:6px;min-height:0;}
/* 默认A/B左 / 中 / 右 三列 */
.dockgrid.cols{grid-template-columns:288px 1fr 248px;grid-template-rows:1fr;}
.col{display:flex;flex-direction:column;gap:6px;min-height:0;min-width:0;}
/* dock 面板 */
.dock{
background:var(--bg-panel);border:1px solid var(--border);border-radius:var(--r);
display:flex;flex-direction:column;min-height:0;overflow:hidden;
}
.dock-hd{
height:30px;flex:0 0 auto;display:flex;align-items:center;gap:6px;padding:0 10px;
background:var(--bg-header);border-bottom:1px solid var(--border);
font-size:12.5px;font-weight:600;color:var(--text-2);
}
.dock-hd .badge{margin-left:auto;font-size:10.5px;font-weight:600;color:var(--text-3);
background:var(--bg-hover);border-radius:9px;padding:1px 7px;}
.dock-bd{flex:1 1 auto;overflow:auto;padding:8px 10px;min-height:0;}
.dock.flex1{flex:1 1 auto;}
.dock.flexN{flex:0 0 auto;}
/* 对象树 / 列表项 */
.tree{list-style:none;}
.tree li{padding:4px 2px;color:var(--text);white-space:nowrap;border-radius:4px;}
.tree li:hover{background:var(--bg-hover);}
.tree li.sel{background:var(--bg-selected);}
.tree .ind1{padding-left:18px;}
.tree .ind2{padding-left:34px;}
.tree .ind3{padding-left:50px;}
.ck{display:inline-block;width:13px;height:13px;border:1px solid var(--border-strong);border-radius:3px;
vertical-align:-2px;margin-right:7px;position:relative;background:var(--canvas-bg);}
.ck.on{background:var(--accent);border-color:var(--accent);}
.ck.on::after{content:"";position:absolute;left:4px;top:1px;width:3px;height:7px;border:solid #fff;border-width:0 2px 2px 0;transform:rotate(45deg);}
.tw{color:var(--text-3);margin-right:4px;font-size:10px;}
.muted{color:var(--text-3);}
.pill{font-size:10px;color:var(--accent-h);border:1px solid var(--canvas-grid);border-radius:8px;padding:0 6px;margin-left:6px;}
/* 工具条 */
.toolbar{display:flex;flex-wrap:wrap;gap:6px;align-items:center;padding:8px 10px;
border-bottom:1px solid var(--border);background:var(--bg-panel-subtle);}
.toolbar.canvas{background:var(--canvas-soft);border-color:var(--canvas-grid);}
select.mini,.btnm{
font:inherit;font-size:11.5px;color:var(--text);background:var(--canvas-bg);
border:1px solid var(--canvas-grid);border-radius:4px;padding:3px 7px;cursor:pointer;
}
.btnm:hover{background:var(--bg-hover);border-color:var(--accent);}
.grp{display:flex;gap:3px;flex-wrap:wrap;}
.grp .btnm{padding:3px 8px;}
/* 工具条分组栏位 */
.toolbar.col{flex-direction:column;align-items:stretch;gap:0;}
.tgrp{display:flex;flex-wrap:wrap;align-items:center;gap:5px;padding:6px 0;border-bottom:1px dashed var(--canvas-grid);}
.tgrp:last-child{border-bottom:none;}
.tgrp .glbl{width:100%;font-size:11px;font-weight:600;color:var(--canvas-dim);margin-bottom:2px;}
.flbl{font-size:11px;color:var(--canvas-dim);display:inline-flex;align-items:center;gap:2px;}
.zin{font:inherit;font-size:11px;width:52px;color:var(--text);background:var(--canvas-bg);
border:1px solid var(--canvas-grid);border-radius:4px;padding:2px 5px;}
/* 表单行:标签固定宽 + 控件填满,不再流式折行 */
.frow{display:flex;align-items:center;gap:8px;width:100%;}
.frow .flbl{width:58px;flex:0 0 auto;}
.frow select.mini{width:132px;flex:0 0 auto;}
.frow .track{flex:1;}
.sval{font-size:11px;color:var(--canvas-text);width:36px;text-align:right;flex:0 0 auto;}
/* 滑块:独立类,不再依赖 .slider 父级(之前那样导致 track 0 高度看不见) */
.track{height:5px;background:var(--canvas-grid);border-radius:3px;position:relative;min-width:80px;}
.track .knob{position:absolute;top:-4px;width:13px;height:13px;border-radius:50%;
background:var(--accent);box-shadow:0 0 0 3px rgba(94,141,245,.22);cursor:pointer;}
.track .fill{position:absolute;left:0;top:0;bottom:0;background:var(--accent);border-radius:3px;opacity:.55;}
/* VTK 视图区 */
.vtk{
flex:1 1 auto;position:relative;min-height:0;border-radius:var(--r);overflow:hidden;
background:radial-gradient(120% 120% at 50% 18%,#16243f 0%,var(--canvas-bg) 60%);
border:1px solid var(--border);
}
.vtk .hd{position:absolute;top:0;left:0;right:0;height:30px;display:flex;align-items:center;padding:0 10px;
background:rgba(18,22,28,.72);border-bottom:1px solid var(--canvas-grid);font-size:12px;color:var(--text-2);z-index:4;backdrop-filter:blur(2px);}
/* 全屏按钮VTK视图 + 数据详情 标题栏右侧) */
.fsbtn{margin-left:auto;width:22px;height:20px;display:grid;place-items:center;cursor:pointer;
border-radius:4px;color:var(--text-3);font-size:13px;}
.fsbtn:hover{background:var(--bg-hover);color:var(--accent);}
.fs-on{position:fixed !important;inset:0;z-index:90;border-radius:0;width:auto !important;height:auto !important;}
/* 假三维:两片帘面 + 体素盒 + 切片 */
.scene{position:absolute;inset:30px 0 0 0;perspective:900px;display:grid;place-items:center;}
.stage{transform-style:preserve-3d;transform:rotateX(60deg) rotateZ(-28deg);}
.curtain{position:absolute;width:230px;height:120px;left:-115px;top:-60px;
background:linear-gradient(90deg,#2a4a8f,#3aa0c0 35%,#7ec96f 60%,#e8c84a 78%,#d9603a);
opacity:.92;border:1px solid rgba(255,255,255,.18);box-shadow:0 0 24px rgba(94,141,245,.25);}
.curtain.b{transform:rotateZ(90deg) translateZ(0);opacity:.78;}
.axes{position:absolute;left:-130px;top:70px;width:0;height:0;}
.axes i{position:absolute;height:2px;transform-origin:left center;}
.ax-x{width:150px;background:#e5605f;}
.ax-y{width:120px;background:#46c07a;transform:rotate(-90deg);}
.grid-floor{position:absolute;width:300px;height:300px;left:-150px;top:-150px;
background-image:linear-gradient(var(--canvas-grid) 1px,transparent 1px),linear-gradient(90deg,var(--canvas-grid) 1px,transparent 1px);
background-size:30px 30px;opacity:.35;transform:translateZ(-2px);}
.slice3d{position:absolute;width:160px;height:90px;left:-80px;top:-45px;
background:repeating-linear-gradient(45deg,rgba(94,141,245,.35) 0 8px,rgba(94,141,245,.12) 8px 16px);
border:2px solid var(--accent);transform:rotateY(0deg) rotateZ(28deg) translateZ(40px);box-shadow:0 0 18px rgba(94,141,245,.4);}
.legend{position:absolute;right:12px;bottom:12px;width:14px;height:96px;border-radius:3px;
background:linear-gradient(#d9603a,#e8c84a,#7ec96f,#3aa0c0,#2a4a8f);border:1px solid var(--canvas-grid);z-index:3;}
.legend::after{content:"Ω·m";position:absolute;left:-24px;top:40px;font-size:10px;color:var(--canvas-dim);}
/* tabs方案 A */
.tabbar{display:flex;gap:2px;border-bottom:1px solid var(--border);background:var(--bg-header);flex:0 0 auto;}
.tabbar .tab{padding:7px 14px;font-size:12.5px;color:var(--text-3);cursor:pointer;border-bottom:2px solid transparent;}
.tabbar .tab:hover{color:var(--text);}
.tabbar .tab.on{color:var(--accent);border-bottom-color:var(--accent);font-weight:600;}
.tabpane{display:none;flex-direction:column;min-height:0;flex:1 1 auto;}
.tabpane.on{display:flex;}
/* 折叠分段(方案 B */
.section .sec-hd{display:flex;align-items:center;gap:6px;padding:7px 10px;cursor:pointer;
background:var(--bg-header);border-top:1px solid var(--border);font-weight:600;color:var(--text-2);font-size:12.5px;}
.section:first-child .sec-hd{border-top:none;}
.section .sec-hd .tw{font-size:11px;}
.section .sec-bd{padding:6px 10px 10px;}
/* 视图内嵌侧栏(方案 C 修正版):抽屉式,画布在右、不遮挡 */
.vtk.with-drawer .scene{left:var(--drawer-w,300px);transition:left .18s;}
.view-drawer{position:absolute;top:30px;left:0;bottom:0;width:300px;z-index:6;
background:rgba(17,27,45,.94);border-right:1px solid var(--canvas-grid);
display:flex;flex-direction:column;backdrop-filter:blur(3px);transition:width .18s;overflow:hidden;}
.view-drawer .tabbar{background:rgba(18,22,28,.55);}
.drawer-toggle{position:absolute;top:38px;z-index:7;left:300px;width:18px;height:46px;
background:rgba(17,27,45,.94);border:1px solid var(--canvas-grid);border-left:none;
border-radius:0 6px 6px 0;display:grid;place-items:center;color:var(--canvas-dim);cursor:pointer;
font-size:11px;transition:left .18s;}
.drawer-toggle:hover{color:var(--accent);}
.vtk.drawer-collapsed .view-drawer{width:0;border-right:none;}
.vtk.drawer-collapsed .scene{left:0;}
.vtk.drawer-collapsed .drawer-toggle{left:0;}
/* 右键菜单 */
.ctx{position:absolute;z-index:30;min-width:150px;background:var(--bg-panel);border:1px solid var(--border-strong);
border-radius:6px;box-shadow:0 10px 30px rgba(0,0,0,.6);padding:4px;display:none;}
.ctx.show{display:block;}
.ctx .it{padding:6px 12px;border-radius:4px;font-size:12.5px;color:var(--text);cursor:pointer;white-space:nowrap;}
.ctx .it:hover{background:var(--accent);color:#fff;}
.ctx .sep{height:1px;background:var(--border);margin:4px 6px;}
.ctx .it.sub::after{content:"▸";float:right;color:var(--text-3);margin-left:18px;}
.hint{font-size:11px;color:var(--text-3);padding:6px 10px;border-top:1px dashed var(--border);}
.note{font-size:11.5px;color:var(--warn);background:rgba(245,166,35,.08);border:1px solid rgba(245,166,35,.25);
border-radius:5px;padding:6px 9px;margin:8px 10px;}
kbd{background:var(--bg-hover);border:1px solid var(--border-strong);border-radius:3px;padding:0 5px;font-size:11px;}
</style>
</head>
<body>
<div class="meta">
<span class="lbl">三栏结构重构 · 布局方案</span>
<div class="opts">
<button data-opt="C" class="on">方案 C · 视图内嵌侧栏(修正·推荐)</button>
<button data-opt="A">方案 A · 左侧独立 dock</button>
<button data-opt="B">方案 B · 竖向分段 dock</button>
</div>
<span class="tag">配色取自 <b>Theme.cpp</b> 深色令牌 · 右键「三维分析」树里的三维体试试创建切片</span>
</div>
<!-- ============ APP 外壳 ============ -->
<div class="app">
<div class="topbar">
<div class="logo"></div>
<div class="ws">地大演示项目 <small>· 工作区</small></div>
<div class="spacer" style="flex:1"></div>
<div class="ico"></div><div class="ico"></div>
<div class="avatar">GZ</div>
</div>
<div class="dockgrid cols" id="grid">
<!-- 左列:内容随方案变 -->
<div class="col" id="leftcol"></div>
<!-- 中列VTK + 详情 -->
<div class="col" style="min-width:0">
<div class="dock flex1" style="padding:0;border:none;background:transparent">
<div class="vtk" id="vtk">
<div class="hd">VTK视图 · 地大演示项目<span class="fsbtn" data-fs title="全屏 / 还原"></span></div>
<div class="scene"><div class="stage">
<div class="grid-floor"></div>
<div class="curtain"></div>
<div class="curtain b"></div>
<div class="slice3d"></div>
<div class="axes"><i class="ax-x"></i><i class="ax-y"></i></div>
</div></div>
<div class="legend"></div>
</div>
</div>
<div class="dock flexN" style="height:120px">
<div class="dock-hd">数据详情<span class="fsbtn" data-fs title="全屏 / 还原"></span></div>
<div class="dock-bd muted" style="font-size:12px">选中数据集查看详情(源数据 / 切片 / 异常 / 插值模型 / 色阶 / 测量)</div>
</div>
</div>
<!-- 右列:异常/属性 -->
<div class="col">
<div class="dock flex1">
<div class="dock-hd">异常 / 对象属性</div>
<div class="dock-bd">
<ul class="tree">
<li><span class="tw"></span>异常体 A <span class="pill">随GS</span></li>
<li class="ind1"><span class="tw"></span>分组-1</li>
<li class="ind2 muted">异常 #1</li>
<li class="ind2 muted">异常 #2</li>
</ul>
</div>
</div>
<div class="dock flexN" style="height:150px">
<div class="dock-hd">数据集属性</div>
<div class="dock-bd muted" style="font-size:12px">名称 / 类型 / 维度 / 创建时间…</div>
</div>
</div>
</div>
</div>
<!-- 三维体数据集 右键菜单原文行21-27切片/色阶/显隐/详情,无删除) -->
<div class="ctx" id="ctx">
<div class="it sub" data-act="slice">切片</div>
<div class="it">色阶</div>
<div class="it">显示 / 隐藏</div>
<div class="it">数据详情</div>
</div>
<div class="ctx" id="ctxSub">
<div class="it">上下</div>
<div class="it">前后</div>
<div class="it">左右</div>
<div class="it">任意</div>
</div>
<!-- 切片数据集 右键菜单原文行29-35保存/保存为/导出/删除/色阶/显隐/详情) -->
<div class="ctx" id="ctxSlice">
<div class="it">保存</div>
<div class="it">保存为</div>
<div class="it">导出</div>
<div class="it" style="color:var(--danger)">删除</div>
<div class="sep"></div>
<div class="it">色阶</div>
<div class="it">显示 / 隐藏</div>
<div class="it">数据详情</div>
</div>
<script>
// ===== 三栏内容片段 =====
function dataset3DList(){return `
<ul class="tree">
<li><span class="ck on"></span>反演剖面-L1 <span class="pill">帘面</span></li>
<li><span class="ck on"></span>反演剖面-L2 <span class="pill">帘面</span></li>
<li><span class="ck"></span>体素模型-V1 <span class="pill">dd_voxel</span></li>
<li><span class="ck"></span>地形 DEM+影像</li>
</ul>`;}
function dataset2DList(){return `
<ul class="tree">
<li><span class="ck on"></span>测线-T1 <span class="pill">俯视</span></li>
<li><span class="ck"></span>轨迹-Tr1 <span class="pill">trajectory</span></li>
</ul>`;}
function analysisTree(){return `
<ul class="tree" id="anaTree">
<li><span class="ck on"></span><span class="tw">▾</span>GS-地大演示</li>
<li class="ind1" data-vol="1"><span class="ck on"></span><span class="tw">▾</span>三维体模型-V1 <span class="pill">右键</span></li>
<li class="ind2" data-slice="1"><span class="ck on"></span><span class="tw">▸</span>切片·上下-01 <span class="pill">右键</span></li>
<li class="ind2" data-slice="1"><span class="ck"></span><span class="tw">▸</span>切片·任意-02</li>
<li class="ind1" data-vol="1"><span class="ck"></span><span class="tw">▸</span>三维体模型-V2</li>
</ul>
<div class="hint">右键<b>三维体</b>→切片▸(上下/前后/左右/任意)·色阶·显隐·详情;右键<b>切片</b>→保存/保存为/导出/删除·色阶·显隐·详情。</div>`;}
function toolbar3D(canvas){return `
<div class="toolbar col ${canvas?'canvas':''}">
<div class="tgrp"><span class="glbl">坐标轴设置</span>
<div class="frow"><span class="flbl">显示方式</span><select class="mini"><option>标准</option><option>三维立体</option><option>不显示</option></select></div>
<div class="frow"><span class="flbl">O点位置</span><span class="btnm">设置…</span></div>
<div class="frow"><span class="flbl">刻度</span><select class="mini"><option>无刻度</option><option selected>米</option><option>英尺</option><option>经纬度</option></select></div>
<div class="frow"><span class="flbl">字体</span><span class="btnm">设置…</span></div>
</div>
<div class="tgrp"><span class="glbl">水平/垂直比例</span>
<div class="frow"><span class="track"><span class="fill" style="width:24%"></span><span class="knob" style="left:24%"></span></span><span class="sval">2.0×</span></div>
</div>
<div class="tgrp"><span class="glbl">快捷视图</span>
<span class="grp"><span class="btnm">前</span><span class="btnm">后</span><span class="btnm">左</span><span class="btnm">右</span><span class="btnm">上</span><span class="btnm">下</span></span>
</div>
<div class="tgrp"><span class="glbl">缩放 (Zoom)</span>
<span class="grp"><span class="btnm">放大</span><span class="btnm">缩小</span><span class="btnm">适配</span></span>
</div>
</div>`;}
function toolbar2D(canvas){return `
<div class="toolbar col ${canvas?'canvas':''}">
<div class="tgrp"><span class="glbl">地图</span>
<div class="frow"><span class="flbl">底图源</span><select class="mini"><option>天地图</option><option>Google Map</option><option>隐藏</option></select></div>
</div>
<div class="tgrp"><span class="glbl">2D视图</span>
<div class="frow"><span class="flbl">位置</span><select class="mini" onchange="document.getElementById('zwrap').style.display=this.value==='自定义'?'inline-flex':'none'">
<option>关闭</option><option selected>Z=0</option><option>顶部</option><option>底部</option><option>自定义</option></select></div>
<div class="frow" id="zwrap" style="display:none"><span class="flbl">Z 值</span><input class="zin" type="number" value="0"><span class="flbl">m</span></div>
</div>
</div>`;}
// 对象树 dockA/B/C 都有,作为勾选源)
function objectDock(){return `
<div class="dock flexN" style="height:170px">
<div class="dock-hd">对象 <span class="badge">勾选源</span></div>
<div class="dock-bd">
<ul class="tree">
<li class="sel"><span class="ck on"></span><span class="tw">▾</span>GS-地大演示</li>
<li class="ind1"><span class="ck on"></span>TM-反演成果</li>
<li class="ind1"><span class="ck"></span>TM-轨迹</li>
<li><span class="ck"></span><span class="tw">▸</span>GS-威立雅</li>
</ul>
</div>
</div>`;}
// ===== 三种方案的左列 =====
function buildA(){ // Tab 切换
return objectDock() + `
<div class="dock flex1">
<div class="tabbar" id="tabbarA">
<div class="tab on" data-t="0">三维数据集</div>
<div class="tab" data-t="1">二维数据集</div>
<div class="tab" data-t="2">三维分析</div>
</div>
<div class="tabpane on">${toolbar3D(false)}<div class="dock-bd">${dataset3DList()}</div></div>
<div class="tabpane">${toolbar2D()}<div class="dock-bd">${dataset2DList()}</div></div>
<div class="tabpane"><div class="dock-bd">${analysisTree()}</div></div>
</div>`;
}
function buildB(){ // 竖向分段
return objectDock() + `
<div class="dock flex1">
<div class="dock-hd">数据集 / 分析 <span class="badge">全可见</span></div>
<div class="dock-bd" style="padding:0">
<div class="section">
<div class="sec-hd"><span class="tw">▾</span>三维数据集</div>
<div class="sec-bd">${toolbar3D(false)}${dataset3DList()}</div>
</div>
<div class="section">
<div class="sec-hd"><span class="tw">▾</span>二维数据集</div>
<div class="sec-bd">${toolbar2D()}${dataset2DList()}</div>
</div>
<div class="section">
<div class="sec-hd"><span class="tw">▾</span>三维分析</div>
<div class="sec-bd">${analysisTree()}</div>
</div>
</div>
</div>`;
}
function buildC(){ // 视图内嵌侧栏(修正):左列保留对象列表(三栏的筛选来源)+ 详情
return objectDock() + `
<div class="dock flex1">
<div class="dock-hd">数据集(详情查看)</div>
<div class="dock-bd">
<ul class="tree">
<li class="muted">反演剖面-L1</li><li class="muted">体素模型-V1</li><li class="muted">测线-T1</li>
</ul>
<div class="note" style="color:var(--accent-h);background:rgba(94,141,245,.08);border-color:rgba(94,141,245,.3)">方案 C修正三个「子列表栏」内嵌在 VTK 视图左侧(抽屉式侧栏),三 tab 切换。画布在其右侧、不被遮挡;点侧栏右缘 <b>◀</b> 可折叠让画布全宽。这正是需求「VTK视图上提供三个子列表栏」的形态。左侧「对象」列表是三栏的筛选来源需求筛勾选对象中的 ds。</div>
</div>
</div>`;
}
function viewDrawerHTML(){return `
<div class="view-drawer">
<div class="tabbar">
<div class="tab on" data-t="0">三维数据集</div>
<div class="tab" data-t="1">二维数据集</div>
<div class="tab" data-t="2">三维分析</div>
</div>
<div class="tabpane on">${toolbar3D(true)}<div class="dock-bd">${dataset3DList()}</div></div>
<div class="tabpane">${toolbar2D(true)}<div class="dock-bd">${dataset2DList()}</div></div>
<div class="tabpane"><div class="dock-bd">${analysisTree()}</div></div>
</div>
<div class="drawer-toggle" id="drawerToggle">◀</div>`;}
// ===== 渲染 =====
const grid=document.getElementById('grid');
const vtk=document.getElementById('vtk');
function render(opt){
document.querySelectorAll('.meta button').forEach(b=>b.classList.toggle('on',b.dataset.opt===opt));
const left=document.getElementById('leftcol');
// 清掉方案 C 的内嵌侧栏
vtk.classList.remove('with-drawer','drawer-collapsed');
vtk.querySelectorAll('.view-drawer,.drawer-toggle').forEach(e=>e.remove());
if(opt==='A'){ left.innerHTML=buildA(); wireTabs(); }
if(opt==='B'){ left.innerHTML=buildB(); wireSections(); }
if(opt==='C'){
left.innerHTML=buildC();
vtk.classList.add('with-drawer');
vtk.insertAdjacentHTML('beforeend', viewDrawerHTML());
wireTabs(vtk.querySelector('.view-drawer'));
const tg=document.getElementById('drawerToggle');
tg.onclick=()=>{const c=vtk.classList.toggle('drawer-collapsed');tg.textContent=c?'▶':'◀';};
}
wireCtx();
}
function wireTabs(scope){
(scope||document).querySelectorAll('.tabbar .tab').forEach(t=>{
t.onclick=()=>{
const bar=t.parentElement, panes=bar.parentElement.querySelectorAll(':scope > .tabpane');
bar.querySelectorAll('.tab').forEach(x=>x.classList.remove('on'));
t.classList.add('on');
panes.forEach((p,i)=>p.classList.toggle('on',i===+t.dataset.t));
};
});
}
function wireSections(){
document.querySelectorAll('.section .sec-hd').forEach(h=>{
h.onclick=()=>{const bd=h.nextElementSibling, tw=h.querySelector('.tw');
const open=bd.style.display!=='none'; bd.style.display=open?'none':''; tw.textContent=open?'▸':'▾';};
});
}
// 右键菜单:三维体(ctx+ctxSub子菜单) / 切片(ctxSlice)
const ctx=document.getElementById('ctx'), ctxSub=document.getElementById('ctxSub'),
ctxSlice=document.getElementById('ctxSlice');
function popAt(menu,e){e.preventDefault();hideCtx();
menu.style.left=e.pageX+'px';menu.style.top=e.pageY+'px';menu.classList.add('show');}
function wireCtx(){
document.querySelectorAll('[data-vol]').forEach(li=>li.oncontextmenu=(e)=>popAt(ctx,e));
document.querySelectorAll('[data-slice]').forEach(li=>li.oncontextmenu=(e)=>popAt(ctxSlice,e));
ctx.querySelector('[data-act=slice]').onmouseenter=()=>{
const r=ctx.getBoundingClientRect();
ctxSub.style.left=r.right+'px';ctxSub.style.top=r.top+'px';ctxSub.classList.add('show');
};
}
function hideCtx(){[ctx,ctxSub,ctxSlice].forEach(m=>m.classList.remove('show'));}
document.addEventListener('click',hideCtx);
document.addEventListener('scroll',hideCtx,true);
// 全屏切换VTK视图 / 数据详情
document.querySelectorAll('[data-fs]').forEach(b=>b.onclick=(e)=>{
e.stopPropagation();
const panel=b.closest('.vtk')||b.closest('.dock');
const on=panel.classList.toggle('fs-on');
b.textContent=on?'🗗':'⛶';
});
document.querySelectorAll('.meta button').forEach(b=>b.onclick=()=>render(b.dataset.opt));
render('C');
</script>
</body>
</html>