|
|
@@ -36,21 +36,48 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
|
|
|
const [renderingEngine, setRenderingEngine] = useState<any>(null);
|
|
|
const [stackViewport, setStackViewport] = useState<any>(null);
|
|
|
const [hasImage, setHasImage] = useState<boolean>(false); // 跟踪是否已加载图像
|
|
|
- // ✅ 用 useMemo 保证 id 只生成一次
|
|
|
- const viewportId = useMemo(() => `stackViewport-${imageId}-${uuidv4()}`, []);
|
|
|
+ const [currentImageId, setCurrentImageId] = useState<string | null>(null); // 跟踪当前已加载的图像ID
|
|
|
+ // ✅ 用 useMemo 保证 id 只生成一次,包含胶片ID和格子索引确保唯一性
|
|
|
+ const viewportId = useMemo(() =>
|
|
|
+ `stackViewport-${currentFilm.id}-${indexOfCell}-${uuidv4()}`,
|
|
|
+ [currentFilm.id, indexOfCell]
|
|
|
+ );
|
|
|
const dispatch = useDispatch();
|
|
|
|
|
|
+ // 统一的图像加载函数,供两种场景使用
|
|
|
+ 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) {
|
|
|
+ console.error(`[DcmCell] 图像加载失败 (${source}):`, error);
|
|
|
+ setHasImage(false);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
// 初始化 Cornerstone 渲染引擎
|
|
|
useEffect(() => {
|
|
|
// initImageLoader();
|
|
|
|
|
|
- // 创建渲染引擎实例
|
|
|
- const engineId = 'myRenderingEngineForPrint';
|
|
|
+ // 创建胶片特定的渲染引擎实例,确保状态隔离
|
|
|
+ const engineId = `renderingEngine-${currentFilm.id}`;
|
|
|
let engine: RenderingEngine | null = cornerstone.getRenderingEngines()?.find(en => en.id === engineId) || null;
|
|
|
// 不存在则创建
|
|
|
if (!engine) {
|
|
|
engine = new RenderingEngine(engineId);
|
|
|
}
|
|
|
+ setRenderingEngine(engine);
|
|
|
|
|
|
// 配置 Viewport
|
|
|
if (viewportRef.current) {
|
|
|
@@ -108,7 +135,7 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
|
|
|
toolGroup.setToolPassive(cornerstoneTools.WindowLevelTool.toolName);
|
|
|
|
|
|
// 将 viewport 添加到工具组
|
|
|
- toolGroup.addViewport(viewportId, 'myRenderingEngineForPrint');
|
|
|
+ toolGroup.addViewport(viewportId, engineId);
|
|
|
|
|
|
console.log(`[DcmCell] 工具组已创建并配置: ${toolGroupId}`);
|
|
|
}
|
|
|
@@ -120,18 +147,63 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
|
|
|
const toolGroupId = `PRINT_TOOL_GROUP_${viewportId}`;
|
|
|
const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(toolGroupId);
|
|
|
if (toolGroup) {
|
|
|
- // 先从工具组中移除 viewport
|
|
|
- toolGroup.removeViewports(engine.id, viewportId);
|
|
|
- // 然后销毁工具组
|
|
|
- cornerstoneTools.ToolGroupManager.destroyToolGroup(toolGroupId);
|
|
|
- console.log(`[DcmCell] 工具组已销毁: ${toolGroupId}`);
|
|
|
+ try {
|
|
|
+ // 先从工具组中移除 viewport
|
|
|
+ toolGroup.removeViewports(`renderingEngine-${currentFilm.id}`, viewportId);
|
|
|
+ // 然后销毁工具组
|
|
|
+ cornerstoneTools.ToolGroupManager.destroyToolGroup(toolGroupId);
|
|
|
+ console.log(`[DcmCell] 工具组已销毁: ${toolGroupId}`);
|
|
|
+ } catch (error) {
|
|
|
+ console.warn(`[DcmCell] 清理工具组时出错: ${error}`);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // 销毁渲染引擎
|
|
|
- renderingEngine?.destroy();
|
|
|
+ // 清理 viewport
|
|
|
+ if (engine) {
|
|
|
+ try {
|
|
|
+ engine.disableElement(viewportId);
|
|
|
+ console.log(`[DcmCell] viewport 已禁用: ${viewportId}`);
|
|
|
+ } catch (error) {
|
|
|
+ console.warn(`[DcmCell] 禁用 viewport 时出错: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
};
|
|
|
}, []);
|
|
|
|
|
|
+ // 监听 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');
|
|
|
+ if (success) {
|
|
|
+ setCurrentImageId(imageId);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // imageId 为空时清空显示
|
|
|
+ if (stackViewport) {
|
|
|
+ try {
|
|
|
+ // 清空 viewport 中的图像堆栈
|
|
|
+ await stackViewport.setStack([], 0);
|
|
|
+ stackViewport.render();
|
|
|
+ } catch (error) {
|
|
|
+ console.warn(`[DcmCell] 清空 viewport 时出错: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ setHasImage(false);
|
|
|
+ setCurrentImageId(null);
|
|
|
+ console.log(`[DcmCell] imageId 为空,清空显示`);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ loadImageFromProps();
|
|
|
+ }, [imageId, stackViewport]);
|
|
|
+
|
|
|
// 处理拖放事件(核心逻辑)
|
|
|
const handleDrop = async (e) => {
|
|
|
e.preventDefault();
|
|
|
@@ -139,22 +211,23 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
|
|
|
|
|
|
// 1. 获取拖拽传递的序列标识
|
|
|
const seriesInstanceUID = e.dataTransfer.getData('seriesInstanceUID');
|
|
|
- if (!seriesInstanceUID || !stackViewport) return;
|
|
|
+ if (!seriesInstanceUID) return;
|
|
|
|
|
|
try {
|
|
|
// 2. 获取该序列的所有图像 ID(需替换为实际接口)
|
|
|
const imageIds = getDcmImageUrl(seriesInstanceUID);
|
|
|
console.log(`拖拽后要加载图像 是 ${imageIds}`);
|
|
|
- // 3. 加载图像并设置到 Viewport
|
|
|
- await stackViewport.setStack([imageIds], 0);
|
|
|
- // 4. 渲染图像
|
|
|
- stackViewport.render();
|
|
|
- // 5. 成功加载后隐藏提示
|
|
|
- setHasImage(true);
|
|
|
- // 6. 通知到slice
|
|
|
- dispatch(addImageToCell({ index: indexOfCell, imageId: imageIds[0] }));
|
|
|
+
|
|
|
+ // 3. 使用统一的加载函数
|
|
|
+ const success = await loadImage(imageIds, 'drag');
|
|
|
+
|
|
|
+ if (success) {
|
|
|
+ setCurrentImageId(imageIds);
|
|
|
+ // 4. 通知到slice
|
|
|
+ dispatch(addImageToCell({ index: indexOfCell, imageId: imageIds }));
|
|
|
+ }
|
|
|
} catch (error) {
|
|
|
- console.error('加载图像失败:', error);
|
|
|
+ console.error('[DcmCell] 拖拽加载图像失败:', error);
|
|
|
}
|
|
|
};
|
|
|
|