Bug ID: #81
严重程度: 高
状态: 已修复
修复日期: 2025-12-09
在 process 模式下,切换体位后点击 "Adjust Brightness and Contrast" 按钮,功能无法启用:
复现路径:
process 模式当在 process 模式下切换体位时,React 会根据新的 imageUrl 创建新的 StackViewer 组件实例:
// ViewerContainer.tsx
<StackViewerWithErrorBoundary
key={actualUrl} // ← 新的 URL = 新的 key = 新的组件实例
viewportId={getViewportIdByUrl(originalUrl)}
// ...
/>
问题流程:
StackViewer 组件 unmount,旧的视口元素被销毁StackViewer 组件 mount,创建新的视口元素ToolGroupManager 中的工具组 ID 基于 viewportId,而 viewportId 没有变化toolGroup.setToolActive() 时,工具组尝试在旧元素上设置鼠标绑定,但该元素已不存在在 stack.image.viewer.tsx:179-230 的 registerTools 函数:
// 原始代码(有问题)
function registerTools(viewportId, renderingEngineId) {
registerGlobalTools();
const toolGroupId = `STACK_TOOL_GROUP_ID_${viewportId}`;
// ❌ 问题:直接创建工具组,没有检查是否已存在
const toolGroupTmp = ToolGroupManager.createToolGroup(toolGroupId);
if (!toolGroupTmp) {
console.error(`[registerTools] Failed to create tool group`);
return;
}
// 如果工具组已存在,createToolGroup() 返回 undefined
// 导致后续代码无法执行,也没有重新绑定视口
}
getToolgroupByViewportId() 能找到现有的工具组(因为 ID 相同)toolGroup.setToolActive() 调用成功(API 层面)src/pages/view/components/viewers/stack.image.viewer.tsx:179-230
function registerTools(viewportId, renderingEngineId) {
// 确保全局工具已注册(只会执行一次)
registerGlobalTools();
// 创建该 viewport 的工具组
const toolGroupId = `STACK_TOOL_GROUP_ID_${viewportId}`;
// 🔧 修复:检查工具组是否已存在
let toolGroup = ToolGroupManager.getToolGroup(toolGroupId);
if (toolGroup) {
// 工具组已存在 - 需要重新绑定到新的视口元素
console.log(`[registerTools] Tool group already exists, re-binding viewport: ${viewportId}`);
try {
// 移除旧的视口绑定(清理过时的引用)
toolGroup.removeViewports(renderingEngineId, viewportId);
console.log(`[registerTools] Removed old viewport binding`);
} catch (error) {
console.warn(`[registerTools] Failed to remove old viewport binding:`, error);
}
// 重新添加视口到工具组(创建新的绑定)
toolGroup.addViewport(viewportId, renderingEngineId);
console.log(`[registerTools] Re-added viewport to tool group`);
return; // 重新绑定完成,退出
}
// 工具组不存在 - 创建新的工具组
const toolGroupTmp = ToolGroupManager.createToolGroup(toolGroupId);
if (!toolGroupTmp) {
console.error(
`[registerTools] Failed to create tool group for viewport: ${viewportId}`
);
return;
}
toolGroup = toolGroupTmp;
// 添加所有工具到工具组
toolGroup.addTool(MagnifyTool.toolName);
toolGroup.addTool(PanTool.toolName);
toolGroup.addTool(WindowLevelTool.toolName);
toolGroup.addTool(StackScrollTool.toolName);
toolGroup.addTool(ZoomTool.toolName);
toolGroup.addTool(LabelTool.toolName);
toolGroup.addTool(PlanarRotateTool.toolName);
toolGroup.addTool(LengthTool.toolName);
toolGroup.addTool(AngleTool.toolName);
toolGroup.addTool(TibialPlateauAngleTool.toolName);
toolGroup.addTool(DARAMeasurementTool.toolName);
toolGroup.addTool(HipDIMeasurementTool.toolName);
toolGroup.addTool(HipNHAAngleMeasurementTool.toolName);
toolGroup.addTool(VHSMeasurementTool.toolName);
toolGroup.addTool(TPLOMeasurementTool.toolName);
toolGroup.addTool(TTAMeasurementTool.toolName);
toolGroup.addTool(CBLOMeasurementTool.toolName);
toolGroup.addTool(HipCoverageMeasurementTool.toolName);
toolGroup.addTool(HipDorsalCoverageTool.toolName);
toolGroup.addTool(CircleCenterMeasurementTool.toolName);
toolGroup.addTool(MidlineMeasurementTool.toolName);
toolGroup.addTool(FindMidpointMeasurementTool.toolName);
toolGroup.addTool(VerticalTiltMeasurementTool.toolName);
toolGroup.addTool(HorizontalTiltMeasurementTool.toolName);
toolGroup.addTool(DicomOverlayTool.toolName);
toolGroup.addTool(PolygonLengthMeasurementTool.toolName);
toolGroup.addTool(PolylineLengthMeasurementTool.toolName);
// 设置默认工具状态
setupDefaultToolStates(toolGroup);
// 添加 viewport 到工具组
toolGroup.addViewport(viewportId, renderingEngineId);
console.log(`[registerTools] Tools registered for viewport: ${viewportId}`);
}
ToolGroupManager.getToolGroup() 检查toolGroup.removeViewports()toolGroup.addViewport()切换体位后,应该看到:
[registerTools] Tool group already exists, re-binding viewport: viewport-{sopInstanceUid}
[registerTools] Removed old viewport binding
[registerTools] Re-added viewport to tool group
process 模式直接修复:
间接受益(所有工具共享相同的绑定机制):
风险等级: 低
理由:
registerTools 函数潜在风险:
removeViewports() API 调用失败:已添加 try-catch 保护src/pages/exam/components/BodyPositionList.tsx - 体位切换 UIsrc/domain/exam/bodyPositionSelection.ts - 体位选择业务逻辑src/pages/view/components/ViewerContainer.tsx - 视口容器和动作处理src/pages/view/components/FunctionArea.tsx - 功能按钮区域问题: 切换体位后工具组绑定失效,导致工具激活无效
根因: 新视口元素创建后,工具组未重新绑定
修复: 在 registerTools 中添加重新绑定逻辑
效果: 所有工具在切换体位后持续可用
风险: 低,修改范围小且有错误保护
修复已完成并验证通过。
修复者: Claude (Architect Mode)
审核者: 待审核
合并日期: 待定