diff --git a/WebSite/src/components/ReverseWorkspace.tsx b/WebSite/src/components/ReverseWorkspace.tsx index a99ba96..690bbfc 100644 --- a/WebSite/src/components/ReverseWorkspace.tsx +++ b/WebSite/src/components/ReverseWorkspace.tsx @@ -6,8 +6,8 @@ import { RotateCw, Rotate3d, AlertCircle, - ChevronLeft, - ChevronRight, + ChevronDown, + ChevronUp, Eye, Layers, Save, @@ -1882,8 +1882,8 @@ export function VoxelizationMappingView({ return (
-
-
+
+
Base DICOM @@ -1891,72 +1891,78 @@ export function VoxelizationMappingView({ Overlay Label Map
-
+
Z {safeSlice + 1}/{Math.max(totalSlices, 1)}
- {dicomPreview ? ( -
- - -
- ) : ( -
- {dicomStatus} -
- )} -
-
- {overlayStatus} - - {overlayStats.activeModules}/{visibleModuleCount} 构件 · {overlayStats.segmentCount} 边 · {overlayStats.filledPixels} px - -
-
- {overlayStats.modules.length ? ( -
- {overlayStats.modules.map((item) => ( -
- - {item.name} - ID {item.partId} - {item.segmentCount} 边 - {item.filledPixels} px -
- ))} -
- ) : ( -
- 当前切片暂无可见构件 -
- )} -
-
-
-
-

Slice Navigator

- - {safeSlice + 1} / {Math.max(totalSlices, 1)} - +
+
+
+ {dicomPreview ? ( +
+ + +
+ ) : ( +
+ {dicomStatus} +
+ )} +
+ +
+
+ {overlayStatus} + + {overlayStats.activeModules}/{visibleModuleCount} 构件 · {overlayStats.segmentCount} 边 · {overlayStats.filledPixels} px + +
+
+ {overlayStats.modules.length ? ( +
+ {overlayStats.modules.map((item) => ( +
+ + {item.name} + ID {item.partId} + {item.segmentCount} 边 + {item.filledPixels} px +
+ ))} +
+ ) : ( +
+ 当前切片暂无可见构件 +
+ )} +
+
-
+ +
); diff --git a/WebSite/src/index.css b/WebSite/src/index.css index a2cd6ee..eb6b25f 100644 --- a/WebSite/src/index.css +++ b/WebSite/src/index.css @@ -116,3 +116,61 @@ .mapping-slice-input:active::-moz-range-thumb { cursor: grabbing; } + +.mapping-slice-vertical-input { + appearance: none; + -webkit-appearance: none; + background: transparent; + height: 100%; + inset: 0; + position: absolute; + width: 100%; + direction: rtl; + writing-mode: vertical-rl; +} + +.mapping-slice-vertical-input:focus { + outline: none; +} + +.mapping-slice-vertical-input::-webkit-slider-runnable-track { + background: transparent; + border: 0; + width: 8px; +} + +.mapping-slice-vertical-input::-webkit-slider-thumb { + appearance: none; + -webkit-appearance: none; + background: #22d3ee; + border: 3px solid #0f172a; + border-radius: 9999px; + box-shadow: 0 0 0 4px rgba(34, 211, 238, 0.16), 0 8px 18px rgba(8, 47, 73, 0.45); + cursor: grab; + height: 22px; + width: 22px; +} + +.mapping-slice-vertical-input::-moz-range-track { + background: transparent; + border: 0; + width: 8px; +} + +.mapping-slice-vertical-input::-moz-range-thumb { + background: #22d3ee; + border: 3px solid #0f172a; + border-radius: 9999px; + box-shadow: 0 0 0 4px rgba(34, 211, 238, 0.16), 0 8px 18px rgba(8, 47, 73, 0.45); + cursor: grab; + height: 16px; + width: 16px; +} + +.mapping-slice-vertical-input:active::-webkit-slider-thumb { + cursor: grabbing; +} + +.mapping-slice-vertical-input:active::-moz-range-thumb { + cursor: grabbing; +} diff --git a/工程分析/实现方案-2026-05-20-15-33-38.md b/工程分析/实现方案-2026-05-20-15-33-38.md new file mode 100644 index 0000000..d68d237 --- /dev/null +++ b/工程分析/实现方案-2026-05-20-15-33-38.md @@ -0,0 +1,51 @@ +# 实现方案:右侧竖向 Slice Navigator 与影像遮挡治理 + +实现方案文档路径:`工程分析/实现方案-2026-05-20-15-33-38.md` + +## 修改目标 + +调整逆向分割映射视图布局:将切片导航从影像底部迁移到右侧竖向栏,并把 Overlay Label Map 的构件统计面板从影像内部移到影像下方,避免遮挡 DICOM 影像。 + +## 涉及路径 + +- `WebSite/src/components/ReverseWorkspace.tsx` +- `工程分析/需求分析-2026-05-20-15-33-38.md` +- `工程分析/实现方案-2026-05-20-15-33-38.md` +- `工程分析/测试方案-2026-05-20-15-33-38.md` +- `工程分析/经验记录.md` + +## 技术路线 + +1. 定位 `VoxelizationMappingView` 中当前底部 `Slice Navigator` 与影像内 Overlay 状态面板。 +2. 将组件整体布局改为横向网格:左侧影像与独立信息区,右侧竖向切片导航栏。 +3. 保留顶部 `Base DICOM`、`Overlay Label Map`、`Z` 状态标签,但缩小为影像角落提示,避免大面积遮挡。 +4. 将构件统计列表改为影像下方独立面板,显示当前 Overlay 状态、构件数、边数、像素数和当前切片构件表。 +5. 为竖向 range 增加内联样式或类名,使用 `writingMode: 'vertical-rl'` 与 `direction: 'rtl'` 实现从上到下浏览切片。 +6. 执行类型检查、构建、部署验证。 + +## 执行步骤 + +- 阅读当前 `VoxelizationMappingView` JSX 与相关样式。 +- 修改布局结构和样式。 +- 检查 `mapping-slice-input` 是否有全局 CSS 影响,必要时增加独立竖向类。 +- 运行 `npm run lint` 和 `npm run build`。 +- 重启 `tmux` 服务并验证健康接口和首页。 +- 更新测试方案与经验记录。 +- 精确暂存、提交并推送 Gitea。 + +## 兼容性与回滚方案 + +- 该改动只影响前端布局,不改变数据结构和 API。 +- 如竖向 `range` 在某浏览器显示异常,可回滚为右侧竖排按钮加数值输入,或补充 WebKit 专用 CSS。 +- 项目库复用同一组件,因此无需额外复制样式逻辑。 + +## 预计文件变更 + +- `WebSite/src/components/ReverseWorkspace.tsx` +- 本轮工程分析文档与 `工程分析/经验记录.md` + +## 提交与部署策略 + +- 暂存本轮相关代码和工程分析文档。 +- commit message 包含 `2026-05-20-15-33-38`。 +- 推送 Gitea 后重启 `revoxelseg-dicom` 服务,验证 `http://127.0.0.1:4000/api/health` 与首页。 diff --git a/工程分析/测试方案-2026-05-20-15-33-38.md b/工程分析/测试方案-2026-05-20-15-33-38.md new file mode 100644 index 0000000..5f5c861 --- /dev/null +++ b/工程分析/测试方案-2026-05-20-15-33-38.md @@ -0,0 +1,55 @@ +# 测试方案:Slice Navigator 竖向外置与 Overlay 遮挡检查 + +测试方案文档路径:`工程分析/测试方案-2026-05-20-15-33-38.md` + +## 静态检查 + +- 确认 `VoxelizationMappingView` 的 `Slice Navigator` 不再位于影像画布底部内部。 +- 确认切片导航栏位于影像右侧,并使用竖向 `range`。 +- 确认构件统计面板不再用绝对定位覆盖 DICOM 影像。 +- 确认 `Base DICOM` 和 `Overlay Label Map` 仅作为小标签显示,不遮挡主体区域。 + +## 构建检查 + +- 在 `WebSite/` 执行 `npm run lint`。 +- 在 `WebSite/` 执行 `npm run build`。 + +## 关键业务场景验证 + +- 逆向工作区中拖动右侧竖向切片条可以切换当前 Z 层。 +- 上下按钮仍可逐层切换。 +- DICOM 原始影像和 Overlay Label Map 不被构件列表遮挡。 +- 项目库中复用的逆向分割映射视图保持可用。 + +## 医学影像数据相关边界验证 + +- 不修改 DICOM/STL 原始数据。 +- 不改变体素化、求交、光栅化和导出逻辑。 +- 仅调整浏览控件与信息面板的位置,保证影像主体可审查。 + +## 部署验证 + +- 验证 `http://127.0.0.1:4000/api/health`。 +- 验证 `http://127.0.0.1:4000/` 返回 200。 + +## Git/Gitea 备份验证 + +- commit message 包含 `2026-05-20-15-33-38`。 +- 推送 Gitea 成功后记录 commit。 +- 确认未暂存历史删除状态、软著材料和运行态文件。 + +## 风险与回归关注点 + +- 竖向 range 的浏览器兼容性。 +- 小屏幕下右侧导航栏是否挤压影像宽度。 +- 项目库复用组件后的宽度约束是否仍能正常显示。 + +## 执行结果 + +- `npm run lint`:通过,TypeScript 无报错。 +- `npm run build`:通过,Vite 完成生产构建;仅保留当前项目已有的大 chunk 体积提示。 +- 静态确认:`VoxelizationMappingView` 已移除底部横向 `Slice Navigator`,改为右侧竖向 `mapping-slice-vertical-input`。 +- 静态确认:Overlay 构件统计面板已从影像绝对定位层移到影像下方独立区域,不再遮挡 DICOM 画布。 +- 部署验证:已重建 `tmux` 会话 `revoxelseg-dicom`,执行 `npm run serve -- --host 0.0.0.0 --port 4000`。 +- `curl -fsS http://127.0.0.1:4000/api/health`:通过,返回 `{"ok":true,"service":"revoxelseg-dicom",...}`。 +- `curl -I -fsS http://127.0.0.1:4000/`:通过,返回 `HTTP/1.1 200 OK`。 diff --git a/工程分析/经验记录.md b/工程分析/经验记录.md index 0eec2b8..192ce54 100644 --- a/工程分析/经验记录.md +++ b/工程分析/经验记录.md @@ -1297,3 +1297,21 @@ C. 解决问题方案 D. 后续如何避免问题 凡是用户要求两个页面“效果一致”,优先抽取或导出已有真实组件,不再维护第二套近似 UI。涉及密码、权限、删除等高风险管理操作时,必须把资料编辑和凭据修改拆成不同入口,并通过显式标签、校验和反馈减少误操作。 + +## 2026-05-20-15-33-38 医学影像视图控件不要覆盖审查画布 + +A. 具体问题 + +用户指出逆向工作区的 `Slice Navigator` 格式与“DICOM 切片范围”不统一,并且 Overlay Label Map 的构件统计区域会遮挡 DICOM 影像,影响对二维切片和叠加分割结果的审查。 + +B. 产生问题原因 + +此前 `VoxelizationMappingView` 为了紧凑展示,把切片导航放在视图底部,同时将 Overlay 状态和构件列表使用绝对定位压在影像画布底部。该设计在普通预览中节省空间,但在医学影像核验场景中会遮挡 DICOM 主体区域。 + +C. 解决问题方案 + +将视图结构调整为“影像画布 + 右侧竖向切片导航 + 下方 Overlay 统计面板”:右侧竖向导航使用独立 `mapping-slice-vertical-input`,显示当前 DICOM 切片位置和上下层按钮;Overlay 构件列表移到画布下方,不再覆盖 DICOM 影像。 + +D. 后续如何避免问题 + +医学影像主画布应优先保持无遮挡,状态、统计、导航控件默认放在画布外侧。若必须悬浮在影像上,只能使用小尺寸状态标识,并在提交前检查是否遮挡解剖结构或分割边界。 diff --git a/工程分析/需求分析-2026-05-20-15-33-38.md b/工程分析/需求分析-2026-05-20-15-33-38.md new file mode 100644 index 0000000..750f7ec --- /dev/null +++ b/工程分析/需求分析-2026-05-20-15-33-38.md @@ -0,0 +1,53 @@ +# 需求分析:逆向映射视图切片导航外置与遮挡优化 + +开始时间:`2026-05-20-15-33-38` + +## 原始需求摘要 + +用户要求修改逆向工作区: + +1. 将逆向工作区中 `Slice Navigator` 的格式与“DICOM 切片范围”的格式统一,不要放在图片里面。 +2. `Slice Navigator` 调整为竖向滚动条,放在图片右侧。 +3. `Overlay Label Map` 部分不要遮挡 DICOM 影像。 + +## 业务目标 + +- 让二维逆向分割映射视图的切片浏览控件不压住医学影像主体。 +- 保持切片导航与中部工具栏内“DICOM 切片范围”控件在视觉语义上统一。 +- 避免状态说明、构件统计面板遮挡 DICOM 原始影像和分割叠加区域,提升临床审查可读性。 + +## 输入与输出 + +输入: + +- `WebSite/src/components/ReverseWorkspace.tsx` +- 复用该组件的 `WebSite/src/components/ProjectLibrary.tsx` + +输出: + +- `VoxelizationMappingView` 的切片导航从底部内嵌区域改为右侧竖向导航栏。 +- 右侧导航栏显示当前层数、竖向 range 控件和上下切片按钮。 +- 构件统计面板移出影像画布覆盖层,作为影像下方独立信息区展示。 + +## 影响范围 + +- 逆向工作区“逆向分割映射视图”。 +- 项目库中复用的“逆向分割映射视图”。 +- 相关 Tailwind 样式和 TypeScript 类型检查。 + +## 关键约束 + +- 不改动 STL/DICOM 映射算法,只调整控件布局和遮挡关系。 +- 影像主体区域仍需保持 Base DICOM 与 Overlay Label Map 的标签提示。 +- 竖向切片条需要可拖动、可点击上下按钮、可通过键盘/辅助技术识别。 +- 不能把无关工作区历史删除和软著材料纳入提交。 + +## 风险点 + +- 原生 `range` 竖向显示在不同浏览器上需要兼容写法。 +- 切片导航移出底部后,需要保证容器高度和图片区域不会被挤压到不可用。 +- 项目库复用同一组件,布局变化会同步影响项目库,需要保持宽窄视口可用。 + +## 默认假设 + +- 用户所说“Overlay Label Map 部分”主要指当前切片构件统计和状态面板遮挡 DICOM 影像,而不是取消分割掩码本身;分割掩码仍应叠加显示。