feat: 升级系统版本至1.2.0并优化仪表盘UI

This commit is contained in:
zhang1106
2026-04-27 13:56:49 +08:00
parent 41b8aba2c7
commit e1367267f8
8 changed files with 1112 additions and 383 deletions
@@ -7,6 +7,9 @@ import {
BarChartOutlined,
AppstoreOutlined,
SettingOutlined,
ApartmentOutlined,
LinkOutlined,
ArrowRightOutlined,
} from '@ant-design/icons';
import { designTokens } from '../../config/theme';
@@ -17,6 +20,7 @@ const NAV_BUTTONS_DATA = [
text: '设备管理',
path: '/devices',
color: designTokens.colors.primary.main,
description: '设备全生命周期管理',
},
{
key: 'racks',
@@ -24,20 +28,23 @@ const NAV_BUTTONS_DATA = [
text: '资源规划',
path: '/racks',
color: '#722ed1',
},
{
key: 'faults',
icon: WarningOutlined,
text: '故障监控',
path: '/faults',
color: designTokens.colors.warning.main,
description: '机柜和机房管理',
},
{
key: 'tickets',
icon: BarChartOutlined,
text: '工单管理',
icon: WarningOutlined,
text: '故障监控',
path: '/tickets',
color: designTokens.colors.warning.main,
description: '工单和故障处理',
},
{
key: 'analytics',
icon: BarChartOutlined,
text: '数据分析',
path: '/',
color: '#13c2c2',
description: '数据统计和分析',
},
{
key: 'consumables',
@@ -45,95 +52,290 @@ const NAV_BUTTONS_DATA = [
text: '耗材管理',
path: '/consumables',
color: '#fa8c16',
description: '耗材库存和领用',
},
{
key: 'visualization',
icon: ApartmentOutlined,
text: '3D可视化',
path: '/visualization-3d',
color: designTokens.colors.success.main,
description: '3D机房可视化',
},
{
key: 'cables',
icon: LinkOutlined,
text: '线缆管理',
path: '/cables',
color: '#eb2f96',
description: '线缆连接管理',
},
{
key: 'settings',
icon: SettingOutlined,
text: '系统配置',
path: '/settings',
color: designTokens.colors.success.main,
color: '#8c8c8c',
description: '系统设置和管理',
},
];
const createNavButtonStyle = (color, isHovered) => ({
height: 'auto',
padding: 'clamp(16px, 4vw, 24px) clamp(12px, 3vw, 20px)',
borderRadius: designTokens.borderRadius.medium,
border: `2px solid ${isHovered ? color : '#f0f0f0'}`,
background: '#fff',
transition: `all ${designTokens.transitions.normal}`,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 'clamp(8px, 2vw, 12px)',
cursor: 'pointer',
boxShadow: isHovered ? designTokens.shadows.large : designTokens.shadows.small,
transform: isHovered ? 'translateY(-4px)' : 'none',
minWidth: 0,
});
const createNavIconContainer = color => ({
width: 'clamp(44px, 10vw, 60px)',
height: 'clamp(44px, 10vw, 60px)',
borderRadius: designTokens.borderRadius.medium,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: `linear-gradient(135deg, ${color}20 0%, ${color}10 100%)`,
fontSize: 'clamp(20px, 5vw, 28px)',
transition: `all ${designTokens.transitions.normal}`,
flexShrink: 0,
});
const navTextStyle = {
fontSize: 'clamp(0.75rem, 2vw, 0.9rem)',
fontWeight: '600',
color: designTokens.colors.text.primary,
textAlign: 'center',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
maxWidth: '100%',
};
const NavigationGrid = ({ hoveredCard, onHover }) => {
const navigate = useNavigate();
const firstRow = NAV_BUTTONS_DATA.slice(0, 4);
const secondRow = NAV_BUTTONS_DATA.slice(4, 8);
return (
<div
className="nav-grid"
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(100px, 1fr))',
gap: 'clamp(8px, 2vw, 16px)',
marginBottom: '24px',
}}
>
{NAV_BUTTONS_DATA.map(({ key, icon: Icon, text, color }) => {
const renderRow = (items) => (
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(4, 1fr)',
gap: '10px',
marginBottom: '10px',
}}>
{items.map(({ key, icon: Icon, text, color, description, path }) => {
const isHovered = hoveredCard === `nav-${key}`;
return (
<div
key={key}
className="nav-button"
style={{
...createNavButtonStyle(color, isHovered),
animationDelay: `${NAV_BUTTONS_DATA.findIndex(b => b.key === key) * 0.1}s`,
padding: '14px 12px',
borderRadius: '12px',
background: '#fff',
border: `1.5px solid ${isHovered ? color : '#f0f0f0'}`,
cursor: 'pointer',
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
boxShadow: isHovered
? `0 12px 28px ${color}12, 0 4px 12px ${color}06`
: designTokens.shadows.small,
transform: isHovered ? 'translateY(-3px)' : 'none',
position: 'relative',
overflow: 'hidden',
}}
onMouseEnter={() => onHover(`nav-${key}`)}
onMouseLeave={() => onHover(null)}
onClick={() => navigate(`/${key}`)}
onClick={() => navigate(path)}
>
<div className="nav-icon" style={createNavIconContainer(color)}>
<Icon style={{ color, fontSize: 'clamp(20px, 5vw, 28px)' }} />
{/* 装饰性背景元素 */}
<div style={{
position: 'absolute',
right: '-15px',
top: '-15px',
width: '70px',
height: '70px',
borderRadius: '50%',
background: `linear-gradient(135deg, ${color}08 0%, ${color}03 100%)`,
opacity: isHovered ? 1 : 0.6,
transition: 'all 0.3s ease',
}} />
{/* 次要装饰元素 */}
<div style={{
position: 'absolute',
right: '15px',
bottom: '-20px',
width: '50px',
height: '50px',
borderRadius: '50%',
background: `linear-gradient(135deg, ${color}05 0%, transparent 100%)`,
opacity: isHovered ? 0.8 : 0.4,
transition: 'all 0.3s ease',
}} />
<div style={{
display: 'flex',
flexDirection: 'column',
gap: '10px',
position: 'relative',
zIndex: 1,
height: '100%',
}}>
{/* 顶部区域:图标和箭头 */}
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'flex-start',
}}>
{/* 图标容器 */}
<div style={{
width: '40px',
height: '40px',
borderRadius: '10px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: isHovered
? `linear-gradient(135deg, ${color} 0%, ${color}dd 100%)`
: `linear-gradient(135deg, ${color}12 0%, ${color}06 100%)`,
color: isHovered ? '#fff' : color,
fontSize: '18px',
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
boxShadow: isHovered
? `0 6px 16px ${color}30`
: 'none',
flexShrink: 0,
}}>
<Icon style={{ fontSize: '20px', transition: 'transform 0.3s ease', transform: isHovered ? 'scale(1.1)' : 'scale(1)' }} />
</div>
{/* 箭头指示器 */}
<div style={{
width: '24px',
height: '24px',
borderRadius: '8px',
background: isHovered
? `linear-gradient(135deg, ${color} 0%, ${color}dd 100%)`
: '#f5f7fa',
color: isHovered ? '#fff' : color,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '12px',
transition: 'all 0.3s ease',
opacity: isHovered ? 1 : 0.5,
transform: isHovered ? 'translateX(3px)' : 'translateX(0)',
}}>
<ArrowRightOutlined />
</div>
</div>
{/* 文本内容区域 */}
<div style={{
display: 'flex',
flexDirection: 'column',
gap: '6px',
}}>
{/* 标题 */}
<div style={{
fontSize: '0.9rem',
fontWeight: '700',
color: designTokens.colors.text.primary,
display: 'flex',
alignItems: 'center',
gap: '4px',
letterSpacing: '-0.01em',
}}>
{text}
</div>
{/* 描述文本 */}
<div style={{
fontSize: '0.78rem',
color: designTokens.colors.text.secondary,
lineHeight: '1.4',
fontWeight: '400',
}}>
{description}
</div>
</div>
</div>
<span className="nav-text" style={navTextStyle}>
{text}
</span>
</div>
);
})}
</div>
);
return (
<div>
{/* 区域标题 */}
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: '16px',
}}>
<div style={{
display: 'flex',
alignItems: 'center',
gap: '10px',
}}>
<div style={{
width: '36px',
height: '36px',
borderRadius: '10px',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: '#fff',
fontSize: '18px',
boxShadow: '0 3px 10px rgba(102, 126, 234, 0.3)',
}}>
<AppstoreOutlined />
</div>
<div>
<h3 style={{
fontSize: '1.1rem',
fontWeight: '700',
color: designTokens.colors.text.primary,
margin: '0',
letterSpacing: '-0.01em',
}}>
功能导航
</h3>
<p style={{
fontSize: '0.82rem',
color: designTokens.colors.text.secondary,
margin: '5px 0 0 0',
}}>
快速访问系统核心功能模块
</p>
</div>
</div>
</div>
{/* 导航网格 - 第一排 */}
{renderRow(firstRow)}
{/* 导航网格 - 第二排 */}
{renderRow(secondRow)}
{/* 响应式样式 */}
<style>{`
@media (max-width: 1400px) {
.nav-button {
padding: 12px 10px !important;
}
}
@media (max-width: 1200px) {
.nav-button {
padding: 12px 10px !important;
}
}
@media (max-width: 992px) {
.nav-button {
padding: 12px 10px !important;
}
}
@media (max-width: 768px) {
.nav-button {
padding: 12px 10px !important;
}
.nav-grid > div > div {
grid-template-columns: repeat(2, 1fr) !important;
gap: 8px !important;
}
}
@media (max-width: 576px) {
.nav-grid > div > div {
grid-template-columns: repeat(2, 1fr) !important;
gap: 8px !important;
}
}
@media (max-width: 400px) {
.nav-grid > div > div {
grid-template-columns: repeat(2, 1fr) !important;
gap: 6px !important;
}
}
`}</style>
</div>
);
};
export default React.memo(NavigationGrid);