diff --git a/PROJECT_EVALUATION.md b/PROJECT_EVALUATION.md index 954415e..6242e39 100644 --- a/PROJECT_EVALUATION.md +++ b/PROJECT_EVALUATION.md @@ -129,7 +129,7 @@ | ~~`api.ts` 1324 行/352 方法~~ → 已拆分为 12 个域文件 (api/kubernetes.ts, deploy.ts, monitor.ts 等) | ✅ 已解决 | 前端 | ~~P0~~ done | | 前端 **1026 处** `any` 类型 | 类型安全缺失,重构风险高 | 前端全局 | ~~P0~~ **done** (any 1076→40, unused 25→6, console 93→1, tsc --noEmit=0) | | ~~K8s 双路由体系(集群域+Kuboard 域)~~ → code 路由统一为主,ID 路由标记 deprecated 向后兼容 | ✅ 已解决 | 后端 | ~~P1~~ done | -| 25 个 ≥500 行巨型组件 | 拆分困难 | 前端 | P1 → **Q6+Q7 done: 最大组件 ResourceVisualEditor 882L→282L (-68%) + AgentCLI 814L→521L (-36%),累计提取 11 个独立组件文件** | +| 25 个 ≥500 行巨型组件 | 拆分困难 | 前端 | P1 → **Q6+Q7+Q8 done: 最大组件 ResourceVisualEditor 882L→282L (-68%) + AgentCLI 814L→521L (-36%) + SystemSettingsTab 754L→173L (-77%),累计提取 14 个独立组件文件** | ### 3.2 🟡 中优先级 diff --git a/ROADMAP.md b/ROADMAP.md index dcb1839..5a5b019 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -479,6 +479,7 @@ services/api.ts (1324L) | U14 | ESLint 冗余 disable 指令清理(unused eslint-disable 6→0) | P3 | **done** | | Q6 | 前端巨型组件拆分(ResourceVisualEditor 882L→282L,提取 5 个独立编辑器) | P0 | **done** | | Q7 | 前端巨型组件拆分(AgentCLI 814L→521L,提取工具栏+常量+空状态+滚动按钮) | P0 | **done** | +| Q8 | 前端巨型组件拆分(SystemSettingsTab 754L→173L,提取通用/安全/LDAP 3 个子 Tab) | P0 | **done** | --- @@ -677,4 +678,35 @@ pending → in_progress → done / blocked - [x] ESLint warnings 保持 **59**(无新增) - [x] `go build` 零错误 - [x] 17/17 集成回归测试通过(零回归) + +--- + +## Q8: 前端巨型组件拆分 — SystemSettingsTab [done] + +**问题**: `SystemSettingsTab.tsx`(754 行)是 Q6/Q7 拆分后项目最大的业务组件之一,包含系统设置页面的全部逻辑:3 个子 Tab(通用设置/安全设置/LDAP 设置)的内联 JSX、审计策略表单、会话安全策略(水印/录制/剪贴板/上传/下载/命令策略/审批/超时/SSO)、LDAP 配置表单、以及 60+ 字段的 props 接口定义。单文件过大导致维护困难、代码导航低效。 + +**已完成 (2026-07-07)**: + +将 3 个子 Tab 的 JSX 内容提取为 `system-settings/` 子目录下的独立文件: + +| 新文件 | 行数 | 职责 | +|--------|------|------| +| `GeneralSettingsTab.tsx` | 154 | 通用设置 Tab(审计策略卡片 + 系统信息卡片) | +| `SecuritySettingsTab.tsx` | 309 | 安全设置 Tab(安全策略 + 会话设置:水印/录制/剪贴板/上传/下载/命令策略/审批/超时/SSO) | +| `LdapSettingsTab.tsx` | 243 | LDAP 设置 Tab(LDAP/AD 配置表单 + LdapManagementSection 转发) | + +主文件 `SystemSettingsTab.tsx` 从 **754 行降至 173 行**(-77%),仅保留:`SystemSettingsTabProps` 接口定义(60+ 字段)、`LdapFormData` 接口(被 `useLdapManagement.ts` 引用)、Tab 路由逻辑(URL searchParams 同步)、子 Tab 分发(`{subTab === "general" && }`)。 + +**提取原则**: 每个子 Tab 是自包含的函数组件,通过 `Pick` 精确声明所需的 props 子集(GeneralSettingsTab 取 9 个审计字段、SecuritySettingsTab 取 17 个安全/会话字段、LdapSettingsTab 取全部 LDAP + 管理透传字段)。主文件通过 `{...props}` 展开传递,零逻辑变更。 + +**验收标准**: +- [x] SystemSettingsTab.tsx 从 754 行降至 **173 行**(-77%) +- [x] 3 个提取的子 Tab 文件全部 < 310 行(最大 SecuritySettingsTab 309 行) +- [x] `tsc -b` 零错误 +- [x] `npm run build` 零错误(✓ built in 17.71s) +- [x] ESLint errors 保持 **0**(无新增) +- [x] ESLint warnings 保持 **59**(无新增) +- [x] `go build` 零错误 +- [x] 17/17 集成回归测试通过(零回归) - [x] Docker 部署 healthy +- [x] 系统设置页 3 个 Tab 功能不变(通用/安全/LDAP 全部可切换) diff --git a/TDD_PLAN.md b/TDD_PLAN.md index 48f61bc..176ff7e 100644 --- a/TDD_PLAN.md +++ b/TDD_PLAN.md @@ -1151,3 +1151,54 @@ python3 /srv/aiops/tests/integration_test.py --host 127.0.0.1 --port 8080 | tail - [x] U6: `npm run build` 零错误(✓ built in 17.50s) - [x] U7: ESLint errors=0(无新增) - [x] U8: 17/17 集成回归测试通过(零回归) + +--- + +## P0-Q8: 前端巨型组件拆分 — SystemSettingsTab + +### 验证用例 + +```bash +# U1: 主文件行数 +wc -l /srv/aiops/frontend/src/components/system/SystemSettingsTab.tsx +# 期望: < 200 行 + +# U2: 子 Tab 文件存在 +ls -la /srv/aiops/frontend/src/components/system/system-settings/ +# 期望: GeneralSettingsTab.tsx, SecuritySettingsTab.tsx, LdapSettingsTab.tsx + +# U3: TypeScript 编译零错误 +cd /srv/aiops/frontend && npx tsc -b 2>&1 | grep 'error TS' | wc -l +# 期望: 0 + +# U4: Vite 构建 +npm run build 2>&1 | tail -1 +# 期望: built in Ns + +# U5: ESLint 无新增 errors +npx eslint src/components/system/SystemSettingsTab.tsx src/components/system/system-settings/ --format json 2>/dev/null | python3 -c " +import json, sys +data = json.load(sys.stdin) +errors = sum(1 for r in data for m in r.get('messages', []) if m.get('severity') == 2) +assert errors == 0, f'{errors} errors' +print('✅ ESLint errors=0') +" + +# U6: go build +cd /srv/aiops/backend && go build ./... +# 期望: 零错误 + +# U7: 集成回归 +python3 /srv/aiops/tests/integration_test.py --host 127.0.0.1 --port 8080 | tail -3 +# 期望: 17/17 passed +``` + +### 通过标准 +- [x] U1: SystemSettingsTab.tsx 从 754 行降至 **173 行**(< 200 ✅) +- [x] U2: 3 个子 Tab 文件全部存在(GeneralSettingsTab/SecuritySettingsTab/LdapSettingsTab) +- [x] U3: `tsc -b` 零错误 +- [x] U4: `npm run build` 零错误(✓ built in 17.71s) +- [x] U5: ESLint errors 保持 **0**(无新增) +- [x] U6: `go build` 零错误 +- [x] U7: 17/17 集成回归测试通过(零回归) + diff --git a/frontend/src/components/system/SystemSettingsTab.tsx b/frontend/src/components/system/SystemSettingsTab.tsx index ded84eb..7859802 100644 --- a/frontend/src/components/system/SystemSettingsTab.tsx +++ b/frontend/src/components/system/SystemSettingsTab.tsx @@ -1,37 +1,11 @@ import { useState, useEffect } from "react"; import { useSearchParams } from "react-router-dom"; -import { - Card, - CardContent, - CardHeader, - CardTitle, - CardDescription, -} from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Switch } from "@/components/ui/switch"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { - Lock, - History, - Key, - Settings, - Network, - RefreshCw, - Loader2, -} from "lucide-react"; import { PageTabs } from "@/components/layout/PageTabs"; -import { LdapManagementSection } from "./LdapManagementSection"; import type { JsonData } from "@/types/common"; -import { useTranslation } from "react-i18next"; import i18n from "@/i18n"; -import { useUserStore } from "@/store"; +import { GeneralSettingsTab } from "./system-settings/GeneralSettingsTab"; +import { SecuritySettingsTab } from "./system-settings/SecuritySettingsTab"; +import { LdapSettingsTab } from "./system-settings/LdapSettingsTab"; // 系统设置子 Tab:通用 / 安全 / LDAP // Logan.Li const SETTINGS_TABS = [ @@ -51,7 +25,7 @@ export interface LdapFormData { search_filter: string; } -interface SystemSettingsTabProps { +export interface SystemSettingsTabProps { // 审计策略 // Logan.Li auditRetentionDays: string; setAuditRetentionDays: (v: string) => void; @@ -157,44 +131,6 @@ interface SystemSettingsTabProps { } export function SystemSettingsTab(props: SystemSettingsTabProps) { - const { t } = useTranslation(); - const currentUsername = useUserStore( - (state) => state.user?.username || "admin", - ); - const { - auditRetentionDays, - setAuditRetentionDays, - auditRecordAll, - setAuditRecordAll, - sessionWatermarkEnabled, - setSessionWatermarkEnabled, - sessionRecordEnabled, - setSessionRecordEnabled, - clipboardEnabled, - setClipboardEnabled, - fileUploadEnabled, - setFileUploadEnabled, - fileDownloadEnabled, - setFileDownloadEnabled, - commandPolicyEnabled, - setCommandPolicyEnabled, - approvalRequired, - setApprovalRequired, - auditLoading, - auditApplying, - handleAuditApply, - auditLastCleanupText, - auditRetentionOptions, - ldapConfig, - ldapForm, - setLdapForm, - ldapSaving, - ldapTesting, - ldapTestResult, - handleLdapSave, - handleLdapTest, - } = props; - const [searchParams, setSearchParams] = useSearchParams(); const tabFromUrl = searchParams.get("tab") as SettingsSubTab | null; const [subTab, setSubTab] = useState( @@ -229,526 +165,9 @@ export function SystemSettingsTab(props: SystemSettingsTabProps) { onChange={changeSubTab} /> - {subTab === "general" && ( -
- - - - - {t("system.audit")} - - - {t("system.auditSettingsDesc")} - - - -
-
-

