import React, { useEffect } from 'react'; import StackViewer, { activateMagnifier, addLMark, addMask, addRLabel, adjustBrightnessAndContrast, ApplyColormap, deleteSelectedMark, fitImageSize, HorizontalFlip, invertContrast, InvertImage, remoteMask, ResetImage, rotateAnyAngle, RotateClockwise90, RotateCounterclockwise90, setOriginalSize, VerticalFlip, activateLengthMeasurement, deactivateLengthMeasurement, clearLengthMeasurements, activateAngleMeasurement, deactivateAngleMeasurement, clearAngleMeasurements, } from './viewers/stack.image.viewer'; import { useSelector, useDispatch } from 'react-redux'; import store, { RootState } from '@/states/store'; import { clearAction } from '@/states/view/functionAreaSlice'; import { selectGridLayout, selectSelectedViewers, selectAllViewerUrls, setGridLayout as setGridLayoutAction, setAllViewers, toggleViewerSelection, } from '@/states/view/viewerContainerSlice'; import { clearMeasurementAction, selectCurrentMeasurementAction, setToolActive, setToolInactive, } from '@/states/view/measurementPanelSlice'; import * as cornerstone from '@cornerstonejs/core'; import * as cornerstoneTools from '@cornerstonejs/tools'; import { SystemMode } from '@/states/systemModeSlice'; import * as cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader'; import { MeasurementToolManager } from '@/utils/measurementToolManager'; const renderingEngineId = 'myRenderingEngine'; const setup = () => { // 初始化 Cornerstone cornerstone.init(); cornerstoneTools.init(); const state = store.getState(); console.log(`当前系统模式:${state.systemMode.mode}`); const token = state.systemMode.mode === SystemMode.Emergency ? state.product.guest : state.userInfo.token; console.log(`token stack.image.viewer: ${token}`); cornerstoneDICOMImageLoader.init({ maxWebWorkers: navigator.hardwareConcurrency || 1, errorInterceptor: (error) => { if (error.status === 401) { console.error('Authentication failed. Please refresh the token.'); } console.error(`请求dcm文件出错:${error}`); }, beforeSend: (xhr, imageId, defaultHeaders) => { return { ...defaultHeaders, Authorization: `Bearer ${token}`, Language: 'en', Product: 'DROS', Source: 'Electron', }; }, }); // 创建渲染引擎 new cornerstone.RenderingEngine(renderingEngineId); }; setup(); interface ViewerContainerProps { imageUrls: string[]; } const ViewerContainer: React.FC = ({ imageUrls }) => { console.log(`[ViewerContainer] 新渲染 imageUrls:`, imageUrls); // 从 Redux 获取状态 const gridLayout = useSelector(selectGridLayout); const selectedViewerUrls = useSelector(selectSelectedViewers); const allViewerUrls = useSelector(selectAllViewerUrls); const action = useSelector((state: RootState) => state.functionArea.action); const measurementAction = useSelector(selectCurrentMeasurementAction); const dispatch = useDispatch(); const selectedBodyPosition = useSelector( (state: RootState) => state.bodyPositionList.selectedBodyPosition ); console.log(`[ViewerContainer] rerendered]`); // 当 imageUrls 改变时,更新 Redux 中的 allViewers useEffect(() => { if (imageUrls.length > 0 && JSON.stringify(imageUrls) !== JSON.stringify(allViewerUrls)) { dispatch(setAllViewers(imageUrls)); } }, [imageUrls, allViewerUrls, dispatch]); // 将 imageUrl 转换为 viewportId const getViewportIdByUrl = (url: string): string | null => { return `viewport-${url}`; }; const renderViewers = (start: number, end: number) => { console.log(`Rendering viewers from ${start} to ${end}`); return imageUrls.slice(start, end).map((url, index) => (
handleSelectViewer(url, event)} >
)); }; useEffect(() => { renderGrid(); }, [selectedBodyPosition, gridLayout]); useEffect(() => { if (action) { console.log( `[ViewerContainer] 处理功能操作: ${action}, selectedViewers:`, selectedViewerUrls ); // 将选中的 imageUrl 转换为 viewportId const selectedViewportIds = selectedViewerUrls .map(getViewportIdByUrl) .filter((id): id is string => id !== null); // Handle the action switch (action) { case 'Add L Mark': selectedViewportIds.forEach((viewportId) => { addLMark(viewportId); }); break; case 'Add R Mark': selectedViewportIds.forEach((viewportId) => { addRLabel(viewportId); }); console.log('Adding R Mark'); break; case 'Delete Selected Mark': { selectedViewportIds.forEach((viewportId) => { deleteSelectedMark(viewportId); }); break; } case 'Horizontal Flip': { console.log(`开始竖直翻转 in viewer container : ${JSON.stringify(selectedViewportIds)}`); selectedViewportIds.forEach((viewportId) => { HorizontalFlip(viewportId); }); break; } case 'Vertical Flip': { console.log(`开始水平翻转 in viewer container : ${JSON.stringify(selectedViewportIds)}`); selectedViewportIds.forEach((viewportId) => { VerticalFlip(viewportId); }); break; } case 'Rotate Counterclockwise 90': { selectedViewportIds.forEach((viewportId) => { RotateCounterclockwise90(viewportId); }); break; } case 'Rotate Clockwise 90': selectedViewportIds.forEach((viewportId) => { RotateClockwise90(viewportId); }); break; case 'Rotate Any Angle': selectedViewportIds.forEach((viewportId) => { rotateAnyAngle(viewportId); }); break; case 'AddMask': selectedViewportIds.forEach((viewportId) => { addMask(viewportId); }); break; case 'Delete Digital Mask': selectedViewportIds.forEach((viewportId) => { remoteMask(viewportId); }); break; case 'Adjust Brightness and Contrast': selectedViewportIds.forEach((viewportId) => { adjustBrightnessAndContrast(viewportId); }); break; case 'Crop Selected Area': console.log('Cropping Selected Area'); break; case 'Delete Mask': console.log('Deleting Mask'); break; case 'Image Comparison': console.log('Comparing Images'); break; case 'Invert Contrast': selectedViewportIds.forEach((viewportId) => { invertContrast(viewportId); }); console.log('Inverting Contrast'); break; case '1x1 Layout': dispatch(setGridLayoutAction('1x1')); console.log(`1x1 Layout`); break; case '1x2 Layout': dispatch(setGridLayoutAction('1x2')); break; case '2x1 Layout': dispatch(setGridLayoutAction('2x1')); break; case '2x2 Layout': dispatch(setGridLayoutAction('2x2')); break; case 'Magnifier': { selectedViewportIds.forEach((viewportId) => { activateMagnifier(viewportId); }); break; } case 'Fit Size': { selectedViewportIds.forEach((viewportId) => { fitImageSize(viewportId); }); console.log('Fitting Image Size'); break; } case 'Original Size': { selectedViewportIds.forEach((viewportId) => { setOriginalSize(viewportId); }); console.log('Setting Image to Original Size'); break; } case 'Zoom Image': console.log('Zooming Image'); break; case 'Reset Cursor': console.log('Resetting Cursor'); break; case 'Pan': console.log('Panning Image'); break; case 'Invert Image': selectedViewportIds.forEach((viewportId) => { InvertImage(viewportId); }); break; case 'Reset Image': selectedViewportIds.forEach((viewportId) => { ResetImage(viewportId); }); break; case 'Snapshot': console.log('Taking Snapshot'); break; case 'Advanced Processing': console.log('Performing Advanced Processing'); break; case 'Musician': console.log('Activating Musician'); break; case 'Image Measurement': console.log('Measuring Image'); break; case 'More': console.log('Showing More Options'); break; case 'Apply Colormap': selectedViewportIds.forEach((viewportId) => { ApplyColormap(viewportId); }); break; // ==================== 线段测量相关操作 ==================== case '线段测量': if (selectedViewportIds.length > 0) { selectedViewportIds.forEach((viewportId) => { activateLengthMeasurement(viewportId); }); } else { // 如果没有选中的 viewport,为所有可见的 viewport 激活 const visibleViewportCount = getVisibleViewportCount(); for (let i = 0; i < visibleViewportCount; i++) { activateLengthMeasurement(`viewport-${i}`); } } console.log('Activating Length Measurement'); break; case '清除测量': if (selectedViewportIds.length > 0) { selectedViewportIds.forEach((viewportId) => { clearLengthMeasurements(viewportId); }); } else { // 如果没有选中的 viewport,清除所有可见 viewport 的测量 const visibleViewportCount = getVisibleViewportCount(); for (let i = 0; i < visibleViewportCount; i++) { clearLengthMeasurements(`viewport-${i}`); } } console.log('Clearing Length Measurements'); break; case '停用线段测量': if (selectedViewportIds.length > 0) { selectedViewportIds.forEach((viewportId) => { deactivateLengthMeasurement(viewportId); }); } else { // 如果没有选中的 viewport,停用所有可见 viewport 的测量工具 const visibleViewportCount = getVisibleViewportCount(); for (let i = 0; i < visibleViewportCount; i++) { deactivateLengthMeasurement(`viewport-${i}`); } } console.log('Deactivating Length Measurement'); break; default: break; } dispatch(clearAction()); //清理后可连续同一个action触发响应 } }, [action, selectedViewerUrls, dispatch]); // ==================== 测量面板 Action 处理 ==================== useEffect(() => { if (measurementAction) { console.log(`[ViewerContainer] 处理测量操作: ${measurementAction}`); // 将选中的 imageUrl 转换为 viewportId const selectedViewportIds = selectedViewerUrls .map(getViewportIdByUrl) .filter((id): id is string => id !== null); // 处理测量相关操作 switch (measurementAction) { case '线段测量': if (selectedViewportIds.length > 0) { selectedViewportIds.forEach((viewportId) => { const success = activateLengthMeasurement(viewportId); if (success) { dispatch( setToolActive({ toolName: 'LengthTool', viewportId: viewportId, }) ); } }); } else { // 如果没有选中的 viewport,为所有可见的 viewport 激活 const visibleViewportCount = getVisibleViewportCount(); for (let i = 0; i < visibleViewportCount; i++) { const success = activateLengthMeasurement(`viewport-${i}`); if (success) { dispatch( setToolActive({ toolName: 'LengthTool', viewportId: `viewport-${i}`, }) ); } } } console.log('Activating Length Measurement from MeasurementPanel'); break; case '清除测量': if (selectedViewportIds.length > 0) { MeasurementToolManager.clearLengthMeasurementsForViewports(selectedViewportIds); MeasurementToolManager.clearAngleMeasurementsForViewports(selectedViewportIds); } console.log('Clearing Length Measurements from MeasurementPanel'); break; case '停用线段测量': if (selectedViewportIds.length > 0) { selectedViewportIds.forEach((viewportId) => { const success = deactivateLengthMeasurement(viewportId); if (success) { dispatch( setToolInactive({ toolName: 'LengthTool', viewportId: viewportId, }) ); } }); } console.log('Deactivating Length Measurement from MeasurementPanel'); break; case '角度测量': console.log(`开始角度测量`); if (selectedViewportIds.length > 0) { selectedViewportIds.forEach((viewportId) => { const success = activateAngleMeasurement(viewportId); if (success) { console.log(`激活角度测量工具成功`); dispatch( setToolActive({ toolName: 'AngleTool', viewportId: viewportId, }) ); } }); } console.log('Activating Angle Measurement from MeasurementPanel'); break; case '测量校正': console.log('Measurement Calibration - 功能待实现'); break; default: console.log(`未处理的测量操作: ${measurementAction}`); break; } // 清理测量 action dispatch(clearMeasurementAction()); } }, [measurementAction, selectedViewerUrls, gridLayout, dispatch]); /** * 获取当前可见的 viewport 数量 */ const getVisibleViewportCount = (): number => { switch (gridLayout) { case '1x1': return 1; case '1x2': case '2x1': return Math.min(2, imageUrls.length); case '2x2': return Math.min(4, imageUrls.length); default: return 1; } }; const handleSelectViewer = (imageUrl: string, event: React.MouseEvent) => { console.log(`handleSelectViewer : ${imageUrl}`); console.log(`selectedViewers 旧值:`, selectedViewerUrls); dispatch(toggleViewerSelection(imageUrl)); }; const renderGrid = () => { console.log('Rendering layout', gridLayout); switch (gridLayout) { case '1x1': { //变成单分格时,自动选中第一个 dispatch(toggleViewerSelection(allViewerUrls[0])); return (
{renderViewers(0, 1)}
); break; } case '1x2': return (
{renderViewers(0, 2)}
); case '2x1': return (
{renderViewers(0, 2)}
); case '2x2': return (
{renderViewers(0, 4)}
); default: return null; } }; return <>{renderGrid()}; }; export default ViewerContainer;