Forráskód Böngészése

(1.49.7 -> 1.50.0):检查报告功能完善,增加性别和申请科室管理的接口

szy 1 hete
szülő
commit
b852977b15

+ 59 - 4
src/API/report/ReportActions.ts

@@ -1,4 +1,5 @@
 /* eslint-disable */
+import { extend } from 'cypress/types/lodash';
 import axiosInstance from '../interceptor';
 
 // 报告查询参数
@@ -10,7 +11,7 @@ export interface ReportQueryParams {
 }
 
 // 报告列表项
-export interface ReportItem {
+export interface ReportItem extends ReportContent {
   study_instance_uid: string;
   study_id: string;
   headers: {
@@ -31,11 +32,21 @@ export interface ReportItem {
   create_time: string;
 }
 
-// 报告内容详情
-export interface ReportContent {
+export interface ReportResponse extends ReportContent {
   '@type': string;
   study_instance_uid: string;
   study_id: string;
+  create_time: string;
+}
+export type ReportContentValue =
+  | string
+  | string[]
+  | ReportContent['headers']
+  | Partial<ReportContent>
+  | boolean // 如果将来添加布尔类型的字段
+  | number;
+// 报告内容详情
+export interface ReportContent {
   headers: {
     age: string;
     bed_number: string;
@@ -46,13 +57,13 @@ export interface ReportContent {
     name: string;
     requesting_department: string;
     sex: string;
+    patient_type?: string
   };
   findings: string;
   impression: string;
   radiologist: string;
   review_physician: string;
   images: string[];
-  create_time: string;
 }
 
 // 报告内容响应
@@ -97,6 +108,32 @@ export interface ReportPreviewRequest {
   images: string[];
 }
 
+export interface DepartmentItem {
+  id: number;
+  department: string;
+  user_id: number;
+}
+
+// 申请科室列表
+export interface createdDepartmentResponse {
+  code: string;
+  description: string;
+  solution: string;
+  data: {
+    '@type': string;
+    count: number;
+    departments: DepartmentItem[];
+  };
+}
+
+// 报告模板响应
+export interface DepartmentResponse {
+  code: string;
+  description: string;
+  solution: string;
+  data: any;
+}
+
 // 报告预览响应
 export interface ReportPreviewResponse extends Blob { }
 
@@ -137,3 +174,21 @@ export const previewReport = async (
   });
   return response.data;
 };
+// 获取申请科室列表
+export const getDepartment = async (
+  params: { scope?: 'all' | 'mine' }
+): Promise<createdDepartmentResponse> => {
+  const response = await axiosInstance.get('/auth/report/department', {
+    params,
+  });
+  return response.data;
+};
+
+// 新建申请科室
+
+export const createdDepartment = async (
+  params: { departments: string[] }
+): Promise<DepartmentResponse> => {
+  const response = await axiosInstance.post('/auth/report/department', params);
+  return response.data;
+};

+ 25 - 0
src/API/system/options.ts

@@ -0,0 +1,25 @@
+import axiosInstance from '../interceptor';
+export interface OptionsItem {
+  text: string;
+  value: string;
+}
+export interface OptionsResponse {
+  code: string;
+  description: string;
+  solution: string;
+  data: {
+    '@type': string;
+    option: OptionsItem[];
+  };
+}
+
+// 获取配置的可选项
+export const getOption = async (params: {
+  group: string;
+  flag: string;
+}): Promise<OptionsResponse> => {
+  const response = await axiosInstance.get('/auth/resource/options', {
+    params,
+  });
+  return response.data;
+};

+ 1 - 1
src/assets/i18n/messages/zh.js

@@ -164,7 +164,7 @@ export default {
   "register.variety.placeholder": "请输入品种",
   "register.patientType": "患者类型",
   "register.patientType.placeholder": "请输入患者类型",
-  "animal.register.patientType": "宠物类",
+  "animal.register.patientType": "宠物类",
   "register.operatorId": "操作员ID",
   "register.operatorId.placeholder": "请输入操作员ID",
   "register.modality": "物理疗法",

+ 32 - 51
src/pages/patient/DiagnosticReport/components/AnimalBaseInfo.tsx

@@ -1,15 +1,13 @@
 import React from 'react';
 import { Row, Col, Input, Select, Form } from 'antd';
-import { RootState, useAppDispatch } from '@/states/store';
-import { useSelector } from 'react-redux';
-import { useEffect } from 'react';
-import { Task, TaskAnimal } from '@/domain/work';
-import { useIntl, FormattedMessage } from 'react-intl';
-import { updateField } from '@/states/patient/DiagnosticReport/slice';
+import { RootState, useAppSelector, useAppDispatch } from '@/states/store';
+import { FormattedMessage } from 'react-intl';
 import { useDiagnosticData } from '@/hooks/useDiagnosticData';
+import { updateReportField } from '@/states/patient/DiagnosticReport/slice';
 const { Option } = Select;
 
 export const AnimalBaseInfo: React.FC = () => {
+  const dispatch = useAppDispatch();
   // 报告信息
   const [name, setName] = useDiagnosticData('headers.name');
   const [sex, setSex] = useDiagnosticData('headers.sex');
@@ -27,58 +25,41 @@ export const AnimalBaseInfo: React.FC = () => {
   const [radiologist, setRadiologist] = useDiagnosticData('radiologist');
   const [reviewPhysician, setReviewPhysician] =
     useDiagnosticData('review_physician');
-  const dispatch = useAppDispatch();
-  const selectedIds = useSelector(
+
+  const reportInfo = useAppSelector((state) => state.diagnosticReport?.report);
+
+  const selectedIds = useAppSelector(
     (s: RootState) => s.workSelection.selectedIds
   );
-  const workEntities = useSelector((s: RootState) => s.workEntities.data);
-  const productName = useSelector((s: RootState) => s.product.productName);
-  const intl = useIntl();
+  const productName = useAppSelector((s: RootState) => s.product.productName);
   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]);
 
   return (
     <Form layout="vertical">
       <Row gutter={12}>
+        <Col span={6}>
+          <Form.Item
+            label={
+              <FormattedMessage
+                id={'animal.register.patientType'}
+                defaultMessage={'animal.register.patientType'}
+              />
+            }
+          >
+            <Input
+              // disabled
+              value={reportInfo?.headers?.patient_type}
+              onChange={(e) =>
+                dispatch(
+                  updateReportField({
+                    path: 'report.headers.patient_type',
+                    value: e.target.value,
+                  })
+                )
+              }
+            />
+          </Form.Item>
+        </Col>
         <Col span={6}>
           <Form.Item
             label={

+ 146 - 171
src/pages/patient/DiagnosticReport/components/BaseInfo.tsx

@@ -1,21 +1,16 @@
-import React, { useState } from 'react';
-import { Row, Col, Input, Select, DatePicker, Button, Form } from 'antd';
-import { RootState, useAppDispatch } from '@/states/store';
-import {
-  updateField,
-  updateDropdown,
-  BaseInfoState,
-} from '@/states/patient/DiagnosticReport/baseInfoSlice';
-import dayjs from 'dayjs';
-import { useSelector } from 'react-redux';
-import { useEffect } from 'react';
-import { Task } from '@/domain/work';
+import React, { useEffect, useState } from 'react';
+import { Row, Col, Input, Select, Form } from 'antd';
 import { useDiagnosticData } from '@/hooks/useDiagnosticData';
-
-const { Option } = Select;
-
+import Space from 'antd/lib/space';
+import { PlusOutlined } from '@ant-design/icons';
+import { DepartmentModal } from './DepartmentModal';
+import { getDepartment } from '@/API/report/ReportActions';
+import { RootState, useAppSelector, useAppDispatch } from '@/states/store';
+import { setDepartments } from '@/states/patient/DiagnosticReport/departmentSlice';
+import { getOption } from '@/API/system/options';
+import { setOptionSex } from '@/states/system/optionSlice';
 export const BaseInfo: React.FC = () => {
-  const data = useSelector((s: RootState) => s.baseInfo);
+  const [departmentOpen, setDepartmentOpen] = useState(false);
   const [name, setName] = useDiagnosticData<string>('headers.name');
   const [sex, setSex] = useDiagnosticData<string>('headers.sex');
   const [age, setAge] = useDiagnosticData<string>('headers.age');
@@ -27,69 +22,42 @@ export const BaseInfo: React.FC = () => {
   const [inspection_method, setInspectionMethod] = useDiagnosticData<string>('headers.inspection_method');
   const [radiologist, setRadiologist] = useDiagnosticData<string>('radiologist');
   const [reviewPhysician, setReviewPhysician] = useDiagnosticData<string>('review_physician');
-
   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 | null =
-    selectedIds.length > 0
-      ? (workEntities.find((t) => t.StudyID === selectedIds[0]) ?? null)
-      : null;
-
-  useEffect(() => {
-    console.log(
-      `【诊断报告】:selectedStudy是空吗?${selectedStudy === null} ${selectedStudy}`
-    );
-    if (selectedStudy) {
-      dispatch(
-        updateField({ key: 'patientName', value: selectedStudy.PatientName })
-      );
-      dispatch(
-        updateField({ key: 'englishName', value: selectedStudy.PatientName })
-      );
-      dispatch(
-        updateField({ key: 'patientNo', value: selectedStudy.PatientID })
-      );
-      dispatch(updateField({ key: 'gender', value: selectedStudy.PatientSex }));
-      dispatch(updateField({ key: 'age', value: selectedStudy.PatientAge }));
-      // dispatch(updateField({ key: 'visitType', value: selectedStudy.visitType }));// 就诊类型
-      // dispatch(updateField({ key: 'clinicNo', value: selectedStudy.clinicNo }));
-      // dispatch(updateField({ key: 'department', value: selectedStudy.department }));//科室
-      dispatch(
-        updateField({
-          key: 'examTime',
-          value: selectedStudy.StudyStartDatetime,
-        })
-      );
-      dispatch(updateField({ key: 'studyId', value: selectedStudy.StudyID }));
-      // dispatch(updateField({ key: 'bodyPart', value: selectedStudy. }));
-      // dispatch(updateField({ key: 'examDesc', value: selectedStudy.examDesc }));
-      // dispatch(updateField({ key: 'examPosition', value: selectedStudy.examPosition }));
-      // dispatch(updateField({ key: 'clinicalDiag', value: selectedStudy.clinicalDiag }));
+  const departments = useAppSelector((state: RootState) => state.department.departments);
+  const optionSex = useAppSelector((state: RootState) => state.options.sex);
+  const getDepartmentData = async () => {
+    const res = await getDepartment({});
+    if (res.code === '0x000000') {
+      dispatch(setDepartments(res?.data?.departments || []));
     }
-  }, [selectedIds]);
-
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  const onChange = (key: keyof BaseInfoState, val: any) => {
-    dispatch(updateField({ key, value: val }));
   };
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  const onDropdownChange = (key: keyof BaseInfoState, val: any) => {
-    dispatch(updateDropdown({ key, value: val }));
+
+  const getSexOption = async () => {
+    const res = await getOption({ group: 'patient', flag: 'sex' });
+    if (res.code === '0x000000') {
+      dispatch(setOptionSex(res?.data?.option || []));
+    }
   };
 
+  useEffect(() => {
+    getDepartmentData();
+    getSexOption();
+  }, []);
+
   return (
-    <Form layout="vertical">
-      <Row gutter={12}>
-        <Col span={6}>
-          <Form.Item label="患者姓名">
-            <Input value={name} onChange={(e) => setName(e.target.value)} />
-          </Form.Item>
-        </Col>
-        {/* <Col span={6}>
+    <>
+      <Form layout="vertical">
+        <Row gutter={12}>
+          <Col span={6}>
+            <Form.Item label="患者姓名">
+              <Input
+                disabled
+                value={name}
+                onChange={(e) => setName(e.target.value)}
+              />
+            </Form.Item>
+          </Col>
+          {/* <Col span={6}>
           <Form.Item label="曾用名">
             <Input
               value={data.usedName}
@@ -105,32 +73,36 @@ export const BaseInfo: React.FC = () => {
             />
           </Form.Item>
         </Col> */}
-        <Col span={6}>
-          <Form.Item label="患者编号">
-            <Input
-              value={medical_record_number}
-              onChange={(e) => setMedicalrecordnumber(e.target.value)}
-            />
-          </Form.Item>
-        </Col>
-        <Col span={6}>
-          <Form.Item label="性别">
-            <Select value={sex} onChange={(v) => setSex(v)}>
-              <Option value="男">男</Option>
-              <Option value="女">女</Option>
-            </Select>
-          </Form.Item>
-        </Col>
-        <Col span={6}>
-          <Form.Item label="年龄">
-            <Input
-              addonAfter="Y"
-              value={age}
-              onChange={(e) => setAge(e.target.value)}
-            />
-          </Form.Item>
-        </Col>
-        {/* <Col span={6}>
+          <Col span={6}>
+            <Form.Item label="患者编号">
+              <Input
+                disabled
+                value={medical_record_number}
+                onChange={(e) => setMedicalrecordnumber(e.target.value)}
+              />
+            </Form.Item>
+          </Col>
+          <Col span={6}>
+            <Form.Item label="性别">
+              <Select
+                value={sex}
+                disabled
+                fieldNames={{ label: 'text' }}
+                onChange={(v) => setSex(v)}
+                options={optionSex}
+              />
+            </Form.Item>
+          </Col>
+          <Col span={6}>
+            <Form.Item label="年龄">
+              <Input
+                disabled
+                value={age}
+                onChange={(e) => setAge(e.target.value)}
+              />
+            </Form.Item>
+          </Col>
+          {/* <Col span={6}>
           <Form.Item label="就诊类型">
             <Row gutter={4}>
               <Col flex="auto">
@@ -149,79 +121,82 @@ export const BaseInfo: React.FC = () => {
             </Row>
           </Form.Item>
         </Col> */}
-        <Col span={6}>
-          <Form.Item label="门诊/住院号">
-            <Input
-              value={hospitalization_number}
-              onChange={(e) => setHospitalizationNumber(e.target.value)}
-            />
-          </Form.Item>
-        </Col>
-        <Col span={6}>
-          <Form.Item label="床号">
-            <Input
-              value={bed_number}
-              onChange={(e) => setBedNumber(e.target.value)}
-            />
-          </Form.Item>
-        </Col>
-        <Col span={6}>
-          <Form.Item label="检查号">
-            <Input
-              value={inspection_number}
-              onChange={(e) => setInspectionNumber(e.target.value)}
-            />
-          </Form.Item>
-        </Col>
-        <Col span={6}>
-          <Form.Item label="检查方法">
-            <Input
-              value={inspection_method}
-              onChange={(e) => setInspectionMethod(e.target.value)}
+          <Col span={6}>
+            <Form.Item label="门诊/住院号">
+              <Input
+                value={hospitalization_number}
+                onChange={(e) => setHospitalizationNumber(e.target.value)}
+              />
+            </Form.Item>
+          </Col>
+          <Col span={6}>
+            <Form.Item label="床号">
+              <Input
+                value={bed_number}
+                onChange={(e) => setBedNumber(e.target.value)}
+              />
+            </Form.Item>
+          </Col>
+          <Col span={6}>
+            <Form.Item label="检查号">
+              <Input
+                value={inspection_number}
+                onChange={(e) => setInspectionNumber(e.target.value)}
+              />
+            </Form.Item>
+          </Col>
+          <Col span={6}>
+            <Form.Item label="检查方法">
+              <Input
+                value={inspection_method}
+                onChange={(e) => setInspectionMethod(e.target.value)}
+              />
+            </Form.Item>
+          </Col>
+          <Col span={6}>
+            <Form.Item label="申请科室">
+              <Select
+                style={{ width: '90%' }}
+                value={requesting_department}
+                fieldNames={{ label: 'department', value: 'department' }}
+                onChange={(v) => setRequestingDepartment(`${v}`)}
+                options={departments}
+              />
+            </Form.Item>
+            <PlusOutlined
+              style={{
+                position: 'absolute',
+                top: '40px',
+                right: '10px',
+                cursor: 'pointer',
+                transition: 'all 0.3s',
+              }}
+              onClick={() => setDepartmentOpen(true)}
             />
-          </Form.Item>
-        </Col>
-        <Col span={6}>
-          <Form.Item label="申请科室">
-            <Input
-              value={requesting_department}
-              onChange={(e) => setRequestingDepartment(e.target.value)}
-            />
-          </Form.Item>
-          {/* <Form.Item label="科室">
-            <Row gutter={4}>
-              <Col flex="auto">
-                <Select
-                  value={data.department}
-                  onChange={(v) => onDropdownChange('department', v)}
-                >
-                  <Option value="内科">内科</Option>
-                  <Option value="外科">外科</Option>
-                </Select>
-              </Col>
-            </Row>
-          </Form.Item> */}
-        </Col>
+          </Col>
 
-
-
-        <Col span={6}>
-          <Form.Item label="放射科医生">
-            <Input
-              value={radiologist}
-              onChange={(e) => setRadiologist(e.target.value)}
-            />
-          </Form.Item>
-        </Col>
-        <Col span={6}>
-          <Form.Item label="审核医师">
-            <Input
-              value={reviewPhysician}
-              onChange={(e) => setReviewPhysician(e.target.value)}
-            />
-          </Form.Item>
-        </Col>
-      </Row>
-    </Form>
+          <Col span={6}>
+            <Form.Item label="放射科医生">
+              <Input
+                value={radiologist}
+                onChange={(e) => setRadiologist(e.target.value)}
+              />
+            </Form.Item>
+          </Col>
+          <Col span={6}>
+            <Form.Item label="审核医师">
+              <Input
+                value={reviewPhysician}
+                onChange={(e) => setReviewPhysician(e.target.value)}
+              />
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+      <DepartmentModal
+        visible={departmentOpen}
+        onClose={() => setDepartmentOpen(false)}
+      />
+    </>
   );
 };

+ 136 - 0
src/pages/patient/DiagnosticReport/components/DepartmentModal.tsx

@@ -0,0 +1,136 @@
+/* eslint-disable */
+import React, { useState, useEffect } from 'react';
+import {
+  Modal,
+  Button,
+  Table,
+  Select,
+  Input,
+  Checkbox,
+  message,
+  Form,
+  Space,
+  Row,
+  Col,
+} from 'antd';
+import { RootState, useAppDispatch, useAppSelector } from '@/states/store';
+import { CloseCircleOutlined, MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
+
+import {
+  fetchTemplatesThunk,
+  createTemplateThunk,
+  updateTemplateThunk,
+  deleteTemplateThunk,
+  setSelectedTag,
+} from '@/states/patient/DiagnosticReport/templateSlice';
+import { ReportTemplate } from '@/API/report/ReportTemplateActions';
+import { createdDepartment, getDepartment } from '@/API/report/ReportActions';
+import { use } from 'chai';
+
+interface DepartmentModalProps {
+  visible: boolean;
+  onClose: () => void;
+}
+
+export const DepartmentModal: React.FC<
+  DepartmentModalProps
+> = ({ visible, onClose }) => {
+  const dispatch = useAppDispatch();
+  const [form] = Form.useForm();
+  const departments = useAppSelector((state: RootState) => state.department.departments);
+  const onFinish = async (values: any) => {
+    console.log('Received values of form:', values);
+    const res = await createdDepartment(values)
+    if (res.code === '0x000000') {
+      message.success('申请科室成功')
+    } else {
+      message.error('申请科室失败')
+    }
+  };
+
+  useEffect(() => {
+    form.setFieldsValue({
+      departments: departments.map(k => k.department)
+    })
+  }, [visible])
+
+  return (
+    <Modal
+      title="申请科室管理"
+      centered
+      closable={false}
+      open={visible}
+      maskClosable={false}
+      destroyOnHidden
+      onCancel={onClose}
+      styles={{ body: { maxHeight: '50vh', overflow: 'auto' } }}
+      okButtonProps={{ autoFocus: true, htmlType: 'submit', style: { marginRight: '50px' } }}
+      modalRender={(dom) => (
+        <Form
+          layout="vertical"
+          form={form}
+          initialValues={{ modifier: 'public' }}
+          clearOnDestroy
+          onFinish={onFinish}
+        >
+          {dom}
+        </Form>
+      )}
+    >
+      < Form.List
+        name="departments"
+      >
+        {(fields, { add, remove }) => (
+          <>
+            {fields.map((field) => (
+              <Form.Item
+                label={''}
+                required={false}
+                key={field.key}
+              >
+                <Form.Item
+                  {...field}
+                  validateTrigger={['onChange', 'onBlur']}
+                  rules={[
+                    {
+                      required: true,
+                      whitespace: true,
+                      message: "Please input passenger's name or delete this field.",
+                    },
+                  ]}
+                  noStyle
+                >
+                  <Input placeholder="passenger name" style={{ width: '90%' }} />
+                </Form.Item>
+                <MinusCircleOutlined
+                  style={{
+                    position: 'relative',
+                    top: '4px',
+                    margin: '0 8px',
+                    color: '#999',
+                    fontSize: '24px',
+                    cursor: 'pointer',
+                  }}
+                  onClick={() => remove(field.name)}
+                />
+              </Form.Item>
+            ))}
+            <Form.Item>
+              <Button
+                // disabled={fields.length >= 6}
+                type="dashed"
+                onClick={() => add()}
+                style={{ width: '90%' }}
+                icon={<PlusOutlined />}
+              >
+                增加
+              </Button>
+            </Form.Item>
+          </>
+        )}
+      </Form.List >
+
+
+    </Modal >
+  );
+};

+ 2 - 2
src/pages/patient/DiagnosticReport/components/FindingsSection.tsx

@@ -6,8 +6,8 @@ import { TemplateManagementModal } from './TemplateManagementModal';
 import { useDiagnosticData } from '@/hooks/useDiagnosticData';
 export const FindingsSection: React.FC = () => {
   const { impression } = useAppSelector(
-    (state) => state.diagnosticReport.report
-  );
+    (state) => state.diagnosticReport?.report
+  ) || {};
 
   const [templateModalVisible, setTemplateModalVisible] = useState(false);
   const [findings, setFindings] = useDiagnosticData<string>('findings');

+ 1 - 4
src/pages/patient/DiagnosticReport/components/ImageList.tsx

@@ -1,4 +1,4 @@
-import React, { useEffect } from 'react';
+import React from 'react';
 import { PlusOutlined } from '@ant-design/icons';
 import { Button, Tooltip, Modal, message } from 'antd';
 import { ImageSelection } from './ImageSelection';
@@ -11,15 +11,12 @@ import {
 } from '@/states/patient/DiagnosticReport/imageSelectionSlice';
 import { fetchTaskDetails } from '@/API/patient/workActions';
 import { getExposedImageUrl } from '@/API/bodyPosition';
-import { useDiagnosticData } from '@/hooks/useDiagnosticData';
 export const ImageList: React.FC = () => {
   const [isModalVisible, setIsModalVisible] = React.useState(false);
   const [isLoading, setIsLoading] = React.useState(false);
   const images = useAppSelector((s) => s.imageList.images);
   const studyId = useAppSelector((s) => s.baseInfo.studyId);
   const dispatch = useAppDispatch();
-  const reportInfo = useAppSelector((s) => s.diagnosticReport?.report);
-  console.log('reportInfo=====>', reportInfo);
 
   // 获取候选图像数据
   const handleOpenModal = async () => {

+ 1 - 1
src/pages/patient/DiagnosticReport/components/TemplateManagementModal.tsx

@@ -117,7 +117,6 @@ export const TemplateManagementModal: React.FC<
   const handleSave = async () => {
     try {
       const values = await form.validateFields();
-
       if (isNewTemplate) {
         // 创建新模板
         await dispatch(createTemplateThunk(values)).unwrap();
@@ -202,6 +201,7 @@ export const TemplateManagementModal: React.FC<
   return (
     <Modal
       title="诊断报告模板管理"
+      centered
       open={visible}
       maskClosable={false}
       destroyOnHidden

+ 10 - 14
src/pages/patient/DiagnosticReport/components/TemplatePanel.tsx

@@ -2,27 +2,24 @@
 import React, { useState, useEffect, useMemo } from 'react';
 import { Input, Button, Modal, message, Spin, Empty } from 'antd';
 import { SearchOutlined } from '@ant-design/icons';
-import { useAppDispatch, useAppSelector } from '@/states/store';
+import { RootState, useAppDispatch, useAppSelector } from '@/states/store';
 import {
   fetchTemplatesThunk,
   setFilterMode,
   setSearchKeyword,
 } from '@/states/patient/DiagnosticReport/templateSlice';
-import { updateInputValue } from '@/states/patient/DiagnosticReport/findingsSlice';
-import { setDiagnosisDescription } from '@/states/patient/DiagnosticReport/diagnosisSlice';
 import { ReportTemplate } from '@/API/report/ReportTemplateActions';
 import { TemplateListItem } from './TemplateListItem';
 import { TemplateManagementModal } from './TemplateManagementModal';
+import { updateReportField } from '@/states/patient/DiagnosticReport/slice';
 
 export const TemplatePanel: React.FC = () => {
   const dispatch = useAppDispatch();
   const { templates, loading, filterMode, searchKeyword } = useAppSelector(
     (state) => state.template
   );
-  const { diagnosticDescriptionFromImage } = useAppSelector(
-    (state) => state.findings
-  );
-  const { diagnosisDescription } = useAppSelector((state) => state.diagnosis);
+
+  const { findings = '', impression = '' } = useAppSelector((state: RootState) => state.diagnosticReport.report) || {};
 
   const [managementModalVisible, setManagementModalVisible] = useState(false);
 
@@ -53,9 +50,8 @@ export const TemplatePanel: React.FC = () => {
   // 应用诊断报告模板
   const handleApplyTemplate = (template: ReportTemplate) => {
     const hasContent =
-      diagnosticDescriptionFromImage.trim() !== '' ||
-      diagnosisDescription.trim() !== '';
-
+      findings.trim() !== '' ||
+      impression.trim() !== '';
     if (hasContent) {
       Modal.confirm({
         title: '确认应用诊断报告模板',
@@ -63,14 +59,14 @@ export const TemplatePanel: React.FC = () => {
         okText: '确定',
         cancelText: '取消',
         onOk: () => {
-          dispatch(updateInputValue(template.findings));
-          dispatch(setDiagnosisDescription(template.impression));
+          dispatch(updateReportField({ path: 'report.findings', value: template.findings }));
+          dispatch(updateReportField({ path: 'report.impression', value: template.impression }));
           message.success('诊断报告模板已应用');
         },
       });
     } else {
-      dispatch(updateInputValue(template.findings));
-      dispatch(setDiagnosisDescription(template.impression));
+      dispatch(updateReportField({ path: 'report.findings', value: template.findings }));
+      dispatch(updateReportField({ path: 'report.impression', value: template.impression }));
       message.success('诊断报告模板已应用');
     }
   };

+ 33 - 22
src/pages/patient/components/ActionPanel.tsx

@@ -15,11 +15,7 @@ import { FormattedMessage } from 'react-intl';
 import { AppDispatch, RootState, useAppSelector } from '@/states/store';
 import Icon from '@/components/Icon';
 import DiagnosticReport from '../DiagnosticReport';
-import {
-  setVisible,
-  setReport,
-  resetReport,
-} from '@/states/patient/DiagnosticReport/slice';
+import { setVisible, setReport, resetReport } from '@/states/patient/DiagnosticReport/slice';
 import EditTaskModal from './EditTaskModal';
 import { openEditModal } from '@/states/patient/edit/editFormSlice';
 import DiagnosticReportBatchDownloadModal from '@/components/DiagnosticReportBatchDownloadModal';
@@ -50,6 +46,7 @@ import {
   resetImageList,
 } from '@/states/patient/DiagnosticReport/imageListSlice';
 import { getApiBaseUrl } from '@/API/config';
+import { ReportContent } from '@/API/report';
 const { useToken } = theme;
 
 interface ActionButtonProps {
@@ -200,24 +197,38 @@ const ActionPanel: React.FC = () => {
       return;
     }
 
-    const reportRes = await fetchDiagnosisReportContent(selectedIds[0]);
-    if (reportRes?.code === '0x000000') {
-      // 重置检查图像列表
-      await dispatch(resetImageList());
-      // 报告接口数据
-      await dispatch(setReport(reportRes?.data));
-
-      // 单独设置报告影像图片
-      (reportRes?.data?.images || []).forEach((image) => {
-        const API_BASE_URL = getApiBaseUrl();
-        const _url = `${API_BASE_URL}pub/thumbnail/${image}`;
-        dispatch(addImage(_url));
-      });
+    const tempReportHeader = {
+      name: selectedWork.PatientName,
+      sex: selectedWork.PatientSex,
+      age: selectedWork.PatientAge,
+      patientType: selectedWork.PatientType,
+      ownerName: selectedWork.owner_name,
+      medical_record_number: selectedWork.PatientID,
+      inspection_number: selectedWork.AccessionNumber,
+    };
 
-      dispatch(setVisible({ visible: true, work: selectedWork })); //
-    } else {
-      message.error(reportRes?.description || '获取报告失败');
-    }
+    const reportRes = await fetchDiagnosisReportContent(selectedIds[0]);
+    // 重置检查图像列表
+    await dispatch(resetImageList());
+    // 报告接口数据
+    const { headers, ...rest } = reportRes?.data || {};
+    await dispatch(
+      setReport({
+        headers: {
+          ...headers,
+          ...tempReportHeader,
+        },
+        ...rest,
+      })
+    );
+
+    // 单独设置报告影像图片
+    (reportRes?.data?.images || []).forEach((image) => {
+      const API_BASE_URL = getApiBaseUrl();
+      const _url = `${API_BASE_URL}pub/thumbnail/${image}`;
+      dispatch(addImage(_url));
+    });
+    dispatch(setVisible({ visible: true, work: selectedWork })); //
   };
 
   const getWorksFromWorklistOrHistory = () => {

+ 22 - 0
src/states/patient/DiagnosticReport/departmentSlice.ts

@@ -0,0 +1,22 @@
+import { createSlice, PayloadAction } from '@reduxjs/toolkit';
+import { DefaultOptionType } from 'antd/es/select';
+
+export interface DepartmentState {
+  departments: DefaultOptionType[];
+}
+const initialState: DepartmentState = {
+  departments: [],
+};
+
+const departmentSlice = createSlice({
+  name: 'department',
+  initialState,
+  reducers: {
+    setDepartments(state, action: PayloadAction<DefaultOptionType[]>) {
+      state.departments = action.payload;
+    },
+  },
+});
+
+export const { setDepartments } = departmentSlice.actions;
+export default departmentSlice.reducer;

+ 12 - 22
src/states/patient/DiagnosticReport/previewReportThunk.ts

@@ -14,37 +14,27 @@ export const previewReportThunk = createAsyncThunk(
 
   async (_, { getState }) => {
     const state = getState() as RootState;
-    const productName = state.product.productName;
 
+    const productName = state.product.productName;
+    const optionSex = state?.options?.sex;
     // 提取图像文件名
     const extractImageFilename = (url: string): string => {
       // 从 URL 中提取文件名部分
       const urlParts = url.split('/');
       return urlParts[urlParts.length - 1]; // 获取最后一部分
     };
-
     const images = state.imageList.images.map(extractImageFilename);
-    // const { headers = {},
-    //   findings, impression, radiologist, review_physician } = state.diagnosticReport.report || {}
-    // const report = productName === 'VETDROS'
-    //   ? {
-    //     headers,
-    //     findings,
-    //     impression,
-    //     radiologist,
-    //     review_physician,
-    //     images: images, // 添加图像列表
-    //   }
-    //   : {
-    //     headers,
-    //     findings,
-    //     impression,
-    //     radiologist,
-    //     review_physician,
-    //     images: images, // 添加图像列表
-    //   };
-    const report = { ...state.diagnosticReport.report, images }
+    const { headers, ...rest } = state.diagnosticReport.report || {}
+    const sex = optionSex.find(item => item.value === headers?.sex)?.text
+    const report = {
+      headers: {
+        ...headers,
+        sex
+      },
+      ...rest,
+      images
 
+    }
     const response = await previewDiagnosisReport(report);
     console.log('【诊断报告】预览返回', response);
     return response;

+ 14 - 9
src/states/patient/DiagnosticReport/slice.ts

@@ -2,12 +2,13 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
 import { saveReportThunk } from './saveReportThunk';
 import { previewReportThunk } from './previewReportThunk';
 import { Task } from '@/domain/work';
-import { ReportContent } from '@/API/report/ReportActions';
+import { ReportContent, ReportContentValue } from '@/API/report/ReportActions';
+
+import { set } from 'lodash';
 
 interface DiagnosticReportState {
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   report: ReportContent | null;
-  originalReport: ReportContent | null; // 后端返回的原始数据
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   previewResponse: any;
   isPreviewing: boolean;
@@ -20,7 +21,6 @@ interface DiagnosticReportState {
 
 const initialState: DiagnosticReportState = {
   report: null,
-  originalReport: null,
   previewResponse: null,
   isPreviewing: false,
   isSaving: false,
@@ -58,15 +58,20 @@ const diagnosticReportSlice = createSlice({
     },
     setReport(state, action: PayloadAction<ReportContent>) {
       state.report = action.payload;
-      state.originalReport = { ...action.payload };
     },
-    updateField(
+    resetReport(state) {
+      state.report = initialState.report;
+    },
+    updateReportField(
       state,
-      action: PayloadAction<{ key: keyof ReportContent | string; value }>
+      action: PayloadAction<{
+        path: keyof ReportContent | string;
+        value: ReportContentValue;
+      }>
     ) {
-      state[action.payload.key] = action.payload.value;
+      const { path, value } = action.payload;
+      set(state, path, value);
     },
-    resetReport: () => initialState,
   },
   extraReducers: (builder) => {
     builder
@@ -100,7 +105,7 @@ export const {
   toggleVisible,
   setNeedsPreview,
   setReport,
-  updateField,
   resetReport,
+  updateReportField,
 } = diagnosticReportSlice.actions;
 export default diagnosticReportSlice.reducer;

+ 5 - 0
src/states/store.ts

@@ -78,6 +78,9 @@ import cameraReducer from './exam/cameraSlice';
 import pacsNodeReducer from './output/pacsNode/pacsNodeSlice';
 import selectedPatientReducer from './patient/worklist/slices/selectedPatientSlice';
 import reregisterReducer from './patient/reregister/reregisterSlice';
+import departmentReducer from './patient/DiagnosticReport/departmentSlice';
+import optionsReducer from './system/optionSlice';
+
 import {
   binEntitiesSlice,
   binFiltersSlice,
@@ -180,6 +183,8 @@ const store = configureStore({
     serverConfig: serverConfigReducer,
     // annotation: annotationReducer,
     versionUpdate: versionUpdateReducer,
+    department: departmentReducer,
+    options: optionsReducer,
   },
   middleware: (getDefaultMiddleware) =>
     getDefaultMiddleware().concat(

+ 26 - 0
src/states/system/optionSlice.ts

@@ -0,0 +1,26 @@
+import { createSlice, PayloadAction } from '@reduxjs/toolkit';
+
+export interface OptionItem {
+  text: string;
+  value: string;
+}
+
+export interface OptionsState {
+  sex: OptionItem[];
+}
+const initialState: OptionsState = {
+  sex: [],
+};
+
+const optionSlice = createSlice({
+  name: 'options',
+  initialState,
+  reducers: {
+    setOptionSex(state, action: PayloadAction<OptionItem[]>) {
+      state.sex = action.payload;
+    },
+  },
+});
+
+export const { setOptionSex } = optionSlice.actions;
+export default optionSlice.reducer;