- {t("system.recordAllOps")} -

-

- {t("system.recordAllOpsDesc")} -

-
- -
-
-
-

- {t("system.logRetention")} -

-

- {t("system.logRetentionHint")} -

- {auditLastCleanupText && ( -

- {auditLastCleanupText} -

- )} -
- -
-
- -
-
-
- - - - - - {t("system.systemInfo")} - - - {t("system.systemInfoDesc")} - - - -
- - {t("system.systemVersion")} - - - v2.5.1 - -
-
- - {t("system.uptime")} - - - {t("system.uptimeValue")} - -
-
-
-
- )} - - {subTab === "security" && ( -
- - - - - {t("system.security")} - - - {t("system.securityDesc")} - - - -
-
-

- {t("system.forcePasswordComplexity")} -

-

- {t("system.forcePasswordComplexityDesc")} -

-
- -
-
-
-

- {t("system.loginLockout")} -

-

- {t("system.loginLockoutDesc")} -

-
- -
-
-
-

- {t("system.twoFactor")} -

-

- {t("system.twoFactorDesc")} -

-
- -
-
-
- - - - - - {t("system.sessionSettings")} - - - {t("system.sessionSettingsDesc")} - - - -
-
-

- {t("system.sessionWatermark")} -

-

- {t("system.sessionWatermarkDesc", { - user: currentUsername, - })} -

-
- -
-
-
-
-
- {currentUsername} - {currentUsername} - {currentUsername} - {currentUsername} -
-
-
-
-
-
-

- {t("system.sessionRecordEnabled")} -

-

- {t("system.sessionRecordEnabledDesc")} -

-
- -
-
-
-

- {t("system.clipboardEnabled")} -

-

- {t("system.clipboardEnabledDesc")} -

-
- -
-
-
-

- {t("system.fileUploadEnabled")} -

-

- {t("system.fileUploadEnabledDesc")} -

-
- -
-
-
-

- {t("system.fileDownloadEnabled")} -

-

- {t("system.fileDownloadEnabledDesc")} -

-
- -
-
-
-

- {t("system.commandPolicyEnabled")} -

-

- {t("system.commandPolicyEnabledDesc")} -

-
- -
-
-
-

- {t("system.approvalRequired")} -

-

- {t("system.approvalRequiredDesc")} -

-
- -
-
-
-
-

- {t("system.sessionTimeoutTime")} -

-

- {t("system.sessionTimeoutDesc")} -

-
- -
-
-
-

- {t("system.sso")} -

-

- {t("system.singleSignOnDesc")} -

-
- -
-
- -
- - -
- )} - - {subTab === "ldap" && ( - <> - - -
-
- - - LDAP / Active Directory - - - {t("system.ldapConfigHint")} - -
-
-
- -
-
- - - setLdapForm({ ...ldapForm, ldap_host: e.target.value }) - } - className="dg-input" - placeholder="ldap.example.com" - /> -
-
- - - setLdapForm({ - ...ldapForm, - ldap_port: Number(e.target.value), - }) - } - className="dg-input" - placeholder="389" - /> -
-
- - - setLdapForm({ ...ldapForm, base_dn: e.target.value }) - } - className="dg-input" - placeholder="ou=users,dc=example,dc=com" - /> -
-
- - - setLdapForm({ ...ldapForm, admin_dn: e.target.value }) - } - className="dg-input" - placeholder="cn=admin,dc=example,dc=com" - /> -
-
- - - setLdapForm({ - ...ldapForm, - admin_password: e.target.value, - }) - } - className="dg-input" - placeholder="******" - /> -
-
- - - setLdapForm({ - ...ldapForm, - search_filter: e.target.value, - }) - } - className="dg-input" - placeholder="(uid=%s)" - /> -
-
- - setLdapForm({ ...ldapForm, use_ldaps: v }) - } - className="data-[state=checked]:bg-blue-600" - /> - - {t("system.enableLdaps")} - -
-
- - {/* 测试/保存按钮 */} -
- {ldapTestResult && ( - - {ldapTestResult} - - )} -
- - -
-
-
-
- - {ldapConfig && } - - )} + {subTab === "general" && } + {subTab === "security" && } + {subTab === "ldap" && }
); } diff --git a/frontend/src/components/system/system-settings/GeneralSettingsTab.tsx b/frontend/src/components/system/system-settings/GeneralSettingsTab.tsx new file mode 100644 index 0000000..0134bbd --- /dev/null +++ b/frontend/src/components/system/system-settings/GeneralSettingsTab.tsx @@ -0,0 +1,154 @@ +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Switch } from "@/components/ui/switch"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { History, Settings, Loader2 } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import type { SystemSettingsTabProps } from "../SystemSettingsTab"; + +// 通用设置 Tab:审计策略 + 系统信息 // Logan.Li +type GeneralSettingsTabProps = Pick< + SystemSettingsTabProps, + | "auditRetentionDays" + | "setAuditRetentionDays" + | "auditRecordAll" + | "setAuditRecordAll" + | "auditLoading" + | "auditApplying" + | "handleAuditApply" + | "auditLastCleanupText" + | "auditRetentionOptions" +>; + +export function GeneralSettingsTab(props: GeneralSettingsTabProps) { + const { t } = useTranslation(); + const { + auditRetentionDays, + setAuditRetentionDays, + auditRecordAll, + setAuditRecordAll, + auditLoading, + auditApplying, + handleAuditApply, + auditLastCleanupText, + auditRetentionOptions, + } = props; + + return ( +
+ + + + + {t("system.audit")} + + + {t("system.auditSettingsDesc")} + + + +
+
+

