Przeglądaj źródła

feat (1.66.4 -> 1.67.0): 添加系统模式选择功能,从master分支的 e2dfe5c5b4e725456e8f9066cebccedab1647bfb 和 de2ef53d4276730fa146726a51df04f648f186f7 提交合并而来

- 在系统设置中新增系统模式选择,包括演示模式、真机模式(发生器受控)和真机模式(发生器不受控)
- 新增SystemMode组件,支持系统模式配置和保存
- 更新多语言文件,添加系统模式相关翻译
- 修改modifyConfig API参数类型,支持number和boolean
- 调整注册表单患者编号标签,从患者编号/病案号改为患者编号
- 优化网络设置提示文本
- 移除报告医师管理相关翻译(可能已废弃)

改动文件:
- scripts/output/i18n/en.js
- scripts/output/i18n/zh.js
- src/API/system/options.ts
- src/assets/i18n/messages/en.js
- src/assets/i18n/messages/zh.js
- src/pages/system/SettingsModal/config.tsx
- src/pages/system/SettingsModal/sections/Network/ip.tsx
- src/pages/system/SettingsModal/sections/SystemHome/index.tsx
- src/pages/system/SettingsModal/sections/SystemHome/SystemMode.tsx
dengdx 1 dzień temu
rodzic
commit
3163bcb831

+ 30 - 0
CHANGELOG.md

@@ -2,6 +2,36 @@
 
 本项目的所有重要变更都将记录在此文件中.
 
+## [1.67.0] - 2026-01-16 16:36
+
+feat (1.66.4 -> 1.67.0): 添加系统模式选择功能
+
+- 在系统设置中新增系统模式选择,包括演示模式、真机模式(发生器受控)和真机模式(发生器不受控)
+
+- 新增SystemMode组件,支持系统模式配置和保存
+
+- 更新多语言文件,添加系统模式相关翻译
+
+- 修改modifyConfig API参数类型,支持number和boolean
+
+- 调整注册表单患者编号标签,从"患者编号/病案号"改为"患者编号"
+
+- 优化网络设置提示文本
+
+- 移除报告医师管理相关翻译(可能已废弃)
+
+改动文件:
+
+- scripts/output/i18n/en.js
+- scripts/output/i18n/zh.js
+- src/API/system/options.ts
+- src/assets/i18n/messages/en.js
+- src/assets/i18n/messages/zh.js
+- src/pages/system/SettingsModal/config.tsx
+- src/pages/system/SettingsModal/sections/Network/ip.tsx
+- src/pages/system/SettingsModal/sections/SystemHome/index.tsx
+- src/pages/system/SettingsModal/sections/SystemHome/SystemMode.tsx
+
 ## [1.66.4] - 2026-1-16 16:01
 
 fix (1.66.3 -> 1.66.4): 修复二次扫码注册时年龄计算出生日未同步到slice的问题

+ 1 - 1
package.json

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

+ 14 - 1
scripts/output/i18n/en.js

@@ -391,5 +391,18 @@
   "exam.exitFeedback.message": "The current examination exposure steps have not been completed. Progress will not be saved after cancellation.",
   "exam.exitFeedback.suspend": "Suspend",
   "exam.exitFeedback.complete": "Complete",
-  "exam.exitFeedback.cancel": "Cancel"
+  "exam.exitFeedback.cancel": "Cancel",
+  "systemMode.title": "System Mode",
+  "systemMode.R": "Demo Mode",
+  "systemMode.S": "Live Mode (Generator Controlled)",
+  "systemMode.R-S": "Live Mode (Generator Uncontrolled)",
+  "systemMode.hint": "Changing system mode requires saving configuration and restarting the software to take effect",
+  "systemMode.save": "Save",
+  "systemMode.loadFailed": "Failed to load system mode",
+  "systemMode.selectRequired": "Please select a system mode",
+  "systemMode.saveSuccess": "System mode saved successfully, restart software to take effect",
+  "systemMode.saveFailed": "Failed to save system mode",
+  "systemMode.R.desc": "Both generator and FPD are virtual",
+  "systemMode.S.desc": "Both generator and FPD are real hardware",
+  "systemMode.R-S.desc": "FPD is real hardware, generator is virtual"
 }

+ 16 - 3
scripts/output/i18n/zh.js

@@ -44,8 +44,8 @@
   "searchPanel.startDate": "开始日期",
   "searchPanel.endDate": "结束日期",
   "searchPanel.search": "查询",
