From 765e4cc41a21bd7c090966400e746d5776f982d3 Mon Sep 17 00:00:00 2001 From: admin <572701190@qq.com> Date: Fri, 8 May 2026 03:07:21 +0800 Subject: [PATCH] =?UTF-8?q?2026-05-08-03-03-52=20=E4=BF=AE=E6=AD=A3DICOM?= =?UTF-8?q?=E5=88=87=E7=89=87=E8=8C=83=E5=9B=B4=E4=B8=BA=E5=8D=95=E8=8C=83?= =?UTF-8?q?=E5=9B=B4=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebSite/src/components/ReverseWorkspace.tsx | 63 +++++++++++++-------- WebSite/src/index.css | 60 ++++++++++++++++++++ 工程分析/实现方案-2026-05-08-03-03-52.md | 53 +++++++++++++++++ 工程分析/测试方案-2026-05-08-03-03-52.md | 45 +++++++++++++++ 工程分析/经验记录.md | 18 ++++++ 工程分析/需求分析-2026-05-08-03-03-52.md | 40 +++++++++++++ 6 files changed, 255 insertions(+), 24 deletions(-) create mode 100644 工程分析/实现方案-2026-05-08-03-03-52.md create mode 100644 工程分析/测试方案-2026-05-08-03-03-52.md create mode 100644 工程分析/需求分析-2026-05-08-03-03-52.md diff --git a/WebSite/src/components/ReverseWorkspace.tsx b/WebSite/src/components/ReverseWorkspace.tsx index d759376..863dad5 100644 --- a/WebSite/src/components/ReverseWorkspace.tsx +++ b/WebSite/src/components/ReverseWorkspace.tsx @@ -818,6 +818,8 @@ export default function ReverseWorkspace({ projectId }: { projectId: string }) { const safeSliceEnd = clamp(sliceEnd, 0, maxSlice); const displayStart = Math.min(safeSliceStart, safeSliceEnd); const displayEnd = Math.max(safeSliceStart, safeSliceEnd); + const rangeStartPercent = maxSlice > 0 ? (displayStart / maxSlice) * 100 : 0; + const rangeEndPercent = maxSlice > 0 ? (displayEnd / maxSlice) * 100 : 0; const selectedDisplay = displayOptions.find((item) => item.id === displayLevel) ?? displayOptions[0]; const selectedDicomOpacity = dicomOpacityOptions.find((item) => item.id === dicomOpacityLevel) ?? dicomOpacityOptions[0]; const preloadPoints = [0.2, 0.4, 0.6, 0.8, 1].map((ratio) => clamp(Math.max(0, Math.round((project?.dicomCount ?? 1) * ratio) - 1), 0, maxSlice)); @@ -924,30 +926,43 @@ export default function ReverseWorkspace({ projectId }: { projectId: string }) { {displayStart + 1} - {displayEnd + 1} / {project?.dicomCount ?? 0} - - +
+
+
+
+ setSliceStart(Number(event.target.value))} + className="dicom-range-input" + style={{ zIndex: safeSliceStart >= safeSliceEnd ? 5 : 4 }} + /> + setSliceEnd(Number(event.target.value))} + className="dicom-range-input" + style={{ zIndex: safeSliceStart >= safeSliceEnd ? 4 : 5 }} + /> +
+
+ 起点 {safeSliceStart + 1} + 范围 + 终点 {safeSliceEnd + 1} +
+

显示范围支持 M-N,两个端点可双向调整;范围变化只改变可视化切片,不改变模型原始位姿。

