diff --git a/frontend/src/pages/SystemSettings.jsx b/frontend/src/pages/SystemSettings.jsx
index 826e5da..7cc708c 100644
--- a/frontend/src/pages/SystemSettings.jsx
+++ b/frontend/src/pages/SystemSettings.jsx
@@ -1,6 +1,5 @@
import React, { useState, useEffect } from 'react';
import {
- Tabs,
Form,
Input,
Switch,
@@ -19,6 +18,7 @@ import {
Typography,
Badge,
Tooltip,
+ Avatar,
} from 'antd';
import {
SettingOutlined,
@@ -38,26 +38,41 @@ import {
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 { TabPane } = Tabs;
-const { Title, Text } = Typography;
+const { Title, Text, Paragraph } = Typography;
const SystemSettings = () => {
const [loading, setLoading] = useState(false);
const [saving, setSaving] = useState(false);
const [settings, setSettings] = useState({});
- const [activeTab, setActiveTab] = useState('general');
+ 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 () => {
@@ -99,18 +114,13 @@ const SystemSettings = () => {
await axios.put('/api/system-settings', { settings: updates });
- // 如果修改了前端端口,同步配置并自动重启(仅开发环境)
if ('frontend_port' in updates) {
try {
- // 1. 同步端口到配置文件
await axios.post('/api/system-settings/frontend/port/sync');
-
- // 2. 检查是否为生产环境
const statusRes = await axios.get('/api/system-settings/frontend/status');
const isProduction = statusRes.data.isProduction;
if (isProduction) {
- // 生产环境:只显示提示,不自动重启
Modal.info({
title: '前端端口已修改(生产环境)',
content: (
@@ -123,10 +133,7 @@ const SystemSettings = () => {
message="请手动更新服务器配置"
description={
-
生产环境由 Nginx 或其他服务器托管,请手动更新服务器配置文件:
-
1. 更新 Nginx 配置中的监听端口
-
2. 重启 Nginx 服务
-
3. 更新 vite.config.js 中的端口配置(用于下次构建)
+
生产环境由 Nginx 或其他服务器托管,请手动更新服务器配置文件
}
type="info"
@@ -138,7 +145,6 @@ const SystemSettings = () => {
okText: '知道了',
});
} else {
- // 开发环境:显示确认对话框并自动重启
Modal.confirm({
title: '前端端口已修改',
icon: ,
@@ -149,13 +155,6 @@ const SystemSettings = () => {
{updates.frontend_port}
是否立即重启前端服务以应用新端口?
-
),
okText: '立即重启',
@@ -163,20 +162,12 @@ const SystemSettings = () => {
onOk: async () => {
const newPort = updates.frontend_port;
const newUrl = `http://localhost:${newPort}`;
-
- // 先显示跳转提示,再调用重启API
- // 因为重启API会导致当前服务中断
Modal.success({
title: '正在重启前端服务',
content: (
-
- 前端服务正在重启,新端口:{newPort}
-
+
前端服务正在重启,新端口:{newPort}
页面将在3秒后自动跳转到新地址...
-
- 如果跳转失败,请手动访问:{newUrl}
-
),
okText: '立即跳转',
@@ -186,23 +177,13 @@ const SystemSettings = () => {
window.location.href = newUrl;
},
});
-
- // 延迟调用重启API,让用户看到提示
setTimeout(async () => {
try {
- // 调用重启API(这个请求可能会因为服务重启而失败)
- await axios.post(
- '/api/system-settings/frontend/restart',
- {},
- { timeout: 5000 }
- );
+ await axios.post('/api/system-settings/frontend/restart', {}, { timeout: 5000 });
} catch (error) {
- // 忽略错误,因为服务重启会导致连接中断
console.log('重启请求已发送,服务正在重启...');
}
}, 1000);
-
- // 延迟3秒后跳转
setTimeout(() => {
window.location.href = newUrl;
}, 3000);
@@ -218,7 +199,6 @@ const SystemSettings = () => {
}
fetchSettings();
- // 重新加载全局配置,使更改立即生效
await reloadConfig();
} catch (error) {
message.error('保存设置失败');
@@ -237,7 +217,6 @@ const SystemSettings = () => {
await axios.post(`/api/system-settings/reset/${key}`);
message.success('重置成功');
fetchSettings();
- // 重新加载全局配置,使更改立即生效
await reloadConfig();
} catch (error) {
message.error('重置失败');
@@ -379,22 +358,24 @@ const SystemSettings = () => {
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'],
},
],
@@ -402,73 +383,287 @@ const SystemSettings = () => {
{
title: '主题颜色',
icon: ,
+ description: '自定义系统主题配色方案',
keys: ['primary_color', 'secondary_color'],
},
{
title: '界面布局',
icon: ,
+ description: '调整界面显示密度和布局方式',
keys: ['compact_mode', 'sidebar_collapsed', 'table_row_height'],
},
{
title: '动画效果',
- icon: ,
+ 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}
-
- }
- style={{ marginBottom: 16, borderRadius: 8 }}
- size="small"
+ style={{
+ marginBottom: 24,
+ borderRadius: 12,
+ border: '1px solid #e8e8e8',
+ boxShadow: '0 2px 8px rgba(0,0,0,0.04)',
+ }}
+ bodyStyle={{ padding: '24px' }}
>
- {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);
- })}
+ {/* 居中的标题区 */}
+
+
+ {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) => (
+
+ }
+ size="large"
+ style={{
+ borderRadius: 8,
+ minWidth: 140,
+ height: 44,
+ fontSize: 15,
+ fontWeight: 500,
+ }}
+ >
+ 保存设置
+
+
+
+ );
+
const renderGeneralSettings = () => {
return (
);
};
@@ -476,280 +671,341 @@ const SystemSettings = () => {
const renderAppearanceSettings = () => {
return (
);
};
const renderAboutPage = () => {
- const aboutKeys = [
- 'app_version',
- 'company_name',
- 'contact_email',
- 'contact_phone',
- 'company_address',
- 'system_description',
- 'privacy_policy',
- 'terms_of_service',
- ];
-
return (
- {/* 系统概览卡片 */}
-
-
+
+
-
-
+
+
机柜管理系统
-
- 版本 {settings.app_version?.value || '1.0.0'}
- |
-
+
+ 版本 {settings.app_version?.value || '1.0.0'}
+ |
+ 运行正常} />
+
+ 专业的数据中心设备管理平台,提供机房、机柜、设备的全生命周期管理
+
- {/* 统计信息卡片 */}
{systemInfo && (
-
- 系统统计
-
- }
- style={{ marginBottom: 16, borderRadius: 8 }}
- size="small"
+ style={{ marginBottom: 16, borderRadius: 12, border: '1px solid #e8e8e8' }}
+ bodyStyle={{ padding: '20px' }}
>
-
-
-
-
+
+
+ } size={32} />
+ 系统统计
+
+
+
+
+
+
{systemInfo.statistics?.devices || 0}
-
设备总数
-
+
设备总数
+
-
-
-
+
+
+
{systemInfo.statistics?.racks || 0}
-
机柜总数
-
+
机柜总数
+
-
-
-
+
+
+
{systemInfo.statistics?.rooms || 0}
-
机房总数
-
+
机房总数
+
-
-
-
+
+
+
{systemInfo.statistics?.users || 0}
-
用户总数
-
+
用户总数
+
-
+
-
+
- {systemInfo.system?.nodeVersion}
+ {systemInfo.system?.nodeVersion}
- {systemInfo.system?.platform} ({systemInfo.system?.arch})
+ {systemInfo.system?.platform} ({systemInfo.system?.arch})
- {systemInfo.system?.pid}
+ {systemInfo.system?.pid}
- {systemInfo.system?.uptime
+ {systemInfo.system?.uptime
? `${Math.floor(systemInfo.system.uptime / 3600)}小时${Math.floor((systemInfo.system.uptime % 3600) / 60)}分钟`
- : '-'}
+ : '-'}
- {systemInfo.system?.memoryUsage
+ {systemInfo.system?.memoryUsage
? `${(systemInfo.system.memoryUsage.heapUsed / 1024 / 1024).toFixed(2)} MB`
- : '-'}
+ : '-'}
- {systemInfo.timestamp
+ {systemInfo.timestamp
? new Date(systemInfo.timestamp).toLocaleString('zh-CN')
- : '-'}
+ : '-'}
)}
- {/* 公司信息卡片 */}
-
- 公司信息
-
- }
- style={{ marginBottom: 16, borderRadius: 8 }}
- size="small"
+ style={{ marginBottom: 16, borderRadius: 12, border: '1px solid #e8e8e8' }}
+ bodyStyle={{ padding: '20px' }}
>
-
);
};
- const tabItems = [
- {
- key: 'general',
- label: (
-
- 全局配置
-
- ),
- children: renderGeneralSettings(),
- },
- {
- key: 'appearance',
- label: (
-
- 外观设置
-
- ),
- children: renderAppearanceSettings(),
- },
- {
- key: 'about',
- label: (
-
- 关于系统
-
- ),
- children: renderAboutPage(),
- },
- ];
+ 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 (
-
-
-
- 系统设置
+
+ {/* 顶部固定导航栏 */}
+
+
+
+
+ 系统设置
- }
- style={{ borderRadius: 12 }}
- bodyStyle={{ padding: 0 }}
- >
-
-
+
+ {/* 桌面端导航 */}
+ {!isMobile && renderTopNav()}
+
+ {/* 移动端导航 */}
+ {isMobile && renderMobileNav()}
+
+
+
+ {/* 主内容区 - 平铺填充 */}
+
+
+ {renderContent()}
+
+
);
};