Kaynağa Gözat

打印页面,选中胶片中的图像单元格,并控制删除按钮的启用与禁用

dengdx 1 ay önce
ebeveyn
işleme
ada434be09

+ 30 - 8
src/pages/output/print/DcmCell.tsx

@@ -9,7 +9,8 @@ import {
 import { v4 as uuidv4 } from 'uuid';
 import { getDcmImageUrl } from '@/API/bodyPosition';
 import { useDispatch } from 'react-redux';
-import { addImageToCell, Film } from '@/states/print/printSlice';
+import { addImageToCell, Film, toggleCellSelection } from '@/states/print/printSlice';
+import { useAppSelector } from '@/states/store';
 // import { dicomWebImageLoader } from '@cornerstonejs/dicom-web-image-loader';
 
 // // 初始化图像加载器(关键:必须提前注册)
@@ -43,6 +44,14 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
     [currentFilm.id, indexOfCell]
   );
   const dispatch = useDispatch();
+  
+  // 生成格子的唯一标识符
+  const cellId = `${currentFilm.id}-${indexOfCell}`;
+  
+  // 检查当前格子是否被选中
+  const isSelected = useAppSelector(state =>
+    state.print.selectedCells.includes(cellId)
+  );
 
   // 统一的图像加载函数,供两种场景使用
   const loadImage = async (targetImageId: string, source: 'props' | 'drag' = 'props'): Promise<boolean> => {
@@ -231,22 +240,35 @@ const ViewportContainer = ({ imageId, className, currentFilm, indexOfCell }: Vie
     }
   };
 
