Переглянути джерело

打印页面实现 pan zoom windowlevel

dengdx 1 місяць тому
батько
коміт
d5e3b6890d

+ 117 - 45
src/pages/output/print/DcmCell.tsx

@@ -11,6 +11,7 @@ import { getDcmImageUrl } from '@/API/bodyPosition';
 import { useDispatch } from 'react-redux';
 import { addImageToCell, Film, toggleCellSelection } from '@/states/print/printSlice';
 import { useAppSelector } from '@/states/store';
+import { registerGlobalTools } from '@/utils/cornerstoneToolsSetup';
 // import { dicomWebImageLoader } from '@cornerstonejs/dicom-web-image-loader';
 
 // // 初始化图像加载器(关键:必须提前注册)
@@ -38,16 +39,17 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
   const [stackViewport, setStackViewport] = useState<any>(null);
   const [hasImage, setHasImage] = useState<boolean>(false); // 跟踪是否已加载图像
   const [currentImageId, setCurrentImageId] = useState<string | null>(null); // 跟踪当前已加载的图像ID
+  const [toolGroup, setToolGroup] = useState<any>(null); // 存储工具组引用
   // ✅ 用 useMemo 保证 id 只生成一次,包含胶片ID和格子索引确保唯一性
   const viewportId = useMemo(() =>
     `stackViewport-${currentFilm.id}-${indexOfCell}-${uuidv4()}`,
     [currentFilm.id, indexOfCell]
   );
   const dispatch = useDispatch();
-  
+
   // 生成格子的唯一标识符
   const cellId = `${currentFilm.id}-${indexOfCell}`;
-  
+
   // 检查当前格子是否被选中
   const isSelected = useAppSelector(state =>
     state.print.selectedCells.includes(cellId)
@@ -59,19 +61,22 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
     return film?.images[indexOfCell] || null;
   });
 
