Browse Source

viewSelectionSLice中可以纪录到selction、patient type、body part的值,为过滤view与过滤protocol做准备

dengdx 2 months ago
parent
commit
7c62401133

+ 88 - 0
src/API/patient/procedureAction.md

@@ -0,0 +1,88 @@
+# 说明
+
+这里是请求procedure的API描述
+
+## 1. 按患者类型和身体部位从后端拉取procedure
+
+### HTTP Method: GET
+
+### Headers
+
+- **Authorization**
+- **Language**: `en` 或 `zh`
+- **Product**: `DROS` 或 `VETDROS`
+- **Source**: `Electron` 或 `Browser` 或 `Android`
+
+### Endpoint URL: dr/api/v1/auth/protocol/procedure
+
+### query parameters
+
+patient_type:从患者类型接口获取,string
+body_part:从身体部位接口获取,string
+is_enabled:true 或 false
+
+### Response Status: 200 (OK)
+
+### Response Data: Returns a JSON
+
+示例:
+
+```json
+{
+  "code": "0x000000",
+  "description": "Success",
+  "solution": "",
+  "data": {
+    "procedure_list": [
+      {
+        "id": "1",
+        "procedure_id": "P0-0001",
+        "procedure_code": "P0-0001",
+        "procedure_name": "颅骨汤氏位",
+        "procedure_name_local": "颅骨汤氏位",
+        "procedure_other_name": "Skull Townes View",
+        "procedure_description": "颅骨汤氏位",
+        "procedure_description_local": "颅骨汤氏位",
+        "patient_type": "Human",
+        "body_part_id": "Human_SKULL",
+        "procedure_type": "NORMAL",
+        "fast_search": false,
+        "protocol_laterality": "U",
+        "procedure_category": "Adult",
+        "modality": "DX",
+        "sort": 1,
+        "is_enabled": true,
+        "product": "DROC",
+        "is_pre_install": true
+      },
+      {
+        "id": "2",
+        "procedure_id": "P0-0002",
+        "procedure_code": "P0-0002",
+        "procedure_name": "颅骨前后位 + 侧位",
+        "procedure_name_local": "颅骨前后位 + 侧位",
+        "procedure_other_name": "Skull AP + LAT",
+        "procedure_description": "颅骨前后位 + 侧位",
+        "procedure_description_local": "颅骨前后位 + 侧位",
+        "patient_type": "Human",
+        "body_part_id": "Human_SKULL",
+        "procedure_type": "NORMAL",
+        "fast_search": false,
+        "protocol_laterality": "U",
+        "procedure_category": "Adult",
+        "modality": "DX",
+        "sort": 1,
+        "is_enabled": true,
+        "product": "DROC",
+        "is_pre_install": true
+      }
+    ]
+  }
+}
+```
+
+### Dynamic Behavior: None
+
+### Delay: 500ms to simulate network latency
+
+### Error Handling: None

+ 53 - 0
src/API/patient/procedureActions.ts

@@ -0,0 +1,53 @@
+import axios from 'axios';
+
+interface Procedure {
+  id: string;
+  procedure_id: string;
+  procedure_code: string;
+  procedure_name: string;
+  procedure_name_local: string;
+  procedure_other_name: string;
+  procedure_description: string;
+  procedure_description_local: string;
+  patient_type: string;
+  body_part_id: string;
+  procedure_type: string;
+  fast_search: boolean;
+  protocol_laterality: string;
+  procedure_category: string;
+  modality: string;
+  sort: number;
+  is_enabled: boolean;
+  product: string;
+  is_pre_install: boolean;
+}
+
+interface ProcedureResponse {
+  code: string;
+  description: string;
+  solution: string;
+  data: {
+    procedure_list: Procedure[];
+  };
+}
+
+export const fetchProcedures = async (
+  patientType: string,
+  bodyPart: string,
+  isEnabled: boolean
+): Promise<ProcedureResponse> => {
+  const response = await axios.get('dr/api/v1/auth/protocol/procedure', {
+    headers: {
+      Authorization: 'Bearer YOUR_TOKEN_HERE',
+      Language: 'en',
+      Product: 'DROS',
+      Source: 'Electron',
+    },
+    params: {
+      patient_type: patientType,
+      body_part: bodyPart,
+      is_enabled: isEnabled,
+    },
+  });
+  return response.data;
+};

+ 16 - 8
src/pages/patient/components/RegisterAvailableFilterBar.tsx

