Browse Source

添加和APR相关的实现,能够在检查中显示参数信息

dengdx 1 month ago
parent
commit
284d3f9653

+ 220 - 0
src/API/exam/APR.md

@@ -0,0 +1,220 @@
+## 12 获取APR详情(通过view_id)
+
+> GET /dr/api/v1/auth/protocol/view/{id}/apr
+
+### 接口说明
+
+> 根据体位ID获取APR详情
+
+### 地址参数(Path Variable)
+
+| 参数名称 | 默认值 | 描述 |
+| -------- | ------ | ---- |
+| id       |        |      |
+
+### 请求头
+
+| 参数名称      | 默认值                                                                                                                                         | 描述                           |
+| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
+| Authorization | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NTEyNzc5NzAsImlkIjoxLCJuYW1lIjoiYWRtaW4ifQ.ooTGwBXaNhtunbKbpqteWbjDwJLjnRmSIl80r5dp1pY |                                |
+| Language      | en                                                                                                                                             | en 或 zh                       |
+| Product       | DROS                                                                                                                                           | DROS 或 VETDROS                |
+| Source        | Electron                                                                                                                                       | Electron 或 Browser 或 Android |
+
+### 请求参数(Query Param)
+
+| 参数名称     | 默认值 | 描述               |
+| ------------ | ------ | ------------------ |
+| patient_type |        | 从患者类型接口获取 |
+| body_part    |        | 从身体部位接口获取 |
+| is_enabled   |        | true 或 false      |
+| procedure_id |        | 从协议列表接口获取 |
+
+### 响应体
+
+● 200: OK 响应数据格式:JSON
+
+```json
+{
+  "code": "0x000000",
+  "description": "Success",
+  "solution": "",
+  "data": {
+    "@type": "type.googleapis.com/dr.protocol.AprReply",
+    "apr_id": "View_DX_T_A_HD_OBL_01",
+    "apr_name": "View_DX_T_A_HD_OBL_01",
+    "apr_description": "右手斜位",
+    "patient_type": "Human",
+    "body_part_id": "Human_UPPER LIMB",
+    "view_position": "OBL",
+    "category": "EXTREMITIES_MEDIUM",
+    "modality": "DX",
+    "sub": [
+      {
+        "work_station_id": 0,
+        "patient_size": "Large",
+        "config_object": {
+          "Common": {
+            "AECDensity": 0,
+            "AECField": "010",
+            "AECFilm": 1,
+            "Dose": 0,
+            "ExposureMode": 0,
+            "Focus": 0,
+            "TOD": 0,
+            "TubeLoad": 0,
+            "kV": 55,
+            "mA": 80,
+            "mAs": 3.2,
+            "ms": 40
+          }
+        }
+      },
+      {
+        "work_station_id": 0,
+        "patient_size": "Medium",
+        "config_object": {
+          "Common": {
+            "AECDensity": 0,
+            "AECField": "010",
+            "AECFilm": 1,
+            "Dose": 0,
+            "ExposureMode": 0,
+            "Focus": 0,
+            "TOD": 0,
+            "TubeLoad": 0,
+            "kV": 50,
+            "mA": 80,
+            "mAs": 2,
+            "ms": 25
+          }
+        }
+      },
+      {
+        "work_station_id": 0,
+        "patient_size": "Paediatric",
+        "config_object": {
+          "Common": {
+            "AECDensity": 0,
+            "AECField": "010",
+            "AECFilm": 1,
+            "Dose": 0,
+            "ExposureMode": 0,
+            "Focus": 0,
+            "TOD": 0,
+            "TubeLoad": 0,
+            "kV": 50,
+            "mA": 80,
+            "mAs": 1.6,
+            "ms": 20
+          }
+        }
+      },
+      {
+        "work_station_id": 0,
+        "patient_size": "Small",
+        "config_object": {
+          "Common": {
+            "AECDensity": 0,
+            "AECField": "010",
+            "AECFilm": 1,
+            "Dose": 0,
+            "ExposureMode": 0,
+            "Focus": 0,
+            "TOD": 0,
+            "TubeLoad": 0,
+            "kV": 50,
+            "mA": 80,
+            "mAs": 2,
+            "ms": 25
+          }
+        }
+      },
+      {
+        "work_station_id": 1,
+        "patient_size": "Large",
+        "config_object": {
+          "Common": {
+            "AECDensity": 0,
+            "AECField": "010",
+            "AECFilm": 1,
+            "Dose": 0,
+            "ExposureMode": 0,
+            "Focus": 0,
+            "TOD": 0,
+            "TubeLoad": 0,
+            "kV": 55,
+            "mA": 80,
+            "mAs": 3.2,
+            "ms": 40
+          }
+        }
+      },
+      {
+        "work_station_id": 1,
+        "patient_size": "Medium",
+        "config_object": {
+          "Common": {
+            "AECDensity": 0,
+            "AECField": "010",
+            "AECFilm": 1,
+            "Dose": 0,
+            "ExposureMode": 0,
+            "Focus": 0,
+            "TOD": 0,
+            "TubeLoad": 0,
+            "kV": 50,
+            "mA": 80,
+            "mAs": 2,
+            "ms": 25
+          }
+        }
+      },
+      {
+        "work_station_id": 1,
+        "patient_size": "Paediatric",
+        "config_object": {
+          "Common": {
+            "AECDensity": 0,
+            "AECField": "010",
+            "AECFilm": 1,
+            "Dose": 0,
+            "ExposureMode": 0,
+            "Focus": 0,
+            "TOD": 0,
+            "TubeLoad": 0,
+            "kV": 50,
+            "mA": 80,
+            "mAs": 1.6,
+            "ms": 20
+          }
+        }
+      },
+      {
+        "work_station_id": 1,
+        "patient_size": "Small",
+        "config_object": {
+          "Common": {
+            "AECDensity": 0,
+            "AECField": "010",
+            "AECFilm": 1,
+            "Dose": 0,
+            "ExposureMode": 0,
+            "Focus": 0,
+            "TOD": 0,
+            "TubeLoad": 0,
+            "kV": 50,
+            "mA": 80,
+            "mAs": 2,
+            "ms": 25
+          }
+        }
+      }
+    ],
+    "sort": 0,
+    "is_enabled": true,
+    "product": "DROS",
+    "is_pre_install": true
+  }
+}
+```

