Bladeren bron

fix (1.53.0 -> 1.53.1): 修复反色对比模态框中反色应用时机问题

- 重构 InvertContrastModal 组件,移除基于 setTimeout 的反色应用逻辑
- 添加 applyInvertContrast 函数,实现更可靠的反色应用
- 在 StackViewer 组件中添加 onSetStackComplete 回调,确保图像栈设置成功后才应用反色
- 扩展 safeSetStack 函数,支持开始和完成回调,提高异步操作的可观察性

改动文件:
- src/pages/view/components/InvertContrastModal.tsx
- src/pages/view/components/viewers/stack.image.viewer.tsx
dengdx 1 week geleden
bovenliggende
commit
835f012626

+ 15 - 0
CHANGELOG.md

@@ -2,6 +2,21 @@
 
 本项目的所有重要变更都将记录在此文件中。
 
+## [1.53.1] - 2026-01-06 14:19
+
+### 修复 (Fixed)
+
+- **修复反色对比模态框中反色应用时机问题** - 重构反色应用逻辑,确保在图像栈设置成功后才应用反色,避免时机问题
+  - 重构 InvertContrastModal 组件,移除基于 setTimeout 的反色应用逻辑
+  - 添加 applyInvertContrast 函数,实现更可靠的反色应用
+  - 在 StackViewer 组件中添加 onSetStackComplete 回调,确保图像栈设置成功后才应用反色
+  - 扩展 safeSetStack 函数,支持开始和完成回调,提高异步操作的可观察性
+
+**改动文件:**
+
+- src/pages/view/components/InvertContrastModal.tsx
+- src/pages/view/components/viewers/stack.image.viewer.tsx
+
 ## [1.53.0] - 2026-01-06 14:03
 
 ### 修复 (Fixed)

+ 1 - 1
package.json

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

+ 27 - 20
src/pages/view/components/InvertContrastModal.tsx

@@ -33,27 +33,18 @@ const InvertContrastModal: React.FC = () => {
     dispatch(closeInvertContrastModal());
   };
 
