fix: 🐛 优化系统配置页部分问题

This commit is contained in:
Stev_Wang
2025-12-12 20:55:53 +08:00
parent 2ca4cd60f6
commit c4ec174828
5 changed files with 89 additions and 519 deletions

View File

@@ -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<EnvSyncModalProps> = ({
visible,
configs,
onClose,
onSync
}) => {
const [form] = Form.useForm();
const [loading, setLoading] = useState<boolean>(false);
const [progress, setProgress] = useState<number>(0);
const [syncResult, setSyncResult] = useState<{
success: boolean;
message: string;
details?: string[];
} | null>(null);
// 准备.env文件内容
const generateEnvContent = (selectedConfigs: string[]): string => {
const envVars: Record<string, string> = {};
// 根据选择的配置生成环境变量
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 (
<Modal
title={
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<SyncOutlined />
.env文件
</div>
}
open={visible}
onCancel={handleClose}
width={600}
footer={[
<Button key="cancel" onClick={handleClose}>
</Button>,
<Button
key="download"
icon={<DownloadOutlined />}
onClick={handleDownload}
disabled={loading}
>
.env文件
</Button>,
<Button
key="sync"
type="primary"
icon={<SyncOutlined />}
loading={loading}
onClick={handleSync}
disabled={syncResult?.success}
>
</Button>
]}
>
<div style={{ marginBottom: '16px' }}>
<Alert
message="配置同步说明"
description="选择要同步到.env文件的系统配置项然后执行同步操作。敏感配置如密钥将被加密处理。"
type="info"
showIcon
style={{ marginBottom: '16px' }}
/>
</div>
{/* 同步进度 */}
{loading && (
<div style={{ marginBottom: '16px' }}>
<Progress
percent={progress}
status="active"
strokeColor={{
'0%': '#108ee9',
'100%': '#87d068',
}}
/>
<Text type="secondary" style={{ fontSize: '12px' }}>
...
</Text>
</div>
)}
{/* 同步结果 */}
{syncResult && (
<div style={{ marginBottom: '16px' }}>
<Alert
message={syncResult.success ? '同步成功' : '同步失败'}
description={syncResult.message}
type={syncResult.success ? 'success' : 'error'}
showIcon
action={
syncResult.success && (
<Button size="small" onClick={handleDownload}>
.env文件
</Button>
)
}
/>
</div>
)}
{/* 配置选择 */}
{!syncResult?.success && (
<Form
form={form}
layout="vertical"
initialValues={{
configOptions: defaultChecked
}}
>
<Form.Item
name="configOptions"
label="选择要同步的配置项"
rules={[
{ required: true, message: '请至少选择一个配置项' }
]}
>
<Checkbox.Group style={{ width: '100%' }}>
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
{configOptions.map(option => (
<div
key={option.key}
style={{
padding: '12px',
border: '1px solid #d9d9d9',
borderRadius: '6px',
background: '#fafafa'
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<Checkbox value={option.key}>
<div style={{ flex: 1 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<Text strong>{option.label}</Text>
{option.sensitive && (
<Text type="danger" style={{ fontSize: '12px' }}>
</Text>
)}
</div>
<Text type="secondary" style={{ fontSize: '12px', display: 'block', marginTop: '4px' }}>
{option.description}
</Text>
<Text code style={{ fontSize: '11px', display: 'block', marginTop: '4px' }}>
{option.envVar}
</Text>
</div>
</Checkbox>
</div>
</div>
))}
</Space>
</Checkbox.Group>
</Form.Item>
</Form>
)}
{/* 操作说明 */}
<Divider />
<div style={{ fontSize: '12px', color: '#666' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px' }}>
<FileTextOutlined />
<strong></strong>
</div>
<ul style={{ margin: 0, paddingLeft: '20px' }}>
<li>.env文件</li>
<li></li>
<li>.env文件</li>
<li>使</li>
</ul>
</div>
</Modal>
);
};
export default EnvSyncModal;

View File

@@ -22,7 +22,7 @@ import { useAuth } from '../contexts/AuthContext';
import { useNavigate, useLocation } from 'react-router-dom'; import { useNavigate, useLocation } from 'react-router-dom';
import TabNavigation from './TabNavigation'; import TabNavigation from './TabNavigation';
const { Header, Sider, Content } = Layout; const { Header, Sider } = Layout;
const { Text } = Typography; const { Text } = Typography;
// 定义菜单项类型 // 定义菜单项类型
@@ -139,6 +139,7 @@ const menuItems: MenuProps['items'] = [
const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => { const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [collapsed, setCollapsed] = useState(false); const [collapsed, setCollapsed] = useState(false);
const [mobileDrawerVisible, setMobileDrawerVisible] = useState(false); const [mobileDrawerVisible, setMobileDrawerVisible] = useState(false);
const [isMobile, setIsMobile] = useState(false);
const { user, logout, isLoading } = useAuth(); const { user, logout, isLoading } = useAuth();
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
@@ -151,6 +152,20 @@ const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
} }
}, [user, navigate, isLoading]); }, [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) { if (isLoading) {
return ( 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 }) => {
</div> </div>
{/* 菜单区域 */} {/* 菜单区域 */}
<div style={{ flex: 1, overflow: 'auto' }}> <div style={{
flex: 1,
overflow: 'auto',
height: '100%'
}}>
<Menu <Menu
mode="inline" mode="inline"
selectedKeys={getSelectedKeys()} selectedKeys={getSelectedKeys()}
@@ -349,7 +352,7 @@ const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
); );
return ( return (
<Layout style={{ minHeight: '100vh' }}> <div style={{ minHeight: '100vh', position: 'relative' }}>
{/* 侧边栏 - 桌面端 */} {/* 侧边栏 - 桌面端 */}
{!isMobile && ( {!isMobile && (
<Sider <Sider
@@ -359,8 +362,15 @@ const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
width={240} width={240}
style={{ style={{
background: token.colorBgContainer, 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} {siderContent}
</Sider> </Sider>
@@ -378,19 +388,22 @@ const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
{siderContent} {siderContent}
</Drawer> </Drawer>
<Layout> {/* 头部导航栏 - 固定定位 */}
{/* 头部 */} <Header style={{
<Header style={{ padding: '0 24px',
padding: '0 24px', background: token.colorBgContainer,
background: token.colorBgContainer, borderBottom: `1px solid ${token.colorBorderSecondary}`,
borderBottom: `1px solid ${token.colorBorderSecondary}`, display: 'flex',
display: 'flex', alignItems: 'center',
alignItems: 'center', justifyContent: 'space-between',
justifyContent: 'space-between', position: 'fixed',
position: 'sticky', top: 0,
top: 0, left: !isMobile ? (collapsed ? 80 : 240) : 0,
zIndex: 100 right: 0,
}}> height: '64px',
zIndex: 100,
transition: 'left 0.2s'
}}>
{/* 左侧控制区 */} {/* 左侧控制区 */}
<Space> <Space>
{/* 折叠按钮 */} {/* 折叠按钮 */}
@@ -441,22 +454,40 @@ const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
</Space> </Space>
</Header> </Header>
{/* 标签导航 */} {/* 标签导航 - 固定在头部下方 */}
<TabNavigation /> <div style={{
position: 'fixed',
top: '64px',
left: !isMobile ? (collapsed ? 80 : 240) : 0,
right: 0,
zIndex: 90,
background: token.colorBgContainer,
borderBottom: `1px solid ${token.colorBorderSecondary}`,
transition: 'left 0.2s'
}}>
<TabNavigation />
</div>
{/* 内容区域 */} {/* 内容区域 */}
<Content style={{ <div style={{
margin: '16px',
padding: '24px', padding: '24px',
background: token.colorBgContainer, background: token.colorBgContainer,
borderRadius: token.borderRadiusLG, minHeight: 'calc(100vh - 112px)',
minHeight: 'calc(100vh - 64px - 48px - 32px)', overflow: 'auto',
overflow: 'auto' marginTop: '112px', // 头部64px + 标签导航48px
marginLeft: !isMobile ? (collapsed ? 80 : 240) : 0,
transition: 'margin-left 0.2s'
}}> }}>
{children} <div style={{
</Content> background: token.colorBgContainer,
</Layout> borderRadius: token.borderRadiusLG,
</Layout> minHeight: 'calc(100vh - 112px)',
padding: '24px'
}}>
{children}
</div>
</div>
</div>
); );
}; };

View File

@@ -5,15 +5,12 @@
*/ */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Tabs, message, Modal } from 'antd'; import { Tabs, message } from 'antd';
import { import {
SettingOutlined, SettingOutlined,
SecurityScanOutlined, SecurityScanOutlined,
CloudServerOutlined, CloudServerOutlined,
HistoryOutlined, ReloadOutlined
ReloadOutlined,
SaveOutlined,
SyncOutlined
} from '@ant-design/icons'; } from '@ant-design/icons';
import { SystemConfig, SaveConfigRequest } from '../types/systemConfig'; import { SystemConfig, SaveConfigRequest } from '../types/systemConfig';
import systemConfigService from '../services/systemConfigService'; import systemConfigService from '../services/systemConfigService';
@@ -21,13 +18,8 @@ import BasicConfigTab from './tabs/BasicConfigTab';
import SecurityConfigTab from './tabs/SecurityConfigTab'; import SecurityConfigTab from './tabs/SecurityConfigTab';
import GameConfigTab from './tabs/GameConfigTab'; import GameConfigTab from './tabs/GameConfigTab';
import ConfigHistoryModal from './ConfigHistoryModal'; import ConfigHistoryModal from './ConfigHistoryModal';
import EnvSyncModal from '../components/EnvSyncModal';
const { TabPane } = Tabs; const SystemConfigPage: React.FC = () => {
interface SystemConfigPageProps {}
const SystemConfigPage: React.FC<SystemConfigPageProps> = () => {
const [configs, setConfigs] = useState<SystemConfig[]>([]); const [configs, setConfigs] = useState<SystemConfig[]>([]);
const [loading, setLoading] = useState<boolean>(true); const [loading, setLoading] = useState<boolean>(true);
const [saving, setSaving] = useState<boolean>(false); const [saving, setSaving] = useState<boolean>(false);
@@ -35,7 +27,6 @@ const SystemConfigPage: React.FC<SystemConfigPageProps> = () => {
const [historyModalVisible, setHistoryModalVisible] = useState<boolean>(false); const [historyModalVisible, setHistoryModalVisible] = useState<boolean>(false);
const [selectedConfigKey, setSelectedConfigKey] = useState<string>(''); const [selectedConfigKey, setSelectedConfigKey] = useState<string>('');
const [hasUnsavedChanges, setHasUnsavedChanges] = useState<boolean>(false); const [hasUnsavedChanges, setHasUnsavedChanges] = useState<boolean>(false);
const [envSyncModalVisible, setEnvSyncModalVisible] = useState<boolean>(false);
// 加载所有配置 // 加载所有配置
const loadConfigs = async () => { const loadConfigs = async () => {
@@ -57,7 +48,7 @@ const SystemConfigPage: React.FC<SystemConfigPageProps> = () => {
}, []); }, []);
// 保存配置 // 保存配置
const handleSaveConfigs = async (configRequests: SaveConfigRequest[], group: string) => { const handleSaveConfigs = async (configRequests: SaveConfigRequest[]) => {
try { try {
setSaving(true); setSaving(true);
const result = await systemConfigService.saveConfigs(configRequests); const result = await systemConfigService.saveConfigs(configRequests);
@@ -107,25 +98,14 @@ const SystemConfigPage: React.FC<SystemConfigPageProps> = () => {
setHistoryModalVisible(true); setHistoryModalVisible(true);
}; };
// 打开.env同步模态框
const handleOpenEnvSync = () => {
setEnvSyncModalVisible(true);
};
// 处理.env同步完成
const handleEnvSyncComplete = () => {
message.success('配置已成功同步到.env文件');
loadConfigs();
};
// 检测未保存的更改 // 检测未保存的更改
const handleConfigChange = () => { const handleConfigChange = () => {
setHasUnsavedChanges(true); setHasUnsavedChanges(true);
}; };
// 获取指定分组的配置 // 获取指定分组的配置
const getConfigsByGroup = (group: string) => { const getConfigsByGroup = (_group: string) => {
return configs.filter(config => config.config_group === group); return configs.filter(config => config.config_group === _group);
}; };
const tabItems = [ const tabItems = [
@@ -142,7 +122,7 @@ const SystemConfigPage: React.FC<SystemConfigPageProps> = () => {
configs={getConfigsByGroup('basic')} configs={getConfigsByGroup('basic')}
loading={loading} loading={loading}
saving={saving} saving={saving}
onSave={(requests) => handleSaveConfigs(requests, 'basic')} onSave={handleSaveConfigs}
onReset={handleResetConfig} onReset={handleResetConfig}
onShowHistory={handleViewHistory} onShowHistory={handleViewHistory}
onConfigChange={handleConfigChange} onConfigChange={handleConfigChange}
@@ -162,7 +142,7 @@ const SystemConfigPage: React.FC<SystemConfigPageProps> = () => {
configs={getConfigsByGroup('security')} configs={getConfigsByGroup('security')}
loading={loading} loading={loading}
saving={saving} saving={saving}
onSave={(requests) => handleSaveConfigs(requests, 'security')} onSave={handleSaveConfigs}
onReset={handleResetConfig} onReset={handleResetConfig}
onShowHistory={handleViewHistory} onShowHistory={handleViewHistory}
onConfigChange={handleConfigChange} onConfigChange={handleConfigChange}
@@ -182,7 +162,7 @@ const SystemConfigPage: React.FC<SystemConfigPageProps> = () => {
configs={getConfigsByGroup('game')} configs={getConfigsByGroup('game')}
loading={loading} loading={loading}
saving={saving} saving={saving}
onSave={(requests) => handleSaveConfigs(requests, 'game')} onSave={handleSaveConfigs}
onReset={handleResetConfig} onReset={handleResetConfig}
onShowHistory={handleViewHistory} onShowHistory={handleViewHistory}
onConfigChange={handleConfigChange} onConfigChange={handleConfigChange}
@@ -206,25 +186,6 @@ const SystemConfigPage: React.FC<SystemConfigPageProps> = () => {
</div> </div>
<div style={{ display: 'flex', gap: '12px' }}> <div style={{ display: 'flex', gap: '12px' }}>
<button
onClick={handleOpenEnvSync}
disabled={saving || loading}
style={{
display: 'flex',
alignItems: 'center',
gap: '8px',
padding: '8px 16px',
border: '1px solid #d9d9d9',
borderRadius: '6px',
background: saving || loading ? '#f5f5f5' : '#fff',
color: saving || loading ? '#999' : '#666',
cursor: saving || loading ? 'not-allowed' : 'pointer'
}}
>
<SyncOutlined />
.env文件
</button>
<button <button
onClick={loadConfigs} onClick={loadConfigs}
disabled={loading} disabled={loading}
@@ -285,14 +246,6 @@ const SystemConfigPage: React.FC<SystemConfigPageProps> = () => {
setSelectedConfigKey(''); setSelectedConfigKey('');
}} }}
/> />
{/* .env文件同步模态框 */}
<EnvSyncModal
visible={envSyncModalVisible}
configs={configs}
onClose={() => setEnvSyncModalVisible(false)}
onSync={handleEnvSyncComplete}
/>
</div> </div>
); );
}; };

View File

@@ -189,7 +189,7 @@ const GameConfigTab: React.FC<GameConfigTabProps> = ({
<div className="game-config-tab" style={{ padding: '0' }}> <div className="game-config-tab" style={{ padding: '0' }}>
{/* 游戏通信警告 */} {/* 游戏通信警告 */}
<Alert <Alert
message="游戏通信配置警告" title="游戏通信配置警告"
description="错误的游戏通信配置可能导致无法与游戏服务端正常交互请确保API地址和密钥配置正确。" description="错误的游戏通信配置可能导致无法与游戏服务端正常交互请确保API地址和密钥配置正确。"
type="warning" type="warning"
showIcon showIcon
@@ -204,7 +204,7 @@ const GameConfigTab: React.FC<GameConfigTabProps> = ({
{/* API地址验证提示 */} {/* API地址验证提示 */}
{formData.game_server_api && ( {formData.game_server_api && (
<Alert <Alert
message={`API地址验证: ${apiUrlValidation.valid ? '✅ 格式正确' : '❌ 格式错误'}`} title={`API地址验证: ${apiUrlValidation.valid ? '✅ 格式正确' : '❌ 格式错误'}`}
description={apiUrlValidation.message} description={apiUrlValidation.message}
type={apiUrlValidation.valid ? 'success' : 'error'} type={apiUrlValidation.valid ? 'success' : 'error'}
showIcon showIcon
@@ -215,7 +215,7 @@ const GameConfigTab: React.FC<GameConfigTabProps> = ({
{/* PSK密钥强度提示 */} {/* PSK密钥强度提示 */}
{formData.game_server_psk && ( {formData.game_server_psk && (
<Alert <Alert
message={`PSK密钥强度: ${pskStrength.level.toUpperCase()}`} title={`PSK密钥强度: ${pskStrength.level.toUpperCase()}`}
description={pskStrength.message} description={pskStrength.message}
type={pskStrength.level === 'strong' ? 'success' : pskStrength.level === 'medium' ? 'warning' : 'error'} type={pskStrength.level === 'strong' ? 'success' : pskStrength.level === 'medium' ? 'warning' : 'error'}
showIcon showIcon

View File

@@ -207,7 +207,7 @@ const SecurityConfigTab: React.FC<SecurityConfigTabProps> = ({
<div className="security-config-tab" style={{ padding: '0' }}> <div className="security-config-tab" style={{ padding: '0' }}>
{/* 安全警告 */} {/* 安全警告 */}
<Alert <Alert
message="安全配置警告" title="安全配置警告"
description="安全配置直接影响系统安全性,请谨慎修改。错误的配置可能导致系统无法正常运行。" description="安全配置直接影响系统安全性,请谨慎修改。错误的配置可能导致系统无法正常运行。"
type="warning" type="warning"
showIcon showIcon
@@ -222,7 +222,7 @@ const SecurityConfigTab: React.FC<SecurityConfigTabProps> = ({
{/* JWT密钥强度提示 */} {/* JWT密钥强度提示 */}
{formData.jwt_secret && ( {formData.jwt_secret && (
<Alert <Alert
message={`JWT密钥强度: ${jwtSecretStrength.level.toUpperCase()}`} title={`JWT密钥强度: ${jwtSecretStrength.level.toUpperCase()}`}
description={jwtSecretStrength.message} description={jwtSecretStrength.message}
type={jwtSecretStrength.level === 'strong' ? 'success' : jwtSecretStrength.level === 'medium' ? 'warning' : 'error'} type={jwtSecretStrength.level === 'strong' ? 'success' : jwtSecretStrength.level === 'medium' ? 'warning' : 'error'}
showIcon showIcon