+ 71 - 0
src/API/exam/APRActions.ts

@@ -0,0 +1,71 @@
+import axiosInstance from '../interceptor';
+
+export interface AprConfig {
+  AECDensity: number;
+  AECField: string;
+  AECFilm: number;
+  Dose: number;
+  ExposureMode: number;
+  Focus: number;
+  TOD: number;
+  TubeLoad: number;
+  kV: number;
+  mA: number;
+  mAs: number;
+  ms: number;
+}
+
+interface AprSub {
+  work_station_id: number;
+  patient_size: string;
+  config_object: {
+    Common: AprConfig;
+  };
+}
+
+interface APR {
+  '@type': string;
+  apr_id: string;
+  apr_name: string;
+  apr_description: string;
+  patient_type: string;
+  body_part_id: string;
+  view_position: string;
+  category: string;
+  modality: string;
+  sub: AprSub[];
+  sort: number;
+  is_enabled: boolean;
+  product: string;
+  is_pre_install: boolean;
+}
+
+// interface AprResponse {
+//   code: string;
+//   description: string;
+//   solution: string;
+//   data: APR;
+// }
+
+const getAprDetails = async (
+  id: string,
+  patientType: string,
+  bodyPart: string,
+  isEnabled: boolean,
+  procedureId: string
+): Promise<APR> => {
+  const response = await axiosInstance.get(
+    `/dr/api/v1/auth/protocol/view/${id}/apr`,
+    {
+      params: {
+        patient_type: patientType,
+        body_part: bodyPart,
+        is_enabled: isEnabled,
+        procedure_id: procedureId,
+      },
+    }
+  );
+  return response.data.data;
+};
+
+export { getAprDetails };