-  // 应用反色效果到右侧图像
-  useEffect(() => {
-    if (isOpen && leftImageUrl && rightImageUrl) {
-      console.log('[InvertContrastModal] Modal opened with:', { leftImageUrl, rightImageUrl });
-
-      // Modal打开后,等待右侧viewport创建完成,然后应用反色
-      const timer = setTimeout(() => {
-        const rightViewportId = 'invert-contrast-right';
-        try {
-          console.log(`[InvertContrastModal] Applying invert to ${rightViewportId} using renderingEngine ${renderingEngineId}`);
-          invertContrast(rightViewportId);
-          console.log('[InvertContrastModal] Invert applied successfully');
-        } catch (error) {
-          console.error('[InvertContrastModal] Failed to apply invert:', error);
-          console.error('[InvertContrastModal] Error stack:', error.stack);
-        }
-      }, 500); // 增加延迟到500ms确保viewport完全加载
-
-      return () => clearTimeout(timer);
+  // 应用反色效果的函数
+  const applyInvertContrast = () => {
+    const rightViewportId = 'invert-contrast-right';
+    try {
+      console.log(`[InvertContrastModal] Applying invert to ${rightViewportId} using renderingEngine ${renderingEngineId}`);
+      invertContrast(rightViewportId);
+      console.log('[InvertContrastModal] Invert applied successfully');
+    } catch (error) {
+      console.error('[InvertContrastModal] Failed to apply invert:', error);
+      console.error('[InvertContrastModal] Error stack:', error.stack);
     }
-  }, [isOpen, leftImageUrl, rightImageUrl]);
+  };
 
   // 工具栏事件处理
   const handleRotateLeft = () => {
@@ -176,6 +167,22 @@ const InvertContrastModal: React.FC = () => {
                   viewportId="invert-contrast-right"
                   renderingEngineId={renderingEngineId}
                   selected={false}
+                  onSetStackStart={(viewportId, imageIds, imageIndex) => {
+                    console.log(`[${viewportId}] setStack 开始:`, { imageIds, imageIndex });
+                  }}
+                  onSetStackComplete={(viewportId, imageIds, imageIndex, duration, success) => {
+                    console.log(`[${viewportId}] setStack 完成:`, {
+                      imageIds,
+                      imageIndex,
+                      duration: `${duration.toFixed(2)}ms`,
+                      success
+                    });
+
+                    if (success) {
+                      // 图像栈设置成功,可以安全地应用反色
+                      applyInvertContrast();
+                    }
+                  }}
                 />
               </div>
             )}

+ 31 - 5
src/pages/view/components/viewers/stack.image.viewer.tsx

@@ -1670,13 +1670,19 @@ export class ImageLoadError extends Error {
  * @param imageIds - 图像 ID 数组
  * @param imageIndex - 当前图像索引
  * @param timeout - 超时时间(毫秒),默认30秒
+ * @param onSetStackStart - setStack 开始回调
+ * @param onSetStackComplete - setStack 完成回调
  */
 export async function safeSetStack(
   viewport: any,
   imageIds: string[],
   imageIndex: number,
-  timeout: number = 30000
+  timeout: number = 30000,
+  onSetStackStart?: (viewportId: string, imageIds: string[], imageIndex: number) => void,
+  onSetStackComplete?: (viewportId: string, imageIds: string[], imageIndex: number, duration: number, success: boolean) => void
 ): Promise<void> {
+  const startTime = performance.now();
+  const viewportId = viewport?.element?.id || 'unknown';
   const errors: string[] = [];
 
   // 错误事件处理器
@@ -1689,6 +1695,9 @@ export async function safeSetStack(
   eventTarget.addEventListener(EVENTS.IMAGE_LOAD_ERROR, handler);
 
   try {
+    // 触发开始回调
+    onSetStackStart?.(viewportId, imageIds, imageIndex);
+
     // 创建超时 Promise
     const timeoutPromise = new Promise<never>((_, reject) => {
       setTimeout(() => {
@@ -1701,7 +1710,9 @@ export async function safeSetStack(
       viewport.setStack(imageIds, imageIndex),
       timeoutPromise
     ]);
-
+    const duration = performance.now() - startTime;
+    // 触发完成回调(成功)
+    onSetStackComplete?.(viewportId, imageIds, imageIndex, duration, true);
     // setStack 完成后,检查是否有错误
     if (errors.length > 0) {
       throw new ImageLoadError(
@@ -1711,6 +1722,11 @@ export async function safeSetStack(
     }
 
     console.log(`✅ 图像栈设置成功: ${imageIds.length} 张图像`);
+  } catch (error) {
+    const duration = performance.now() - startTime;
+    // 触发完成回调(失败)
+    onSetStackComplete?.(viewportId, imageIds, imageIndex, duration, false);
+    throw error;
   } finally {
     // 无论成功失败,都移除监听器(清理资源)
     eventTarget.removeEventListener(EVENTS.IMAGE_LOAD_ERROR, handler);
@@ -1722,13 +1738,17 @@ const StackViewer = ({
   imageUrls = [],
   viewportId,
   renderingEngineId,
-  selected
+  selected,
+  onSetStackStart,
+  onSetStackComplete
 }: {
   imageIndex?: number;
   imageUrls?: string[];
   viewportId: string;
   renderingEngineId: string;
   selected?: boolean;
+  onSetStackStart?: (viewportId: string, imageIds: string[], imageIndex: number) => void;
+  onSetStackComplete?: (viewportId: string, imageIds: string[], imageIndex: number, duration: number, success: boolean) => void;
 }) => {
   const elementRef = useRef<HTMLDivElement>(null);
   // 用于捕获异步错误并在渲染时抛出,让 Error Boundary 能够捕获
@@ -1952,7 +1972,7 @@ const StackViewer = ({
 
         try {
           // 设置图像栈,使用扩展后的imageUrls
-          await safeSetStack(viewport, finalImageUrls, imageIndex);
+          await safeSetStack(viewport, finalImageUrls, imageIndex, 30000, onSetStackStart, onSetStackComplete);
 
           // 加载完成 - 隐藏进度条
           dispatch(completeLoading(viewportId));
@@ -2211,7 +2231,9 @@ export const StackViewerWithErrorBoundary = ({
   renderingEngineId,
   selected,
   maxRetries = 3,
-  onError
+  onError,
+  onSetStackStart,
+  onSetStackComplete
 }: {
   imageIndex?: number;
   imageUrls?: string[];
@@ -2220,6 +2242,8 @@ export const StackViewerWithErrorBoundary = ({
   selected?: boolean;
   maxRetries?: number;
   onError?: (error: Error, errorInfo: any) => void;
+  onSetStackStart?: (viewportId: string, imageIds: string[], imageIndex: number) => void;
+  onSetStackComplete?: (viewportId: string, imageIds: string[], imageIndex: number, duration: number, success: boolean) => void;
 }) => {
   return (
     <ImageViewerErrorBoundary
@@ -2232,6 +2256,8 @@ export const StackViewerWithErrorBoundary = ({
         viewportId={viewportId}
         renderingEngineId={renderingEngineId}
         selected={selected}
+        onSetStackStart={onSetStackStart}
+        onSetStackComplete={onSetStackComplete}
       />
     </ImageViewerErrorBoundary>
   );