Browse Source

feat(report): add pet-related diagnostic reports (no impact on human medical version identified currently)

sw 3 weeks ago
parent
commit
eebaafa1d1

+ 18 - 2
src/API/patient/DiagnosisReportActions.ts

@@ -19,6 +19,22 @@ interface DiagnosisReport {
   review_physician: string;
 }
 
+interface DiagnosisReportAnimal {
+  headers: {
+    name: string;
+    sex: string;
+    age: string;
+    owner_name: string;
+    requesting_department: string;
+    inspection_number: string;
+    inspection_method: string;
+  };
+  findings: string;
+  impression: string;
+  radiologist: string;
+  review_physician: string;
+}
+
 interface DiagnosisReportResponse {
   code: string;
   description: string;
@@ -28,7 +44,7 @@ interface DiagnosisReportResponse {
 
 export const saveDiagnosisReport = async (
   studyId: string,
-  report: DiagnosisReport
+  report: DiagnosisReport | DiagnosisReportAnimal
 ): Promise<DiagnosisReportResponse> => {
   const response = await axiosInstance.post(
     `/auth/study/${studyId}/report`,
@@ -38,7 +54,7 @@ export const saveDiagnosisReport = async (
 };
 
 export const previewDiagnosisReport = async (
-  report: DiagnosisReport
+  report: DiagnosisReport | DiagnosisReportAnimal
 ): Promise<DiagnosisReportResponse> => {
 const response = await axiosInstance.post(
   `/auth/report/preview`,

+ 7 - 0
src/domain/work.ts

@@ -38,3 +38,10 @@ export interface Task {
   MappedStatus: boolean;
   IsDelete: boolean;
 }
+
+export type TaskAnimal = Omit<Task, 'pregnancy_status'> & {
+  sex_neutered: string;
+  owner_name: string;
+  chip_number: object;
+  variety: string;
+};

+ 153 - 0
src/pages/patient/DiagnosticReport/components/AnimalBaseInfo.tsx

@@ -0,0 +1,153 @@
+import React from 'react';
+import { Row, Col, Input, Select, Form } from 'antd';
+import { RootState, useAppDispatch } from '@/states/store';
+import {
+  updateField,
+  updateDropdown,
+  AnimalBaseInfoState,
+} from '@/states/patient/DiagnosticReport/animalBaseInfoSlice';
+import { useSelector } from 'react-redux';
+import { useEffect } from 'react';
+import { Task, TaskAnimal } from '@/domain/work';
+
+const { Option } = Select;
+
+export const AnimalBaseInfo: React.FC = () => {
+  const data = useSelector((s: RootState) => s.animalBaseInfo);
+  const dispatch = useAppDispatch();
+  const selectedIds = useSelector(
+    (s: RootState) => s.workSelection.selectedIds
+  );
+  const workEntities = useSelector((s: RootState) => s.workEntities.data);
+  console.log(`【诊断报告】:选中的study id :${selectedIds[0]}`);
+  const selectedStudy: Task | TaskAnimal | null =
+    selectedIds.length > 0
+      ? (workEntities.find((t) => t.StudyID === selectedIds[0]) ?? null)
+      : null;
+
+  useEffect(() => {
+    console.log(
+      `【诊断报告】:selectedStudy是空吗?${selectedStudy === null} ${selectedStudy}`
+    );
+    if (selectedStudy) {
+      const selectedStudyAnimal = selectedStudy as TaskAnimal;
+      dispatch(
+        updateField({
+          key: 'patientName',
+          value: selectedStudyAnimal.PatientName,
+        })
+      );
+      dispatch(
+        updateField({ key: 'sex', value: selectedStudyAnimal.PatientSex })
+      );
+      dispatch(
+        updateField({ key: 'age', value: selectedStudyAnimal.PatientAge })
+      );
+      dispatch(
+        updateField({ key: 'ownerName', value: selectedStudyAnimal.owner_name })
+      );
+      // dispatch(
+      //   updateField({ key: 'inspectionNumber', value: selectedStudyAnimal.ins })
+      // );
+      // dispatch(
+      //   updateField({ key: 'inspectionMethod', value: selectedStudy.InspectionMethod })
+      // );
+      // dispatch(
+      //   updateField({ key: 'radiologist', value: selectedStudy.Radiologist })
+      // );
+      // dispatch(
+      //   updateField({ key: 'reviewPhysician', value: selectedStudy.ReviewPhysician })
+      // );
+    }
+  }, [selectedIds]);
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  const onChange = (key: keyof AnimalBaseInfoState, val: any) => {
+    dispatch(updateField({ key, value: val }));
+  };
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  const onDropdownChange = (key: keyof AnimalBaseInfoState, val: any) => {
+    dispatch(updateDropdown({ key, value: val }));
+  };
+
+  return (
+    <Form layout="vertical">
+      <Row gutter={12}>
+        <Col span={6}>
+          <Form.Item label="患者姓名">
+            <Input
+              value={data.patientName}
+              onChange={(e) => onChange('patientName', e.target.value)}
+            />
+          </Form.Item>
+        </Col>
+        <Col span={6}>
+          <Form.Item label="性别">
+            <Select
+              value={data.sex}
+              onChange={(v) => onDropdownChange('sex', v)}
+            >
+              <Option value="雄性">雄性</Option>
+              <Option value="雌性">雌性</Option>
+            </Select>
+          </Form.Item>
+        </Col>
+        <Col span={6}>
+          <Form.Item label="年龄">
+            <Input
+              value={data.age}
+              onChange={(e) => onChange('age', e.target.value)}
+            />
+          </Form.Item>
+        </Col>
+        <Col span={6}>
+          <Form.Item label="主人姓名">
+            <Input
+              value={data.ownerName}
+              onChange={(e) => onChange('ownerName', e.target.value)}
+            />
+          </Form.Item>
+        </Col>
+        <Col span={6}>
+          <Form.Item label="申请科室">
+            <Input
+              value={data.requestingDepartment}
+              onChange={(e) => onChange('requestingDepartment', e.target.value)}
+            />
+          </Form.Item>
+        </Col>
+        <Col span={6}>
+          <Form.Item label="检查号">
+            <Input
+              value={data.inspectionNumber}
+              onChange={(e) => onChange('inspectionNumber', e.target.value)}
+            />
+          </Form.Item>
+        </Col>
+        <Col span={6}>
+          <Form.Item label="检查方法">
+            <Input
+              value={data.inspectionMethod}
+              onChange={(e) => onChange('inspectionMethod', e.target.value)}
+            />
+          </Form.Item>
+        </Col>
+        <Col span={6}>
+          <Form.Item label="放射科医生">
+            <Input
+              value={data.radiologist}
+              onChange={(e) => onChange('radiologist', e.target.value)}
+            />
+          </Form.Item>
+        </Col>
+        <Col span={6}>
+          <Form.Item label="审核医师">
+            <Input
+              value={data.reviewPhysician}
+              onChange={(e) => onChange('reviewPhysician', e.target.value)}
+            />
+          </Form.Item>
+        </Col>
+      </Row>
+    </Form>
+  );
+};

+ 26 - 16
src/pages/patient/DiagnosticReport/components/MainContent.tsx

@@ -1,23 +1,33 @@
 import React from 'react';
 import { Card } from 'antd';
 import { BaseInfo } from './BaseInfo';
+import { AnimalBaseInfo } from './AnimalBaseInfo';
 import { ImageList } from './ImageList';
 import { FindingsSection } from './FindingsSection';
 import { DiagnosisSection } from './DiagnosisSection';
 
-export const MainContent: React.FC = () => (
-  <div className="flex flex-col gap-2">
-    <Card size="small" title="基本信息">
-      <BaseInfo />
-    </Card>
-    <Card size="small" title="图像">
-      <ImageList />
-    </Card>
-    <Card size="small">
-      <FindingsSection />
-    </Card>
-    <Card size="small">
-      <DiagnosisSection />
-    </Card>
-  </div>
-);
+import { useSelector } from 'react-redux';
+import { RootState } from '@/states/store';
+
+export const MainContent: React.FC = () => {
+  const productName = useSelector(
+    (state: RootState) => state.product.productName
+  );
+
+  return (
+    <div className="flex flex-col gap-2">
+      <Card size="small" title="基本信息">
+        {productName === 'VETDROS' ? <AnimalBaseInfo /> : <BaseInfo />}
+      </Card>
+      <Card size="small" title="图像">
+        <ImageList />
+      </Card>
+      <Card size="small">
+        <FindingsSection />
+      </Card>
+      <Card size="small">
+        <DiagnosisSection />
+      </Card>
+    </div>
+  );
+};

+ 50 - 0
src/states/patient/DiagnosticReport/animalBaseInfoSlice.ts

@@ -0,0 +1,50 @@
+/* eslint-disable */
+import { createSlice, PayloadAction } from '@reduxjs/toolkit';
+
+export interface AnimalBaseInfoState {
+  patientName: string;
+  sex: string;
+  age: string;
+  ownerName: string;
+  requestingDepartment: string;
+  inspectionNumber: string;
+  inspectionMethod: string;
+  radiologist: string;
+  reviewPhysician: string;
+}
+
+const initialState: AnimalBaseInfoState = {
+  patientName: '',
+  sex: '',
+  age: '',
+  ownerName: '',
+  requestingDepartment: '',
+  inspectionNumber: '',
+  inspectionMethod: '',
+  radiologist: '',
+  reviewPhysician: '',
+};
+
+const animalBaseInfoSlice = createSlice({
+  name: 'animalBaseInfo',
+  initialState,
+  reducers: {
+    updateField(
+      state,
+      action: PayloadAction<{ key: keyof AnimalBaseInfoState; value: any }>
+    ) {
+      state[action.payload.key] = action.payload.value as string;
+    },
+    updateDropdown(
+      state,
+      action: PayloadAction<{ key: keyof AnimalBaseInfoState; value: any }>
+    ) {
+      state[action.payload.key] = action.payload.value as string;
+    },
+    resetAnimalBaseInfo: () => initialState,
+  },
+});
+
+export const { updateField, updateDropdown, resetAnimalBaseInfo } =
+  animalBaseInfoSlice.actions;
+export default animalBaseInfoSlice.reducer;

+ 37 - 18
src/states/patient/DiagnosticReport/previewReportThunk.ts

@@ -14,24 +14,43 @@ export const previewReportThunk = createAsyncThunk(
 
   async (_, { getState }) => {
     const state = getState() as RootState;
-    const report0 = {
-      headers: {
-        name: state.baseInfo.patientName,
-        sex: state.baseInfo.gender,
-        age: state.baseInfo.age,
-        medical_record_number: state.baseInfo.patientNo,
-        hospitalization_number: state.baseInfo.clinicNo,
-        bed_number: state.baseInfo.department,
-        requesting_department: state.baseInfo.department,
-        inspection_number: state.baseInfo.patientNo, //todo 暂时使用患者编号
-        inspection_method: state.baseInfo.examDesc,
-      },
-      findings: state.findings.diagnosticDescriptionFromImage,
-      impression: state.diagnosis.diagnosisDescription,
-      radiologist: state.baseInfo.radiologist,
-      review_physician: state.baseInfo.reviewPhysician,
-    };
-    const response = await previewDiagnosisReport(report0);
+    const productName = state.product.productName;
+
+    const report = productName === 'VETDROS'
+      ? {
+          headers: {
+            name: state.animalBaseInfo.patientName,
+            sex: state.animalBaseInfo.sex,
+            age: state.animalBaseInfo.age,
+            owner_name: state.animalBaseInfo.ownerName,
+            requesting_department: state.animalBaseInfo.requestingDepartment,
+            inspection_number: state.animalBaseInfo.inspectionNumber,
+            inspection_method: state.animalBaseInfo.inspectionMethod,
+          },
+          findings: state.findings.diagnosticDescriptionFromImage,
+          impression: state.diagnosis.diagnosisDescription,
+          radiologist: state.animalBaseInfo.radiologist,
+          review_physician: state.animalBaseInfo.reviewPhysician,
+        }
+      : {
+          headers: {
+            name: state.baseInfo.patientName,
+            sex: state.baseInfo.gender,
+            age: state.baseInfo.age,
+            medical_record_number: state.baseInfo.patientNo,
+            hospitalization_number: state.baseInfo.clinicNo,
+            bed_number: state.baseInfo.department,
+            requesting_department: state.baseInfo.department,
+            inspection_number: state.baseInfo.patientNo, //todo 暂时使用患者编号
+            inspection_method: state.baseInfo.examDesc,
+          },
+          findings: state.findings.diagnosticDescriptionFromImage,
+          impression: state.diagnosis.diagnosisDescription,
+          radiologist: state.baseInfo.radiologist,
+          review_physician: state.baseInfo.reviewPhysician,
+        };
+
+    const response = await previewDiagnosisReport(report);
     console.log('【诊断报告】预览返回', response);
     return response;
   }

+ 36 - 17
src/states/patient/DiagnosticReport/saveReportThunk.ts

@@ -8,23 +8,42 @@ export const saveReportThunk = createAsyncThunk(
   async (_, { getState, dispatch }) => {
     const state = getState() as RootState;
     const studyId = state.baseInfo.studyId;
-    const report = {
-      headers: {
-        name: state.baseInfo.patientName,
-        sex: state.baseInfo.gender,
-        age: state.baseInfo.age,
-        medical_record_number: state.baseInfo.patientNo,
-        hospitalization_number: state.baseInfo.clinicNo,
-        bed_number: state.baseInfo.department,
-        requesting_department: state.baseInfo.department,
-        inspection_number: state.baseInfo.patientNo, //todo 暂时使用患者编号
-        inspection_method: state.baseInfo.examDesc,
-      },
-      findings: state.findings.diagnosticDescriptionFromImage,
-      impression: state.diagnosis.diagnosisDescription,
-      radiologist: state.baseInfo.radiologist,
-      review_physician: state.baseInfo.reviewPhysician,
-    };
+    const productName = state.product.productName;
+
+    const report =
+      productName === 'VETDROS'
+        ? {
+            headers: {
+              name: state.animalBaseInfo.patientName,
+              sex: state.animalBaseInfo.sex,
+              age: state.animalBaseInfo.age,
+              owner_name: state.animalBaseInfo.ownerName,
+              requesting_department: state.animalBaseInfo.requestingDepartment,
+              inspection_number: state.animalBaseInfo.inspectionNumber,
+              inspection_method: state.animalBaseInfo.inspectionMethod,
+            },
+            findings: state.findings.diagnosticDescriptionFromImage,
+            impression: state.diagnosis.diagnosisDescription,
+            radiologist: state.animalBaseInfo.radiologist,
+            review_physician: state.animalBaseInfo.reviewPhysician,
+          }
+        : {
+            headers: {
+              name: state.baseInfo.patientName,
+              sex: state.baseInfo.gender,
+              age: state.baseInfo.age,
+              medical_record_number: state.baseInfo.patientNo,
+              hospitalization_number: state.baseInfo.clinicNo,
+              bed_number: state.baseInfo.department,
+              requesting_department: state.baseInfo.department,
+              inspection_number: state.baseInfo.patientNo, //todo 暂时使用患者编号
+              inspection_method: state.baseInfo.examDesc,
+            },
+            findings: state.findings.diagnosticDescriptionFromImage,
+            impression: state.diagnosis.diagnosisDescription,
+            radiologist: state.baseInfo.radiologist,
+            review_physician: state.baseInfo.reviewPhysician,
+          };
 
     const response = await saveDiagnosisReport(studyId, report);
     return response.data;

+ 3 - 3
src/states/patient/worklist/slices/workSlice.ts

@@ -3,7 +3,7 @@ import {
   createFetchThunk,
   createDeleteThunk,
 } from '../../../list_template/thunk.factory';
-import { work } from '../types/worklist';
+import { work, workAnimal } from '../types/worklist';
 import { WorkFilter } from '../types/workfilter';
 import { PayloadAction } from '@reduxjs/toolkit';
 import {
@@ -24,7 +24,7 @@ import {
 } from '../../../../API/patient/workActions';
 import store from '@/states/store';
 
-export const fetchWorkThunk = createFetchThunk<WorkFilter, work>(
+export const fetchWorkThunk = createFetchThunk<WorkFilter, work | workAnimal>(
   'worklist',
   async ({ page, pageSize, filters }) => {
     // const filtersEx: WorkFilter = { ...filters, status: 'Arrived' }
@@ -98,7 +98,7 @@ const {
   paginationSlice,
   selectionSlice,
   uiSlice,
-} = createEntityListSlices<work, WorkFilter>(
+} = createEntityListSlices<work | workAnimal, WorkFilter>(
   'worklist',
   fetchWorkThunk,
   deleteWorkThunk,

+ 1 - 0
src/states/patient/worklist/types/worklist.ts

@@ -1 +1,2 @@
 export { Task as work } from '@/domain/work';
+export { TaskAnimal as workAnimal } from '@/domain/work';

+ 2 - 0
src/states/store.ts

@@ -46,6 +46,7 @@ import formReducer from './patient/register/formSlice';
 import deviceReducer from './device/deviceSlice';
 import headerReducer from './patient/DiagnosticReport/headerSlice';
 import baseInfoReducer from './patient/DiagnosticReport/baseInfoSlice';
+import animalBaseInfoReducer from './patient/DiagnosticReport/animalBaseInfoSlice';
 import imageListReducer from './patient/DiagnosticReport/imageListSlice';
 import findingsReducer from './patient/DiagnosticReport/findingsSlice';
 import diagnosisReducer from './patient/DiagnosticReport/diagnosisSlice';
@@ -92,6 +93,7 @@ const store = configureStore({
     device: deviceReducer,
     header: headerReducer,
     baseInfo: baseInfoReducer,
+    animalBaseInfo: animalBaseInfoReducer,
     imageList: imageListReducer,
     findings: findingsReducer,
     diagnosis: diagnosisReducer,