import React, { useState, useEffect } from 'react'; import { Form, Input, Switch, Select, Button, Card, Space, message, Modal, Tag, Divider, Descriptions, Alert, Row, Col, Typography, Badge, Tooltip, Avatar, ColorPicker, } from 'antd'; import { SettingOutlined, GlobalOutlined, BgColorsOutlined, InfoCircleOutlined, CheckCircleOutlined, ExclamationCircleOutlined, LockOutlined, DatabaseOutlined, DesktopOutlined, ClockCircleOutlined, MailOutlined, PhoneOutlined, EnvironmentOutlined, FileTextOutlined, SafetyCertificateOutlined, ReloadOutlined, SaveOutlined, BellOutlined, CloudServerOutlined, UserOutlined, SecurityScanOutlined, ApiOutlined, ThunderboltOutlined, MenuFoldOutlined, MenuUnfoldOutlined, } from '@ant-design/icons'; import axios from 'axios'; import { useConfig } from '../context/ConfigContext'; const { Option } = Select; const { Title, Text, Paragraph } = Typography; const SystemSettings = () => { const [loading, setLoading] = useState(false); const [saving, setSaving] = useState(false); const [settings, setSettings] = useState({}); const [activeMenu, setActiveMenu] = useState('general'); const [systemInfo, setSystemInfo] = useState(null); const [form] = Form.useForm(); const [drawerVisible, setDrawerVisible] = useState(false); const [isMobile, setIsMobile] = useState(window.innerWidth < 768); const { reloadConfig } = useConfig(); useEffect(() => { fetchSettings(); fetchSystemInfo(); const handleResize = () => { setIsMobile(window.innerWidth < 768); }; window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); const fetchSettings = async () => { setLoading(true); try { const response = await axios.get('/api/system-settings'); setSettings(response.data); const formValues = {}; Object.entries(response.data).forEach(([key, data]) => { formValues[key] = data.value; }); form.setFieldsValue(formValues); } catch (error) { message.error('获取设置失败'); } finally { setLoading(false); } }; const fetchSystemInfo = async () => { try { const response = await axios.get('/api/system-settings/system/info'); setSystemInfo(response.data); } catch (error) { console.error('获取系统信息失败'); } }; const handleSaveSettings = async values => { setSaving(true); try { const updates = {}; Object.entries(values).forEach(([key, value]) => { if (settings[key] && settings[key].isEditable) { updates[key] = value; } }); await axios.put('/api/system-settings', { settings: updates }); if ('frontend_port' in updates) { try { await axios.post('/api/system-settings/frontend/port/sync'); const statusRes = await axios.get('/api/system-settings/frontend/status'); const isProduction = statusRes.data.isProduction; if (isProduction) { Modal.info({ title: '前端端口已修改(生产环境)', content: (

前端端口已从 {settings.frontend_port?.value} 更改为{' '} {updates.frontend_port}

生产环境由 Nginx 或其他服务器托管,请手动更新服务器配置文件

} type="info" showIcon style={{ marginTop: 16 }} /> ), okText: '知道了', }); } else { Modal.confirm({ title: '前端端口已修改', icon: , content: (

前端端口已从 {settings.frontend_port?.value} 更改为{' '} {updates.frontend_port}

是否立即重启前端服务以应用新端口?

), okText: '立即重启', cancelText: '稍后手动重启', onOk: async () => { const newPort = updates.frontend_port; const newUrl = `http://localhost:${newPort}`; Modal.success({ title: '正在重启前端服务', content: (

前端服务正在重启,新端口:{newPort}

页面将在3秒后自动跳转到新地址...

), okText: '立即跳转', closable: false, maskClosable: false, onOk: () => { window.location.href = newUrl; }, }); setTimeout(async () => { try { await axios.post('/api/system-settings/frontend/restart', {}, { timeout: 5000 }); } catch (error) { console.log('重启请求已发送,服务正在重启...'); } }, 1000); setTimeout(() => { window.location.href = newUrl; }, 3000); }, }); } } catch (syncError) { console.warn('同步前端端口配置失败:', syncError); message.warning('端口配置已保存,但同步到配置文件失败'); } } else { message.success('设置保存成功'); } fetchSettings(); await reloadConfig(); } catch (error) { message.error('保存设置失败'); } finally { setSaving(false); } }; const handleResetSetting = key => { Modal.confirm({ title: '确认重置', icon: , content: `确定要将 "${settings[key]?.description || key}" 重置为默认值吗?`, onOk: async () => { try { await axios.post(`/api/system-settings/reset/${key}`); message.success('重置成功'); fetchSettings(); await reloadConfig(); } catch (error) { message.error('重置失败'); } }, }); }; const renderFormItem = (key, data) => { if (!data.isEditable) { return ( } /> ); } switch (data.type) { case 'boolean': return ( ); case 'number': const isPortField = key === 'frontend_port'; return ( Number(value), }, ] : []), ]} > ); case 'select': const options = getSelectOptions(key); return ( ); default: return ( ); } }; const getSelectOptions = key => { const optionsMap = { timezone: [ { value: 'Asia/Shanghai', label: '亚洲/上海 (UTC+8)' }, { value: 'Asia/Beijing', label: '亚洲/北京 (UTC+8)' }, { value: 'America/New_York', label: '美洲/纽约 (UTC-5)' }, { value: 'Europe/London', label: '欧洲/伦敦 (UTC+0)' }, { value: 'UTC', label: 'UTC (UTC+0)' }, ], date_format: [ { value: 'YYYY-MM-DD', label: '2024-01-01' }, { value: 'YYYY/MM/DD', label: '2024/01/01' }, { value: 'DD/MM/YYYY', label: '01/01/2024' }, { value: 'MM/DD/YYYY', label: '01/01/2024' }, ], primary_color: [ { value: '#667eea', label: '蓝色 (#667eea)' }, { value: '#764ba2', label: '紫色 (#764ba2)' }, { value: '#f093fb', label: '粉色 (#f093fb)' }, { value: '#4facfe', label: '浅蓝色 (#4facfe)' }, { value: '#43e97b', label: '绿色 (#43e97b)' }, { value: '#fa709a', label: '红色 (#fa709a)' }, { value: '#fee140', label: '黄色 (#fee140)' }, { value: '#00b4db', label: '青色 (#00b4db)' }, { value: '#0083b0', label: '深蓝色 (#0083b0)' }, { value: '#fcb045', label: '橙色 (#fcb045)' }, ], secondary_color: [ { value: '#764ba2', label: '紫色 (#764ba2)' }, { value: '#667eea', label: '蓝色 (#667eea)' }, { value: '#4facfe', label: '浅蓝色 (#4facfe)' }, { value: '#43e97b', label: '绿色 (#43e97b)' }, { value: '#fa709a', label: '红色 (#fa709a)' }, { value: '#fee140', label: '黄色 (#fee140)' }, { value: '#00b4db', label: '青色 (#00b4db)' }, { value: '#0083b0', label: '深蓝色 (#0083b0)' }, { value: '#fcb045', label: '橙色 (#fcb045)' }, { value: '#f093fb', label: '粉色 (#f093fb)' }, ], table_row_height: [ { value: 'small', label: '紧凑 (Small)' }, { value: 'default', label: '默认 (Default)' }, { value: 'middle', label: '中等 (Middle)' }, { value: 'large', label: '宽松 (Large)' }, ], dark_mode: [ { value: 'false', label: '关闭' }, { value: 'true', label: '开启' }, ], compact_mode: [ { value: 'false', label: '关闭' }, { value: 'true', label: '开启' }, ], animation_enabled: [ { value: 'false', label: '关闭' }, { value: 'true', label: '开启' }, ], sidebar_collapsed: [ { value: 'false', label: '展开' }, { value: 'true', label: '折叠' }, ], }; return optionsMap[key] || []; }; const settingGroups = { general: [ { title: '站点信息', icon: , description: '配置系统基本信息和站点标识', keys: ['site_name', 'site_logo'], }, { title: '时间设置', icon: , description: '设置系统时区和日期显示格式', keys: ['timezone', 'date_format'], }, { title: '安全设置', icon: , description: '配置登录安全策略和维护模式', keys: ['idle_timeout', 'max_login_attempts', 'maintenance_mode'], }, ], appearance: [ { title: '主题颜色', icon: , description: '自定义系统主题配色方案', keys: ['primary_color', 'secondary_color'], }, { title: '界面布局', icon: , description: '调整界面显示密度和布局方式', keys: ['compact_mode', 'sidebar_collapsed', 'table_row_height'], }, { title: '动画效果', icon: , description: '控制界面动画和过渡效果', keys: ['animation_enabled'], }, ], }; const menuItems = [ { key: 'general', icon: , label: '全局配置', description: '站点信息、时间、安全设置', }, { key: 'appearance', icon: , label: '外观设置', description: '主题颜色、界面布局', }, { key: 'about', icon: , label: '关于系统', description: '系统信息、版本详情', }, ]; const renderSettingGroup = (group) => { // 根据分组决定布局 const isSiteInfo = group.title === '站点信息'; const isTimeSetting = group.title === '时间设置'; const isSecurity = group.title === '安全设置'; return ( {/* 居中的标题区 */}
{group.icon}
{group.title}
{group.description}
{/* 表单内容区 */} {isSiteInfo && ( 网站名称} name="site_name" style={{ marginBottom: 0 }} > 网站Logo URL} name="site_logo" style={{ marginBottom: 0 }} > 预览 } /> )} {isTimeSetting && ( 时区设置} name="timezone" style={{ marginBottom: 0 }} > 日期格式} name="date_format" style={{ marginBottom: 0 }} > )} {isSecurity && ( 用户空闲超时时间} name="idle_timeout" style={{ marginBottom: 0 }} > 分钟} /> 最大登录尝试次数} name="max_login_attempts" style={{ marginBottom: 0 }} > 次} /> 维护模式} name="maintenance_mode" valuePropName="checked" style={{ marginBottom: 0 }} > )} {!isSiteInfo && !isTimeSetting && !isSecurity && ( {group.keys.map(key => { const settingData = { ...settings[key] }; if (key === 'timezone' || key === 'date_format') { settingData.type = 'select'; } if (key === 'primary_color' || key === 'secondary_color') { settingData.type = 'select'; } return ( {renderFormItem(key, settingData)} ); })} )}
); }; // 底部固定操作栏 const renderFixedFooter = (onSubmit) => (
); const renderGeneralSettings = () => { return (
{settingGroups.general.map(group => renderSettingGroup(group))}
{renderFixedFooter()}
); }; const presetColors = [ '#667eea', '#764ba2', '#f093fb', '#4facfe', '#43e97b', '#fa709a', '#fee140', '#00b4db', '#0083b0', '#fcb045', '#1890ff', '#52c41a', '#eb2f96', '#722ed1', '#13c2c2', '#fa8c16', ]; const handleColorChange = (color, key) => { const hexColor = typeof color === 'string' ? color : color.toHexString(); form.setFieldValue(key, hexColor); const root = document.documentElement; if (key === 'primary_color') { root.style.setProperty('--primary-color', hexColor); root.style.setProperty('--primary-light', `${hexColor}20`); } else if (key === 'secondary_color') { root.style.setProperty('--secondary-color', hexColor); root.style.setProperty('--secondary-light', `${hexColor}20`); } }; const renderColorFormItem = (key, data) => { const currentValue = form.getFieldValue(key) || settings[key]?.value || '#667eea'; return ( {data.description || key}
} name={key} style={{ marginBottom: 0 }} > ); }; const renderAppearanceSettings = () => { const primaryColor = form.getFieldValue('primary_color') || settings.primary_color?.value || '#667eea'; const secondaryColor = form.getFieldValue('secondary_color') || settings.secondary_color?.value || '#764ba2'; return (
主题颜色
自定义系统主题配色方案
主色调 用于按钮、链接等主要交互元素
handleColorChange(color, 'primary_color')} format="hex" showText presets={[ { label: '推荐配色', colors: presetColors, }, ]} style={{ width: '100%' }} /> handleColorChange(e.target.value, 'primary_color')} placeholder="#667eea" style={{ borderRadius: 8 }} prefix={} />
次色调 用于渐变、悬停效果等辅助元素
handleColorChange(color, 'secondary_color')} format="hex" showText presets={[ { label: '推荐配色', colors: presetColors, }, ]} style={{ width: '100%' }} /> handleColorChange(e.target.value, 'secondary_color')} placeholder="#764ba2" style={{ borderRadius: 8 }} prefix={} />
实时预览 标签样式
界面布局
调整界面显示密度和布局方式
紧凑模式} name="compact_mode" valuePropName="checked" style={{ marginBottom: 0 }} > 侧边栏折叠} name="sidebar_collapsed" valuePropName="checked" style={{ marginBottom: 0 }} > 表格行高度} name="table_row_height" style={{ marginBottom: 0 }} >
动画效果
控制界面动画和过渡效果
启用动画} name="animation_enabled" valuePropName="checked" style={{ marginBottom: 0 }} >
{renderFixedFooter()} ); }; const renderAboutPage = () => { return (
机柜管理系统 版本 {settings.app_version?.value || '1.0.0'} | 运行正常} /> 专业的数据中心设备管理平台,提供机房、机柜、设备的全生命周期管理
{systemInfo && (
} size={32} /> 系统统计
{systemInfo.statistics?.devices || 0}
设备总数
{systemInfo.statistics?.racks || 0}
机柜总数
{systemInfo.statistics?.rooms || 0}
机房总数
{systemInfo.statistics?.users || 0}
用户总数
{systemInfo.system?.nodeVersion} {systemInfo.system?.platform} ({systemInfo.system?.arch}) {systemInfo.system?.pid} {systemInfo.system?.uptime ? `${Math.floor(systemInfo.system.uptime / 3600)}小时${Math.floor((systemInfo.system.uptime % 3600) / 60)}分钟` : '-'} {systemInfo.system?.memoryUsage ? `${(systemInfo.system.memoryUsage.heapUsed / 1024 / 1024).toFixed(2)} MB` : '-'} {systemInfo.timestamp ? new Date(systemInfo.timestamp).toLocaleString('zh-CN') : '-'}
)}
} size={32} /> 公司信息
} placeholder="请输入公司名称" /> } placeholder="请输入联系邮箱" /> } placeholder="请输入联系电话" /> } placeholder="请输入公司地址" />
); }; const renderContent = () => { switch (activeMenu) { case 'general': return renderGeneralSettings(); case 'appearance': return renderAppearanceSettings(); case 'about': return renderAboutPage(); default: return renderGeneralSettings(); } }; // 顶部导航栏菜单 const renderTopNav = () => (
{menuItems.map(item => (
setActiveMenu(item.key)} style={{ display: 'flex', alignItems: 'center', padding: '8px 16px', borderRadius: 20, cursor: 'pointer', transition: 'all 0.3s ease', backgroundColor: activeMenu === item.key ? 'rgba(255,255,255,0.25)' : 'transparent', color: '#fff', whiteSpace: 'nowrap', fontSize: 14, fontWeight: activeMenu === item.key ? 600 : 400, }} onMouseEnter={(e) => { if (activeMenu !== item.key) { e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.1)'; } }} onMouseLeave={(e) => { if (activeMenu !== item.key) { e.currentTarget.style.backgroundColor = 'transparent'; } }} > {item.icon} {item.label}
))}
); // 移动端下拉菜单 const renderMobileNav = () => ( ); return (
{/* 顶部固定导航栏 */}
系统设置 {/* 桌面端导航 */} {!isMobile && renderTopNav()} {/* 移动端导航 */} {isMobile && renderMobileNav()}
{/* 主内容区 - 平铺填充 */}
{renderContent()}
); }; export default SystemSettings;