@@ -2,9 +2,11 @@ import React from 'react';
 import { Row, Col, Select, Segmented } from 'antd';
 import { Row, Col, Select, Segmented } from 'antd';
 import { useSelector, useDispatch } from 'react-redux';
 import { useSelector, useDispatch } from 'react-redux';
 import { setCurrentPatientType } from '@/states/patientTypeSlice';
 import { setCurrentPatientType } from '@/states/patientTypeSlice';
+import { setSelected } from '@/states/patient/register/SelectionTypeSlice';
 import { RootState } from '@/states/store';
 import { RootState } from '@/states/store';
 import { PatientType } from '@/API/patientType';
 import { PatientType } from '@/API/patientType';
 import { BodyPart } from '@/API/bodyPart';
 import { BodyPart } from '@/API/bodyPart';
+import { setCurrentBodyPart } from '@/states/bodyPartSlice';
 
 
 interface Props {
 interface Props {
   selected: 'protocol' | 'view';
   selected: 'protocol' | 'view';
@@ -18,21 +20,21 @@ interface Props {
 }
 }
 
 
 const RegisterAvailableFilterBar: React.FC<Props> = ({
 const RegisterAvailableFilterBar: React.FC<Props> = ({
-  selected,
-  setSelected,
-  bodyPart,
-  setBodyPart,
   setPatientType,
   setPatientType,
   enabled,
   enabled,
   setEnabled,
   setEnabled,
 }) => {
 }) => {
   const dispatch = useDispatch();
   const dispatch = useDispatch();
-  const bodyParts: BodyPart[] = useSelector(
+  const selected = useSelector((state: RootState) => state.selection.selected);
+  const bodyParts = useSelector(
     (state: RootState) => state.bodyPart.byPatientType
     (state: RootState) => state.bodyPart.byPatientType
   ); // [{ label, value }]
   ); // [{ label, value }]
   const currentPatientType = useSelector(
   const currentPatientType = useSelector(
     (state: RootState) => state.patientType.current
     (state: RootState) => state.patientType.current
   );
   );
+  const currentBodyPart = useSelector(
+    (state: RootState) => state.bodyPart.current
+  );
   const patientTypes = useSelector(
   const patientTypes = useSelector(
     (state: RootState) => state.patientType.items
     (state: RootState) => state.patientType.items
   ); // [{ label, value }]
   ); // [{ label, value }]
@@ -52,7 +54,9 @@ const RegisterAvailableFilterBar: React.FC<Props> = ({
               { label: '体位', value: 'view' },
               { label: '体位', value: 'view' },
             ]}
             ]}
             value={selected}
             value={selected}
-            onChange={(val) => setSelected(val as 'protocol' | 'view')}
+            onChange={(val) =>
+              dispatch(setSelected(val as 'protocol' | 'view'))
+            }
             block
             block
           />
           />
         </Col>
         </Col>
@@ -65,8 +69,12 @@ const RegisterAvailableFilterBar: React.FC<Props> = ({
               label: item.body_part_name,
               label: item.body_part_name,
               value: item.body_part_id,
               value: item.body_part_id,
             }))}
             }))}
-            value={bodyPart}
-            onChange={setBodyPart}
+            value={currentBodyPart?.body_part_id}
+            onChange={(val) => {
+              const selectedBodyPart =
+                bodyParts.find((item) => item.body_part_id == val) || null;
+              dispatch(setCurrentBodyPart(selectedBodyPart));
+            }}
             disabled={!currentPatientType}
             disabled={!currentPatientType}
           />
           />
         </Col>
         </Col>

+ 14 - 1
src/states/bodyPartSlice.ts

@@ -1,4 +1,4 @@
-import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
+import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
 import { fetchBodyParts, BodyPart, BodyPartParams } from '../API/bodyPart';
 import { fetchBodyParts, BodyPart, BodyPartParams } from '../API/bodyPart';
 import { AxiosError } from 'axios';
 import { AxiosError } from 'axios';
 import { setCurrentPatientType } from './patientTypeSlice';
 import { setCurrentPatientType } from './patientTypeSlice';
