286 lines
13 KiB
HTML
286 lines
13 KiB
HTML
<!doctype html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>DICOM / UPP 配准工作台</title>
|
|
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='8' fill='%2319d6c3'/%3E%3Cpath d='M9 17h14M16 10v14' stroke='%233378f6' stroke-width='4' stroke-linecap='round'/%3E%3C/svg%3E" />
|
|
<link rel="stylesheet" href="/static/styles.css" />
|
|
<script type="importmap">
|
|
{
|
|
"imports": {
|
|
"three": "/static/vendor/three.module.min.js"
|
|
}
|
|
}
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div id="loginOverlay" class="login-overlay">
|
|
<form id="loginForm" class="login-panel">
|
|
<span class="brand-mark"></span>
|
|
<h1>DICOM / UPP 配准工作台</h1>
|
|
<p>登录后从完整关联 CT 中选择 DICOM 与 STL 配准关系</p>
|
|
<input id="username" autocomplete="username" placeholder="账号" />
|
|
<input id="password" type="password" autocomplete="current-password" placeholder="密码" />
|
|
<button type="submit">登录</button>
|
|
<span id="loginError"></span>
|
|
</form>
|
|
</div>
|
|
|
|
<header class="topbar">
|
|
<div class="brand">
|
|
<span class="brand-mark"></span>
|
|
<div>
|
|
<h1>DICOM / UPP 配准工作台</h1>
|
|
<p id="subtitle">完整关联 CT · STL 搭配 · 位姿参数</p>
|
|
</div>
|
|
</div>
|
|
<div class="top-actions">
|
|
<button id="relationBtn" class="ghost-btn accent" type="button">数据库关联可视化</button>
|
|
<button id="viewerBtn" class="ghost-btn" type="button">DICOM 阅片系统</button>
|
|
<span id="dbStatus" class="status-pill">数据库</span>
|
|
<button id="refreshBtn" class="ghost-btn" type="button">刷新</button>
|
|
<span id="userBadge" class="user-badge">未登录</span>
|
|
<button id="logoutBtn" class="ghost-btn" type="button">退出</button>
|
|
</div>
|
|
</header>
|
|
<div id="topLoading" class="top-loading hidden"><span></span></div>
|
|
|
|
<main class="workspace">
|
|
<aside class="case-panel panel">
|
|
<div class="panel-head">
|
|
<div class="panel-title-row">
|
|
<div>
|
|
<h2>完整关联 CT</h2>
|
|
<p>DICOM + STL</p>
|
|
</div>
|
|
<button id="caseToggleBtn" class="icon-btn" type="button" title="收起/展开完整关联 CT">
|
|
<span></span><span></span><span></span>
|
|
</button>
|
|
</div>
|
|
<span id="caseCount">0</span>
|
|
</div>
|
|
<input id="caseSearch" class="search-input" placeholder="搜索 CT号 / 姓名 / 模型" />
|
|
<div class="filter-row">
|
|
<button class="filter active" data-status="">全部</button>
|
|
<button class="filter" data-status="unregistered">未配准</button>
|
|
<button class="filter" data-status="registered">已配准</button>
|
|
</div>
|
|
<div class="part-filter">
|
|
<button class="filter active" data-part="">全部部位</button>
|
|
<button class="filter" data-part="head_neck">头颈部</button>
|
|
<button class="filter" data-part="chest">胸部</button>
|
|
<button class="filter" data-part="upper_abdomen">上腹部</button>
|
|
<button class="filter" data-part="lower_abdomen">下腹部</button>
|
|
<button class="filter" data-part="pelvis">盆腔</button>
|
|
</div>
|
|
<div id="caseList" class="case-list"></div>
|
|
</aside>
|
|
|
|
<section class="main-panel">
|
|
<section class="case-strip panel">
|
|
<div>
|
|
<h2 id="activeTitle">未选择 CT</h2>
|
|
<p id="activeMeta">从左侧选择一个完整关联检查</p>
|
|
</div>
|
|
<div id="activeTags" class="tag-line"></div>
|
|
</section>
|
|
|
|
<section class="registration-board">
|
|
<aside class="tool-dock panel">
|
|
<div class="tool-tabs">
|
|
<button class="active" data-tool-tab="series">序列</button>
|
|
<button data-tool-tab="models">组织分割</button>
|
|
<button data-tool-tab="pose">位姿</button>
|
|
</div>
|
|
|
|
<section class="tool-pane active" data-tool-pane="series">
|
|
<div class="patient-card">
|
|
<strong id="patientSummary">未选择 CT</strong>
|
|
<dl>
|
|
<div><dt>检查号</dt><dd id="summaryCt">-</dd></div>
|
|
<div><dt>患者号</dt><dd id="summaryPatientId">-</dd></div>
|
|
<div><dt>检查日期</dt><dd id="summaryStudyTime">-</dd></div>
|
|
<div><dt>检查部位</dt><dd id="summaryBodyPart">-</dd></div>
|
|
</dl>
|
|
</div>
|
|
<div class="tool-section-title">
|
|
<span>3D 重建所需序列</span>
|
|
<button id="reloadFusionBtn" type="button">重选序列</button>
|
|
</div>
|
|
<div id="matchedSeries" class="matched-series"></div>
|
|
<div class="tool-section-title">
|
|
<span>全部检查和序列</span>
|
|
<em id="seriesCount">0</em>
|
|
</div>
|
|
<div id="seriesList" class="series-list compact-list"></div>
|
|
</section>
|
|
|
|
<section class="tool-pane" data-tool-pane="models">
|
|
<div class="tool-section-title">
|
|
<span>STL 模型</span>
|
|
<em id="stlCount">0</em>
|
|
</div>
|
|
<div id="modelRail" class="chip-rail"></div>
|
|
<div id="stlList" class="stl-list compact-list"></div>
|
|
</section>
|
|
|
|
<section class="tool-pane" data-tool-pane="pose">
|
|
<div class="tool-section-title">
|
|
<span>位姿手动调整</span>
|
|
<em id="saveState">未保存</em>
|
|
</div>
|
|
<div class="pose-actions">
|
|
<button id="resetRotationBtn" type="button">重置旋转</button>
|
|
<button id="resetTransformBtn" type="button">重置平移缩放</button>
|
|
<button id="resetFlipBtn" type="button">重置镜像</button>
|
|
</div>
|
|
<div class="flip-row">
|
|
<button data-flip="flipX">镜像 X</button>
|
|
<button data-flip="flipY">镜像 Y</button>
|
|
<button data-flip="flipZ">镜像 Z</button>
|
|
</div>
|
|
<div id="poseGrid" class="pose-grid"></div>
|
|
|
|
<div class="tool-section-title">
|
|
<span>位姿自动调整</span>
|
|
<em id="autoState">未运行</em>
|
|
</div>
|
|
<div class="auto-box compact-auto">
|
|
<button id="openAutoModalBtn" class="wide-action" type="button">打开自动调整区域</button>
|
|
<div id="autoResult" class="auto-result">选择 DICOM 序列和 STL 后可运行。</div>
|
|
</div>
|
|
<textarea id="notes" class="notes" rows="2" placeholder="配准备注"></textarea>
|
|
</section>
|
|
</aside>
|
|
|
|
<section class="viewer-panel fusion-panel panel">
|
|
<div class="viewer-head">
|
|
<div class="segmented">
|
|
<button data-fusion-mode="fusion" class="active">融合结果</button>
|
|
<button data-fusion-mode="model">单独模型</button>
|
|
</div>
|
|
<div class="segmented">
|
|
<button data-window="default" class="active">默认</button>
|
|
<button data-window="bone">骨窗</button>
|
|
<button data-window="soft">软组织</button>
|
|
<button data-window="contrast">高对比</button>
|
|
</div>
|
|
<div class="viewer-tools">
|
|
<button id="stretchXBtn" class="ghost-btn">X拉伸</button>
|
|
<button id="stretchYBtn" class="ghost-btn">Y拉伸</button>
|
|
<button id="stretchZBtn" class="ghost-btn">Z拉伸</button>
|
|
<button id="fitBtn" class="ghost-btn">视角复位</button>
|
|
<button id="saveBtn" class="primary-btn">保存配准</button>
|
|
<button id="statusBtn" class="state-btn">标为已配准</button>
|
|
</div>
|
|
</div>
|
|
<div class="fusion-stage">
|
|
<div id="fusionViewport"></div>
|
|
<div class="fusion-hud left" id="fusionStatus">等待选择 DICOM 与 STL</div>
|
|
<div class="fusion-hud right" id="fusionMeta">DICOM - · STL -</div>
|
|
<div id="fusionLoading" class="fusion-loading hidden">
|
|
<span>正在构建融合视图</span>
|
|
<div><i id="fusionProgressFill"></i></div>
|
|
<em id="fusionProgressText">0%</em>
|
|
</div>
|
|
</div>
|
|
<div class="slice-range-panel">
|
|
<div class="slice-range-head">
|
|
<strong>DICOM 切片范围</strong>
|
|
<span id="sliceRangeLabel">0 - 0 / 0</span>
|
|
</div>
|
|
<div class="dual-range">
|
|
<input id="sliceRangeStart" type="range" min="0" max="0" value="0" />
|
|
<input id="sliceRangeEnd" type="range" min="0" max="0" value="0" />
|
|
</div>
|
|
<div class="slice-range-foot">
|
|
<span id="sliceStartLabel">起点 0</span>
|
|
<span>范围</span>
|
|
<span id="sliceEndLabel">终点 0</span>
|
|
</div>
|
|
<div class="slice-current-row">
|
|
<span id="sliceLabel">切片 0 / 0</span>
|
|
<input id="sliceSlider" type="range" min="0" max="0" value="0" />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="viewer-panel dicom-panel panel">
|
|
<div class="viewer-head">
|
|
<div>
|
|
<h2>逆向分割映射视图</h2>
|
|
<p id="dicomPanelMeta">Base DICOM · Overlay Label Map</p>
|
|
</div>
|
|
<div class="viewer-tools">
|
|
<div class="segmented mini-segmented">
|
|
<button data-map-window="default" class="active">默认</button>
|
|
<button data-map-window="bone">骨窗</button>
|
|
<button data-map-window="soft">软组织</button>
|
|
<button data-map-window="contrast">高对比</button>
|
|
</div>
|
|
<button id="mappingRotateLeftBtn" class="ghost-btn" type="button">左旋</button>
|
|
<button id="mappingRotateRightBtn" class="ghost-btn" type="button">右旋</button>
|
|
<button id="mappingResetBtn" class="ghost-btn" type="button">位置重置</button>
|
|
<button id="openViewerBtn" class="ghost-btn" type="button">阅片系统</button>
|
|
</div>
|
|
</div>
|
|
<div class="dicom-stage">
|
|
<div id="mappingViewport" class="mapping-viewport">
|
|
<div id="mappingLayer" class="mapping-layer">
|
|
<img id="dicomPreview" alt="DICOM 切片" />
|
|
<canvas id="mappingCanvas"></canvas>
|
|
</div>
|
|
</div>
|
|
<div class="mapping-slice-rail">
|
|
<input id="mappingSliceSlider" type="range" min="0" max="0" value="0" />
|
|
<span id="mappingSliceLabel">0 / 0</span>
|
|
</div>
|
|
<div class="dicom-overlay top-left" id="dicomInfoLeft">未选择序列</div>
|
|
<div class="dicom-overlay top-right" id="dicomInfoRight"></div>
|
|
<div class="dicom-overlay bottom-left" id="dicomInfoBottom">Img: -</div>
|
|
<div id="dicomLoading" class="fusion-loading hidden">
|
|
<span>正在加载逆向分割映射视图</span>
|
|
<div><i></i></div>
|
|
</div>
|
|
</div>
|
|
<div class="mapping-summary">
|
|
<div class="mapping-summary-head">
|
|
<span>Overlay Label Map</span>
|
|
<em id="mappingStats">0/0 构件 · 0 边 · 0 px</em>
|
|
</div>
|
|
<div id="annotationTags" class="annotation-tags"></div>
|
|
<div id="mappingLegend" class="mapping-legend"></div>
|
|
</div>
|
|
</section>
|
|
</section>
|
|
</section>
|
|
</main>
|
|
|
|
<div id="autoModal" class="modal-shell hidden">
|
|
<div class="modal-panel">
|
|
<div class="modal-head">
|
|
<div>
|
|
<h2>位姿自动调整</h2>
|
|
<p>选择参与自动配准的调整方向</p>
|
|
</div>
|
|
<button id="closeAutoModalBtn" class="ghost-btn" type="button">关闭</button>
|
|
</div>
|
|
<div class="auto-options modal-options">
|
|
<label><input id="autoX" type="checkbox" checked /> X 方向</label>
|
|
<label><input id="autoY" type="checkbox" checked /> Y 方向</label>
|
|
<label><input id="autoZ" type="checkbox" checked /> Z 方向</label>
|
|
<label><input id="autoScale" type="checkbox" checked /> 缩放</label>
|
|
</div>
|
|
<div class="pose-actions modal-actions">
|
|
<button id="suggestBoneBtn" type="button">建议骨骼模型</button>
|
|
<button id="autoCoarseBtn" type="button">自动粗配准</button>
|
|
<button id="autoFineBtn" type="button">自动微调</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script type="module" src="/static/app.js"></script>
|
|
</body>
|
|
</html>
|