+  // 监听工具模式状态
+  const toolMode = useAppSelector(state => state.print.toolMode);
+
   // 统一的图像加载函数,供两种场景使用
   const loadImage = async (targetImageId: string, source: 'props' | 'drag' = 'props'): Promise<boolean> => {
     if (!stackViewport || !targetImageId) return false;
-    
+
     try {
       const fullImageUrl = targetImageId.startsWith('dicomweb:')
         ? targetImageId
         : getDcmImageUrl(targetImageId);
-      
+
       await stackViewport.setStack([fullImageUrl], 0);
       stackViewport.render();
       setHasImage(true);
-      
+
       console.log(`[DcmCell] 图像加载成功 (${source}): ${targetImageId}`);
       return true;
     } catch (error) {
@@ -114,43 +119,47 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
 
       // ========== 添加工具支持 ==========
       const toolGroupId = `PRINT_TOOL_GROUP_${viewportId}`;
-
+      // 注册工具,反复调用也会只注册一次
+      registerGlobalTools();
       // 创建工具组
-      const toolGroup = cornerstoneTools.ToolGroupManager.createToolGroup(toolGroupId);
+      const currentToolGroup = cornerstoneTools.ToolGroupManager.createToolGroup(toolGroupId);
+
+      if (currentToolGroup) {
 
-      if (toolGroup) {
         // 添加基础工具
-        toolGroup.addTool(cornerstoneTools.StackScrollTool.toolName);
-        toolGroup.addTool(cornerstoneTools.ZoomTool.toolName);
-        toolGroup.addTool(cornerstoneTools.PanTool.toolName);
-        toolGroup.addTool(cornerstoneTools.WindowLevelTool.toolName);
-
-        // 激活滚轮滚动工具(解决滚轮错误)
-        toolGroup.setToolActive(cornerstoneTools.StackScrollTool.toolName, {
-          bindings: [{
-            mouseButton: cornerstoneTools.Enums.MouseBindings.Wheel
-          }]
-        });
-
-        // 激活缩放工具(右键)
-        toolGroup.setToolActive(cornerstoneTools.ZoomTool.toolName, {
-          bindings: [{
-            mouseButton: cornerstoneTools.Enums.MouseBindings.Secondary
-          }]
-        });
-
-        // 激活平移工具(中键)
-        toolGroup.setToolActive(cornerstoneTools.PanTool.toolName, {
-          bindings: [{
-            mouseButton: cornerstoneTools.Enums.MouseBindings.Auxiliary
-          }]
-        });
-
-        // 其他工具设置为被动状态
-        toolGroup.setToolPassive(cornerstoneTools.WindowLevelTool.toolName);
+        currentToolGroup.addTool(cornerstoneTools.StackScrollTool.toolName);
+        currentToolGroup.addTool(cornerstoneTools.ZoomTool.toolName);
+        currentToolGroup.addTool(cornerstoneTools.PanTool.toolName);
+        currentToolGroup.addTool(cornerstoneTools.WindowLevelTool.toolName);
+
+        // // 激活滚轮滚动工具(解决滚轮错误)
+        // currentToolGroup.setToolActive(cornerstoneTools.StackScrollTool.toolName, {
+        //   bindings: [{
+        //     mouseButton: cornerstoneTools.Enums.MouseBindings.Wheel
+        //   }]
+        // });
+
+        // // 设置初始工具绑定(正常模式)
+        // currentToolGroup.setToolActive(cornerstoneTools.ZoomTool.toolName, {
+        //   bindings: [{
+        //     mouseButton: cornerstoneTools.Enums.MouseBindings.Secondary // 右键
+        //   }]
+        // });
+
+        // currentToolGroup.setToolActive(cornerstoneTools.PanTool.toolName, {
+        //   bindings: [{
+        //     mouseButton: cornerstoneTools.Enums.MouseBindings.Auxiliary // 中键
+        //   }]
+        // });
+
+        // // 窗宽窗位工具设置为被动状态
+        // currentToolGroup.setToolPassive(cornerstoneTools.WindowLevelTool.toolName);
 
         // 将 viewport 添加到工具组
-        toolGroup.addViewport(viewportId, engineId);
+        currentToolGroup.addViewport(viewportId, engineId);
+
+        // 保存工具组引用
+        setToolGroup(currentToolGroup);
 
         console.log(`[DcmCell] 工具组已创建并配置: ${toolGroupId}`);
       }
@@ -160,11 +169,11 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
     return () => {
       // 销毁工具组
       const toolGroupId = `PRINT_TOOL_GROUP_${viewportId}`;
-      const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(toolGroupId);
-      if (toolGroup) {
+      const currentToolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(toolGroupId);
+      if (currentToolGroup) {
         try {
           // 先从工具组中移除 viewport
-          toolGroup.removeViewports(`renderingEngine-${currentFilm.id}`, viewportId);
+          currentToolGroup.removeViewports(`renderingEngine-${currentFilm.id}`, viewportId);
           // 然后销毁工具组
           cornerstoneTools.ToolGroupManager.destroyToolGroup(toolGroupId);
           console.log(`[DcmCell] 工具组已销毁: ${toolGroupId}`);
@@ -185,14 +194,77 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
     };
   }, []);
 
+  // 监听工具模式变化,动态切换工具绑定
+  useEffect(() => {
+    if (!toolGroup) return;
+
+    console.log(`[DcmCell] 工具模式切换到: ${toolMode}`);
+
+    try {
+      switch (toolMode) {
+        case 'zoom':
+          // 缩放模式:ZoomTool绑定到左键,其他保持默认
+          toolGroup.setToolActive(cornerstoneTools.ZoomTool.toolName, {
+            bindings: [{
+              mouseButton: cornerstoneTools.Enums.MouseBindings.Primary // 左键
+            }]
+          });
+          toolGroup.setToolPassive(cornerstoneTools.PanTool.toolName);
+          toolGroup.setToolPassive(cornerstoneTools.WindowLevelTool.toolName);
+          break;
+
+        case 'pan':
+          // 漫游模式:PanTool绑定到左键,其他保持默认
+          toolGroup.setToolActive(cornerstoneTools.PanTool.toolName, {
+            bindings: [{
+              mouseButton: cornerstoneTools.Enums.MouseBindings.Primary // 左键
+            }]
+          });
+          toolGroup.setToolPassive(cornerstoneTools.ZoomTool.toolName);
+          toolGroup.setToolPassive(cornerstoneTools.WindowLevelTool.toolName);
+          break;
+
+        case 'windowLevel':
+          // 窗宽窗位模式:WindowLevelTool绑定到左键,其他保持默认
+          toolGroup.setToolActive(cornerstoneTools.WindowLevelTool.toolName, {
+            bindings: [{
+              mouseButton: cornerstoneTools.Enums.MouseBindings.Primary // 左键
+            }]
+          });
+          toolGroup.setToolPassive(cornerstoneTools.ZoomTool.toolName);
+          toolGroup.setToolPassive(cornerstoneTools.PanTool.toolName);
+          break;
+
+        case 'normal':
+        default:
+          // 正常模式:恢复默认绑定
+          /**
+           * 默认1:窗宽窗位绑定右键
+           */
+          toolGroup.setToolActive(cornerstoneTools.WindowLevelTool.toolName, {
+            bindings: [{
+              mouseButton: cornerstoneTools.Enums.MouseBindings.Secondary // 右键
+            }]
+          });
+          toolGroup.setToolPassive(cornerstoneTools.ZoomTool.toolName);
+          toolGroup.setToolPassive(cornerstoneTools.PanTool.toolName);
+          break;
+      }
+
+      console.log(`[DcmCell] 工具模式 ${toolMode} 配置完成`);
+    } catch (error) {
+      console.error(`[DcmCell] 工具模式切换失败:`, error);
+    }
+  }, [toolMode, toolGroup]);
+
   // 监听 imageId 变化,自动加载图像
   useEffect(() => {
     const loadImageFromProps = async () => {
       if (!stackViewport) return;
-      
+
       // 避免重复加载相同图像
       if (imageId === currentImageId) return;
-      
+
       if (imageId && imageId.trim() !== '') {
         console.log(`[DcmCell] 开始从 props 加载图像: ${imageId}`);
         const success = await loadImage(imageId, 'props');
@@ -223,7 +295,7 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
   // 监听Redux状态变化,当图像被删除时清理viewport
   useEffect(() => {
     if (!stackViewport) return;
-    
+
     // 如果Redux中的图像ID为null,但viewport还有图像,则清理
     if (!currentCellImageId && hasImage) {
       try {
@@ -253,10 +325,10 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
       // 2. 获取该序列的所有图像 ID(需替换为实际接口)
       const imageIds = getDcmImageUrl(seriesInstanceUID);
       console.log(`拖拽后要加载图像 是 ${imageIds}`);
-      
+
       // 3. 使用统一的加载函数
       const success = await loadImage(imageIds, 'drag');
-      
+
       if (success) {
         setCurrentImageId(imageIds);
         // 4. 通知到slice

+ 14 - 4
src/pages/output/print/ImageOperationPanel.tsx

@@ -3,7 +3,11 @@ import { Button, Flex } from 'antd';
 import { useAppSelector } from '@/states/store';
 import Icon from '@/components/Icon';
 import { showNotImplemented } from '@/utils/notificationHelper';
-
+import {
+  toggleToolMode,
+  ToolMode,
+} from '@/states/print/printSlice';
+import { useDispatch } from 'react-redux';
 interface ImageOperationButtonProps {
   title: string;
   iconName: string;
@@ -45,6 +49,12 @@ const ImageOperationButton: React.FC<ImageOperationButtonProps> = ({
 };
 
 const ImageOperationPanel: React.FC = () => {
+    const dispatch = useDispatch();
+      // 工具模式切换处理函数
+  const handleToggleToolMode = (mode: ToolMode) => {
+    dispatch(toggleToolMode(mode));
+  };
+
   return (
     <div>
       <div className="font-semibold">图像操作</div>
@@ -52,12 +62,12 @@ const ImageOperationPanel: React.FC = () => {
         <ImageOperationButton
           title="缩放图像"
           iconName="Zoom"
-          onClick={() => showNotImplemented('缩放图像')}
+          onClick={() => handleToggleToolMode('zoom')}
         />
         <ImageOperationButton
           title="漫游图像"
           iconName="Pan"
-          onClick={() => showNotImplemented('漫游图像')}
+          onClick={() => handleToggleToolMode('pan')}
         />
         <ImageOperationButton
           title="逆时针旋转90度"
@@ -102,7 +112,7 @@ const ImageOperationPanel: React.FC = () => {
         <ImageOperationButton
           title="鼠标调节亮度和对比度"
           iconName="btn_BrightnessContrast"
-          onClick={() => showNotImplemented('鼠标调节亮度和对比度')}
+          onClick={() => handleToggleToolMode('windowLevel')}
         />
         <ImageOperationButton
           title="真实尺寸打印"

+ 26 - 0
src/states/print/printSlice.ts

@@ -2,6 +2,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
 
 export type FilmLayout = '1x1' | '1x2' | '2x1' | '2x2';
 export type FilmOrientation = 'horizontal' | 'vertical';
+export type ToolMode = 'normal' | 'zoom' | 'pan' | 'windowLevel';
 
 export interface Film {
   id: string;
@@ -18,6 +19,14 @@ interface PrintState {
    * 例如: ["1-0", "1-1", "2-0"] 表示胶片1的格子0和1,胶片2的格子0被选中
    */
   selectedCells: string[];
+  /**
+   * 当前工具模式
+   * normal: 正常模式(工具保持默认绑定)
+   * zoom: 缩放模式(ZoomTool绑定到左键)
+   * pan: 漫游模式(PanTool绑定到左键)
+   * windowLevel: 窗宽窗位模式(WindowLevelTool绑定到左键)
+   */
+  toolMode: ToolMode;
 }
 
 const initialFilm: Film = {
@@ -34,6 +43,7 @@ const initialState: PrintState = {
   films: [initialFilm],
   activeFilmId: '1',
   selectedCells: [],
+  toolMode: 'normal',
 };
 
 const printSlice = createSlice({
@@ -152,6 +162,20 @@ const printSlice = createSlice({
       tmpImages[action.payload.index] = action.payload.imageId;
       film.images = tmpImages;
     },
+
+    // 设置工具模式
+    setToolMode: (state, action: PayloadAction<ToolMode>) => {
+      state.toolMode = action.payload;
+    },
+
+    // 切换工具模式(如果当前模式相同则切换到normal,否则切换到指定模式)
+    toggleToolMode: (state, action: PayloadAction<ToolMode>) => {
+      if (state.toolMode === action.payload) {
+        state.toolMode = 'normal';
+      } else {
+        state.toolMode = action.payload;
+      }
+    },
   },
 });
 
@@ -165,6 +189,8 @@ export const {
   clearAllSelections,
   deleteSelectedImages,
   addImageToCell,
+  setToolMode,
+  toggleToolMode,
 } = printSlice.actions;
 
 export default printSlice.reducer;