소스 검색

feat (1.66.1 -> 1.66.2): 实现诊断报告医师管理和身份证识别功能

- 在 ReportActions.ts 中添加医师相关接口和数据类型
- 在 BaseInfo.tsx 中集成检查医师和审核医师选择功能
- 新增 ExaminationPhysicianModal 和 ReviewPhysicianModal 组件用于医师管理
- 新增 examinationPhysicianSlice 和 reviewPhysicianSlice 用于状态管理
- 新增 IDCardRecognitionModal 组件支持身份证自动识别
- 在 messages 中添加相关国际化文本
- 重构 Workflow.tsx 设置页面布局和配置项

改动文件:
- src/API/report/ReportActions.ts
- src/assets/i18n/messages/en.js
- src/assets/i18n/messages/zh.js
- src/pages/patient/DiagnosticReport/components/BaseInfo.tsx
- src/pages/system/SettingsModal/sections/Preferences/Workflow.tsx
- src/states/store.ts
- src/components/IDCardRecognitionModal.tsx
- src/pages/patient/DiagnosticReport/components/ExaminationPhysicianModal.tsx
- src/pages/patient/DiagnosticReport/components/ReviewPhysicianModal.tsx
- src/states/patient/DiagnosticReport/examinationPhysicianSlice.ts
- src/states/patient/DiagnosticReport/reviewPhysicianSlice.ts
szy 3 일 전
부모
커밋
e9bb5a54b3

+ 28 - 6
CHANGELOG.md

@@ -2,7 +2,34 @@
 
 本项目的所有重要变更都将记录在此文件中.
 
+## [1.66.2] - 2026-1-15 18:06
+
+feat (1.66.1 -> 1.66.2): 实现诊断报告医师管理和身份证识别功能
+
+- 在 ReportActions.ts 中添加医师相关接口和数据类型
+- 在 BaseInfo.tsx 中集成检查医师和审核医师选择功能
+- 新增 ExaminationPhysicianModal 和 ReviewPhysicianModal 组件用于医师管理
+- 新增 examinationPhysicianSlice 和 reviewPhysicianSlice 用于状态管理
+- 新增 IDCardRecognitionModal 组件支持身份证自动识别
+- 在 messages 中添加相关国际化文本
+- 重构 Workflow.tsx 设置页面布局和配置项
+
+改动文件:
+
+- src/API/report/ReportActions.ts
+- src/assets/i18n/messages/en.js
+- src/assets/i18n/messages/zh.js
+- src/pages/patient/DiagnosticReport/components/BaseInfo.tsx
+- src/pages/system/SettingsModal/sections/Preferences/Workflow.tsx
+- src/states/store.ts
+- src/components/IDCardRecognitionModal.tsx
+- src/pages/patient/DiagnosticReport/components/ExaminationPhysicianModal.tsx
+- src/pages/patient/DiagnosticReport/components/ReviewPhysicianModal.tsx
+- src/states/patient/DiagnosticReport/examinationPhysicianSlice.ts
+- src/states/patient/DiagnosticReport/reviewPhysicianSlice.ts
+
 ## [1.66.1 - 2026-01-15 17:56
+
 fix (1.66.0 -> 1.66.1): 修复服务器配置连接检测和实时更新功能
 
 - 在interceptor.ts中添加updateAxiosBaseUrl函数,用于动态更新axios baseURL
@@ -15,6 +42,7 @@ fix (1.66.0 -> 1.66.1): 修复服务器配置连接检测和实时更新功能
 禅道bug编号:209
 
 改动文件:
+
 - src/API/interceptor.ts
 - src/app.tsx
 - src/features/serverConfig/components/ServerConfigModal.tsx
@@ -41,7 +69,6 @@ fix (1.66.0 -> 1.66.1): 修复服务器配置连接检测和实时更新功能
 
 **研发需求ID:** 75
 
-
 ## [1.65.2] - 2026-01-12 20:35
 
 ### 修复 (Fixed)
@@ -53,7 +80,6 @@ fix (1.66.0 -> 1.66.1): 修复服务器配置连接检测和实时更新功能
 
 - src/pages/patient/components/register.form.tsx
 
-
 ## [1.65.1] - 2026-01-12 20:25
 
 ### 修复 (Fixed)
@@ -66,7 +92,6 @@ fix (1.66.0 -> 1.66.1): 修复服务器配置连接检测和实时更新功能
 - src/pages/patient/register.tsx
 - src/states/patient/register/formSlice.ts
 
-
 ## [1.65.0] - 2026-01-12 19:15
 
 ### 新增 (Added)
@@ -82,7 +107,6 @@ fix (1.66.0 -> 1.66.1): 修复服务器配置连接检测和实时更新功能
 - src/pages/patient/components/register.form.tsx
 - src/states/patient/register/formSlice.ts
 
-
 ## [1.64.0] - 2026-01-12 14:58
 
 ### 新增 (Added)
@@ -97,12 +121,10 @@ fix (1.66.0 -> 1.66.1): 修复服务器配置连接检测和实时更新功能
 
 - src/components/QRCodeScanner/QRCodeScanModal.tsx
 
-
 ## [1.63.4] - 2026-01-12 09:50
 
 - 人医第一个,注册时有默认值
 
-
 ## [1.63.3] - 2026-01-09 12:50
 
 ### 修复 (Fixed)

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "zsis",
-  "version": "1.66.1",
+  "version": "1.66.2",
   "private": true,
   "description": "医学成像系统",
   "main": "main.js",

+ 57 - 1
src/API/report/ReportActions.ts

@@ -113,6 +113,14 @@ export interface DepartmentItem {
   department: string;
   user_id: number;
 }
+export interface PhysiciansItem {
+  id: number;
+  name: string;
+  referring: boolean,
+  performed: boolean,
+  review: boolean
+}
+
 
 // 申请科室列表
 export interface createdDepartmentResponse {
@@ -126,6 +134,18 @@ export interface createdDepartmentResponse {
   };
 }
 
