/** * Admin Layout with Sidebar */ import { useState, useEffect } from 'react'; import { Outlet, useNavigate, useLocation } from 'react-router-dom'; import { Layout, Menu, Avatar, Dropdown } from 'antd'; import { MenuFoldOutlined, MenuUnfoldOutlined, DashboardOutlined, DatabaseOutlined, DownloadOutlined, UserOutlined, LogoutOutlined, RocketOutlined, AppstoreOutlined, SettingOutlined, TeamOutlined, ControlOutlined, LockOutlined, IdcardOutlined, StarOutlined, } from '@ant-design/icons'; import type { MenuProps } from 'antd'; import { authAPI } from '../../utils/request'; import { auth } from '../../utils/auth'; import { useToast } from '../../contexts/ToastContext'; const { Header, Sider, Content } = Layout; // Icon mapping const iconMap: Record = { dashboard: , database: , planet: , data: , download: , settings: , users: , sliders: , profile: , star: , }; export function AdminLayout() { const [collapsed, setCollapsed] = useState(false); const [menus, setMenus] = useState([]); const [loading, setLoading] = useState(true); const navigate = useNavigate(); const location = useLocation(); const user = auth.getUser(); const toast = useToast(); // Load menus from backend useEffect(() => { loadMenus(); }, []); // Redirect to first menu if on root path useEffect(() => { if (menus.length > 0 && (location.pathname === '/admin' || location.pathname === '/user')) { const firstMenu = menus[0]; if (firstMenu.path) { navigate(firstMenu.path, { replace: true }); } } }, [menus, location.pathname, navigate]); const loadMenus = async () => { try { const { data } = await authAPI.getMenus(); setMenus(data); } catch (error) { toast.error('加载菜单失败'); } finally { setLoading(false); } }; // Convert backend menu to Ant Design menu format const convertMenus = (menus: any[], isChild = false): MenuProps['items'] => { return menus.map((menu) => { const item: any = { key: menu.path || menu.name, icon: isChild ? null : (iconMap[menu.icon || ''] || null), label: menu.title, }; if (menu.children && menu.children.length > 0) { item.children = convertMenus(menu.children, true); } return item; }); }; const handleMenuClick: MenuProps['onClick'] = ({ key }) => { navigate(key); }; const handleLogout = async () => { try { await authAPI.logout(); auth.logout(); toast.success('登出成功'); navigate('/login'); } catch (error) { // Even if API fails, clear local auth auth.logout(); navigate('/login'); } }; const userMenuItems: MenuProps['items'] = [ { key: 'change-password', icon: , label: '修改密码', onClick: () => navigate('/admin/change-password'), }, { type: 'divider', }, { key: 'logout', icon: , label: '退出登录', onClick: handleLogout, }, ]; return (
{collapsed ? '🌌' : '🌌 COSMO'}
setCollapsed(!collapsed)} style={{ fontSize: 18, cursor: 'pointer' }} > {collapsed ? : }
} style={{ marginRight: 8 }} /> {user?.username || 'User'}
); }