@@ -8,6 +8,7 @@ interface BodyPartState {
   loading: boolean;
   loading: boolean;
   error: string | null;
   error: string | null;
   byPatientType: BodyPart[];
   byPatientType: BodyPart[];
+  current: BodyPart | null;
 }
 }
 
 
 const initialState: BodyPartState = {
 const initialState: BodyPartState = {
@@ -15,6 +16,7 @@ const initialState: BodyPartState = {
   loading: false,
   loading: false,
   error: null,
   error: null,
   byPatientType: [],
   byPatientType: [],
+  current: null,
 };
 };
 
 
 export const getBodyParts = createAsyncThunk(
 export const getBodyParts = createAsyncThunk(
@@ -47,6 +49,16 @@ const bodyPartSlice = createSlice({
   name: 'bodyPart',
   name: 'bodyPart',
   initialState,
   initialState,
   reducers: {
   reducers: {
+    setCurrentBodyPart: (state, action: PayloadAction<BodyPart | null>) => {
+      if (action.payload) {
+        state.current =
+          state.byPatientType.find(
+            (item) => item.body_part_id === action.payload?.body_part_id
+          ) || null;
+      } else {
+        state.current = null;
+      }
+    },
     // setBodyPartsByPatientType: (state, action: PayloadAction<{ patientTypeId: string }>) => {
     // setBodyPartsByPatientType: (state, action: PayloadAction<{ patientTypeId: string }>) => {
     //   // state.byPatientType[action.payload.patientTypeId] = action.payload.bodyParts;
     //   // state.byPatientType[action.payload.patientTypeId] = action.payload.bodyParts;
     // },
     // },
@@ -86,4 +98,5 @@ const bodyPartSlice = createSlice({
   },
   },
 });
 });
 
 
+export const { setCurrentBodyPart } = bodyPartSlice.actions;
 export default bodyPartSlice.reducer;
 export default bodyPartSlice.reducer;

+ 25 - 0
src/states/patient/register/SelectionTypeSlice.ts

@@ -0,0 +1,25 @@
+// This file contains the slice for managing the selection type in the patient registration process.
+
+import { createSlice, PayloadAction } from '@reduxjs/toolkit';
+
+interface SelectionState {
+  selected: 'protocol' | 'view';
+}
+
+const initialState: SelectionState = {
+  selected: 'protocol',
+};
+
+const selectionSlice = createSlice({
+  name: 'selection',
+  initialState,
+  reducers: {
+    setSelected: (state, action: PayloadAction<'protocol' | 'view'>) => {
+      state.selected = action.payload;
+    },
+  },
+});
+
+export const { setSelected } = selectionSlice.actions;
+export type { SelectionState };
+export default selectionSlice.reducer;

+ 39 - 11
src/states/patient/viewSelection/index.ts

@@ -1,4 +1,12 @@
 import { createSlice, PayloadAction } from '@reduxjs/toolkit';
 import { createSlice, PayloadAction } from '@reduxjs/toolkit';
+import { setCurrentPatientType } from '../../patientTypeSlice';
+import { BodyPart } from '@/API/bodyPart';
+import { PatientType } from '@/API/patientType';
+import {
+  SelectionState,
+  setSelected,
+} from '@/states/patient/register/SelectionTypeSlice';
+import { setCurrentBodyPart } from '../../bodyPartSlice';
 
 
 // 体位类型
 // 体位类型
 export interface View {
 export interface View {
@@ -58,15 +66,9 @@ interface ViewSelectionState {
   selectedViews: View[]; // 已选择体位列表
   selectedViews: View[]; // 已选择体位列表
   availableViews: View[]; // 待选择体位列表
   availableViews: View[]; // 待选择体位列表
   protocols: Procedure[]; // 协议列表(只会出现在待选择列表)
   protocols: Procedure[]; // 协议列表(只会出现在待选择列表)
-}
-
-//type SelectionType = 'protocol' | 'view';
-
-interface ViewSelectionState {
-  selectedViews: View[]; // 已选择体位列表
-  availableViews: View[]; // 待选择体位列表
-  protocols: Procedure[]; // 协议列表(只会出现在待选择列表)
-  //selectionType: SelectionType; // 可选择值为 'protocol' | 'view'
+  currentBodyPart: BodyPart | null;
+  currentPatientType: PatientType | null;
+  currentSelectionType: SelectionState;
 }
 }
 
 
 const initialState: ViewSelectionState = {
 const initialState: ViewSelectionState = {
@@ -120,7 +122,9 @@ const initialState: ViewSelectionState = {
     },
     },
   ],
   ],
   protocols: [],
   protocols: [],
-  //selectionType: 'protocol',
+  currentBodyPart: null,
+  currentPatientType: null,
+  currentSelectionType: { selected: 'protocol' },
 };
 };
 
 
 const viewSelectionSlice = createSlice({
 const viewSelectionSlice = createSlice({
@@ -133,7 +137,7 @@ const viewSelectionSlice = createSlice({
     },
     },
     // 添加协议中的所有体位到已选择体位列表
     // 添加协议中的所有体位到已选择体位列表
     // eslint-disable-next-line
     // eslint-disable-next-line
-        addProtocolViews(state, action: PayloadAction<Procedure>) {
+    addProtocolViews(state, action: PayloadAction<Procedure>) {
       // 假设协议中包含 ProcedureViews 属性,存储体位数组
       // 假设协议中包含 ProcedureViews 属性,存储体位数组
       // todo 这里涉及到基于协议查询其名下的体位,然后再添加
       // todo 这里涉及到基于协议查询其名下的体位,然后再添加
       // if (Array.isArray(action.payload.ProcedureViews)) {
       // if (Array.isArray(action.payload.ProcedureViews)) {
@@ -148,6 +152,30 @@ const viewSelectionSlice = createSlice({
       state.protocols = action.payload;
       state.protocols = action.payload;
     },
     },
   },
   },
+  extraReducers: (builder) => {
+    builder
+      .addCase(setCurrentPatientType, (state, action) => {
+        const currentPatientType = action.payload;
+        if (currentPatientType) {
+          // console.log(`在view section中感知到 current patient type 变化: ${currentPatientType.patient_type_name}`);
+          // 基于过滤条件查询协议或者体位
+          state.currentPatientType = currentPatientType;
+        }
+      })
+      .addCase(setCurrentBodyPart, (state, action) => {
+        const currentBodyPart = action.payload;
+        if (currentBodyPart) {
+          state.currentBodyPart = currentBodyPart;
+          // console.log(`在view section中感知到 currentBodyPart 变化: ${JSON.stringify(currentBodyPart, null, 2)}`);
+        }
+      })
+      .addCase(setSelected, (state, action) => {
+        console.log(
+          `在view section中感知到 currentSelectionType : ${action.payload}`
+        );
+        state.currentSelectionType.selected = action.payload;
+      });
+  },
 });
 });
 
 
 export const {
 export const {

+ 5 - 0
src/states/patientTypeSlice.ts

@@ -29,6 +29,10 @@ export const setCurrentPatientType = createAction<PatientType | null>(
   'patientType/setCurrentPatientType'
   'patientType/setCurrentPatientType'
 );
 );
 
 
+export const patientTypeChanged = createAction<PatientType | null>(
+  'patientType/patientTypeChanged'
+);
+
 export const getPatientTypes = createAsyncThunk(
 export const getPatientTypes = createAsyncThunk(
   'patientType/getPatientTypes',
   'patientType/getPatientTypes',
   async (
   async (
@@ -69,6 +73,7 @@ const patientTypeSlice = createSlice({
         // const patientTypeId = action.payload.patient_type_id;
         // const patientTypeId = action.payload.patient_type_id;
         // setBodyPartsByPatientType(patientTypeId);
         // setBodyPartsByPatientType(patientTypeId);
       }
       }
+      // Dispatch the action using useDispatch in a component
     },
     },
   },
   },
   extraReducers: (builder) => {
   extraReducers: (builder) => {

+ 2 - 0
src/states/store.ts

@@ -3,6 +3,7 @@ import userInfoReducer from './user_info';
 import viewSelectionReducer from './patient/viewSelection';
 import viewSelectionReducer from './patient/viewSelection';
 import patientTypeReducer from './patientTypeSlice';
 import patientTypeReducer from './patientTypeSlice';
 import bodyPartReducer from './bodyPartSlice';
 import bodyPartReducer from './bodyPartSlice';
+import selectionReducer from './patient/register/SelectionTypeSlice';
 
 
 const store = configureStore({
 const store = configureStore({
   reducer: {
   reducer: {
@@ -10,6 +11,7 @@ const store = configureStore({
     viewSelection: viewSelectionReducer,
     viewSelection: viewSelectionReducer,
     patientType: patientTypeReducer,
     patientType: patientTypeReducer,
     bodyPart: bodyPartReducer,
     bodyPart: bodyPartReducer,
+    selection: selectionReducer,
   },
   },
 });
 });