+ {t("system.recordAllOps")} +

+

+ {t("system.recordAllOpsDesc")} +

+
+ +
+
+
+

+ {t("system.logRetention")} +

+

+ {t("system.logRetentionHint")} +

+ {auditLastCleanupText && ( +

+ {auditLastCleanupText} +

+ )} +
+ +
+
+ +
+
+
+ + + + + + {t("system.systemInfo")} + + + {t("system.systemInfoDesc")} + + + +
+ + {t("system.systemVersion")} + + + v2.5.1 + +
+
+ + {t("system.uptime")} + + + {t("system.uptimeValue")} + +
+
+
+
+ ); +} diff --git a/frontend/src/components/system/system-settings/LdapSettingsTab.tsx b/frontend/src/components/system/system-settings/LdapSettingsTab.tsx new file mode 100644 index 0000000..de4b2dc --- /dev/null +++ b/frontend/src/components/system/system-settings/LdapSettingsTab.tsx @@ -0,0 +1,243 @@ +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Switch } from "@/components/ui/switch"; +import { Network, RefreshCw, Loader2 } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import { LdapManagementSection } from "../LdapManagementSection"; +import type { SystemSettingsTabProps } from "../SystemSettingsTab"; + +// LDAP 设置 Tab:LDAP/AD 配置表单 + 组织用户管理区 // Logan.Li +// 需要转发全部 props 给 LdapManagementSection,所以接收整个 props 对象 +type LdapSettingsTabProps = Pick< + SystemSettingsTabProps, + | "ldapConfig" + | "ldapForm" + | "setLdapForm" + | "ldapSaving" + | "ldapTesting" + | "ldapTestResult" + | "handleLdapSave" + | "handleLdapTest" +> & + // LdapManagementSection 需要的透传字段 + Pick< + SystemSettingsTabProps, + | "ldapMgmtOpen" + | "setLdapMgmtOpen" + | "ldapOUs" + | "selectedOUDN" + | "setSelectedOUDN" + | "ldapUsers" + | "selectedLDAPUsers" + | "setSelectedLDAPUsers" + | "loadLdapOUs" + | "loadLdapOUUsers" + | "handleLdapImport" + | "importing" + | "importFeedback" + | "importResultOpen" + | "setImportResultOpen" + | "createOUOpen" + | "setCreateOUOpen" + | "ouForm" + | "setOuForm" + | "handleCreateOU" + | "createUserOpen" + | "setCreateUserOpen" + | "newUser" + | "setNewUser" + | "handleCreateLDAPUser" + | "ldapUserTestOpen" + | "setLdapUserTestOpen" + | "ldapTestUser" + | "setLdapTestUser" + | "ldapTestPass" + | "setLdapTestPass" + | "ldapUserTesting" + | "ldapUserTestResult" + | "handleLdapUserTest" + | "editLdapUser" + | "setEditLdapUser" + | "editLdapForm" + | "setEditLdapForm" + | "handleUpdateLdapUser" + | "handleDeleteLDAPEntry" + | "openEditLdapUser" + >; + +export function LdapSettingsTab(props: LdapSettingsTabProps) { + const { t } = useTranslation(); + const { + ldapConfig, + ldapForm, + setLdapForm, + ldapSaving, + ldapTesting, + ldapTestResult, + handleLdapSave, + handleLdapTest, + } = props; + + return ( + <> + + +
+
+ + + LDAP / Active Directory + + + {t("system.ldapConfigHint")} + +
+
+
+ +
+
+ + + setLdapForm({ ...ldapForm, ldap_host: e.target.value }) + } + className="dg-input" + placeholder="ldap.example.com" + /> +
+
+ + + setLdapForm({ + ...ldapForm, + ldap_port: Number(e.target.value), + }) + } + className="dg-input" + placeholder="389" + /> +
+
+ + + setLdapForm({ ...ldapForm, base_dn: e.target.value }) + } + className="dg-input" + placeholder="ou=users,dc=example,dc=com" + /> +
+
+ + + setLdapForm({ ...ldapForm, admin_dn: e.target.value }) + } + className="dg-input" + placeholder="cn=admin,dc=example,dc=com" + /> +
+
+ + + setLdapForm({ + ...ldapForm, + admin_password: e.target.value, + }) + } + className="dg-input" + placeholder="******" + /> +
+
+ + + setLdapForm({ + ...ldapForm, + search_filter: e.target.value, + }) + } + className="dg-input" + placeholder="(uid=%s)" + /> +
+
+ + setLdapForm({ ...ldapForm, use_ldaps: v }) + } + className="data-[state=checked]:bg-blue-600" + /> + + {t("system.enableLdaps")} + +
+
+ + {/* 测试/保存按钮 */} +
+ {ldapTestResult && ( + + {ldapTestResult} + + )} +
+ + +
+
+
+
+ + {ldapConfig && } + + ); +} diff --git a/frontend/src/components/system/system-settings/SecuritySettingsTab.tsx b/frontend/src/components/system/system-settings/SecuritySettingsTab.tsx new file mode 100644 index 0000000..089e796 --- /dev/null +++ b/frontend/src/components/system/system-settings/SecuritySettingsTab.tsx @@ -0,0 +1,309 @@ +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Switch } from "@/components/ui/switch"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Lock, Key, Loader2 } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import { useUserStore } from "@/store"; +import type { SystemSettingsTabProps } from "../SystemSettingsTab"; + +// 安全设置 Tab:安全策略 + 会话设置(水印/录制/剪贴板/上传/下载/命令策略/审批/超时/SSO) // Logan.Li +type SecuritySettingsTabProps = Pick< + SystemSettingsTabProps, + | "sessionWatermarkEnabled" + | "setSessionWatermarkEnabled" + | "sessionRecordEnabled" + | "setSessionRecordEnabled" + | "clipboardEnabled" + | "setClipboardEnabled" + | "fileUploadEnabled" + | "setFileUploadEnabled" + | "fileDownloadEnabled" + | "setFileDownloadEnabled" + | "commandPolicyEnabled" + | "setCommandPolicyEnabled" + | "approvalRequired" + | "setApprovalRequired" + | "auditLoading" + | "auditApplying" + | "handleAuditApply" +>; + +export function SecuritySettingsTab(props: SecuritySettingsTabProps) { + const { t } = useTranslation(); + const currentUsername = useUserStore( + (state) => state.user?.username || "admin", + ); + const { + sessionWatermarkEnabled, + setSessionWatermarkEnabled, + sessionRecordEnabled, + setSessionRecordEnabled, + clipboardEnabled, + setClipboardEnabled, + fileUploadEnabled, + setFileUploadEnabled, + fileDownloadEnabled, + setFileDownloadEnabled, + commandPolicyEnabled, + setCommandPolicyEnabled, + approvalRequired, + setApprovalRequired, + auditLoading, + auditApplying, + handleAuditApply, + } = props; + + return ( +
+ + + + + {t("system.security")} + + + {t("system.securityDesc")} + + + +
+
+