+// 医师列表
+export interface createdPhysiciansResponse {
+  code: string;
+  description: string;
+  solution: string;
+  data: {
+    '@type': string;
+    count: number;
+    physicians: PhysiciansItem[];
+  };
+}
+
 // 报告模板响应
 export interface DepartmentResponse {
   code: string;
@@ -192,10 +212,46 @@ export const getDepartment = async (
 };
 
 // 新建申请科室
-
 export const createdDepartment = async (
   params: { departments: string[] }
 ): Promise<DepartmentResponse> => {
   const response = await axiosInstance.post('/auth/report/department', params);
   return response.data;
 };
+
+
+// 获取审核医师列表
+export const getReviewPhysician = async (
+  params: { scope?: 'all' | 'mine' }
+): Promise<createdPhysiciansResponse> => {
+  const response = await axiosInstance.get('/auth/report/physician/review', {
+    params,
+  });
+  return response.data;
+};
+
+// 更新审核医师列表
+export const createdReviewPhysician = async (
+  params: { names: string[] }
+): Promise<DepartmentResponse> => {
+  const response = await axiosInstance.post('/auth/report/physician/review', params);
+  return response.data;
+};
+
+// 获取检查医师列表
+export const getExaminationPhysician = async (
+  params: { scope?: 'all' | 'mine' }
+): Promise<createdPhysiciansResponse> => {
+  const response = await axiosInstance.get('/auth/report/physician/performed', {
+    params,
+  });
+  return response.data;
+};
+
+// 更新检查医师列表
+export const createdExaminationPhysician = async (
+  params: { names: string[] }
+): Promise<DepartmentResponse> => {
+  const response = await axiosInstance.post('/auth/report/physician/performed', params);
+  return response.data;
+};

+ 6 - 0
src/assets/i18n/messages/en.js

@@ -294,6 +294,12 @@ export default {
   "report.department.inputEmptyCheck": "Please enter the department name or delete this field",
   "report.department.saveSuccess": "The information of the applied department has been successfully saved",
   "report.department.saveFail": "Please enter the department name or delete this field",
+  "report.examinationPhysician.title": "Examination Physician Management",
+  "report.examinationPhysician.placeholder": "Please enter physician name",
+  "report.examinationPhysician.inputEmptyCheck": "Please enter physician name or remove this field",
+  "report.saveSuccess": "Information saved successfully",
+  "report.saveFail": "Information save failed",
+  "report.reviewPhysician.title": "Reviewing Physician Management",
   "thumbnailList.title": "Image Thumbnails",
   "thumbnailList.noImages": "No images to display",
   "functionArea.addLMark": "Add L Mark",

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

@@ -44,7 +44,7 @@ export default {
   "searchPanel.startDate": "开始日期",
   "searchPanel.endDate": "结束日期",
   "searchPanel.search": "查询",
-  "register.patientId": "患者编号",
+  "register.patientId": "病案号",
   "animal.register.patientId": "宠物编号",
   "register.patientId.placeholder": "请输入患者编号",
   "animal.register.patientId.placeholder": "请输入宠物编号",
@@ -294,6 +294,12 @@ export default {
   "report.department.inputEmptyCheck": "请输入科室名称或删除此字段",
   "report.department.saveSuccess": "申请科室信息保存成功",
   "report.department.saveFail": "申请科室信息保存异常",
+  "report.examinationPhysician.title": "检查医师管理",
+  "report.examinationPhysician.placeholder": "请输入医师姓名",
+  "report.examinationPhysician.inputEmptyCheck": "请输入医师姓名或删除此字段",
+  "report.saveSuccess": "信息保存成功",
+  "report.saveFail": "信息保存异常",
+  "report.reviewPhysician.title": "审核医师管理",
   "thumbnailList.title": "图像缩略图",
   "thumbnailList.noImages": "暂无图像显示",
   "functionArea.addLMark": "添加左标记",

+ 433 - 0
src/components/IDCardRecognitionModal.tsx

@@ -0,0 +1,433 @@
+import React, { useEffect, useRef, useState } from 'react';
+import { Modal, Button, Space, message, Spin, Upload } from 'antd';
+import {
+  CameraOutlined,
+  CloseOutlined,
+  ReloadOutlined,
+  UploadOutlined,
+  IdcardOutlined,
+} from '@ant-design/icons';
+import { CameraServiceFactory } from '@/services/camera/CameraServiceFactory';
+import axiosInstance from '@/API/interceptor';
+
+interface IDCardRecognitionModalProps {
+  visible: boolean;
+  onClose: () => void;
+  onRecognitionSuccess: (data: IDCardData) => void;
+}
+
+interface IDCardData {
+  name?: string;
+  idNumber?: string;
+  gender?: string;
+  birthDate?: string;
+  address?: string;
+}
+
+/**
+ * 身份证识别 Modal 组件
+ * 支持自动拍照和手动上传图片两种方式
+ */
+const IDCardRecognitionModal: React.FC<IDCardRecognitionModalProps> = ({
+  visible,
+  onClose,
+  onRecognitionSuccess,
+}) => {
+  const videoRef = useRef<HTMLVideoElement>(null);
+  const streamRef = useRef<MediaStream | null>(null);
+
+  const [capturedImage, setCapturedImage] = useState<string | null>(null);
+  const [uploadedImage, setUploadedImage] = useState<string | null>(null);
+  const [isInitializing, setIsInitializing] = useState(false);
+  const [isRecognizing, setIsRecognizing] = useState(false);
+  const [mode, setMode] = useState<'camera' | 'upload'>('camera');
+
+  /**
+   * 初始化摄像头
+   */
+  useEffect(() => {
+    if (visible && mode === 'camera' && !capturedImage) {
+      initCamera();
+    }
+
+    return () => {
+      stopCamera();
+    };
+  }, [visible, mode, capturedImage]);
+
+  /**
+   * 初始化摄像头
+   */
+  const initCamera = async () => {
+    setIsInitializing(true);
+
+    try {
+      console.log('[IDCardRecognition] 初始化摄像头...');
+
+      const cameraService = CameraServiceFactory.create();
+
+      const hasPermission = await cameraService.requestPermission();
+      if (!hasPermission) {
+        throw new Error('用户拒绝了摄像头权限');
+      }
+
+      const stream = await cameraService.getMediaStream({
+        video: {
+          width: { ideal: 1280 },
+          height: { ideal: 720 },
+          facingMode: 'environment', // 使用后置摄像头
+        },
+      });
+
+      streamRef.current = stream;
+
+      if (videoRef.current) {
+        videoRef.current.onloadedmetadata = async () => {
+          try {
+            if (videoRef.current) {
+              await videoRef.current.play();
+              console.log('[IDCardRecognition] 视频开始播放');
+            }
+          } catch (playError: any) {
+            console.error('[IDCardRecognition] 视频播放失败:', playError);
+            message.error('视频播放失败: ' + playError.message);
+          }
+        };
+
+        videoRef.current.srcObject = stream;
+      }
+
+      console.log('[IDCardRecognition] 摄像头初始化成功');
+    } catch (err: any) {
+      console.error('[IDCardRecognition] 摄像头初始化失败:', err);
+      message.error(err.message || '摄像头初始化失败');
+    } finally {
+      setIsInitializing(false);
+    }
+  };
+
+  /**
+   * 停止摄像头
+   */
+  const stopCamera = (): void => {
+    if (streamRef.current) {
+      console.log('[IDCardRecognition] 停止摄像头');
+      streamRef.current.getTracks().forEach((track) => track.stop());
+      streamRef.current = null;
+    }
+  };
+
+  /**
+   * 拍照
+   */
+  const handleCapture = async () => {
+    if (!streamRef.current) {
+      message.error('摄像头未就绪');
+      return;
+    }
+
+    try {
+      console.log('[IDCardRecognition] 开始拍照...');
+
+      const cameraService = CameraServiceFactory.create();
+      const base64Image = await cameraService.capturePhoto(streamRef.current);
+
+      console.log(
+        '[IDCardRecognition] 拍照成功,图片大小:',
+        base64Image.length
+      );
+
+      setCapturedImage(base64Image);
+      stopCamera();
+
+      message.success('拍照成功');
+    } catch (err: any) {
+      console.error('[IDCardRecognition] 拍照失败:', err);
+      message.error(err.message || '拍照失败');
+    }
+  };
+
+  /**
+   * 重拍
+   */
+  const handleRetake = () => {
+    console.log('[IDCardRecognition] 重拍');
+    setCapturedImage(null);
+  };
+
+  /**
+   * 上传文件处理
+   */
+  const handleUpload = (file: File) => {
+    const reader = new FileReader();
+    reader.onload = (e) => {
+      const result = e.target?.result as string;
+      setUploadedImage(result);
+      setCapturedImage(null);
+      message.success('图片上传成功');
+    };
+    reader.readAsDataURL(file);
+    return false; // 阻止自动上传
+  };
+
+  /**
+   * 识别身份证
+   */
+  const handleRecognize = async () => {
+    const imageData = capturedImage || uploadedImage;
+    if (!imageData) {
+      message.error('请先拍照或上传身份证图片');
+      return;
+    }
+
+    try {
+      setIsRecognizing(true);
+
+      console.log('[IDCardRecognition] 开始识别身份证...');
+
+      // 上传图片到后端进行识别
+      const response = await axiosInstance.post('/auth/task/ocr/id_card', {
+        type: 'png',
+        data: imageData.replace(/^data:image\/[a-z]+;base64,/, ''),
+      });
+
+      if (response.data.code !== '0x000000') {
+        throw new Error(response.data.description || '识别失败');
+      }
+
+      const recognitionData: IDCardData = response?.data?.data;
+
+      console.log('[IDCardRecognition] 识别成功:', recognitionData);
+
+      message.success('身份证识别成功');
+      await onRecognitionSuccess(recognitionData);
+      handleClose();
+    } catch (err: any) {
+      console.error('[IDCardRecognition] 识别失败:', err);
+      message.error(
+        err.response?.data?.description || err.message || '识别失败'
+      );
+    } finally {
+      setIsRecognizing(false);
+    }
+  };
+
+  /**
+   * 关闭 Modal
+   */
+  const handleClose = () => {
+    console.log('[IDCardRecognition] 关闭 Modal');
+    stopCamera();
+    setCapturedImage(null);
+    setUploadedImage(null);
+    onClose();
+  };
+
+  const uploadProps = {
+    beforeUpload: handleUpload,
+    showUploadList: false,
+    accept: 'image/*',
+  };
+
+  return (
+    <Modal
+      title={
+        <Space>
+          <IdcardOutlined />
+          身份证识别
+        </Space>
+      }
+      open={visible}
+      onCancel={handleClose}
+      footer={null}
+      width={800}
+      centered
+      destroyOnClose
+    >
+      <div style={{ textAlign: 'center' }}>
+        {/* 模式选择 */}
+        <Space style={{ marginBottom: 16 }}>
+          <Button
+            type={mode === 'camera' ? 'primary' : 'default'}
+            onClick={() => setMode('camera')}
+          >
+            自动拍照
+          </Button>
+          <Button
+            type={mode === 'upload' ? 'primary' : 'default'}
+            onClick={() => setMode('upload')}
+          >
+            手动上传
+          </Button>
+        </Space>
+
+        {/* 摄像头模式 */}
+        {mode === 'camera' && (
+          <div style={{ position: 'relative' }}>
+            {!capturedImage && (
+              <>
+                <video
+                  ref={videoRef}
+                  autoPlay
+                  playsInline
+                  muted
+                  style={{
+                    width: '100%',
+                    maxHeight: '400px',
+                    backgroundColor: '#000',
+                    borderRadius: 8,
+                    display: isInitializing ? 'none' : 'block',
+                  }}
+                />
+
+                {isInitializing && (
+                  <div
+                    style={{
+                      width: '100%',
+                      height: '400px',
+                      display: 'flex',
+                      alignItems: 'center',
+                      justifyContent: 'center',
+                      backgroundColor: '#000',
+                      borderRadius: 8,
+                    }}
+                  >
+                    <Spin size="large" tip="正在初始化摄像头..." />
+                  </div>
+                )}
+
+                {!isInitializing && (
+                  <Space style={{ marginTop: 16 }} size="large">
+                    <Button
+                      type="primary"
+                      size="large"
+                      icon={<CameraOutlined />}
+                      onClick={handleCapture}
+                    >
+                      拍照
+                    </Button>
+                    <Button
+                      size="large"
+                      icon={<CloseOutlined />}
+                      onClick={handleClose}
+                    >
+                      取消
+                    </Button>
+                  </Space>
+                )}
+              </>
+            )}
+
+            {capturedImage && (
+              <>
+                <img
+                  src={capturedImage}
+                  alt="拍摄的身份证"
+                  style={{
+                    width: '100%',
+                    maxHeight: '400px',
+                    borderRadius: 8,
+                    objectFit: 'contain',
+                  }}
+                />
+                <Space style={{ marginTop: 16 }} size="large">
+                  <Button
+                    type="primary"
+                    size="large"
+                    icon={<IdcardOutlined />}
+                    onClick={handleRecognize}
+                    loading={isRecognizing}
+                  >
+                    识别身份证
+                  </Button>
+                  <Button
+                    size="large"
+                    icon={<ReloadOutlined />}
+                    onClick={handleRetake}
+                    disabled={isRecognizing}
+                  >
+                    重拍
+                  </Button>
+                  <Button
+                    size="large"
+                    icon={<CloseOutlined />}
+                    onClick={handleClose}
+                    disabled={isRecognizing}
+                  >
+                    取消
+                  </Button>
+                </Space>
+              </>
+            )}
+          </div>
+        )}
+
+        {/* 上传模式 */}
+        {mode === 'upload' && (
+          <div>
+            {!uploadedImage ? (
+              <div
+                style={{
+                  padding: '40px',
+                  border: '2px dashed #d9d9d9',
+                  borderRadius: 8,
+                }}
+              >
+                <Upload {...uploadProps}>
+                  <Space direction="vertical">
+                    <UploadOutlined
+                      style={{ fontSize: 48, color: '#1890ff' }}
+                    />
+                    <div>点击或拖拽身份证图片到此处上传</div>
+                    <div style={{ color: '#999' }}>只支持 PNG 格式</div>
+                  </Space>
+                </Upload>
+              </div>
+            ) : (
+              <>
+                <img
+                  src={uploadedImage}
+                  alt="上传的身份证"
+                  style={{
+                    width: '100%',
+                    maxHeight: '400px',
+                    borderRadius: 8,
+                    objectFit: 'contain',
+                  }}
+                />
+                <Space style={{ marginTop: 16 }} size="large">
+                  <Button
+                    type="primary"
+                    size="large"
+                    icon={<IdcardOutlined />}
+                    onClick={handleRecognize}
+                    loading={isRecognizing}
+                  >
+                    识别身份证
+                  </Button>
+                  <Button
+                    size="large"
+                    onClick={() => setUploadedImage(null)}
+                    disabled={isRecognizing}
+                  >
+                    重新上传
+                  </Button>
+                  <Button
+                    size="large"
+                    icon={<CloseOutlined />}
+                    onClick={handleClose}
+                    disabled={isRecognizing}
+                  >
+                    取消
+                  </Button>
+                </Space>
+              </>
+            )}
+          </div>
+        )}
+      </div>
+    </Modal>
+  );
+};
+
+export default IDCardRecognitionModal;

+ 74 - 7
src/pages/patient/DiagnosticReport/components/BaseInfo.tsx

@@ -3,13 +3,24 @@ import { Row, Col, Input, Select, Form } from 'antd';
 import { useDiagnosticData } from '@/hooks/useDiagnosticData';
 import { PlusOutlined } from '@ant-design/icons';
 import { DepartmentModal } from './DepartmentModal';
-import { getDepartment } from '@/API/report/ReportActions';
+import {
+  getDepartment,
+  getExaminationPhysician,
+  getReviewPhysician,
+} 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';
+import { ExaminationPhysicianModal } from './ExaminationPhysicianModal';
+import { ReviewPhysicianModal } from './ReviewPhysicianModal';
+import { setExaminationPhysicians } from '@/states/patient/DiagnosticReport/examinationPhysicianSlice';
+import { setReviewPhysicians } from '@/states/patient/DiagnosticReport/reviewPhysicianSlice';
 export const BaseInfo: React.FC = () => {
   const [departmentOpen, setDepartmentOpen] = useState(false);
+  const [examinationPhysicianOpen, setExaminationPhysicianOpen] =
+    useState(false);
+  const [reviewPhysicianOpen, setReviewPhysicianOpen] = useState(false);
   const [name, setName] = useDiagnosticData<string>('headers.name');
   const [sex, setSex] = useDiagnosticData<string>('headers.sex');
   const [age, setAge] = useDiagnosticData<string>('headers.age');
@@ -35,6 +46,12 @@ export const BaseInfo: React.FC = () => {
   const departments = useAppSelector(
     (state: RootState) => state.department.departments
   );
+  const examinationPhysicians = useAppSelector(
+    (state: RootState) => state.examinationPhysician.names
+  );
+  const reviewPhysicians = useAppSelector(
+    (state: RootState) => state.reviewPhysician.names
+  );
   const optionSex = useAppSelector((state: RootState) => state.options.sex);
   const getDepartmentData = async () => {
     const res = await getDepartment({});
@@ -43,6 +60,20 @@ export const BaseInfo: React.FC = () => {
     }
   };
 
+  const getExaminationPhysicianData = async () => {
+    const res = await getExaminationPhysician({});
+    if (res.code === '0x000000') {
+      dispatch(setExaminationPhysicians(res?.data?.physicians || []));
+    }
+  };
+
+  const getReviewPhysicianData = async () => {
+    const res = await getReviewPhysician({});
+    if (res.code === '0x000000') {
+      dispatch(setReviewPhysicians(res?.data?.physicians || []));
+    }
+  };
+
   const getSexOption = async () => {
     const res = await getOption({ group: 'patient', flag: 'sex' });
     if (res.code === '0x000000') {
@@ -52,6 +83,8 @@ export const BaseInfo: React.FC = () => {
 
   useEffect(() => {
     getDepartmentData();
+    getExaminationPhysicianData();
+    getReviewPhysicianData();
     getSexOption();
   }, []);
 
@@ -85,7 +118,7 @@ export const BaseInfo: React.FC = () => {
           </Form.Item>
         </Col> */}
           <Col span={6}>
-            <Form.Item label="患者编号">
+            <Form.Item label="病案号">
               <Input
                 disabled
                 value={medical_record_number}
@@ -187,20 +220,46 @@ export const BaseInfo: React.FC = () => {
           </Col>
 
           <Col span={6}>
-            <Form.Item label="放射科医生">
-              <Input
+            <Form.Item label="检查医师">
+              <Select
+                style={{ width: '90%' }}
                 value={radiologist}
-                onChange={(e) => setRadiologist(e.target.value)}
+                fieldNames={{ label: 'name', value: 'name' }}
+                onChange={(v) => setRadiologist(v)}
+                options={examinationPhysicians}
               />
             </Form.Item>
+            <PlusOutlined
+              style={{
+                position: 'absolute',
+                top: '40px',
+                right: '10px',
+                cursor: 'pointer',
+                transition: 'all 0.3s',
+              }}
+              onClick={() => setExaminationPhysicianOpen(true)}
+            />
           </Col>
           <Col span={6}>
             <Form.Item label="审核医师">
-              <Input
+              <Select
+                style={{ width: '90%' }}
                 value={reviewPhysician}
-                onChange={(e) => setReviewPhysician(e.target.value)}
+                fieldNames={{ label: 'name', value: 'name' }}
+                onChange={(v) => setReviewPhysician(v)}
+                options={reviewPhysicians}
               />
             </Form.Item>
+            <PlusOutlined
+              style={{
+                position: 'absolute',
+                top: '40px',
+                right: '10px',
+                cursor: 'pointer',
+                transition: 'all 0.3s',
+              }}
+              onClick={() => setReviewPhysicianOpen(true)}
+            />
           </Col>
         </Row>
       </Form>
@@ -208,6 +267,14 @@ export const BaseInfo: React.FC = () => {
         visible={departmentOpen}
         onClose={() => setDepartmentOpen(false)}
       />
+      <ExaminationPhysicianModal
+        visible={examinationPhysicianOpen}
+        onClose={() => setExaminationPhysicianOpen(false)}
+      />
+      <ReviewPhysicianModal
+        visible={reviewPhysicianOpen}
+        onClose={() => setReviewPhysicianOpen(false)}
+      />
     </>
   );
 };

+ 147 - 0
src/pages/patient/DiagnosticReport/components/ExaminationPhysicianModal.tsx

@@ -0,0 +1,147 @@
+import React, { useEffect, useState } from 'react';
+import { Modal, Button, Input, message, Form } from 'antd';
+import { RootState, useAppDispatch, useAppSelector } from '@/states/store';
+import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
+
+import {
+  createdExaminationPhysician,
+  getExaminationPhysician,
+} from '@/API/report/ReportActions';
+import useIntl from 'react-intl/src/components/useIntl';
+import { setExaminationPhysicians } from '@/states/patient/DiagnosticReport/examinationPhysicianSlice';
+
+interface ExaminationPhysicianModalProps {
+  visible: boolean;
+  onClose: () => void;
+}
+
+export const ExaminationPhysicianModal: React.FC<
+  ExaminationPhysicianModalProps
+> = ({ visible, onClose }) => {
+  const intl = useIntl();
+  const dispatch = useAppDispatch();
+  const [form] = Form.useForm();
+  const [submitting, setSubmitting] = useState(false);
+  const examinationPhysicians = useAppSelector(
+    (state: RootState) => state.examinationPhysician.names
+  );
+
+  const getExaminationPhysicianData = async () => {
+    const res = await getExaminationPhysician({});
+    if (res.code === '0x000000') {
+      dispatch(setExaminationPhysicians(res?.data?.physicians || []));
+    }
+  };
+  const onFinish = async (values: { names: string[] }) => {
+    // 防止重复提交
+    if (submitting) return;
+
+    setSubmitting(true);
+    try {
+      const res = await createdExaminationPhysician(values);
+      if (res.code === '0x000000') {
+        await getExaminationPhysicianData();
+        onClose();
+        message.success(intl.formatMessage({ id: 'report.saveSuccess' }));
+      } else {
+        message.error(intl.formatMessage({ id: 'report.saveFail' }));
+      }
+    } catch {
+      message.error(intl.formatMessage({ id: 'report.saveFail' }));
+    } finally {
+      setSubmitting(false);
+    }
+  };
+
+  useEffect(() => {
+    if (visible) {
+      form.setFieldsValue({
+        names: examinationPhysicians.map((k) => k.name),
+      });
+    }
+  }, [visible]);
+
+  return (
+    <Modal
+      title={intl.formatMessage({
+        id: 'report.examinationPhysician.title',
+      })}
+      centered
+      closable={false}
+      open={visible}
+      maskClosable={false}
+      destroyOnHidden
+      onCancel={onClose}
+      okText={intl.formatMessage({ id: 'report.department.okButtonText' })}
+      cancelText={intl.formatMessage({
+        id: 'report.department.cancelButtonText',
+      })}
+      styles={{ body: { maxHeight: '50vh', overflow: 'auto' } }}
+      okButtonProps={{
+        autoFocus: true,
+        htmlType: 'submit',
+        loading: submitting,
+        style: { marginRight: '50px' },
+      }}
+      modalRender={(dom) => (
+        <Form layout="vertical" form={form} clearOnDestroy onFinish={onFinish}>
+          {dom}
+        </Form>
+      )}
+    >
+      <Form.List name="names">
+        {(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: intl.formatMessage({
+                        id: 'report.examinationPhysician.inputEmptyCheck',
+                      }),
+                    },
+                  ]}
+                  noStyle
+                >
+                  <Input
+                    placeholder={intl.formatMessage({
+                      id: 'report.examinationPhysician.placeholder',
+                    })}
+                    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 />}
+              >
+                {intl.formatMessage({ id: 'report.department.addButtonText' })}
+              </Button>
+            </Form.Item>
+          </>
+        )}
+      </Form.List>
+    </Modal>
+  );
+};

+ 148 - 0
src/pages/patient/DiagnosticReport/components/ReviewPhysicianModal.tsx

@@ -0,0 +1,148 @@
+import React, { useEffect, useState } from 'react';
+import { Modal, Button, Input, message, Form } from 'antd';
+import { RootState, useAppDispatch, useAppSelector } from '@/states/store';
+import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
+
+import {
+  createdReviewPhysician,
+  getReviewPhysician,
+} from '@/API/report/ReportActions';
+import useIntl from 'react-intl/src/components/useIntl';
+import { setReviewPhysicians } from '@/states/patient/DiagnosticReport/reviewPhysicianSlice';
+
+interface ReviewPhysicianModalProps {
+  visible: boolean;
+  onClose: () => void;
+}
+
+export const ReviewPhysicianModal: React.FC<ReviewPhysicianModalProps> = ({
+  visible,
+  onClose,
+}) => {
+  const intl = useIntl();
+  const dispatch = useAppDispatch();
+  const [form] = Form.useForm();
+  const [submitting, setSubmitting] = useState(false);
+  const reviewPhysicians = useAppSelector(
+    (state: RootState) => state.reviewPhysician.names
+  );
+
+  const getReviewPhysicianData = async () => {
+    const res = await getReviewPhysician({});
+    if (res.code === '0x000000') {
+      dispatch(setReviewPhysicians(res?.data?.physicians || []));
+    }
+  };
+  const onFinish = async (values: { names: string[] }) => {
+    // 防止重复提交
+    if (submitting) return;
+
+    setSubmitting(true);
+    try {
+      const res = await createdReviewPhysician(values);
+      if (res.code === '0x000000') {
+        await getReviewPhysicianData();
+        onClose();
+        message.success(intl.formatMessage({ id: 'report.saveSuccess' }));
+      } else {
+        message.error(intl.formatMessage({ id: 'report.saveFail' }));
+      }
+    } catch {
+      message.error(intl.formatMessage({ id: 'report.saveFail' }));
+    } finally {
+      setSubmitting(false);
+    }
+  };
+
+  useEffect(() => {
+    if (visible) {
+      form.setFieldsValue({
+        names: reviewPhysicians.map((k) => k.name),
+      });
+    }
+  }, [visible]);
+
+  return (
+    <Modal
+      title={intl.formatMessage({
+        id: 'report.reviewPhysician.title',
+      })}
+      centered
+      closable={false}
+      open={visible}
+      maskClosable={false}
+      destroyOnHidden
+      onCancel={onClose}
+      okText={intl.formatMessage({ id: 'report.department.okButtonText' })}
+      cancelText={intl.formatMessage({
+        id: 'report.department.cancelButtonText',
+      })}
+      styles={{ body: { maxHeight: '50vh', overflow: 'auto' } }}
+      okButtonProps={{
+        autoFocus: true,
+        htmlType: 'submit',
+        loading: submitting,
+        style: { marginRight: '50px' },
+      }}
+      modalRender={(dom) => (
+        <Form layout="vertical" form={form} clearOnDestroy onFinish={onFinish}>
+          {dom}
+        </Form>
+      )}
+    >
+      <Form.List name="names">
+        {(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: intl.formatMessage({
+                        id: 'report.examinationPhysician.inputEmptyCheck',
+                      }),
+                    },
+                  ]}
+                  noStyle
+                >
+                  <Input
+                    placeholder={intl.formatMessage({
+                      id: 'report.examinationPhysician.placeholder',
+                    })}
+                    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 />}
+              >
+                {intl.formatMessage({ id: 'report.department.addButtonText' })}
+              </Button>
+            </Form.Item>
+          </>
+        )}
+      </Form.List>
+    </Modal>
+  );
+};

+ 247 - 168
src/pages/system/SettingsModal/sections/Preferences/Workflow.tsx

@@ -1,19 +1,84 @@
 /**
  * 工作流 - 工作流设置组件
  */
-import React, { useState } from 'react';
-import { Form, Card, Switch, Input, InputNumber, Radio, Checkbox, Table, Button, Space, Divider } from 'antd';
+import React, { useEffect, useState } from 'react';
+import {
+  Form,
+  Card,
+  Switch,
+  Input,
+  InputNumber,
+  Radio,
+  Checkbox,
+  Table,
+  Button,
+  Space,
+  Divider,
+  Row,
+  Col,
+  message,
+} from 'antd';
 import { SPACING } from '../../constants';
+import { getConfig, modifyConfig } from '@/API/system/options';
 
 const { Group: RadioGroup } = Radio;
 
 // 字段配置数据类型
 interface FieldConfig {
-  key: string;
+  Order: string;
   field: string;
-  visible: boolean;
+  FieldName: string;
+  Visible: boolean;
 }
 
+const listFieldBase: FieldConfig[] = [
+  { key: '1', field: '动物ID', FieldName: 'patient_id', visible: true },
+  { key: '2', field: '动物名称', FieldName: 'patient_id', visible: true },
+  { key: '3', field: '动物类型', FieldName: 'patient_id', visible: true },
+  { key: '4', field: '性别', FieldName: 'patient_id', visible: true },
+  { key: '5', field: '年龄', FieldName: 'patient_id', visible: true },
+  { key: '6', field: '检查描述', FieldName: 'patient_id', visible: true },
+  { key: '7', field: '检查时间', FieldName: 'patient_id', visible: true },
+  { key: '8', field: '主人', FieldName: 'patient_id', visible: true },
+  { key: '9', field: '登记号', FieldName: 'patient_id', visible: true },
+];
+
+const initValues = {
+  // 是否自动跳转到下个视图
+  'Inspection/AutoAcceptImage': false,
+  // 是否开启自动选择曝光模式
+  'Exam/AutoSelectExposureMode': false,
+  // 自动跳转到下个视图延迟时间
+  'Inspection/AutoJumpToNextView': 0,
+  suggestAPRTable: false,
+  allowMergeTasks: false,
+  allowEditAfterAcquisition: false,
+  // 是否要求拒绝原因
+  'Inspection/RequireRejectReason': false,
+  // 是否显示拒绝图片
+  'SystemConfig/ShowRejectImage': false,
+  smallAnimalExposureMode: 'bodyThickness',
+  // 是否显示电池状态
+  'Display/BatteryStatusEnable': false,
+  tubePower: false,
+  // 是否显示温度状态
+  'Display/TemperatureStatusEnable': false,
+  detectorPower: false,
+  // 是否显示WIFI状态
+  'Display/WIFIStatusEnable': false,
+  detectorTemp: false,
+  // 是否显示真实渲染图像
+  'SystemConfig/IsShowRealImage': false,
+  // 是否显示解刨图像
+  'SystemConfig/IsShowAnatomicalImage': false,
+  // 是否显示曝光指示器
+  'Exam/ShowExposureIndicator': false,
+  // 导出图像模式
+  exportPathMode: 'default',
+  // 导出图像路径
+  'System/ExportDirectory': '',
+  doseAdjustmentEnabled: false,
+};
 /**
  * 工作流组件
  * 实现图像采集、多任务管理、APR编辑等设置功能
@@ -22,38 +87,26 @@ const Workflow: React.FC = () => {
   const [form] = Form.useForm();
 
   // 任务清单显示配置数据
-  const [taskListConfig, setTaskListConfig] = useState<FieldConfig[]>([
-    { key: '1', field: '动物ID', visible: true },
-    { key: '2', field: '动物名称', visible: true },
-    { key: '3', field: '动物类型', visible: true },
-    { key: '4', field: '性别', visible: true },
-    { key: '5', field: '年龄', visible: true },
-    { key: '6', field: '检查描述', visible: true },
-    { key: '7', field: '检查时间', visible: true },
-    { key: '8', field: '主人', visible: true },
-    { key: '9', field: '登记号', visible: true },
-  ]);
+  const [taskListConfig, setTaskListConfig] =
+    useState<FieldConfig[]>(listFieldBase);
 
   // 历史清单显示配置数据
-  const [historyListConfig, setHistoryListConfig] = useState<FieldConfig[]>([
-    { key: '1', field: '动物ID', visible: true },
-    { key: '2', field: '动物名称', visible: true },
-    { key: '3', field: '动物类型', visible: true },
-    { key: '4', field: '性别', visible: true },
-    { key: '5', field: '年龄', visible: true },
-  ]);
+  const [historyListConfig, setHistoryListConfig] =
+    useState<FieldConfig[]>(listFieldBase);
 
   // 表格列配置
-  const getTableColumns = (onChange: (key: string, visible: boolean) => void) => [
+  const getTableColumns = (
+    onChange: (key: string, visible: boolean) => void
+  ) => [
     {
       title: '可见',
-      dataIndex: 'visible',
-      key: 'visible',
+      dataIndex: 'Visible',
+      key: 'Visible',
       width: 80,
-      render: (visible: boolean, record: FieldConfig) => (
+      render: (visible: boolean, record: FieldConfig): React.ReactElement => (
         <Checkbox
           checked={visible}
-          onChange={(e) => onChange(record.key, e.target.checked)}
+          onChange={(e) => onChange(record.Order, e.target.checked)}
         />
       ),
     },
@@ -65,99 +118,112 @@ const Workflow: React.FC = () => {
   ];
 
   // 处理任务清单配置变更
-  const handleTaskListConfigChange = (key: string, visible: boolean) => {
-    setTaskListConfig(prev =>
-      prev.map(config =>
-        config.key === key ? { ...config, visible } : config
+  const handleTaskListConfigChange = (key: string, visible: boolean): void => {
+    setTaskListConfig((prev) =>
+      prev.map((config) =>
+        config.Order === key ? { ...config, visible } : config
       )
     );
   };
 
   // 处理历史清单配置变更
-  const handleHistoryListConfigChange = (key: string, visible: boolean) => {
-    setHistoryListConfig(prev =>
-      prev.map(config =>
-        config.key === key ? { ...config, visible } : config
+  const handleHistoryListConfigChange = (
+    key: string,
+    visible: boolean
+  ): void => {
+    setHistoryListConfig((prev) =>
+      prev.map((config) =>
+        config.Order === key ? { ...config, visible } : config
       )
     );
   };
 
+  // 获取工作流配置信息
+  const getWorkFlowData = async (): Promise<void> => {
+    const params = Object.keys(initValues).map((key) => key);
+    const res = await getConfig({
+      uri: [...params, 'Patient/WorkListFields', 'Patient/HistoryListFields'],
+    });
+    if (res?.code !== '0x000000') {
+      message.error(res?.description || '保存失败');
+      return;
+    }
+    const formValues = (res?.data?.configs || []).reduce((acc, cur) => {
+      acc[cur.uri] = cur.config_value;
+      return acc;
+    }, {});
+    const {
+      'Patient/WorkListFields': workListFields = '{}',
+      'Patient/HistoryListFields': historyListFields = '{}',
+    } = formValues;
+    setTaskListConfig(JSON.parse(workListFields));
+    setHistoryListConfig(JSON.parse(historyListFields));
+    console.log('=====================>, formValues', formValues);
+    // form.setFieldsValue(formValues);
+  };
+
   // 处理保存
-  const handleSave = () => {
-    form.validateFields().then(values => {
-      console.log('保存设置:', {
-        ...values,
-        taskListConfig,
-        historyListConfig
-      });
-      // TODO: 调用API保存设置
+  const handleSaveWorkFlow = async (): Promise<void> => {
+    const values = await form.validateFields();
+    console.log('保存设置:');
+    const res = await modifyConfig({
+      ...values,
+      'Patient/WorkListFields': JSON.stringify(taskListConfig),
+      'Patient/HistoryListFields': JSON.stringify(historyListConfig),
     });
+    if (res?.code !== '0x000000') {
+      message.error(res?.description || '保存失败');
+      return;
+    }
+    message.success('保存成功');
   };
 
   // 处理取消
-  const handleCancel = () => {
+  const handleCancel = (): void => {
     form.resetFields();
     // 重置任务清单配置到初始状态
-    setTaskListConfig([
-      { key: '1', field: '动物ID', visible: true },
-      { key: '2', field: '动物名称', visible: true },
-      { key: '3', field: '动物类型', visible: true },
-      { key: '4', field: '性别', visible: true },
-      { key: '5', field: '年龄', visible: true },
-      { key: '6', field: '检查描述', visible: true },
-      { key: '7', field: '检查时间', visible: true },
-      { key: '8', field: '主人', visible: true },
-      { key: '9', field: '登记号', visible: true },
-    ]);
+    setTaskListConfig(listFieldBase);
     // 重置历史清单配置到初始状态
-    setHistoryListConfig([
-      { key: '1', field: '动物ID', visible: true },
-      { key: '2', field: '动物名称', visible: true },
-      { key: '3', field: '动物类型', visible: true },
-      { key: '4', field: '性别', visible: true },
-      { key: '5', field: '年龄', visible: true },
-    ]);
+    setHistoryListConfig(listFieldBase);
   };
 
+  useEffect(() => {
+    // 获取系统配置
+    getWorkFlowData();
+  }, []);
+
   return (
     <div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
       {/* 主内容区 - 分割视图 */}