+ 68 - 2
src/pages/exam/ContentAreaLarge.tsx

@@ -19,10 +19,46 @@ import {
 import { patientSizes } from '../../states/patientSize';
 import { WorkstationTypeLabels } from '../../states/workstation';
 import { FormattedMessage } from 'react-intl';
+import { useSelector, useDispatch } from 'react-redux';
+import { RootState } from '../../states/store';
+import {
+  setAprConfig,
+  setBodysize,
+  setWorkstation,
+  setIsAECEnabled,
+  setCurrentExposureMode,
+} from '../../states/exam/aprSlice';
 import BodyPositionList from './components/BodyPositionList';
 import BodyPositionDetail from './components/BodyPositionDetail';
 
 const ContentAreaLarge = () => {
+  const dispatch = useDispatch();
+  const aprConfig = useSelector((state: RootState) => state.apr.aprConfig);
+  const bodysize = useSelector((state: RootState) => state.apr.bodysize);
+  const workstation = useSelector((state: RootState) => state.apr.workstation);
+  const isAECEnabled = useSelector(
+    (state: RootState) => state.apr.isAECEnabled
+  );
+  const currentExposureMode = useSelector(
+    (state: RootState) => state.apr.currentExposureMode
+  );
+
+  const handleBodysizeChange = (value: string) => {
+    dispatch(setBodysize(value));
+  };
+
+  const handleWorkstationChange = (value: string) => {
+    dispatch(setWorkstation(value));
+  };
+
+  const handleAECChange = (checked: boolean) => {
+    dispatch(setIsAECEnabled(checked));
+  };
+
+  const handleExposureModeChange = (value: string) => {
+    dispatch(setCurrentExposureMode(value));
+  };
+
   return (
     <Row>
       <Col span={16}>
@@ -46,6 +82,8 @@ const ContentAreaLarge = () => {
               <Select
                 placeholder="选择体型"
                 style={{ width: '100%', marginBottom: 8 }}
+                value={bodysize}
+                onChange={handleBodysizeChange}
               >
                 {Object.entries(patientSizes).map(
                   ([key, value]: [string, string]) => (
@@ -60,6 +98,8 @@ const ContentAreaLarge = () => {
               <Select
                 placeholder="选择工作位"
                 style={{ width: '100%', marginBottom: 8 }}
+                value={workstation}
+                onChange={handleWorkstationChange}
               >
                 {Object.entries(WorkstationTypeLabels).map(
                   ([key, value]: [string, string]) => (
@@ -77,27 +117,52 @@ const ContentAreaLarge = () => {
             <InputNumber
               placeholder="mA"
               style={{ width: '100%', marginBottom: 8 }}
+              value={aprConfig.mA ?? undefined}
+              onChange={(value) =>
+                dispatch(setAprConfig({ ...aprConfig, mA: value ?? 0 }))
+              }
             />
             <InputNumber
               placeholder="ms"
               style={{ width: '100%', marginBottom: 8 }}
+              value={aprConfig.ms ?? undefined}
+              onChange={(value) =>
+                dispatch(setAprConfig({ ...aprConfig, ms: value ?? 0 }))
+              }
             />
             <InputNumber
               placeholder="mAs"
               style={{ width: '100%', marginBottom: 8 }}
+              value={aprConfig.mAs ?? undefined}
+              onChange={(value) =>
+                dispatch(setAprConfig({ ...aprConfig, mAs: value ?? 0 }))
+              }
             />
             <InputNumber
               placeholder="KV"
               style={{ width: '100%', marginBottom: 8 }}
+              value={aprConfig.kV ?? undefined}
+              onChange={(value) =>
+                dispatch(setAprConfig({ ...aprConfig, kV: value ?? 0 }))
+              }
             />
             <InputNumber
               placeholder="density"
               style={{ width: '100%', marginBottom: 8 }}
+              value={aprConfig.AECDensity ?? undefined}
+              onChange={(value) =>
+                dispatch(setAprConfig({ ...aprConfig, AECDensity: value ?? 0 }))
+              }
             />
           </div>
           <Row gutter={16} align="middle">
             <Col span={12}>
-              <Select placeholder="选择曝光模式" style={{ width: '100%' }}>
+              <Select
+                placeholder="选择曝光模式"
+                style={{ width: '100%' }}
+                value={currentExposureMode}
+                onChange={handleExposureModeChange}
+              >
                 <Select.Option value="mAs">mAs</Select.Option>
                 <Select.Option value="time">time</Select.Option>
               </Select>
@@ -106,7 +171,8 @@ const ContentAreaLarge = () => {
               <Switch
                 checkedChildren="开启AEC"
                 unCheckedChildren="关闭AEC"
-                defaultChecked
+                checked={isAECEnabled}
+                onChange={handleAECChange}
               />
             </Col>
             <Col span={3}>

+ 62 - 0
src/states/exam/aprSlice.ts

@@ -0,0 +1,62 @@
+import { createSlice, PayloadAction } from '@reduxjs/toolkit';
+import { AprConfig } from '../../API/exam/APRActions';
+
+interface AprState {
+  aprConfig: AprConfig;
+  bodysize: string;
+  workstation: string;
+  isAECEnabled: boolean;
+  currentExposureMode: string;
+}
+
+const initialState: AprState = {
+  aprConfig: {
+    AECDensity: 0,
+    AECField: '',
+    AECFilm: 0,
+    Dose: 0,
+    ExposureMode: 0,
+    Focus: 0,
+    TOD: 0,
+    TubeLoad: 0,
+    kV: 0,
+    mA: 0,
+    mAs: 0,
+    ms: 0,
+  },
+  bodysize: '',
+  workstation: '',
+  isAECEnabled: false,
+  currentExposureMode: '',
+};
+
+const aprSlice = createSlice({
+  name: 'apr',
+  initialState,
+  reducers: {
+    setAprConfig: (state, action: PayloadAction<AprConfig>) => {
+      state.aprConfig = action.payload;
+    },
+    setBodysize: (state, action: PayloadAction<string>) => {
+      state.bodysize = action.payload;
+    },
+    setWorkstation: (state, action: PayloadAction<string>) => {
+      state.workstation = action.payload;
+    },
+    setIsAECEnabled: (state, action: PayloadAction<boolean>) => {
+      state.isAECEnabled = action.payload;
+    },
+    setCurrentExposureMode: (state, action: PayloadAction<string>) => {
+      state.currentExposureMode = action.payload;
+    },
+  },
+});
+
+export const {
+  setAprConfig,
+  setBodysize,
+  setWorkstation,
+  setIsAECEnabled,
+  setCurrentExposureMode,
+} = aprSlice.actions;
+export default aprSlice.reducer;

+ 2 - 0
src/states/store.ts

@@ -10,6 +10,7 @@ import systemModeReducer from './systemModeSlice';
 import examWorksCacheReducer from './exam/examWorksCacheSlice';
 import bodyPositionListReducer from './exam/bodyPositionListSlice';
 import bodyPositionDetailReducer from './exam/bodyPositionDetailSlice';
+import aprReducer from './exam/aprSlice';
 import {
   workEntitiesSlice,
   workFiltersSlice,
@@ -31,6 +32,7 @@ const store = configureStore({
     examWorksCache: examWorksCacheReducer,
     bodyPositionList: bodyPositionListReducer,
     bodyPositionDetail: bodyPositionDetailReducer,
+    apr: aprReducer,
     workEntities: workEntitiesSlice.reducer,
     workFilters: workFiltersSlice.reducer,
     workPagination: workPaginationSlice.reducer,