From c4ec17482869fd247ce419471aa2edb88550d0f1 Mon Sep 17 00:00:00 2001 From: Stev_Wang <304865932@qq.com> Date: Fri, 12 Dec 2025 20:55:53 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=90=9B=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E9=85=8D=E7=BD=AE=E9=A1=B5=E9=83=A8=E5=88=86?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/EnvSyncModal.tsx | 414 --------------------------- src/layouts/AdminLayout.tsx | 119 +++++--- src/pages/SystemConfigPage.tsx | 65 +---- src/pages/tabs/GameConfigTab.tsx | 6 +- src/pages/tabs/SecurityConfigTab.tsx | 4 +- 5 files changed, 89 insertions(+), 519 deletions(-) delete mode 100644 src/components/EnvSyncModal.tsx diff --git a/src/components/EnvSyncModal.tsx b/src/components/EnvSyncModal.tsx deleted file mode 100644 index d6496a2..0000000 --- a/src/components/EnvSyncModal.tsx +++ /dev/null @@ -1,414 +0,0 @@ -/** - * .env配置同步功能组件 - * @author MHXY Development Team - * @version 1.0.0 - */ - -import React, { useState } from 'react'; -import { Modal, Form, Button, Space, Alert, Typography, Divider, Checkbox, Progress } from 'antd'; -import { - SyncOutlined, - FileTextOutlined, - CheckCircleOutlined, - ExclamationCircleOutlined, - DownloadOutlined -} from '@ant-design/icons'; -import { SystemConfig } from '../types/systemConfig'; -import systemConfigService from '../services/systemConfigService'; - -const { Text, Paragraph } = Typography; -const { confirm } = Modal; - -interface EnvSyncModalProps { - visible: boolean; - configs: SystemConfig[]; - onClose: () => void; - onSync?: () => void; -} - -const EnvSyncModal: React.FC = ({ - visible, - configs, - onClose, - onSync -}) => { - const [form] = Form.useForm(); - const [loading, setLoading] = useState(false); - const [progress, setProgress] = useState(0); - const [syncResult, setSyncResult] = useState<{ - success: boolean; - message: string; - details?: string[]; - } | null>(null); - - // 准备.env文件内容 - const generateEnvContent = (selectedConfigs: string[]): string => { - const envVars: Record = {}; - - // 根据选择的配置生成环境变量 - configs.forEach(config => { - if (selectedConfigs.includes(config.config_key)) { - switch (config.config_key) { - case 'jwt_secret': - envVars.JWT_SECRET = config.config_value; - break; - case 'jwt_expires_in': - envVars.JWT_EXPIRES_IN = config.config_value; - break; - case 'game_server_api': - envVars.GAME_SERVER_API = config.config_value; - break; - case 'game_server_psk': - envVars.GAME_SERVER_PSK = config.config_value; - break; - case 'game_server_timeout': - envVars.GAME_SERVER_TIMEOUT = config.config_value; - break; - case 'game_server_retry_count': - envVars.GAME_SERVER_RETRY_COUNT = config.config_value; - break; - case 'site_name': - envVars.SITE_NAME = config.config_value; - break; - case 'maintenance_mode': - envVars.MAINTENANCE_MODE = config.config_value; - break; - } - } - }); - - // 生成.env文件内容 - const envContent = [ - '# .env文件 - 由系统配置自动生成', - `# 生成时间: ${new Date().toLocaleString('zh-CN')}`, - '# 警告: 请不要手动修改此文件,系统配置变更时此文件将被覆盖', - '', - '# === 系统基本信息 ===', - `SITE_NAME="${envVars.SITE_NAME || '梦幻西游运营管理系统'}"`, - `MAINTENANCE_MODE=${envVars.MAINTENANCE_MODE || '0'}`, - '', - '# === JWT安全配置 ===', - `JWT_SECRET="${envVars.JWT_SECRET || ''}"`, - `JWT_EXPIRES_IN=${envVars.JWT_EXPIRES_IN || '24'}`, - '', - '# === 游戏服务端配置 ===', - `GAME_SERVER_API="${envVars.GAME_SERVER_API || ''}"`, - `GAME_SERVER_PSK="${envVars.GAME_SERVER_PSK || ''}"`, - `GAME_SERVER_TIMEOUT=${envVars.GAME_SERVER_TIMEOUT || '30'}`, - `GAME_SERVER_RETRY_COUNT=${envVars.GAME_SERVER_RETRY_COUNT || '3'}`, - '', - '# === 其他配置 ===', - '# 请在此处添加其他环境变量', - '' - ].join('\n'); - - return envContent; - }; - - // 执行同步操作 - const handleSync = async () => { - try { - const values = await form.validateFields(); - const selectedConfigs = Object.keys(values).filter(key => values[key]); - - if (selectedConfigs.length === 0) { - throw new Error('请至少选择一个配置项进行同步'); - } - - setLoading(true); - setProgress(0); - setSyncResult(null); - - // 模拟同步进度 - const progressSteps = [ - { percent: 20, message: '准备配置数据...' }, - { percent: 40, message: '生成.env文件内容...' }, - { percent: 60, message: '验证配置格式...' }, - { percent: 80, message: '执行同步操作...' }, - { percent: 100, message: '同步完成' } - ]; - - for (const step of progressSteps) { - await new Promise(resolve => setTimeout(resolve, 500)); - setProgress(step.percent); - } - - // 准备环境配置数据 - const envConfigs = selectedConfigs.reduce((acc, configKey) => { - const config = configs.find(c => c.config_key === configKey); - if (config) { - switch (configKey) { - case 'jwt_secret': - acc.JWT_SECRET = config.config_value; - break; - case 'jwt_expires_in': - acc.JWT_EXPIRES_IN = config.config_value; - break; - case 'game_server_api': - acc.GAME_SERVER_API = config.config_value; - break; - case 'game_server_psk': - acc.GAME_SERVER_PSK = config.config_value; - break; - } - } - return acc; - }, {} as any); - - // 调用服务同步 - const result = await systemConfigService.syncToEnvFile(envConfigs); - - setSyncResult({ - success: result.success, - message: result.message, - details: selectedConfigs - }); - - if (result.success) { - onSync?.(); - } - - } catch (error) { - setSyncResult({ - success: false, - message: error instanceof Error ? error.message : '同步失败', - details: [] - }); - } finally { - setLoading(false); - } - }; - - // 下载.env文件 - const handleDownload = () => { - const selectedConfigs = ['jwt_secret', 'jwt_expires_in', 'game_server_api', 'game_server_psk']; - const envContent = generateEnvContent(selectedConfigs); - - const blob = new Blob([envContent], { type: 'text/plain;charset=utf-8' }); - const url = URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = url; - link.download = '.env'; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - URL.revokeObjectURL(url); - - // 重置状态 - setSyncResult(null); - setProgress(0); - }; - - // 关闭模态框 - const handleClose = () => { - setSyncResult(null); - setProgress(0); - form.resetFields(); - onClose(); - }; - - // 可同步的配置项 - const configOptions = [ - { - key: 'jwt_secret', - label: 'JWT密钥', - description: 'JWT签名密钥,用于令牌验证', - sensitive: true, - envVar: 'JWT_SECRET' - }, - { - key: 'jwt_expires_in', - label: 'JWT过期时间', - description: 'JWT访问令牌有效期(小时)', - sensitive: false, - envVar: 'JWT_EXPIRES_IN' - }, - { - key: 'game_server_api', - label: '游戏服务端API', - description: '游戏服务端HTTP接口地址', - sensitive: false, - envVar: 'GAME_SERVER_API' - }, - { - key: 'game_server_psk', - label: '游戏服务端PSK', - description: '游戏服务端预共享密钥', - sensitive: true, - envVar: 'GAME_SERVER_PSK' - }, - { - key: 'game_server_timeout', - label: '请求超时时间', - description: '与游戏服务端通信超时时间(秒)', - sensitive: false, - envVar: 'GAME_SERVER_TIMEOUT' - }, - { - key: 'game_server_retry_count', - label: '重试次数', - description: 'API请求失败时的重试次数', - sensitive: false, - envVar: 'GAME_SERVER_RETRY_COUNT' - } - ]; - - // 默认选中的配置 - const defaultChecked = ['jwt_secret', 'jwt_expires_in', 'game_server_api', 'game_server_psk']; - - return ( - - - 同步配置到.env文件 - - } - open={visible} - onCancel={handleClose} - width={600} - footer={[ - , - , - - ]} - > -
- -
- - {/* 同步进度 */} - {loading && ( -
- - - 正在执行同步操作... - -
- )} - - {/* 同步结果 */} - {syncResult && ( -
- - 下载.env文件 - - ) - } - /> -
- )} - - {/* 配置选择 */} - {!syncResult?.success && ( -
- - - - {configOptions.map(option => ( -
-
- -
-
- {option.label} - {option.sensitive && ( - - 敏感 - - )} -
- - {option.description} - - - {option.envVar} - -
-
-
-
- ))} -
-
-
-
- )} - - {/* 操作说明 */} - -
-
- - 操作说明 -
-
    -
  • 同步操作将把选中的配置写入项目的.env文件
  • -
  • 敏感配置(如密钥、密码)将被加密或掩码处理
  • -
  • 建议在同步前备份现有的.env文件
  • -
  • 同步完成后需要重启应用程序以使配置生效
  • -
