import { useState, useEffect, useCallback } from 'react'; import { Layout, Menu, Avatar, Dropdown, Typography, Badge, Popover, List, Tag, Empty } from 'antd'; import { DashboardOutlined, MonitorOutlined, BarChartOutlined, AlertOutlined, FileTextOutlined, CloudOutlined, SettingOutlined, UserOutlined, MenuFoldOutlined, MenuUnfoldOutlined, LogoutOutlined, BellOutlined, ThunderboltOutlined, AppstoreOutlined, WarningOutlined, CloseCircleOutlined, InfoCircleOutlined, FundProjectionScreenOutlined, GlobalOutlined, } from '@ant-design/icons'; import { Outlet, useNavigate, useLocation } from 'react-router-dom'; import { getUser, removeToken } from '../utils/auth'; import { getAlarmStats, getAlarmEvents } from '../services/api'; const { Header, Sider, Content } = Layout; const { Text } = Typography; const menuItems = [ { key: '/', icon: , label: '能源总览' }, { key: '/monitoring', icon: , label: '实时监控' }, { key: '/devices', icon: , label: '设备管理' }, { key: '/analysis', icon: , label: '能耗分析' }, { key: '/alarms', icon: , label: '告警管理' }, { key: '/carbon', icon: , label: '碳排放管理' }, { key: '/reports', icon: , label: '报表管理' }, { key: 'bigscreen-group', icon: , label: '可视化大屏', children: [ { key: '/bigscreen', icon: , label: '2D 能源大屏' }, { key: '/bigscreen-3d', icon: , label: '3D 园区大屏' }, ], }, { key: '/system', icon: , label: '系统管理', children: [ { key: '/system/users', label: '用户管理' }, { key: '/system/roles', label: '角色权限' }, { key: '/system/settings', label: '系统设置' }, ], }, ]; const SEVERITY_CONFIG: Record = { critical: { icon: , color: 'red' }, warning: { icon: , color: 'orange' }, info: { icon: , color: 'blue' }, }; export default function MainLayout() { const [collapsed, setCollapsed] = useState(false); const [alarmCount, setAlarmCount] = useState(0); const [recentAlarms, setRecentAlarms] = useState([]); const navigate = useNavigate(); const location = useLocation(); const user = getUser(); const fetchAlarms = useCallback(async () => { try { const [stats, events] = await Promise.all([ getAlarmStats(), getAlarmEvents({ status: 'active', page_size: 5 }), ]); // Stats shape: { severity: { status: count } } — sum all "active" counts const statsData = (stats as any) || {}; let activeTotal = 0; for (const severity of Object.values(statsData)) { if (severity && typeof severity === 'object') { activeTotal += (severity as any).active || 0; } } setAlarmCount(activeTotal); const items = (events as any)?.items || (events as any) || []; setRecentAlarms(Array.isArray(items) ? items : []); } catch { // silently ignore - notifications are non-critical } }, []); useEffect(() => { fetchAlarms(); const timer = setInterval(fetchAlarms, 30000); return () => clearInterval(timer); }, [fetchAlarms]); const handleLogout = () => { removeToken(); localStorage.removeItem('user'); navigate('/login'); }; const userMenu = { items: [ { key: 'profile', icon: , label: '个人信息' }, { type: 'divider' as const }, { key: 'logout', icon: , label: '退出登录', onClick: handleLogout }, ], }; return ( {!collapsed && 天普EMS} { if (key === '/bigscreen' || key === '/bigscreen-3d') { window.open(key, '_blank'); } else { navigate(key); } }} /> setCollapsed(!collapsed)}> {collapsed ? : } 告警通知 {alarmCount > 0 && {alarmCount} 条活跃} } content={ {recentAlarms.length === 0 ? ( ) : ( { const sev = SEVERITY_CONFIG[alarm.severity] || SEVERITY_CONFIG.info; return ( navigate('/alarms')} > {alarm.device_name || alarm.title || '未知设备'}} description={<> {alarm.message || alarm.title} {alarm.triggered_at} >} /> ); }} /> )} navigate('/alarms')} style={{ fontSize: 13 }}>查看全部告警 } > } style={{ background: '#1890ff' }} /> {user?.full_name || user?.username || '用户'} ); }