+  // 处理格子点击事件
+  const handleCellClick = (e: React.MouseEvent) => {
+    e.stopPropagation();
+    dispatch(toggleCellSelection(cellId));
+  };
+
+  // 动态样式
+  const containerStyle = {
+    width: '100%',
+    height: '600px',
+    border: isSelected ? '3px solid #0066cc' : '2px dashed #666',
+    borderRadius: '8px',
+    position: 'relative' as const,
+    cursor: 'pointer',
+    backgroundColor: isSelected ? 'rgba(0, 102, 204, 0.1)' : 'transparent',
+    boxShadow: isSelected ? '0 0 10px rgba(0, 102, 204, 0.3)' : 'none',
+    transition: 'all 0.2s ease-in-out'
+  };
 
   return (
     <div
       className="viewport-container"
+      onClick={handleCellClick}
       onDragOver={(e) => {
         e.preventDefault(); // 允许放置
         e.dataTransfer.dropEffect = 'copy';
       }}
       onDrop={handleDrop}
-      style={{
-        width: '100%',
-        height: '600px',
-        border: '2px dashed #666',
-        borderRadius: '8px',
-        position: 'relative'
-      }}
+      style={containerStyle}
     >
       {/* Cornerstone 渲染挂载点 */}
       <div ref={viewportRef} style={{ width: '100%', height: '100%' }} />

+ 27 - 5
src/pages/output/print/FilmOperationPanel.tsx

@@ -7,7 +7,8 @@ import {
   deleteFilm,
   toggleFilmOrientation,
   setFilmLayout,
-  deleteSelectedImage,
+  deleteSelectedImages,
+  clearAllSelections,
 } from '@/states/print/printSlice';
 import Icon from '@/components/Icon';
 import { showNotImplemented } from '@/utils/notificationHelper';
@@ -56,6 +57,12 @@ const FilmOperationPanel: React.FC = () => {
   const dispatch = useDispatch();
   const activeFilmId = useAppSelector((state) => state.print.activeFilmId);
   const films = useAppSelector((state) => state.print.films);
+  const selectedCells = useAppSelector((state) => state.print.selectedCells);
+  
+  // 计算当前胶片的选中格子数量
+  const currentFilmSelectedCount = selectedCells.filter(cellId =>
+    cellId.startsWith(`${activeFilmId}-`)
+  ).length;
 
   const handleAddFilm = () => {
     dispatch(addFilm());
@@ -73,8 +80,16 @@ const FilmOperationPanel: React.FC = () => {
     dispatch(toggleFilmOrientation());
   };
 
-  const handleDeleteSelectedImage = () => {
-    dispatch(deleteSelectedImage());
+  const handleDeleteSelectedImages = () => {
+    if (selectedCells.length === 0) {
+      showNotImplemented('请先选择要删除的图像');
+      return;
+    }
+    dispatch(deleteSelectedImages());
+  };
+
+  const handleClearAllSelections = () => {
+    dispatch(clearAllSelections());
   };
 
   const handleSetLayout = (layout: '1x1' | '1x2' | '2x1' | '2x2') => {
@@ -101,9 +116,16 @@ const FilmOperationPanel: React.FC = () => {
           onClick={handleToggleOrientation}
         />
         <FilmOperationButton
-          title="删除选中图像"
+          title={`删除选中图像${currentFilmSelectedCount > 0 ? ` (${currentFilmSelectedCount})` : ''}`}
+          iconName="RotateAnyDegree"
+          onClick={handleDeleteSelectedImages}
+          disabled={selectedCells.length === 0}
+        />
+        <FilmOperationButton
+          title="清空选中"
           iconName="RotateAnyDegree"
-          onClick={handleDeleteSelectedImage}
+          onClick={handleClearAllSelections}
+          disabled={selectedCells.length === 0}
         />
       </Flex>
 

+ 38 - 14
src/states/print/printSlice.ts

@@ -13,10 +13,11 @@ export interface Film {
 interface PrintState {
   films: Film[];
   activeFilmId: string;
-    /**
-   * 本质是 sop instance uid
+  /**
+   * 存储选中的格子,格式为 `${filmId}-${cellIndex}`
+   * 例如: ["1-0", "1-1", "2-0"] 表示胶片1的格子0和1,胶片2的格子0被选中
    */
-  selectedImageIndex: string | null; // 当前选中的图像在格子中的索引
+  selectedCells: string[];
 }
 
 const initialFilm: Film = {
@@ -32,7 +33,7 @@ const initialFilm: Film = {
 const initialState: PrintState = {
   films: [initialFilm],
   activeFilmId: '1',
-  selectedImageIndex: null,
+  selectedCells: [],
 };
 
 const printSlice = createSlice({
@@ -68,7 +69,7 @@ const printSlice = createSlice({
     // 切换当前胶片
     setActiveFilm: (state, action: PayloadAction<string>) => {
       state.activeFilmId = action.payload;
-      state.selectedImageIndex = null; // 切换胶片时清除选中
+      state.selectedCells = []; // 切换胶片时清除所有选中
     },
 
     // 设置胶片布局
@@ -103,20 +104,42 @@ const printSlice = createSlice({
       film.orientation = film.orientation === 'horizontal' ? 'vertical' : 'horizontal';
     },
 
-    // 设置选中的图像格子
-    setSelectedImageIndex: (state, action: PayloadAction<string | null>) => {
-      state.selectedImageIndex = action.payload;
+    // Toggle 选中/取消选中格子
+    toggleCellSelection: (state, action: PayloadAction<string>) => {
+      const cellId = action.payload;
+      const index = state.selectedCells.indexOf(cellId);
+      if (index > -1) {
+        state.selectedCells.splice(index, 1); // 取消选中
+      } else {
+        state.selectedCells.push(cellId); // 选中
+      }
+    },
+
+    // 清空所有选中
+    clearAllSelections: (state) => {
+      state.selectedCells = [];
     },
 
     // 删除选中的图像
-    deleteSelectedImage: (state) => {
-      if (state.selectedImageIndex === null) return;
+    deleteSelectedImages: (state) => {
+      if (state.selectedCells.length === 0) return;
       
       const film = state.films.find((f) => f.id === state.activeFilmId);
       if (!film) return;
       
-      film.images[state.selectedImageIndex] = null;
-      state.selectedImageIndex = null;
+      // 解析选中的格子并删除对应图像
+      state.selectedCells.forEach(cellId => {
+        const [filmId, cellIndexStr] = cellId.split('-');
+        if (filmId === state.activeFilmId) {
+          const cellIndex = parseInt(cellIndexStr, 10);
+          if (cellIndex >= 0 && cellIndex < film.images.length) {
+            film.images[cellIndex] = null;
+          }
+        }
+      });
+      
+      // 清空选中状态
+      state.selectedCells = [];
     },
 
     // 添加图像到指定格子
@@ -138,8 +161,9 @@ export const {
   setActiveFilm,
   setFilmLayout,
   toggleFilmOrientation,
-  setSelectedImageIndex,
-  deleteSelectedImage,
+  toggleCellSelection,
+  clearAllSelections,
+  deleteSelectedImages,
   addImageToCell,
 } = printSlice.actions;