Browse Source

feat: 优化注释删除逻辑,实现基于 FrameOfReferenceUID 的正确删除

- 重构 deleteAnnotation 方法,使用 annotation.metadata.FrameOfReferenceUID 查找相关 viewport elements
- 新增 findElementsByFOR 方法,支持通过 referencedImageId 查找包含该图像的 viewport elements
- 获取删除操作后的完整注释状态并保存,确保数据一致性
- 修复 annotation 命名冲突,重命名为 corAnnotation 避免与参数冲突

改动文件:
- src/features/imageAnnotation/services/AnnotationManager.ts
- CHANGELOG.md
- package.json
dengdx 4 weeks ago
parent
commit
325259a17f
3 changed files with 85 additions and 11 deletions
  1. 20 0
      CHANGELOG.md
  2. 1 1
      package.json
  3. 64 10
      src/features/imageAnnotation/services/AnnotationManager.ts

+ 20 - 0
CHANGELOG.md

@@ -5,6 +5,26 @@
 格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
 版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
 
+## [1.9.0] - 2025-12-15 18:40
+
+### 新增 (Added)
+- **注释删除逻辑优化** ([src/features/imageAnnotation/services/AnnotationManager.ts](src/features/imageAnnotation/services/AnnotationManager.ts))
+  - 重构 `deleteAnnotation` 方法,实现基于 `FrameOfReferenceUID` 的正确删除逻辑
+  - 新增 `findElementsByFOR` 方法,支持通过 `referencedImageId` 查找相关 viewport elements
+  - 使用 `annotation.state.getAnnotations()` 获取删除操作后的完整注释状态
+  - 修复 `annotation` 命名冲突,重命名为 `corAnnotation` 避免与参数冲突
+  - 实现注释数据的完整性验证和错误处理
+
+**核心改进:**
+- 基于 FrameOfReference 级别的注释管理,确保多视口场景下的数据一致性
+- 正确处理 Cornerstone3D v3.28 的注释状态获取机制
+- 提升注释删除操作的可靠性和性能
+
+**改动文件:**
+- src/features/imageAnnotation/services/AnnotationManager.ts
+
+---
+
 ## [1.8.0] - 2025-12-15 17:03
 
 ### 重构 (Refactored)

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "zsis",
-  "version": "1.8.0",
+  "version": "1.9.0",
   "private": true,
   "description": "医学成像系统",
   "main": "main.js",

+ 64 - 10
src/features/imageAnnotation/services/AnnotationManager.ts

@@ -1,14 +1,15 @@
 // src/features/imageAnnotation/services/AnnotationManager.ts
 // 全局注释管理器 - 负责监听Cornerstone事件并管理注释的保存/加载
 
-import { eventTarget } from '@cornerstonejs/core';
+import { eventTarget, getRenderingEngines } from '@cornerstonejs/core';
 import { EVENTS } from '@cornerstonejs/core';
-import { annotation, Enums } from '@cornerstonejs/tools';
+import { annotation as corAnnotation, Enums } from '@cornerstonejs/tools';
 import { AnnotationSerializer } from './AnnotationSerializer';
 import { AnnotationValidator } from './AnnotationValidator';
 import { annotationAPI } from '../../../API/annotation';
 import { extractSopInstanceUid } from '../../../utils/dicomUtils';
 import type { AnnotationData, AnnotationEventType } from '../types/annotation';
+import { IRenderingEngine, IStackViewport } from '@cornerstonejs/core/dist/esm/types';
 
 export class AnnotationManager {
   private eventListenersRegistered: boolean = false;
@@ -222,7 +223,8 @@ export class AnnotationManager {
     console.log('🎯 监听到注释删除事件:', evt);
     console.log('📋 事件详情:', evt.detail);
 
-    const { annotationUID } = evt.detail;
+    const { annotation } = evt.detail;
+    const annotationUID = annotation.annotationUID;
     console.log(`【annotationmanager】🗑️ 注释删除完成: ${annotationUID}`);
 
     // 确保有活动的图像ID
@@ -232,7 +234,7 @@ export class AnnotationManager {
     }
 
     // 实现注释删除逻辑
-    this.deleteAnnotation(annotationUID);
+    this.deleteAnnotation(annotation);
   };
 
   // ============ 注释操作方法 ============
@@ -305,8 +307,9 @@ export class AnnotationManager {
     }
   }
 
