Explorar el Código

feat: 实现体位列表智能流程切换功能

- 在 exam 模式点击已曝光体位自动切换到 process 模式
- 在 process 模式点击未曝光体位自动切换到 exam 模式
- 使用事件发射机制确保流程切换完成后再选中体位
- 添加超时保护机制避免永久阻塞

修改文件:
- src/domain/exam/bodyPositionSelection.ts
- src/pages/exam/components/BodyPositionList.tsx
- src/states/businessFlowMiddlewareLogic.ts

close #70
sw hace 1 semana
padre
commit
3faefddd46

+ 82 - 0
src/domain/exam/bodyPositionSelection.ts

@@ -11,6 +11,8 @@ import {
 } from '@/states/exam/bodyPositionListSlice';
 import { setBodyPositionDetail } from '@/states/exam/bodyPositionDetailSlice';
 import { changeBodyPosition } from '@/API/exam/changeBodyPosition';
+import { setBusinessFlow } from '@/states/BusinessFlowSlice';
+import emitter from '@/utils/eventEmitter';
 
 export const selectBodyPositionWithFullLogic = async (
   bodyPosition: ExtendedBodyPosition,
@@ -107,3 +109,83 @@ export const manualSelectBodyPosition = async (
     true // 手动选中时显示用户消息
   );
 };
+
+/**
+ * 带流程切换的体位选择(用户点击体位时调用,根据曝光状态自动切换流程)
+ */
+export const manualSelectBodyPositionWithFlowSwitch = async (
+  bodyPosition: ExtendedBodyPosition,
+  dispatch: AppDispatch,
+  currentKey: string
+): Promise<void> => {
+  const isExposed = bodyPosition.dview.expose_status === 'Exposed';
+  const isUnexposed = bodyPosition.dview.expose_status === 'Unexposed';
+
+  let targetFlow = currentKey;
+  let needSwitch = false;
+
+  // 判断是否需要切换流程
+  if (currentKey === 'exam' && isExposed) {
+    targetFlow = 'process';
+    needSwitch = true;
+    console.log(
+      `[bodyPositionSelection] Detected exposed position in exam mode, will switch to process`
+    );
+  } else if (currentKey === 'process' && isUnexposed) {
+    targetFlow = 'exam';
+    needSwitch = true;
+    console.log(
+      `[bodyPositionSelection] Detected unexposed position in process mode, will switch to exam`
+    );
+  }
+
+  if (needSwitch) {
+    console.log(
+      `[bodyPositionSelection] Switching flow from ${currentKey} to ${targetFlow}`
+    );
+
+    // 创建一个 Promise 来等待流程切换完成
+    const flowSwitchPromise = new Promise<void>((resolve) => {
+      // 设置一个一次性监听器
+      const handler = (data: {
+        from: string;
+        to: string;
+        timestamp: number;
+      }) => {
+        if (data.to === targetFlow) {
+          console.log(
+            `[bodyPositionSelection] Flow switch completed: ${data.from} -> ${data.to}`
+          );
+          // 移除监听器
+          emitter.off('BUSINESS_FLOW_CHANGED', handler);
+          resolve();
+        }
+      };
+
+      emitter.on('BUSINESS_FLOW_CHANGED', handler);
+
+      // 设置超时保护,防止事件永远不触发
+      setTimeout(() => {
+        emitter.off('BUSINESS_FLOW_CHANGED', handler);
+        console.warn(
+          '[bodyPositionSelection] Flow switch timeout, proceeding anyway'
+        );
+        resolve();
+      }, 2000); // 2秒超时
+    });
+
+    // 触发流程切换
+    dispatch(setBusinessFlow(targetFlow));
+
+    // 等待流程切换完成
+    await flowSwitchPromise;
+  }
+
+  // 使用正确的流程 key 选中体位
+  await selectBodyPositionWithFullLogic(
+    bodyPosition,
+    dispatch,
+    targetFlow,
+    true
+  );
+};

+ 6 - 2
src/pages/exam/components/BodyPositionList.tsx

@@ -7,7 +7,7 @@ import AppendViewIcon from '@/assets/imgs/append-view.svg';
 import ImageViewer from './ImageViewer';
 import { getExposedImageUrl, getViewIconUrl } from '../../../API/bodyPosition';
 import {
-  manualSelectBodyPosition,
+  manualSelectBodyPositionWithFlowSwitch,
   autoSelectFirstBodyPosition,
 } from '@/domain/exam/bodyPositionSelection';
 import AppendViewModal from './AppendViewModal';
@@ -29,7 +29,11 @@ const BodyPositionList: React.FC<BodyPositionListProps> = ({
   const handleImageClick = async (
     bodyPosition: ExtendedBodyPosition
   ): Promise<void> => {
-    await manualSelectBodyPosition(bodyPosition, dispatch, currentKey);
+    await manualSelectBodyPositionWithFlowSwitch(
+      bodyPosition,
+      dispatch,
+      currentKey
+    );
   };
   const bodyPositions = useSelector(
     (state: RootState) => state.bodyPositionList.bodyPositions

+ 16 - 1
src/states/businessFlowMiddlewareLogic.ts

@@ -6,6 +6,7 @@ import { suspendOrCompleteStudy } from '@/API/patient/workActions';
 import { RootState } from './store';
 import { getQuota } from '@/API/security/quotaActions';
 import { showQuotaAlert } from './security/quotaModalSlice';
+import emitter from '@/utils/eventEmitter';
 import {
   setBodyPositions,
   transformWorksToBodyPositions,
@@ -31,6 +32,7 @@ const businessFlowMiddlewareLogic: Middleware =
     }
     const state = store.getState().BusinessFlow as BusinessFlowState;
     const currentKey = state.currentKey;
+    const targetKey = action.payload;
     console.log(`[businessFlowMiddleware] currentKey: ${currentKey}`);
     console.log(
       `[businessFlowMiddleware] Current business flow is now: ${state.currentKey} ; Last key was: ${state.lastKey}`
@@ -283,7 +285,20 @@ const businessFlowMiddlewareLogic: Middleware =
       );
       return next({ ...action, payload: continueBusinessFlow });
     }
-    next(action);
+    const result = next(action);
+
+    // 发射业务流程切换完成事件
+    emitter.emit('BUSINESS_FLOW_CHANGED', {
+      from: currentKey,
+      to: targetKey,
+      timestamp: Date.now(),
+    });
+
+    console.log(
+      `[businessFlowMiddleware] Emitted BUSINESS_FLOW_CHANGED event: ${currentKey} -> ${targetKey}`
+    );
+
+    return result;
   };
 /**
  * 从检查到图像处理