|
@@ -11,6 +11,7 @@ import { getDcmImageUrl } from '@/API/bodyPosition';
|
|
|
import { useDispatch } from 'react-redux';
|
|
import { useDispatch } from 'react-redux';
|
|
|
import { addImageToCell, Film, toggleCellSelection } from '@/states/print/printSlice';
|
|
import { addImageToCell, Film, toggleCellSelection } from '@/states/print/printSlice';
|
|
|
import { useAppSelector } from '@/states/store';
|
|
import { useAppSelector } from '@/states/store';
|
|
|
|
|
+import { registerGlobalTools } from '@/utils/cornerstoneToolsSetup';
|
|
|
// import { dicomWebImageLoader } from '@cornerstonejs/dicom-web-image-loader';
|
|
// 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 [stackViewport, setStackViewport] = useState<any>(null);
|
|
|
const [hasImage, setHasImage] = useState<boolean>(false); // 跟踪是否已加载图像
|
|
const [hasImage, setHasImage] = useState<boolean>(false); // 跟踪是否已加载图像
|
|
|
const [currentImageId, setCurrentImageId] = useState<string | null>(null); // 跟踪当前已加载的图像ID
|
|
const [currentImageId, setCurrentImageId] = useState<string | null>(null); // 跟踪当前已加载的图像ID
|
|
|
|
|
+ const [toolGroup, setToolGroup] = useState<any>(null); // 存储工具组引用
|
|
|
// ✅ 用 useMemo 保证 id 只生成一次,包含胶片ID和格子索引确保唯一性
|
|
// ✅ 用 useMemo 保证 id 只生成一次,包含胶片ID和格子索引确保唯一性
|
|
|
const viewportId = useMemo(() =>
|
|
const viewportId = useMemo(() =>
|
|
|
`stackViewport-${currentFilm.id}-${indexOfCell}-${uuidv4()}`,
|
|
`stackViewport-${currentFilm.id}-${indexOfCell}-${uuidv4()}`,
|
|
|
[currentFilm.id, indexOfCell]
|
|
[currentFilm.id, indexOfCell]
|
|
|
);
|
|
);
|
|
|
const dispatch = useDispatch();
|
|
const dispatch = useDispatch();
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 生成格子的唯一标识符
|
|
// 生成格子的唯一标识符
|
|
|
const cellId = `${currentFilm.id}-${indexOfCell}`;
|
|
const cellId = `${currentFilm.id}-${indexOfCell}`;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 检查当前格子是否被选中
|
|
// 检查当前格子是否被选中
|
|
|
const isSelected = useAppSelector(state =>
|
|
const isSelected = useAppSelector(state =>
|
|
|
state.print.selectedCells.includes(cellId)
|
|
state.print.selectedCells.includes(cellId)
|
|
@@ -59,19 +61,22 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
|
|
|
return film?.images[indexOfCell] || null;
|
|
return film?.images[indexOfCell] || null;
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ // 监听工具模式状态
|
|
|
|
|
+ const toolMode = useAppSelector(state => state.print.toolMode);
|
|
|
|
|
+
|
|
|
// 统一的图像加载函数,供两种场景使用
|
|
// 统一的图像加载函数,供两种场景使用
|
|
|
const loadImage = async (targetImageId: string, source: 'props' | 'drag' = 'props'): Promise<boolean> => {
|
|
const loadImage = async (targetImageId: string, source: 'props' | 'drag' = 'props'): Promise<boolean> => {
|
|
|
if (!stackViewport || !targetImageId) return false;
|
|
if (!stackViewport || !targetImageId) return false;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
const fullImageUrl = targetImageId.startsWith('dicomweb:')
|
|
const fullImageUrl = targetImageId.startsWith('dicomweb:')
|
|
|
? targetImageId
|
|
? targetImageId
|
|
|
: getDcmImageUrl(targetImageId);
|
|
: getDcmImageUrl(targetImageId);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
await stackViewport.setStack([fullImageUrl], 0);
|
|
await stackViewport.setStack([fullImageUrl], 0);
|
|
|
stackViewport.render();
|
|
stackViewport.render();
|
|
|
setHasImage(true);
|
|
setHasImage(true);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
console.log(`[DcmCell] 图像加载成功 (${source}): ${targetImageId}`);
|
|
console.log(`[DcmCell] 图像加载成功 (${source}): ${targetImageId}`);
|
|
|
return true;
|
|
return true;
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
@@ -114,43 +119,47 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
|
|
|
|
|
|
|
|
// ========== 添加工具支持 ==========
|
|
// ========== 添加工具支持 ==========
|
|
|
const toolGroupId = `PRINT_TOOL_GROUP_${viewportId}`;
|
|
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 添加到工具组
|
|
// 将 viewport 添加到工具组
|
|
|
- toolGroup.addViewport(viewportId, engineId);
|
|
|
|
|
|
|
+ currentToolGroup.addViewport(viewportId, engineId);
|
|
|
|
|
+
|
|
|
|
|
+ // 保存工具组引用
|
|
|
|
|
+ setToolGroup(currentToolGroup);
|
|
|
|
|
|
|
|
console.log(`[DcmCell] 工具组已创建并配置: ${toolGroupId}`);
|
|
console.log(`[DcmCell] 工具组已创建并配置: ${toolGroupId}`);
|
|
|
}
|
|
}
|
|
@@ -160,11 +169,11 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
|
|
|
return () => {
|
|
return () => {
|
|
|
// 销毁工具组
|
|
// 销毁工具组
|
|
|
const toolGroupId = `PRINT_TOOL_GROUP_${viewportId}`;
|
|
const toolGroupId = `PRINT_TOOL_GROUP_${viewportId}`;
|
|
|
- const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(toolGroupId);
|
|
|
|
|
- if (toolGroup) {
|
|
|
|
|
|
|
+ const currentToolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(toolGroupId);
|
|
|
|
|
+ if (currentToolGroup) {
|
|
|
try {
|
|
try {
|
|
|
// 先从工具组中移除 viewport
|
|
// 先从工具组中移除 viewport
|
|
|
- toolGroup.removeViewports(`renderingEngine-${currentFilm.id}`, viewportId);
|
|
|
|
|
|
|
+ currentToolGroup.removeViewports(`renderingEngine-${currentFilm.id}`, viewportId);
|
|
|
// 然后销毁工具组
|
|
// 然后销毁工具组
|
|
|
cornerstoneTools.ToolGroupManager.destroyToolGroup(toolGroupId);
|
|
cornerstoneTools.ToolGroupManager.destroyToolGroup(toolGroupId);
|
|
|
console.log(`[DcmCell] 工具组已销毁: ${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 变化,自动加载图像
|
|
// 监听 imageId 变化,自动加载图像
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
const loadImageFromProps = async () => {
|
|
const loadImageFromProps = async () => {
|
|
|
if (!stackViewport) return;
|
|
if (!stackViewport) return;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 避免重复加载相同图像
|
|
// 避免重复加载相同图像
|
|
|
if (imageId === currentImageId) return;
|
|
if (imageId === currentImageId) return;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (imageId && imageId.trim() !== '') {
|
|
if (imageId && imageId.trim() !== '') {
|
|
|
console.log(`[DcmCell] 开始从 props 加载图像: ${imageId}`);
|
|
console.log(`[DcmCell] 开始从 props 加载图像: ${imageId}`);
|
|
|
const success = await loadImage(imageId, 'props');
|
|
const success = await loadImage(imageId, 'props');
|
|
@@ -223,7 +295,7 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
|
|
|
// 监听Redux状态变化,当图像被删除时清理viewport
|
|
// 监听Redux状态变化,当图像被删除时清理viewport
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
if (!stackViewport) return;
|
|
if (!stackViewport) return;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 如果Redux中的图像ID为null,但viewport还有图像,则清理
|
|
// 如果Redux中的图像ID为null,但viewport还有图像,则清理
|
|
|
if (!currentCellImageId && hasImage) {
|
|
if (!currentCellImageId && hasImage) {
|
|
|
try {
|
|
try {
|
|
@@ -253,10 +325,10 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
|
|
|
// 2. 获取该序列的所有图像 ID(需替换为实际接口)
|
|
// 2. 获取该序列的所有图像 ID(需替换为实际接口)
|
|
|
const imageIds = getDcmImageUrl(seriesInstanceUID);
|
|
const imageIds = getDcmImageUrl(seriesInstanceUID);
|
|
|
console.log(`拖拽后要加载图像 是 ${imageIds}`);
|
|
console.log(`拖拽后要加载图像 是 ${imageIds}`);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 3. 使用统一的加载函数
|
|
// 3. 使用统一的加载函数
|
|
|
const success = await loadImage(imageIds, 'drag');
|
|
const success = await loadImage(imageIds, 'drag');
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (success) {
|
|
if (success) {
|
|
|
setCurrentImageId(imageIds);
|
|
setCurrentImageId(imageIds);
|
|
|
// 4. 通知到slice
|
|
// 4. 通知到slice
|