import { useCallback, useState, memo, useEffect } from 'react'; import { Layout, Avatar, Space, Dropdown, Tooltip, theme } from 'antd'; import { HomeOutlined, UserOutlined, SafetyOutlined, ApartmentOutlined, SettingOutlined, MenuFoldOutlined, MenuUnfoldOutlined, PartitionOutlined, LogoutOutlined, MessageOutlined, SearchOutlined, BulbOutlined, BulbFilled, AppstoreOutlined, TeamOutlined, TableOutlined, TagsOutlined, RightOutlined, } from '@ant-design/icons'; import { useNavigate, useLocation } from 'react-router-dom'; import { useAppStore } from '../stores/app'; import { useAuthStore } from '../stores/auth'; import { usePluginStore } from '../stores/plugin'; import type { PluginMenuGroup } from '../stores/plugin'; import NotificationPanel from '../components/NotificationPanel'; const { Header, Sider, Content, Footer } = Layout; interface MenuItem { key: string; icon: React.ReactNode; label: string; } const mainMenuItems: MenuItem[] = [ { key: '/', icon: , label: '工作台' }, { key: '/users', icon: , label: '用户管理' }, { key: '/roles', icon: , label: '权限管理' }, { key: '/organizations', icon: , label: '组织架构' }, ]; const bizMenuItems: MenuItem[] = [ { key: '/workflow', icon: , label: '工作流' }, { key: '/messages', icon: , label: '消息中心' }, ]; const sysMenuItems: MenuItem[] = [ { key: '/settings', icon: , label: '系统设置' }, { key: '/plugins/admin', icon: , label: '插件管理' }, ]; const routeTitleMap: Record = { '/': '工作台', '/users': '用户管理', '/roles': '权限管理', '/organizations': '组织架构', '/workflow': '工作流', '/messages': '消息中心', '/settings': '系统设置', '/plugins/admin': '插件管理', }; // 侧边栏菜单项 - 提取为独立组件避免重复渲染 const SidebarMenuItem = memo(function SidebarMenuItem({ item, isActive, collapsed, onClick, indented, }: { item: MenuItem; isActive: boolean; collapsed: boolean; onClick: () => void; indented?: boolean; }) { return (
{item.icon} {!collapsed && {item.label}}
); }); // 动态图标映射 const pluginIconMap: Record = { AppstoreOutlined: , team: , TeamOutlined: , user: , UserOutlined: , message: , MessageOutlined: , tags: , TagsOutlined: , apartment: , ApartmentOutlined: , TableOutlined: , DashboardOutlined: , }; function getPluginIcon(iconName: string): React.ReactNode { return pluginIconMap[iconName] || ; } // 插件子菜单组 — 可折叠二级标题 + 三级菜单项 const SidebarSubMenu = memo(function SidebarSubMenu({ group, collapsed, currentPath, onNavigate, }: { group: PluginMenuGroup; collapsed: boolean; currentPath: string; onNavigate: (key: string) => void; }) { const [expanded, setExpanded] = useState(true); const hasActive = group.items.some((item) => currentPath === item.key); if (collapsed) { // 折叠模式:显示插件图标,Tooltip 列出所有子项 const tooltipContent = group.items.map((item) => item.label).join(' / '); return (
{ const first = group.items[0]; if (first) onNavigate(first.key); }} className={`erp-sidebar-item ${hasActive ? 'erp-sidebar-item-active' : ''}`} >
); } return (
setExpanded((e) => !e)} > {group.pluginName}
{expanded && group.items.map((item) => ( onNavigate(item.key)} indented /> ))}
); }); export default function MainLayout({ children }: { children: React.ReactNode }) { const { sidebarCollapsed, toggleSidebar, theme: themeMode, setTheme } = useAppStore(); const { user, logout } = useAuthStore(); const { pluginMenuItems, pluginMenuGroups, fetchPlugins } = usePluginStore(); theme.useToken(); const navigate = useNavigate(); const location = useLocation(); const currentPath = location.pathname || '/'; // 加载插件菜单 useEffect(() => { fetchPlugins(1, 'running'); }, [fetchPlugins]); const handleLogout = useCallback(async () => { await logout(); navigate('/login'); }, [logout, navigate]); const userMenuItems = [ { key: 'profile', icon: , label: user?.display_name || user?.username || '用户', disabled: true, }, { type: 'divider' as const }, { key: 'logout', icon: , label: '退出登录', danger: true, onClick: handleLogout, }, ]; const sidebarWidth = sidebarCollapsed ? 72 : 240; const isDark = themeMode === 'dark'; return ( {/* 现代深色侧边栏 */} {/* Logo 区域 */}
navigate('/')}>
E
{!sidebarCollapsed && ( ERP Platform )}
{/* 菜单组:基础模块 */} {!sidebarCollapsed &&
基础模块
}
{mainMenuItems.map((item) => ( navigate(item.key)} /> ))}
{/* 菜单组:业务模块 */} {!sidebarCollapsed &&
业务模块
}
{bizMenuItems.map((item) => ( navigate(item.key)} /> ))}
{/* 菜单组:插件 */} {pluginMenuGroups.length > 0 && ( <> {!sidebarCollapsed &&
插件
}
{pluginMenuGroups.map((group) => ( navigate(key)} /> ))}
)} {/* 菜单组:系统 */} {!sidebarCollapsed &&
系统
}
{sysMenuItems.map((item) => ( navigate(item.key)} /> ))}
{/* 右侧主区域 */} {/* 顶部导航栏 */}
{/* 左侧:折叠按钮 + 标题 */}
{sidebarCollapsed ? : }
{routeTitleMap[currentPath] || pluginMenuItems.find((p) => p.key === currentPath)?.label || '页面'}
{/* 右侧:搜索 + 通知 + 主题切换 + 用户 */}
setTheme(isDark ? 'light' : 'dark')}> {isDark ? : }
{(user?.display_name?.[0] || user?.username?.[0] || 'U').toUpperCase()} {!sidebarCollapsed && ( {user?.display_name || user?.username || 'User'} )}
{/* 内容区域 */} {children} {/* 底部 */}
); }