-  "register.patientId": "患者编号/病案号",
-  "register.patientId.placeholder": "请输入患者编号/病案号",
+  "register.patientId": "患者编号",
+  "register.patientId.placeholder": "请输入患者编号",
   "animal.register.patientId.placeholder": "请输入宠物编号",
   "register.patientName": "患者姓名",
   "register.patientName.placeholder": "请输入患者姓名",
@@ -391,5 +391,18 @@
   "exam.exitFeedback.message": "当前检查的曝光步骤尚未完成。中止后,本次检查的进度将不会保存。",
   "exam.exitFeedback.suspend": "挂起",
   "exam.exitFeedback.complete": "完成",
-  "exam.exitFeedback.cancel": "取消"
+  "exam.exitFeedback.cancel": "取消",
+  "systemMode.title": "系统模式",
+  "systemMode.R": "演示模式",
+  "systemMode.S": "真机模式(发生器受控)",
+  "systemMode.R-S": "真机模式(发生器不受控)",
+  "systemMode.hint": "切换系统模式,需保存配置并重启软件才能生效",
+  "systemMode.save": "保存",
+  "systemMode.loadFailed": "加载系统模式失败",
+  "systemMode.selectRequired": "请选择系统模式",
+  "systemMode.saveSuccess": "系统模式保存成功,重启软件后生效",
+  "systemMode.saveFailed": "保存系统模式失败",
+  "systemMode.R.desc": "发生器、FPD均为虚拟",
+  "systemMode.S.desc": "发生器、FPD均为真实硬件",
+  "systemMode.R-S.desc": "FPD真实、发生器虚拟"
 }

+ 1 - 1
src/API/system/options.ts