+ {t("system.forcePasswordComplexity")} +

+

+ {t("system.forcePasswordComplexityDesc")} +

+
+ +
+
+
+

+ {t("system.loginLockout")} +

+

+ {t("system.loginLockoutDesc")} +

+
+ +
+
+
+

+ {t("system.twoFactor")} +

+

+ {t("system.twoFactorDesc")} +

+
+ +
+
+
+ + + + + + {t("system.sessionSettings")} + + + {t("system.sessionSettingsDesc")} + + + +
+
+

+ {t("system.sessionWatermark")} +

+

+ {t("system.sessionWatermarkDesc", { + user: currentUsername, + })} +

+
+ +
+
+
+
+
+ {currentUsername} + {currentUsername} + {currentUsername} + {currentUsername} +
+
+
+
+
+
+

+ {t("system.sessionRecordEnabled")} +

+

+ {t("system.sessionRecordEnabledDesc")} +

+
+ +
+
+
+

+ {t("system.clipboardEnabled")} +

+

+ {t("system.clipboardEnabledDesc")} +

+
+ +
+
+
+

+ {t("system.fileUploadEnabled")} +

+

+ {t("system.fileUploadEnabledDesc")} +

+
+ +
+
+
+

+ {t("system.fileDownloadEnabled")} +

+

+ {t("system.fileDownloadEnabledDesc")} +

+
+ +
+
+
+

+ {t("system.commandPolicyEnabled")} +

+

+ {t("system.commandPolicyEnabledDesc")} +

+
+ +
+
+
+

+ {t("system.approvalRequired")} +

+

+ {t("system.approvalRequiredDesc")} +

+
+ +
+
+
+
+

+ {t("system.sessionTimeoutTime")} +

+

+ {t("system.sessionTimeoutDesc")} +

+
+ +
+
+
+

+ {t("system.sso")} +

+

+ {t("system.singleSignOnDesc")} +

+
+ +
+
+ +
+ + +
+ ); +}