-      <div style={{ flex: 1, display: 'flex', gap: SPACING.LG, padding: SPACING.LG }}>
+      <div
+        style={{
+          flex: 1,
+          display: 'flex',
+          gap: SPACING.LG,
+          padding: SPACING.LG,
+        }}
+      >
         {/* 左侧面板 - 表单设置 */}
-        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: SPACING.MD, overflowY: 'auto' }}>
+        <div
+          style={{
+            flex: 1,
+            display: 'flex',
+            flexDirection: 'column',
+            gap: SPACING.MD,
+            overflowY: 'auto',
+          }}
+        >
           <Form
             form={form}
-            layout="vertical"
-            initialValues={{
-              autoJumpToNextView: false,
-              autoExposureCountdown: false,
-              jumpDelaySeconds: 0,
-              suggestAPRTable: false,
-              allowMergeTasks: false,
-              allowEditAfterAcquisition: false,
-              requireRejectionReason: false,
-              showRejectedImages: false,
-              smallAnimalExposureMode: 'bodyThickness',
-              generatorPower: false,
-              tubePower: false,
-              generatorTemp: false,
-              detectorPower: false,
-              detectorWifi: false,
-              detectorTemp: false,
-              showRealImage: false,
-              showAnatomyImage: false,
-              exposureStatusEnabled: false,
-              exportPathMode: 'default',
-              exportCustomPath: '',
-              doseAdjustmentEnabled: false,
-            }}
+            layout="horizontal"
+            initialValues={{ ...initValues }}
           >
             {/* 图像采集设置 */}
             <Card title="图像采集设置" size="small">
               <Space direction="vertical" style={{ width: '100%' }}>
                 <Form.Item
                   label="是否自动跳转到下个视图"
