Ver Fonte

实现拖拽bodypositionlist列表中的信息到胶片上

dengdx há 1 mês atrás
pai
commit
4b7270f587
2 ficheiros alterados com 142 adições e 7 exclusões
  1. 132 0
      src/pages/output/print/DcmCell.tsx
  2. 10 7
      src/pages/output/print/Film.tsx

+ 132 - 0
src/pages/output/print/DcmCell.tsx

@@ -0,0 +1,132 @@
+// ViewportContainer.jsx
+import React, { useRef, useEffect, useState, useMemo } from 'react';
+import * as cornerstone from '@cornerstonejs/core';
+import {
+  RenderingEngine,
+  Enums
+} from '@cornerstonejs/core';
+import { v4 as uuidv4 } from 'uuid';
+import { getDcmImageUrl } from '@/API/bodyPosition';
+// import { dicomWebImageLoader } from '@cornerstonejs/dicom-web-image-loader';
+
+// // 初始化图像加载器(关键:必须提前注册)
+// const initImageLoader = () => {
+//   // 注册 DICOM Web 加载器(根据实际场景选择加载器)
+//   cornerstone.registerImageLoader('dicomweb', dicomWebImageLoader);
+//   // 配置加载器(Worker 路径需指向实际文件)
+//   dicomWebImageLoader.configure({
+//     webWorkerPath: '/cornerstone-workers/',
+//     maxWebWorkers: Math.max(navigator.hardwareConcurrency - 1, 1),
+//   });
+// };
+
+type ViewportContainerProps = {
+  imageId: string;
+  className?: string;   // 可选
+};
+
+const ViewportContainer = ({ imageId, className }: ViewportContainerProps) => {
+    console.log(`ViewportContainer 得到的属性 :${imageId}`)
+  const viewportRef = useRef(null);
+  const [renderingEngine, setRenderingEngine] = useState<any>(null);
+  const [stackViewport, setStackViewport] = useState<any>(null);
+    // ✅ 用 useMemo 保证 id 只生成一次
+  const viewportId = useMemo(() => `stackViewport-${imageId}-${uuidv4()}`, []);
+
+  // 初始化 Cornerstone 渲染引擎
+  useEffect(() => {
+    // initImageLoader();
+
+    // 创建渲染引擎实例
+    const engine = new RenderingEngine('myRenderingEngineForPrint');
+    setRenderingEngine(engine);
+
+    // 配置 Viewport
+    if (viewportRef.current) {
+      const viewportInput:cornerstone.Types.PublicViewportInput = {
+        viewportId: viewportId,
+        type: Enums.ViewportType.STACK, // 序列视图类型
+        element: viewportRef.current,
+        defaultOptions: {
+          background: [0.1, 0.1, 0.1], // 深灰背景
+        },
+      };
+
+      // 绑定 Viewport 到引擎
+      engine.enableElement(viewportInput);
+      // 获取 Viewport 实例
+      const viewport = engine.getViewport(viewportId);
+      setStackViewport(viewport);
+    }
+
+    // 组件卸载时清理
+    return () => {
+      renderingEngine?.destroy();
+    };
+  }, []);
+
+  // 处理拖放事件(核心逻辑)
+  const handleDrop = async (e) => {
+    e.preventDefault();
+    e.stopPropagation();
+
+    // 1. 获取拖拽传递的序列标识
+    const seriesInstanceUID = e.dataTransfer.getData('seriesInstanceUID');
+    if (!seriesInstanceUID || !stackViewport) return;
+
+    try {
+      // 2. 获取该序列的所有图像 ID(需替换为实际接口)
+      const imageIds = getDcmImageUrl(seriesInstanceUID);//await fetchImageIds(seriesInstanceUID);
+      console.log(`拖拽后要加载图像 是 ${imageIds}`);
+      // 3. 加载图像并设置到 Viewport
+      await stackViewport.setStack([imageIds],0);
+      // 4. 渲染图像
+      stackViewport.render();
+    } catch (error) {
+      console.error('加载图像失败:', error);
+    }
+  };
+
+  // 辅助函数:从服务端获取图像 ID 列表
+  const fetchImageIds = async (seriesInstanceUID) => {
+    // 实际项目中替换为真实接口
+    const response = await fetch(`/api/dicom/series/${seriesInstanceUID}/images`);
+    const data = await response.json();
+    // 图像 ID 格式示例: dicomweb://server/studies/1.2.3/series/4.5/instances/6.7
+    return data.imageIds;
+  };
+
+  return (
+    <div
+      className="viewport-container"
+      onDragOver={(e) => {
+        e.preventDefault(); // 允许放置
+        e.dataTransfer.dropEffect = 'copy';
+      }}
+      onDrop={handleDrop}
+      style={{
+        width: '100%',
+        height: '600px',
+        border: '2px dashed #666',
+        borderRadius: '8px',
+        position: 'relative'
+      }}
+    >
+      {/* Cornerstone 渲染挂载点 */}
+      <div ref={viewportRef} style={{ width: '100%', height: '100%' }} />
+      
+      {/* 拖拽提示文字 */}
+      <div style={{
+        position: 'absolute',
+        top: '50%',
+        left: '50%',
+        transform: 'translate(-50%, -50%)',
+        color: '#999'
+      }}>
+        拖拽序列到此处显示
+      </div>
+    </div>
+  );
+};
+
+export default ViewportContainer;

+ 10 - 7
src/pages/output/print/Film.tsx

@@ -4,6 +4,8 @@ import { useDispatch } from 'react-redux';
 import { setSelectedImageIndex } from '@/states/print/printSlice';
 import { StackViewerWithErrorBoundary } from '@/pages/view/components/viewers/stack.image.viewer';
 import { getDcmImageUrl } from '@/API/bodyPosition';
+import ViewportContainer from './DcmCell';
+import { v4 as uuidv4 } from 'uuid';
 
 const renderingEngineId = 'renderingEngineForPrint';
 
@@ -45,6 +47,7 @@ const Film: React.FC = () => {
   };
   const generateCell = (imageId) => {
     const actualUrl = getDcmImageUrl(imageId)
+    return (<ViewportContainer imageId={imageId} />);
     return (
       <div
         key={imageId}
@@ -80,24 +83,24 @@ const Film: React.FC = () => {
     const { layout } = currentFilm;
     const cells: any[] = [];
     if (layout === '1x1') {
-      cells.push(generateCell(null));
+      cells.push(generateCell(uuidv4()));
       return cells;
     } else if (layout === '1x2') {
-      cells.push(generateCell(null));
-      cells.push(generateCell(null));
+      cells.push(generateCell(uuidv4()));
+      cells.push(generateCell(uuidv4()));
       return cells;
     } else if (layout === '2x1') {
-      cells.push(generateCell(null));
-      cells.push(generateCell(null));
+      cells.push(generateCell(uuidv4()));
+      cells.push(generateCell(uuidv4()));
       return cells;
     } else if (layout === '2x2') {
       for (let i = 0; i < 4; i++) {
-        cells.push(generateCell(null));
+        cells.push(generateCell(uuidv4()));
       }
       return cells;
     }
     console.warn(`没有得到布局信息`);
-    return generateCell(null);//不知道什么布局,认为只有一个格子
+    return generateCell(uuidv4());//不知道什么布局,认为只有一个格子
   }
 
   const handleCellClick = (sop_instance_uid: string) => {