@@ -55,7 +55,7 @@ export const getConfig = async (params: {
 
 // 修改配置项
 export const modifyConfig = async (
-  data: Record<string, string>
+  data: Record<string, string | number | boolean>
 ): Promise<configResponse> => {
   const response = await axiosInstance.post('/auth/resource/config', data);
   return response.data;

+ 15 - 8
src/assets/i18n/messages/en.js

@@ -44,9 +44,9 @@ export default {
   "searchPanel.startDate": "Start Date",
   "searchPanel.endDate": "End Date",
   "searchPanel.search": "Search",
-  "register.patientId": "Patient ID/Medical Record Number",
+  "register.patientId": "Patient ID",
   "animal.register.patientId": "pet ID",
-  "register.patientId.placeholder": "Enter patient ID/Medical Record Number",
+  "register.patientId.placeholder": "Enter patient ID",
   "animal.register.patientId.placeholder": "Enter pet ID",
   "register.patientName": "Patient Name",
   "animal.register.patientName": "pet nickname",
@@ -294,12 +294,6 @@ 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",
@@ -400,4 +394,17 @@ export default {
   "exam.exitFeedback.suspend": "Suspend",
   "exam.exitFeedback.complete": "Complete",
   "exam.exitFeedback.cancel": "Cancel",
+  'systemMode.title': 'System Mode',
+  'systemMode.R': 'Demo Mode',
+  'systemMode.S': 'Live Mode (Generator Controlled)',
+  'systemMode.R-S': 'Live Mode (Generator Uncontrolled)',
+  'systemMode.hint': 'Changing system mode requires saving configuration and restarting the software to take effect',
+  'systemMode.save': 'Save',
+  'systemMode.loadFailed': 'Failed to load system mode',
+  'systemMode.selectRequired': 'Please select a system mode',
+  'systemMode.saveSuccess': 'System mode saved successfully, restart software to take effect',
+  'systemMode.saveFailed': 'Failed to save system mode',
+  'systemMode.R.desc': 'Both generator and FPD are virtual',
+  'systemMode.S.desc': 'Both generator and FPD are real hardware',
+  'systemMode.R-S.desc': 'FPD is real hardware, generator is virtual',
 };

+ 15 - 9
src/assets/i18n/messages/zh.js

@@ -44,9 +44,9 @@ export default {
   "searchPanel.startDate": "开始日期",
   "searchPanel.endDate": "结束日期",
   "searchPanel.search": "查询",
-  "register.patientId": "患者编号/病案号",
+  "register.patientId": "患者编号",
   "animal.register.patientId": "宠物编号",
-  "register.patientId.placeholder": "请输入患者编号/病案号",
+  "register.patientId.placeholder": "请输入患者编号",
   "animal.register.patientId.placeholder": "请输入宠物编号",
   "register.patientName": "患者姓名",
   "animal.register.patientName": "宠物昵称",
@@ -294,12 +294,6 @@ 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": "添加左标记",
@@ -400,5 +394,17 @@ export default {
   "exam.exitFeedback.suspend": "挂起",
   "exam.exitFeedback.complete": "完成",
   "exam.exitFeedback.cancel": "取消",
-
+  "systemMode.title": "系统模式",
+  "systemMode.R": "演示模式",
+  "systemMode.S": "真机模式(发生器受控)",
+  "systemMode.R-S": "真机模式(发生器不受控)",
+  "systemMode.hint": "切换系统模式,需保存配置并重启软件才能生效",
+  "systemMode.save": "保存",
+  "systemMode.loadFailed": "加载系统模式失败",
+  "systemMode.selectRequired": "请选择系统模式",
+  "systemMode.saveSuccess": "系统模式保存成功,重启软件后生效",
+  "systemMode.saveFailed": "保存系统模式失败",
+  "systemMode.R.desc": "发生器、FPD均为虚拟",
+  "systemMode.S.desc": "发生器、FPD均为真实硬件",
+  "systemMode.R-S.desc": "FPD真实、发生器虚拟",
 };

+ 5 - 0
src/pages/system/SettingsModal/config.tsx

@@ -86,6 +86,11 @@ export const settingsConfig: SettingsCategory[] = [
         title: '关于系统',
         component: SystemHome.AboutSystem,
       },
+      {
+        id: 'system_mode',
+        title: '系统模式',
+        component: SystemHome.SystemMode,
+      },
     ],
   },
   {

+ 1 - 1
src/pages/system/SettingsModal/sections/Network/ip.tsx

@@ -72,7 +72,7 @@ const Ip: React.FC = () => {
         </Card>
         <Text type="secondary">
           <InfoCircleOutlined style={{ marginRight: 6 }} />
-          选择IP地址,保存后需要重启软件才能生效
+          选择IP地址,需保存配置并重启软件才能生效
         </Text>
         <div>
           <Button type="primary" onClick={saveServerInfo}>

+ 212 - 0
src/pages/system/SettingsModal/sections/SystemHome/SystemMode.tsx

@@ -0,0 +1,212 @@
+import React, { useEffect, useState } from 'react';
+import { CheckCircleOutlined, InfoCircleOutlined } from '@ant-design/icons';
+import { SPACING } from '../../constants';
+import Space from 'antd/es/space';
+import Card from 'antd/es/card';
+import Typography from 'antd/es/typography';
+import { Button, message, Spin } from 'antd';
+import { useIntl } from 'react-intl';
+import { getConfig, modifyConfig } from '@/API/system/options';
+import { theme } from 'antd';
+
+interface SystemModeOption {
+  label: string;
+  value: string;
+  SimulatorGEN: number;
+  SimulatorFPD: number;
+  desc: string;
+}
+
+const SYSTEM_MODE_OPTIONS: SystemModeOption[] = [
+  {
+    label: '演示模式',
+    value: 'S',
+    SimulatorGEN: 1,
+    SimulatorFPD: 1,
+    desc: '发生器、FPD均为虚拟',
+  },
+  {
+    label: '真机模式(发生器受控)',
+    value: 'R',
+    SimulatorGEN: 0,
+    SimulatorFPD: 0,
+    desc: '发生器、FPD均为真实硬件',
+  },
+  {
+    label: '真机模式(发生器不受控)',
+    value: 'R-S',
+    SimulatorGEN: 1,
+    SimulatorFPD: 0,
+    desc: 'FPD真实、发生器虚拟',
+  },
+];
+
+export const SystemMode: React.FC = () => {
+  const { Text } = Typography;
+  const intl = useIntl();
+  const [selectedMode, setSelectedMode] = useState<string>('');
+  const [loading, setLoading] = useState(false);
+  const [saving, setSaving] = useState(false);
+
+  const { useToken } = theme;
+  const colorPrimary = useToken().token.colorPrimary;
+  useEffect(() => {
+    loadCurrentMode();
+  }, []);
+
+  const loadCurrentMode = async () => {
+    setLoading(true);
+    try {
+      const response = await getConfig({
+        uri: ['System/SimulatorGEN', 'System/SimulatorFPD'],
+      });
+
+      const configs = response.data.configs;
+      const SimulatorGEN = parseInt(
+        configs.find((c) => c.config_key === 'SimulatorGEN')?.config_value ||
+          '0'
+      );
+      const SimulatorFPD = parseInt(
+        configs.find((c) => c.config_key === 'SimulatorFPD')?.config_value ||
+          '0'
+      );
+
+      // 根据当前值确定模式
+      const currentMode = SYSTEM_MODE_OPTIONS.find(
+        (option) =>
+          option.SimulatorGEN === SimulatorGEN &&
+          option.SimulatorFPD === SimulatorFPD
+      );
+
+      if (currentMode) {
+        setSelectedMode(currentMode.value);
+      }
+    } catch (error) {
+      console.error('加载系统模式失败:', error);
+      message.error(intl.formatMessage({ id: 'systemMode.loadFailed' }));
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const saveSystemMode = async () => {
+    const selectedOption = SYSTEM_MODE_OPTIONS.find(
+      (option) => option.value === selectedMode
+    );
+    if (!selectedOption) {
+      message.error(intl.formatMessage({ id: 'systemMode.selectRequired' }));
+      return;
+    }
+
+    setSaving(true);
+    try {
+      await modifyConfig({
+        'System/SimulatorGEN': selectedOption.SimulatorGEN,
+        'System/SimulatorFPD': selectedOption.SimulatorFPD,
+      });
+
+      message.success(intl.formatMessage({ id: 'systemMode.saveSuccess' }));
+    } catch (error) {
+      console.error('保存系统模式失败:', error);
+      message.error(intl.formatMessage({ id: 'systemMode.saveFailed' }));
+    } finally {
+      setSaving(false);
+    }
+  };
+
+  const radioOptions = SYSTEM_MODE_OPTIONS.map((option) => ({
+    ...option,
+    label: intl.formatMessage({ id: `systemMode.${option.value}` }),
+    desc: intl.formatMessage({ id: `systemMode.${option.value}.desc` }),
+  }));
+
+  const handleModeSelect = (modeValue: string) => {
+    setSelectedMode(modeValue);
+  };
+
+  return (
+    <div style={{ padding: SPACING.LG }}>
+      <Space direction="vertical" style={{ display: 'flex', width: '100%' }}>
+        <Text strong style={{ fontSize: '16px' }}>
+          {intl.formatMessage({ id: 'systemMode.title' })}
+        </Text>
+
+        {loading ? (
+          <div style={{ textAlign: 'center', padding: '20px' }}>
+            <Spin />
+          </div>
+        ) : (
+          <div style={{ display: 'flex', gap: '16px', flexWrap: 'wrap' }}>
+            {radioOptions.map((option) => (
+              <Card
+                key={option.value}
+                hoverable
+                style={{
+                  flex: '1',
+                  minWidth: '200px',
+                  cursor: 'pointer',
+                  borderColor:
+                    selectedMode === option.value ? colorPrimary : 'inherit',
+                  borderWidth: '2px',
+                  // border: `2px solid ${
+                  //   selectedMode === option.value ? colorPrimary
+                  // }`,
+
+                  transition: 'all 0.2s ease',
+                  position: 'relative',
+                }}
+                onClick={() => handleModeSelect(option.value)}
+                styles={{
+                  body: { padding: '16px' },
+                }}
+              >
+                <div
+                  style={{ display: 'flex', alignItems: 'center', gap: '8px' }}
+                >
+                  <div style={{ flex: 1 }}>
+                    <Text
+                      strong
+                      style={{
+                        display: 'block',
+                        marginBottom: '4px',
+                      }}
+                    >
+                      {option.label}
+                    </Text>
+                    <Text type="secondary" style={{ fontSize: '12px' }}>
+                      {option.desc}
+                    </Text>
+                  </div>
+                  {selectedMode === option.value && (
+                    <CheckCircleOutlined
+                      style={{
+                        color: colorPrimary,
+                        fontSize: '20px',
+                      }}
+                    />
+                  )}
+                </div>
+              </Card>
+            ))}
+          </div>
+        )}
+
+        <Text type="secondary" style={{ marginTop: '16px' }}>
+          <InfoCircleOutlined style={{ marginRight: 6 }} />
+          {intl.formatMessage({ id: 'systemMode.hint' })}
+        </Text>
+
+        <div style={{ marginTop: '16px' }}>
+          <Button
+            type="primary"
+            onClick={saveSystemMode}
+            loading={saving}
+            disabled={!selectedMode}
+          >
+            {intl.formatMessage({ id: 'systemMode.save' })}
+          </Button>
+        </div>
+      </Space>
+    </div>
+  );
+};

+ 1 - 0
src/pages/system/SettingsModal/sections/SystemHome/index.tsx

@@ -12,6 +12,7 @@ export { default as AboutSystem } from './AboutSystem';
 
 // 用户账户 - 已实现
 export { default as UserAccount } from './UserAccount';
+export { SystemMode } from './SystemMode';
 
 // 磁盘空间管理
 export const DiskSpaceManagement: React.FC = () => (