Преглед на файлове

feat (1.65.0 -> 1.66.0): 实现系统模式选择功能并添加多语言支持

- 新增 SystemMode 组件,支持演示模式、真机模式(发生器受控/不受控)三种模式的切换
- 在系统设置模态框中添加系统模式配置选项卡,支持模式选择和保存
- 添加完整的系统模式相关多语言支持,包括英文和中文翻译
- 优化API配置接口,支持数字和布尔类型参数传递
- 更新网络IP设置提示文本,统一提示格式
- 调整Cypress测试用例的i18n处理逻辑

改动文件:
- cypress/support/mock/handlers/i18n.ts
- 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
szy преди 21 часа
родител
ревизия
e2dfe5c5b4

+ 24 - 0
CHANGELOG.md

@@ -2,6 +2,30 @@
 
 本项目的所有重要变更都将记录在此文件中.
 
+## [1.66.0] - 2026-01-12 14:47
+
+### 新增 (Added)
+
+- **实现系统模式选择功能,支持演示模式、真机模式等多种模式切换** - 在系统设置中新增系统模式选择功能,支持演示模式(发生器、FPD均为虚拟)、真机模式(发生器受控,均为真实硬件)、真机模式(发生器不受控,FPD真实发生器虚拟)三种模式切换
+  - 新增 SystemMode 组件,实现系统模式的选择和保存功能
+  - 在系统设置模态框中添加系统模式配置选项卡
+  - 添加完整的多语言支持,包括英文和中文翻译
+  - 优化API配置接口,支持数字和布尔类型参数传递
+  - 更新网络IP设置提示文本,统一提示格式
+
+**改动文件:**
+
+- cypress/support/mock/handlers/i18n.ts
+- 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.65.0] - 2026-01-09 21:30
 
 ### 新增 (Added)

+ 2 - 1
cypress/support/mock/handlers/i18n.ts

@@ -344,7 +344,8 @@ export function mockI18nSuccess(locale: 'zh_CN' | 'en_US') {
     "exam.action.toggleCamera": "打开/关闭摄像头",
     "exam.action.reject": "拒绝",
     "exam.action.restore": "恢复",
-    "exam.action.addMorePositions": "添加更多体位"
+    "exam.action.addMorePositions": "添加更多体位",
+
   } : {
     greeting: 'Hello, world!',
     name: 'John Doe',

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "zsis",
-  "version": "1.65.0",
+  "version": "1.66.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"
 }

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

@@ -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;

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

@@ -394,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',
 };

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

@@ -394,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}>

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

@@ -0,0 +1,161 @@
+import React, { useEffect, useState } from 'react';
+import { InfoCircleOutlined, QuestionCircleOutlined } 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, Radio, message, Spin, Tooltip } from 'antd';
+import { useIntl } from 'react-intl';
+import { getConfig, modifyConfig } from '@/API/system/options';
+
+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);
+
+  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` }),
+  }));
+
+  return (
+    <div style={{ padding: SPACING.LG }}>
+      <Space direction="vertical" style={{ display: 'flex' }}>
+        <Card
+          title={intl.formatMessage({ id: 'systemMode.title' })}
+          variant="borderless"
+        >
+          {loading ? (
+            <Spin size="small" />
+          ) : (
+            <Radio.Group
+              block
+              value={selectedMode}
+              onChange={(e) => setSelectedMode(e.target.value)}
+              optionType="button"
+              buttonStyle="solid"
+            >
+              {radioOptions.map((radioItem) => (
+                <Tooltip
+                  title={radioItem.desc}
+                  placement="bottom"
+                  key={radioItem.value}
+                >
+                  <Radio value={radioItem.value}>{radioItem.label}</Radio>
+                </Tooltip>
+              ))}
+            </Radio.Group>
+          )}
+        </Card>
+        <Text type="secondary">
+          <InfoCircleOutlined style={{ marginRight: 6 }} />
+          {intl.formatMessage({ id: 'systemMode.hint' })}
+        </Text>
+        <div>
+          <Button type="primary" onClick={saveSystemMode} loading={saving}>
+            {intl.formatMessage({ id: 'systemMode.save' })}
+          </Button>
+        </div>
+      </Space>
+    </div>
+  );
+};

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

@@ -10,6 +10,8 @@ export { default as SiteInfo } from './SiteInfo';
 // 关于系统 - 已实现
 export { default as AboutSystem } from './AboutSystem';
 
+export { SystemMode } from './SystemMode';
+
 // 用户账户
 export const UserAccount: React.FC = () => (
   <PlaceholderSection title="用户账户" />