-  private async deleteAnnotation(annotationUID: string): Promise<void> {
+  private async deleteAnnotation(annotation: any): Promise<void> {
     try {
+      const annotationUID = annotation.annotationUID;
       console.log(`【annotationmanager】🗑️ 删除注释: ${annotationUID}`);
 
       if (!this.activeImageId) {
@@ -314,15 +317,66 @@ export class AnnotationManager {
         return;
       }
 
-      // 调用API删除注释
-      await annotationAPI.deleteAnnotations(this.activeImageId, annotationUID);
+      // 从 annotation 中获取 FrameOfReferenceUID
+      const forUID = annotation.metadata?.referencedImageId;
+
+      if (!forUID) {
+        console.warn('【annotationmanager】FrameOfReferenceUID 未找到,跳过删除操作');
+        return;
+      }
+
+      // 用 FrameOfReferenceUID 查找相关的 elements
+      const elements = this.findElementsByFOR(forUID);
+
+      // 收集删除后的所有注释
+      const allAnnotationStrings: string[] = [];
+
+      elements.forEach(element => {
+        const anns = corAnnotation.state.getAnnotations('', element);
+
+        if (anns) {
+          // 序列化所有注释
+          for (const [toolName, ann] of Object.entries(anns as any)) {
+            try {
+              const serialized = this.serializer.serialize(ann, toolName);
+              allAnnotationStrings.push(serialized);
+            } catch (error) {
+              console.error('序列化注释失败:', error);
+              // 跳过序列化失败的注释
+            }
+          }
+        }
+      });
+
+      // 保存所有注释
+      await annotationAPI.saveAnnotations(this.activeImageId, allAnnotationStrings);
 
-      console.log(`【annotationmanager】✅ 注释删除完成: ${annotationUID}`);
+      console.log(`【annotationmanager】✅ 注释删除完成: ${annotationUID} (剩余 ${allAnnotationStrings.length} 个注释)`);
     } catch (error) {
-      console.error(`【annotationmanager】❌ 注释删除失败 ${annotationUID}:`, error);
+      console.error(`【annotationmanager】❌ 注释删除失败:`, error);
     }
   }
 
+  // 用 FrameOfReferenceUID 查找 viewport elements
+  private findElementsByFOR(imageId: string): any[] {
+    const elements: any[] = [];
+    if (!!getRenderingEngines) {
+      const renderingEngines = getRenderingEngines();
+      renderingEngines?.forEach((re: IRenderingEngine) => {
+        re.getViewports().forEach((vp: IStackViewport) => {
+          if (vp && vp.hasImageId(imageId) === true && vp.element) {
+            elements.push(vp.element);
+          }
+        });
+      });
+    } else {
+      console.warn('【annotationmanager】getRenderingEngines 方法不可用,无法查找元素');
+    }
+
+
+    return elements;
+  }
+
   private async renderAnnotations(annotationStrings: string[]): Promise<void> {
     console.log(`【annotationmanager】🎨 渲染 ${annotationStrings.length} 个注释`);
 
@@ -332,7 +386,7 @@ export class AnnotationManager {
         const deserializedAnnotation = this.serializer.deserialize(annotationString);
 
         // 添加到Cornerstone注释状态
-        annotation.state.addAnnotation(deserializedAnnotation, deserializedAnnotation.metadata?.toolName || 'UnknownTool');
+        corAnnotation.state.addAnnotation(deserializedAnnotation, deserializedAnnotation.metadata?.toolName || 'UnknownTool');
 
         console.log(`【annotationmanager】✅ 注释渲染成功`);
       } catch (error) {