diff --git a/WebSite/src/index.css b/WebSite/src/index.css index f1d8c73..4d298ad 100644 --- a/WebSite/src/index.css +++ b/WebSite/src/index.css @@ -1 +1,61 @@ @import "tailwindcss"; + +.dicom-range-input { + appearance: none; + -webkit-appearance: none; + background: transparent; + height: 100%; + inset: 0; + pointer-events: none; + position: absolute; + width: 100%; +} + +.dicom-range-input:focus { + outline: none; +} + +.dicom-range-input::-webkit-slider-runnable-track { + background: transparent; + border: 0; + height: 8px; +} + +.dicom-range-input::-webkit-slider-thumb { + appearance: none; + -webkit-appearance: none; + background: #2563eb; + border: 3px solid #ffffff; + border-radius: 9999px; + box-shadow: 0 2px 8px rgba(37, 99, 235, 0.28); + cursor: grab; + height: 20px; + margin-top: -6px; + pointer-events: auto; + width: 20px; +} + +.dicom-range-input::-moz-range-track { + background: transparent; + border: 0; + height: 8px; +} + +.dicom-range-input::-moz-range-thumb { + background: #2563eb; + border: 3px solid #ffffff; + border-radius: 9999px; + box-shadow: 0 2px 8px rgba(37, 99, 235, 0.28); + cursor: grab; + height: 14px; + pointer-events: auto; + width: 14px; +} + +.dicom-range-input:active::-webkit-slider-thumb { + cursor: grabbing; +} + +.dicom-range-input:active::-moz-range-thumb { + cursor: grabbing; +} diff --git a/工程分析/实现方案-2026-05-08-03-03-52.md b/工程分析/实现方案-2026-05-08-03-03-52.md new file mode 100644 index 0000000..abfcd88 --- /dev/null +++ b/工程分析/实现方案-2026-05-08-03-03-52.md @@ -0,0 +1,53 @@ +# 实现方案:单条 DICOM 范围滑块 + +时间戳:2026-05-08-03-03-52 + +## 修改目标 + +将逆向体素化工作区“DICOM 切片范围”从两个上下排列的原生进度条,改为一条自绘轨道叠加两个可拖动端点的范围条。 + +## 涉及路径 + +- `WebSite/src/components/ReverseWorkspace.tsx` +- `WebSite/src/index.css` +- `工程分析/经验记录.md` + +## 技术路线 + +1. 在 `ReverseWorkspace.tsx` 中基于 `safeSliceStart`、`safeSliceEnd`、`displayStart`、`displayEnd` 计算范围条起止百分比。 +2. 替换原有两个 ``。 +3. 使用一个 `relative` 容器绘制: + - 灰色底轨; + - 蓝色选中范围; + - 两个透明轨道的 `range input`,只显示滑块端点。 +4. 在 `index.css` 中新增 `.dicom-range-input` 样式,隐藏原生轨道,保留 thumb,并让 thumb 可点击拖动。 +5. 端点重合时提高“起点”滑块层级,便于从默认 `300-300` 状态向左拖出范围。 + +## 数据流与交互流程 + +- 用户拖动起点端点:调用 `setSliceStart(Number(event.target.value))`。 +- 用户拖动终点端点:调用 `setSliceEnd(Number(event.target.value))`。 +- 当前显示范围继续由 `displayStart = min(start, end)` 与 `displayEnd = max(start, end)` 决定。 +- 后续 `loadFusionVolume(displayStart/displayEnd)` 相关逻辑保持现状。 + +## 兼容性与回滚方案 + +- 若自定义样式在目标浏览器表现异常,可回滚本次对 `ReverseWorkspace.tsx` 和 `index.css` 的修改,恢复两个原生 range。 +- 本次不修改后端 API 和数据结构,回滚风险较低。 + +## 风险控制 + +- 保留无障碍 `aria-label`,明确起点和终点端点。 +- 通过 `npm run lint` 和 `npm run build` 检查 TypeScript 与构建。 +- 部署后通过页面源码或构建产物搜索确认不存在“起点/终点两个 label + 两个进度条”的旧结构。 + +## 预计文件变更 + +- 新增本次需求、实现、测试方案文档。 +- 修改 `ReverseWorkspace.tsx` 中 DICOM 范围控件 JSX。 +- 修改 `index.css` 增加范围条浏览器兼容样式。 +- 追加 `经验记录.md`。 + +## 人工审核状态 + +用户已在项目工作流历史中确认后续直接执行,本次不等待二次人工审核。 diff --git a/工程分析/测试方案-2026-05-08-03-03-52.md b/工程分析/测试方案-2026-05-08-03-03-52.md new file mode 100644 index 0000000..7781234 --- /dev/null +++ b/工程分析/测试方案-2026-05-08-03-03-52.md @@ -0,0 +1,45 @@ +# 测试方案:DICOM 单范围条修复 + +时间戳:2026-05-08-03-03-52 + +## 静态检查 + +- 执行 `npm run lint`,验证 TypeScript 类型检查通过。 +- 执行 `npm run build`,验证 Vite 生产构建通过。 + +## 关键业务场景验证 + +- 打开逆向体素化工作区,查看“DICOM 切片范围”只显示一条范围轨道。 +- 默认 `300 - 300 / 300` 状态下,拖动起点端点可向左形成 `M - 300 / 300`。 +- 拖动终点端点可更新终点,起点和终点允许交叉,显示值仍自动按小到大输出。 +- 变化范围后,三维融合视角仍加载对应 DICOM 切片范围。 + +## 医学影像数据边界验证 + +- DICOM 总数为 1 时,范围条不除以 0,端点显示 `1 - 1 / 1`。 +- DICOM 总数为 300 时,最大端点仍为第 300 张。 + +## 回归风险 + +- 本次只改 UI 控件,不改 DICOM 数据读取和 STL 叠加逻辑。 +- 需要确认自定义 range 样式不会影响同页面其它原生滑块。 + +## 验收标准 + +- 页面不再出现上下两条“起点/终点”蓝色进度条。 +- `ReverseWorkspace.tsx` 中不再保留旧的两个 label range 结构。 +- 构建和重新部署成功。 + +## 无法测试的风险 + +- 当前无法在用户浏览器中直接确认缓存是否清除;部署后若仍看到旧界面,需要强制刷新浏览器缓存。 + +## 人工审核状态 + +用户已在项目工作流历史中确认后续直接执行,本次不等待二次人工审核。 + +## 执行结果 + +- `npm run lint`:通过。 +- `npm run build`:通过;仅出现 Vite chunk 大小提示,不影响运行。 +- `rg` 验证:`ReverseWorkspace.tsx` 中旧的 `grid grid-cols-[76px_1fr_64px]` 双 range 结构已移除,构建产物包含新的 `dicom-range-input` 单范围条结构。 diff --git a/工程分析/经验记录.md b/工程分析/经验记录.md index 9697f76..6d5e01e 100644 --- a/工程分析/经验记录.md +++ b/工程分析/经验记录.md @@ -721,3 +721,21 @@ C. 解决问题方案 D. 后续如何避免问题 所有配准相关可视化都必须区分“显示范围”和“空间基准”:显示范围可以变化,但 DICOM 物理尺寸、模型缩放基准和坐标原点不能跟着变化;任何切割或 Mask 预览都要明确只是辅助显示,不能修改原始 DICOM/STL 位姿。 + +## 2026-05-08-03-03-52 项目定位错误导致控件未修复 + +A. 具体问题 + +用户要求将“DICOM 切片范围”的起点、终点合并为一个范围条,但页面仍显示两个上下排列的进度条。 + +B. 产生问题原因 + +前一次处理时依据当前工作目录误改了 `Head_CT_Morph`,而用户截图中的真实页面来自 `ReVoxelSeg_DICOM/WebSite/src/components/ReverseWorkspace.tsx`。没有先用截图中的页面文案和 DOM 片段全局定位真实渲染文件,导致改动没有作用到用户看到的程序。 + +C. 解决问题方案 + +在 `/home/wkmgc/Desktop` 范围内搜索 `DICOM 切片范围` 和 `三维融合场景已就绪`,定位到正确项目 `ReVoxelSeg_DICOM`。将 `ReverseWorkspace.tsx` 中两个独立 `range` 替换为单条自绘轨道叠加两个端点的范围控件,并在 `index.css` 中隐藏原生 range 轨道、保留可拖动 thumb。 + +D. 后续如何避免问题 + +收到 UI 截图或 DOM 片段时,先用页面可见文案、class 片段和组件文本在所有相关项目目录中定位真实源码,再修改代码。若当前仓库内找不到截图中的文本,必须立即扩大搜索范围并向用户说明实际项目位置,不能默认当前工作目录就是目标项目。 diff --git a/工程分析/需求分析-2026-05-08-03-03-52.md b/工程分析/需求分析-2026-05-08-03-03-52.md new file mode 100644 index 0000000..c89b915 --- /dev/null +++ b/工程分析/需求分析-2026-05-08-03-03-52.md @@ -0,0 +1,40 @@ +# 需求分析:修正 DICOM 切片范围控件 + +时间戳:2026-05-08-03-03-52 + +## 原始需求 + +用户反馈页面仍然显示“起点”“终点”两个独立进度条,并指出当前截图中的 DOM: + +- `起点 ` +- `终点 ` + +要求将其改成一个范围控件,而不是上下两条进度条。 + +## 业务目标 + +- 在逆向体素化工作区的“DICOM 切片范围”中,用一个范围条承载起点和终点两个端点。 +- 起点、终点仍能独立拖动,并允许调整顺序。 +- 显示范围仍使用 `M - N / 总数`,不改变 DICOM/STL 数据加载逻辑。 + +## 影响范围 + +- 页面路径:逆向体素化/影像与模型融合视角。 +- 主要文件:`WebSite/src/components/ReverseWorkspace.tsx`。 +- 样式文件:`WebSite/src/index.css`。 + +## 约束 + +- 必须保留现有 `sliceStart`、`sliceEnd` 状态和 `displayStart/displayEnd` 归一化逻辑。 +- 只修复当前控件形态,不改动模型切割、Mask 生成和数据 API。 +- 本次是对上次误改错误项目的纠正,必须确认真实渲染文件来自 `ReVoxelSeg_DICOM`。 + +## 风险点 + +- 双滑块叠在同一个轨道上时,端点相同的情况下可能难以拖动。 +- 原生 range 轨道默认样式可能在浏览器中显示为两条蓝线,需用 CSS 隐藏原生轨道并自绘单条轨道。 +- Vite 开发服务需要重新部署到当前项目实际端口 `4000`。 + +## 待确认事项 + +- 用户已在历史工作流中确认后续直接执行;本次不再停等人工二次确认。