/** * 体位选择协调器 - 统一处理体位选中的完整逻辑 * 确保自动选中和手动选中使用相同的业务流程 */ import { message } from 'antd'; import { AppDispatch } from '@/states/store'; import { setSelectedBodyPosition, ExtendedBodyPosition, } from '@/states/exam/bodyPositionListSlice'; import { setBodyPositionDetail } from '@/states/exam/bodyPositionDetailSlice'; import { changeBodyPosition } from '@/API/exam/changeBodyPosition'; import { setExpEnable } from '@/API/exam/deviceActions'; import { setBusinessFlow } from '@/states/BusinessFlowSlice'; import emitter from '@/utils/eventEmitter'; export const selectBodyPositionWithFullLogic = async ( bodyPosition: ExtendedBodyPosition, dispatch: AppDispatch, currentKey: string, showMessage = false ): Promise => { try { console.log( `[bodyPositionSelection] Selecting body position: ${bodyPosition.view_name}` ); // 1. 更新选中状态 dispatch(setSelectedBodyPosition(bodyPosition)); // 2. 设置详情显示区域的数据 dispatch( setBodyPositionDetail({ view_name: bodyPosition.view_name, view_description: bodyPosition.view_description, view_icon_name: bodyPosition.view_icon_name, patient_name: bodyPosition.patient_name, patient_id: bodyPosition.patient_id, registration_number: bodyPosition.registration_number, study_description: bodyPosition.study_description, body_position_image: bodyPosition.view_big_icon_name, collimator_length: bodyPosition.collimator_length, collimator_width: bodyPosition.collimator_width, sid: bodyPosition.sid, // 🆕 添加新字段 expose_status: bodyPosition.dview.expose_status, sop_instance_uid: bodyPosition.sop_instance_uid, }) ); // 3. 🆕 如果在exam模式,根据曝光状态决定是否同步设备 if (currentKey === 'exam') { if (bodyPosition.dview.expose_status === 'Unexposed') { // 未曝光体位:同步设备,使能曝光 await changeBodyPosition(bodyPosition.sop_instance_uid); // 切换体位成功后,使能发生器曝光 await setExpEnable(); const successMsg = `Body position changed successfully: ${bodyPosition.view_name}`; console.log(`[bodyPositionSelection] ${successMsg}`); if (showMessage) { message.success(successMsg); } } else { // 已曝光体位:跳过设备同步,仅更新显示 console.log( `[bodyPositionSelection] Exposed position selected, skipping device sync: ${bodyPosition.view_name}` ); if (showMessage) { message.info(`已选中已曝光体位: ${bodyPosition.view_name}`); } } } else { console.log( `[bodyPositionSelection] Current key is ${currentKey}, not executing changeBodyPosition.` ); } } catch (error) { const errorMsg = 'Failed to change body position'; console.error(`[bodyPositionSelection] ${errorMsg}:`, error); if (showMessage) { message.error(errorMsg); } throw error; // 向上传播错误,让调用者决定如何处理 } }; /** * 自动选中第一个体位(通常在进入exam时调用) * - exam 模式:自动选择第一个未曝光的体位 * - process 模式:自动选择第一个已曝光的体位 * - 其他模式:选择第一个体位 */ export const autoSelectFirstBodyPosition = async ( bodyPositions: ExtendedBodyPosition[], dispatch: AppDispatch, currentKey: string ): Promise => { if (bodyPositions.length === 0) { console.log('[bodyPositionSelection] No body positions available'); return; } // 根据当前业务流程选择合适的体位 let targetBodyPosition: ExtendedBodyPosition | undefined; if (currentKey === 'exam') { // exam 模式:选择第一个未曝光的体位 targetBodyPosition = bodyPositions.find( (bp) => bp.dview.expose_status === 'Unexposed' ); console.log( '[bodyPositionSelection] Auto-selecting first unexposed body position in exam mode' ); } else if (currentKey === 'process') { // process 模式:选择第一个已曝光的体位 targetBodyPosition = bodyPositions.find( (bp) => bp.dview.expose_status === 'Exposed' ); console.log( '[bodyPositionSelection] Auto-selecting first exposed body position in process mode' ); } else { // 其他模式:默认选择第一个 targetBodyPosition = bodyPositions[0]; console.log( `[bodyPositionSelection] Auto-selecting first body position in ${currentKey} mode` ); } if (targetBodyPosition) { await selectBodyPositionWithFullLogic( targetBodyPosition, dispatch, currentKey, false // 自动选中时不显示用户消息 ); } else { console.warn( `[bodyPositionSelection] No suitable body position found for ${currentKey} mode` ); } }; /** * 手动选中体位(用户点击时调用) */ export const manualSelectBodyPosition = async ( bodyPosition: ExtendedBodyPosition, dispatch: AppDispatch, currentKey: string ): Promise => { console.log( `[bodyPositionSelection] Manual selection: ${bodyPosition.view_name}` ); await selectBodyPositionWithFullLogic( bodyPosition, dispatch, currentKey, true // 手动选中时显示用户消息 ); }; /** * 带流程切换的体位选择(用户点击体位时调用,根据曝光状态自动切换流程) * @param bodyPosition 要选中的体位 * @param dispatch Redux dispatch * @param currentKey 当前业务流程 key * @param allowFlowSwitch 是否允许自动切换流程(默认 true) */ export const manualSelectBodyPositionWithFlowSwitch = async ( bodyPosition: ExtendedBodyPosition, dispatch: AppDispatch, currentKey: string, allowFlowSwitch: boolean = true ): Promise => { const isExposed = bodyPosition.dview.expose_status === 'Exposed'; const isUnexposed = bodyPosition.dview.expose_status === 'Unexposed'; let targetFlow = currentKey; let needSwitch = false; // 🆕 只有在允许流程切换时才判断是否需要切换 if (allowFlowSwitch) { // 判断是否需要切换流程 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` ); } } else { console.log( `[bodyPositionSelection] Flow switch disabled, staying in ${currentKey} mode` ); } if (needSwitch) { console.log( `[bodyPositionSelection] Switching flow from ${currentKey} to ${targetFlow}` ); // 创建一个 Promise 来等待流程切换完成 const flowSwitchPromise = new Promise((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 ); };