-
-
- ); -}; - -export default EnvSyncModal; \ No newline at end of file diff --git a/src/layouts/AdminLayout.tsx b/src/layouts/AdminLayout.tsx index aded250..98624ff 100644 --- a/src/layouts/AdminLayout.tsx +++ b/src/layouts/AdminLayout.tsx @@ -22,7 +22,7 @@ import { useAuth } from '../contexts/AuthContext'; import { useNavigate, useLocation } from 'react-router-dom'; import TabNavigation from './TabNavigation'; -const { Header, Sider, Content } = Layout; +const { Header, Sider } = Layout; const { Text } = Typography; // 定义菜单项类型 @@ -139,6 +139,7 @@ const menuItems: MenuProps['items'] = [ const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [collapsed, setCollapsed] = useState(false); const [mobileDrawerVisible, setMobileDrawerVisible] = useState(false); + const [isMobile, setIsMobile] = useState(false); const { user, logout, isLoading } = useAuth(); const navigate = useNavigate(); const location = useLocation(); @@ -151,6 +152,20 @@ const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => { } }, [user, navigate, isLoading]); + // 响应式检测 + useEffect(() => { + const checkIsMobile = () => { + setIsMobile(window.innerWidth < 768); + if (window.innerWidth >= 768) { + setMobileDrawerVisible(false); + } + }; + + checkIsMobile(); + window.addEventListener('resize', checkIsMobile); + return () => window.removeEventListener('resize', checkIsMobile); + }, []); + // 加载状态 if (isLoading) { return ( @@ -167,22 +182,6 @@ const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => { ); } - // 响应式检测 - const [isMobile, setIsMobile] = useState(false); - - useEffect(() => { - const checkIsMobile = () => { - setIsMobile(window.innerWidth < 768); - if (window.innerWidth >= 768) { - setMobileDrawerVisible(false); - } - }; - - checkIsMobile(); - window.addEventListener('resize', checkIsMobile); - return () => window.removeEventListener('resize', checkIsMobile); - }, []); - /** * 处理用户下拉菜单点击 */ @@ -331,7 +330,11 @@ const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => { {/* 菜单区域 */} -
+
= ({ children }) => { ); return ( - +
{/* 侧边栏 - 桌面端 */} {!isMobile && ( = ({ children }) => { width={240} style={{ background: token.colorBgContainer, - borderRight: `1px solid ${token.colorBorderSecondary}` + borderRight: `1px solid ${token.colorBorderSecondary}`, + position: 'fixed', + left: 0, + top: 0, // 从页面顶部开始 + bottom: 0, + zIndex: 99, + overflow: 'hidden' }} + className="admin-sider" > {siderContent} @@ -378,19 +388,22 @@ const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => { {siderContent} - - {/* 头部 */} -
+ {/* 头部导航栏 - 固定定位 */} +
{/* 左侧控制区 */} {/* 折叠按钮 */} @@ -441,22 +454,40 @@ const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
- {/* 标签导航 */} - + {/* 标签导航 - 固定在头部下方 */} +
+ +
{/* 内容区域 */} - - {children} - - - +
+ {children} +
+
+
); }; diff --git a/src/pages/SystemConfigPage.tsx b/src/pages/SystemConfigPage.tsx index e597f2d..14b07fe 100644 --- a/src/pages/SystemConfigPage.tsx +++ b/src/pages/SystemConfigPage.tsx @@ -5,15 +5,12 @@ */ import React, { useState, useEffect } from 'react'; -import { Tabs, message, Modal } from 'antd'; +import { Tabs, message } from 'antd'; import { SettingOutlined, SecurityScanOutlined, CloudServerOutlined, - HistoryOutlined, - ReloadOutlined, - SaveOutlined, - SyncOutlined + ReloadOutlined } from '@ant-design/icons'; import { SystemConfig, SaveConfigRequest } from '../types/systemConfig'; import systemConfigService from '../services/systemConfigService'; @@ -21,13 +18,8 @@ import BasicConfigTab from './tabs/BasicConfigTab'; import SecurityConfigTab from './tabs/SecurityConfigTab'; import GameConfigTab from './tabs/GameConfigTab'; import ConfigHistoryModal from './ConfigHistoryModal'; -import EnvSyncModal from '../components/EnvSyncModal'; -const { TabPane } = Tabs; - -interface SystemConfigPageProps {} - -const SystemConfigPage: React.FC = () => { +const SystemConfigPage: React.FC = () => { const [configs, setConfigs] = useState([]); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); @@ -35,7 +27,6 @@ const SystemConfigPage: React.FC = () => { const [historyModalVisible, setHistoryModalVisible] = useState(false); const [selectedConfigKey, setSelectedConfigKey] = useState(''); const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); - const [envSyncModalVisible, setEnvSyncModalVisible] = useState(false); // 加载所有配置 const loadConfigs = async () => { @@ -57,7 +48,7 @@ const SystemConfigPage: React.FC = () => { }, []); // 保存配置 - const handleSaveConfigs = async (configRequests: SaveConfigRequest[], group: string) => { + const handleSaveConfigs = async (configRequests: SaveConfigRequest[]) => { try { setSaving(true); const result = await systemConfigService.saveConfigs(configRequests); @@ -107,25 +98,14 @@ const SystemConfigPage: React.FC = () => { setHistoryModalVisible(true); }; - // 打开.env同步模态框 - const handleOpenEnvSync = () => { - setEnvSyncModalVisible(true); - }; - - // 处理.env同步完成 - const handleEnvSyncComplete = () => { - message.success('配置已成功同步到.env文件'); - loadConfigs(); - }; - // 检测未保存的更改 const handleConfigChange = () => { setHasUnsavedChanges(true); }; // 获取指定分组的配置 - const getConfigsByGroup = (group: string) => { - return configs.filter(config => config.config_group === group); + const getConfigsByGroup = (_group: string) => { + return configs.filter(config => config.config_group === _group); }; const tabItems = [ @@ -142,7 +122,7 @@ const SystemConfigPage: React.FC = () => { configs={getConfigsByGroup('basic')} loading={loading} saving={saving} - onSave={(requests) => handleSaveConfigs(requests, 'basic')} + onSave={handleSaveConfigs} onReset={handleResetConfig} onShowHistory={handleViewHistory} onConfigChange={handleConfigChange} @@ -162,7 +142,7 @@ const SystemConfigPage: React.FC = () => { configs={getConfigsByGroup('security')} loading={loading} saving={saving} - onSave={(requests) => handleSaveConfigs(requests, 'security')} + onSave={handleSaveConfigs} onReset={handleResetConfig} onShowHistory={handleViewHistory} onConfigChange={handleConfigChange} @@ -182,7 +162,7 @@ const SystemConfigPage: React.FC = () => { configs={getConfigsByGroup('game')} loading={loading} saving={saving} - onSave={(requests) => handleSaveConfigs(requests, 'game')} + onSave={handleSaveConfigs} onReset={handleResetConfig} onShowHistory={handleViewHistory} onConfigChange={handleConfigChange} @@ -206,25 +186,6 @@ const SystemConfigPage: React.FC = () => {
- -
); }; diff --git a/src/pages/tabs/GameConfigTab.tsx b/src/pages/tabs/GameConfigTab.tsx index 96b4ce2..7b70a3d 100644 --- a/src/pages/tabs/GameConfigTab.tsx +++ b/src/pages/tabs/GameConfigTab.tsx @@ -189,7 +189,7 @@ const GameConfigTab: React.FC = ({
{/* 游戏通信警告 */} = ({ {/* API地址验证提示 */} {formData.game_server_api && ( = ({ {/* PSK密钥强度提示 */} {formData.game_server_psk && ( = ({
{/* 安全警告 */} = ({ {/* JWT密钥强度提示 */} {formData.jwt_secret && (