标记功能Bug修复总结.md 4.8 KB

标记功能 Bug 修复总结

问题描述

点击预定义标记按钮后,标记无法在图像上显示。

根本原因分析

发现了两个关键问题导致标记功能失效:

1. Action 分发和匹配不一致 ⚠️

问题位置: MarkPanel.tsxViewerContainer.tsx

问题描述:

  • MarkPanel.tsx 发送的 action 是 'AddPredefinedMark:拉姿' (带参数的字符串)
  • ViewerContainer.tsx 的 switch case 匹配的是 'AddPredefinedMark' (不带参数)
  • 导致 switch 无法匹配,代码走到 default 分支不执行任何操作

修复方案:ViewerContainer.tsx 的 useEffect 中,在 switch 语句之前添加预处理逻辑:

// 预处理带参数的 action
if (action.startsWith('AddPredefinedMark:')) {
  const markText = action.substring('AddPredefinedMark:'.length);
  console.log(`添加预定义标记到图像: ${markText}`);
  selectedViewportIds.forEach((viewportId) => {
    addCustomMark(viewportId, markText);
  });
  dispatch(clearAction());
  return;
}

2. StackViewer 组件缺少 id 属性 🔴 (关键问题)

问题位置: stack.image.viewer.tsx

问题描述:

  • addCustomMark 函数使用 document.getElementById(currentViewportId) 来获取元素
  • 但 StackViewer 组件渲染的 <div> 没有设置 id 属性
  • 导致 getElementById 返回 null,无法获取元素尺寸和添加标记

修复方案: 给 StackViewer 组件的根 div 添加 id 属性:

return (
  <div
    id={viewportId}  // ✅ 添加此行
    ref={elementRef}
    onContextMenu={(e) => e.preventDefault()}
    style={{...}}
  />
);

3. 删除标记功能未实现

问题位置: MarkPanel.tsx

问题描述:

  • handleDeleteMark 函数只有 TODO 注释,没有实际实现

修复方案:

const handleDeleteMark = () => {
  // 触发删除选中标记的action
  dispatch({ type: 'functionArea/setAction', payload: 'Delete Selected Mark' });
  console.log('删除选中的标记');
};

修改的文件

  1. src/pages/view/components/ViewerContainer.tsx

    • 在 useEffect 中添加预处理带参数的 action 逻辑
    • 移除了原本无法匹配的 case 'AddPredefinedMark' 分支
    • 优化了日志输出
  2. src/pages/view/components/MarkPanel.tsx

    • 实现了 handleDeleteMark 函数
  3. src/pages/view/components/viewers/stack.image.viewer.tsx

    • 给 StackViewer 根元素添加 id={viewportId} 属性

功能验证

修复后,以下功能应该正常工作:

✅ 预定义标记

  • 点击任何预定义标记按钮(拉姿、仰卧、俯卧等)
  • 标记文本应该显示在选中视口的图像中心

✅ 时间戳标记

  • 点击时间戳按钮
  • 当前时间(格式:YYYY-MM-DD HH:mm:ss)应该显示在图像上

✅ 自定义标记

  • 在输入框输入文本并添加到下拉框
  • 从下拉框选择标记
  • 点击"添加"按钮
  • 自定义文本应该显示在图像上

✅ 删除标记

  • 点击"删除标记"按钮
  • 图像上的所有 L 和 R 标记应该被删除

技术要点

Action 预处理模式

对于带参数的 action,使用字符串前缀匹配的方式:

if (action.startsWith('ActionPrefix:')) {
  const param = action.substring('ActionPrefix:'.length);
  // 处理逻辑
  dispatch(clearAction());
  return;
}

DOM 元素查询

当底层函数需要通过 DOM API 访问元素时,确保:

  1. 组件渲染的元素有唯一的 id 属性
  2. id 值与传递给底层函数的 viewportId 保持一致

LabelTool 使用

使用 Cornerstone 的 LabelTool 添加文本标记:

toolGroup.setToolActive(LabelTool.toolName, { bindings: [] });
const position: Types.Point3 = [x, y, 0];
LabelTool.hydrate(viewportId, position, text);
toolGroup.setToolPassive(LabelTool.toolName, { removeAllBindings: true });

后续改进建议

  1. 标记位置优化

    • 目前标记都添加在图像中心
    • 可考虑实现点击位置添加标记
  2. 标记样式定制

    • 支持自定义标记颜色、字体大小等
  3. 标记持久化

    • 考虑将自定义标记列表保存到本地存储
    • 页面刷新后恢复之前添加的自定义标记
  4. 删除功能增强

    • 目前只能删除 L 和 R 标记
    • 应该支持删除所有类型的标记,包括自定义标记和时间戳
  5. 错误处理

    • 添加更完善的错误处理和用户反馈
    • 当操作失败时给出明确的提示

测试建议

  1. 在不同的网格布局(1x1, 1x2, 2x1, 2x2)下测试
  2. 测试选中单个和多个视口的情况
  3. 测试快速连续添加多个标记
  4. 测试添加、删除标记的交替操作
  5. 验证标记在图像旋转、翻转后的表现

修复日期

2025年10月22日

修复人员

Cline AI Assistant