-                  name="autoJumpToNextView"
+                  name="Inspection/AutoAcceptImage"
                   valuePropName="checked"
                 >
                   <Switch />
@@ -165,7 +231,7 @@ const Workflow: React.FC = () => {
 
                 <Form.Item
                   label="是否开启倒计时自动曝光"
-                  name="autoExposureCountdown"
+                  name="Exam/AutoSelectExposureMode"
                   valuePropName="checked"
                 >
                   <Switch />
@@ -173,12 +239,17 @@ const Workflow: React.FC = () => {
 
                 <Form.Item
                   label="自动跳转到下个视图延迟时间(秒)"
-                  name="jumpDelaySeconds"
+                  name="Inspection/AutoJumpToNextView"
                   rules={[
-                    { type: 'number', min: 0, max: 60, message: '延迟时间必须在0-60秒之间' }
+                    {
+                      type: 'number',
+                      min: 0,
+                      max: 60,
+                      message: '延迟时间必须在0-60秒之间',
+                    },
                   ]}
                 >
-                  <InputNumber min={0} max={60} style={{ width: '100%' }} />
+                  <InputNumber min={0} max={60} />
                 </Form.Item>
 
                 <Form.Item
@@ -218,7 +289,7 @@ const Workflow: React.FC = () => {
               <Space direction="vertical" style={{ width: '100%' }}>
                 <Form.Item
                   label="当拒绝图片时需输入拒绝原因"
-                  name="requireRejectionReason"
+                  name="Inspection/RequireRejectReason"
                   valuePropName="checked"
                 >
                   <Switch />
@@ -226,7 +297,7 @@ const Workflow: React.FC = () => {
 
                 <Form.Item
                   label="是否显示拒绝图像"
-                  name="showRejectedImages"
+                  name="SystemConfig/ShowRejectImage"
                   valuePropName="checked"
                 >
                   <Switch />
@@ -249,55 +320,58 @@ const Workflow: React.FC = () => {
 
             {/* 状态栏图标 */}
             <Card title="状态栏图标" size="small">
-              <Space direction="vertical" style={{ width: '100%' }}>
-                <Form.Item
-                  label="发生器电量"
-                  name="generatorPower"
-                  valuePropName="checked"
-                >
-                  <Switch />
-                </Form.Item>
-
-                <Form.Item
-                  label="球管管电量"
-                  name="tubePower"
-                  valuePropName="checked"
-                >
-                  <Switch />
-                </Form.Item>
-
-                <Form.Item
-                  label="发生器温度"
-                  name="generatorTemp"
-                  valuePropName="checked"
-                >
-                  <Switch />
-                </Form.Item>
-
-                <Form.Item
-                  label="探测器电量"
-                  name="detectorPower"
-                  valuePropName="checked"
-                >
-                  <Switch />
-                </Form.Item>
-
-                <Form.Item
-                  label="探测器Wi-Fi"
-                  name="detectorWifi"
-                  valuePropName="checked"
-                >
-                  <Switch />
-                </Form.Item>
-
-                <Form.Item
-                  label="探测器温度"
-                  name="detectorTemp"
-                  valuePropName="checked"
-                >
-                  <Switch />
-                </Form.Item>
-              </Space>
+              <Row gutter={24}>
+                <Col span={12}>
+                  <Form.Item
+                    label="发生器电量"
+                    name="Display/BatteryStatusEnable"
+                    valuePropName="checked"
+                  >
+                    <Switch />
+                  </Form.Item>
+
+                  <Form.Item
+                    label="球管管电量"
+                    name="tubePower"
+                    valuePropName="checked"
+                  >
+                    <Switch />
+                  </Form.Item>
+
+                  <Form.Item
+                    label="发生器温度"
+                    name="Display/TemperatureStatusEnable"
+                    valuePropName="checked"
+                  >
+                    <Switch />
+                  </Form.Item>
+                </Col>
+                <Col span={12}>
+                  <Form.Item
+                    label="探测器电量"
+                    name="detectorPower"
+                    valuePropName="checked"
+                  >
+                    <Switch />
+                  </Form.Item>
+
+                  <Form.Item
+                    label="探测器Wi-Fi"
+                    name="Display/WIFIStatusEnable"
+                    valuePropName="checked"
+                  >
+                    <Switch />
+                  </Form.Item>
+
+                  <Form.Item
+                    label="探测器温度"
+                    name="detectorTemp"
+                    valuePropName="checked"
+                  >
+                    <Switch />
+                  </Form.Item>
+                </Col>
+              </Row>
             </Card>
 
             {/* 辅助定位 */}
@@ -305,7 +379,7 @@ const Workflow: React.FC = () => {
               <Space direction="vertical" style={{ width: '100%' }}>
                 <Form.Item
                   label="是否显示真实效果图"
-                  name="showRealImage"
+                  name="SystemConfig/IsShowRealImage"
                   valuePropName="checked"
                 >
                   <Switch />
@@ -313,7 +387,7 @@ const Workflow: React.FC = () => {
 
                 <Form.Item
                   label="是否显示解剖图"
-                  name="showAnatomyImage"
+                  name="SystemConfig/IsShowAnatomicalImage"
                   valuePropName="checked"
                 >
                   <Switch />
@@ -325,7 +399,7 @@ const Workflow: React.FC = () => {
             <Card title="曝光状态显示" size="small">
               <Form.Item
                 label="是否开启曝光状态显示"
-                name="exposureStatusEnabled"
+                name="Exam/ShowExposureIndicator"
                 valuePropName="checked"
               >
                 <Switch />
@@ -333,22 +407,16 @@ const Workflow: React.FC = () => {
             </Card>
 
             {/* 导出图像路径 */}
-            <Card title="导出图像路径" size="small">
+            <Card title="导出图像路径" size="small" hidden>
               <Space direction="vertical" style={{ width: '100%' }}>
-                <Form.Item
-                  label="路径选择"
-                  name="exportPathMode"
-                >
+                <Form.Item label="路径选择" name="exportPathMode">
                   <RadioGroup>
                     <Radio value="default">默认路径</Radio>
                     <Radio value="lastUsed">上一次导出路径</Radio>
                   </RadioGroup>
                 </Form.Item>
 
-                <Form.Item
-                  label="上一次导出路径"
-                  name="exportCustomPath"
-                >
+                <Form.Item label="上一次导出路径" name="System/ExportDirectory">
                   <Input placeholder="请输入导出路径" />
                 </Form.Item>
               </Space>
@@ -368,9 +436,16 @@ const Workflow: React.FC = () => {
         </div>
 
         {/* 右侧面板 - 表格配置 */}
-        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: SPACING.MD }}>
+        <div
+          style={{
+            flex: 1,
+            display: 'flex',
+            flexDirection: 'column',
+            gap: SPACING.MD,
+          }}
+        >
           {/* 任务清单显示配置 */}
-          <Card title="任务清单显示配置" size="small" style={{ flex: 1 }}>
+          <Card title="任务清单显示配置" size="small">
             <Table
               columns={getTableColumns(handleTaskListConfigChange)}
               dataSource={taskListConfig}
@@ -381,28 +456,32 @@ const Workflow: React.FC = () => {
           </Card>
 
           {/* 历史清单显示配置 */}
-          <Card title="历史清单显示配置" size="small" style={{ flex: 1 }}>
+          <Card title="历史清单显示配置" size="small">
             <Table
               columns={getTableColumns(handleHistoryListConfigChange)}
               dataSource={historyListConfig}
               pagination={false}
               size="small"
-              scroll={{ y: 200 }}
+              scroll={{ y: 400 }}
             />
           </Card>
         </div>
       </div>
 
       {/* 底部操作区 */}
-      <div style={{
-        padding: SPACING.LG,
-        borderTop: '1px solid #f0f0f0',
-        display: 'flex',
-        justifyContent: 'flex-end',
-        gap: SPACING.MD
-      }}>
+      <div
+        style={{
+          padding: SPACING.LG,
+          borderTop: '1px solid #f0f0f0',
+          display: 'flex',
+          justifyContent: 'flex-end',
+          gap: SPACING.MD,
+        }}
+      >
         <Button onClick={handleCancel}>取消</Button>
-        <Button type="primary" onClick={handleSave}>保存</Button>
+        <Button type="primary" onClick={handleSaveWorkFlow}>
+          保存
+        </Button>
       </div>
     </div>
   );

+ 25 - 0
src/states/patient/DiagnosticReport/examinationPhysicianSlice.ts

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

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

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

+ 4 - 0
src/states/store.ts

@@ -79,6 +79,8 @@ 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 examinationPhysicianReducer from './patient/DiagnosticReport/examinationPhysicianSlice';
+import reviewPhysicianReducer from './patient/DiagnosticReport/reviewPhysicianSlice';
 import optionsReducer from './system/optionSlice';
 
 import {
@@ -185,6 +187,8 @@ const store = configureStore({
     // annotation: annotationReducer,
     versionUpdate: versionUpdateReducer,
     department: departmentReducer,
+    examinationPhysician: examinationPhysicianReducer,
+    reviewPhysician: reviewPhysicianReducer,
     options: optionsReducer,
     imageLoading: imageLoadingReducer,
   },