import { useState, useEffect } from 'react' import { useNavigate, useLocation } from 'react-router-dom' import { DashboardOutlined, DesktopOutlined, GlobalOutlined, CloudServerOutlined, FileSearchOutlined, UserOutlined, AppstoreOutlined, SettingOutlined, BlockOutlined, FolderOutlined, FileTextOutlined, SafetyOutlined, TeamOutlined, ProjectOutlined, RocketOutlined, ReadOutlined, BookOutlined, CommentOutlined, } from '@ant-design/icons' import { getUserMenus } from '@/api/menu' import Toast from '@/components/Toast/Toast' import useUserStore from '@/stores/userStore' import ModernSidebar from '../ModernSidebar/ModernSidebar' // 图标映射 const iconMap = { DashboardOutlined: , DesktopOutlined: , GlobalOutlined: , CloudServerOutlined: , FileSearchOutlined: , UserOutlined: , AppstoreOutlined: , SettingOutlined: , BlockOutlined: , FolderOutlined: , FileTextOutlined: , SafetyOutlined: , TeamOutlined: , ProjectOutlined: , ReadOutlined: , BookOutlined: , CommentOutlined: , } const builtInMenuMetaMap = { dashboard: { path: '/dashboard', icon: 'DashboardOutlined', }, desktop: { path: '/desktop', icon: 'DesktopOutlined', }, 'projects:my': { path: '/projects/my', icon: 'FolderOutlined', }, 'projects:share': { path: '/projects/share', icon: 'TeamOutlined', }, 'knowledge:my': { path: '/chat', icon: 'CommentOutlined', }, 'system:users': { path: '/system/users', icon: 'UserOutlined', }, 'system:roles': { path: '/system/roles', icon: 'TeamOutlined', }, 'system:permissions': { path: '/system/permissions', icon: 'SafetyOutlined', }, 'system:model-configs': { path: '/system/model-configs', icon: 'CloudServerOutlined', }, 'system:logs': { path: '/system/logs', icon: 'FileSearchOutlined', }, } const resolveBuiltInMenuMeta = (item) => { const directMeta = builtInMenuMetaMap[item.menu_code] if (directMeta) { return directMeta } if (item.path === '/dashboard' || item.menu_name === '管理面板') { return { path: '/dashboard', icon: 'DashboardOutlined', } } if (item.path === '/desktop' || item.menu_name === '个人桌面') { return { path: '/desktop', icon: 'DesktopOutlined', } } if ( item.path === '/projects' || item.path === '/projects/my' || item.menu_name === '我的项目' || item.menu_name === '项目空间' ) { return { path: '/projects/my', icon: 'FolderOutlined', } } if (item.path === '/projects/share' || item.menu_name === '参与项目') { return { path: '/projects/share', icon: 'TeamOutlined', } } if (item.path === '/knowledge' || item.path === '/knowledge/my' || item.menu_name === '我的知识库') { return { path: '/chat', icon: 'CommentOutlined', } } if (item.path === '/system/users' || item.menu_name === '用户管理') { return { path: '/system/users', icon: 'UserOutlined', } } if (item.path === '/system/roles' || item.menu_name === '角色管理') { return { path: '/system/roles', icon: 'TeamOutlined', } } if (item.path === '/system/permissions' || item.menu_name === '权限管理') { return { path: '/system/permissions', icon: 'SafetyOutlined', } } if (item.path === '/system/model-configs' || item.menu_name === '模型配置') { return { path: '/system/model-configs', icon: 'CloudServerOutlined', } } if (item.path === '/system/logs' || item.menu_name === '系统日志') { return { path: '/system/logs', icon: 'FileSearchOutlined', } } return null } function AppSider({ collapsed, onToggle }) { const navigate = useNavigate() const location = useLocation() const { user, logout } = useUserStore() const [menuGroups, setMenuGroups] = useState([]) // 加载菜单数据 useEffect(() => { loadMenus() }, []) const loadMenus = async () => { try { const res = await getUserMenus() if (res.data) { // 过滤菜单:只显示 type=1 (目录) 和 type=2 (菜单) const validMenus = res.data.filter(item => [1, 2].includes(item.menu_type)) transformMenuData(validMenus) } } catch (error) { console.error('Load menus error:', error) Toast.error('加载菜单失败') } } const transformMenuData = (data) => { const groups = [] // 默认组 (用于存放一级菜单即是叶子节点的情况) const defaultGroup = { title: '', // 空标题或 '通用' items: [] } data.forEach(item => { // 检查是否有子菜单 const validChildren = item.children ? item.children.filter(child => [1, 2].includes(child.menu_type)) : [] if (validChildren.length > 0) { // 一级菜单作为组标题 const groupItems = validChildren.map(child => { const normalizedChild = normalizeMenuItem(child) const icon = typeof normalizedChild.icon === 'string' ? (iconMap[normalizedChild.icon] || ) : normalizedChild.icon return { key: normalizedChild.menu_code, label: normalizedChild.menu_name, icon: icon, path: normalizedChild.path } }) groups.push({ title: item.menu_name, // e.g. "系统管理" items: groupItems }) } else { // 一级菜单是叶子节点,放入默认组 const normalizedItem = normalizeMenuItem(item) const icon = typeof normalizedItem.icon === 'string' ? (iconMap[normalizedItem.icon] || ) : normalizedItem.icon defaultGroup.items.push({ key: normalizedItem.menu_code, label: normalizedItem.menu_name, icon: icon, path: normalizedItem.path }) } }) // 如果默认组有内容,放在最前面 if (defaultGroup.items.length > 0) { groups.unshift(defaultGroup) } setMenuGroups(groups) } const normalizeMenuItem = (item) => { const builtInMeta = resolveBuiltInMenuMeta(item) if (!builtInMeta) { return item } return { ...item, icon: builtInMeta.icon || item.icon, path: builtInMeta.path || item.path, } } const handleNavigate = (key, item) => { if (item.path) { navigate(item.path) } } const handleLogout = () => { logout() navigate('/login') } const handleProfileClick = () => { navigate('/profile') } // 获取当前激活的 key // 简单匹配 path const getActiveKey = () => { const path = location.pathname // 遍历所有 items 找匹配 for (const group of menuGroups) { for (const item of group.items) { if (item.path === path) return item.key } } return '' } const logoNode = (
logo {!collapsed && ( NexDocus )}
) // 获取用户头像URL const getUserAvatarUrl = () => { if (!user?.avatar) return null // avatar 字段存储的是相对路径,如:2/avatar/xxx.jpg // 需要转换为 API 端点: /api/v1/auth/avatar/{user_id}/{filename} // 如果已经是 http 开头(第三方),则直接返回 if (user.avatar.startsWith('http')) return user.avatar const parts = user.avatar.split('/') if (parts.length >= 3) { const userId = parts[0] const filename = parts[2] return `/api/v1/auth/avatar/${userId}/${filename}` } return null } const userObj = user ? { name: user.nickname || user.username, role: user.role_name || 'Admin', avatar: getUserAvatarUrl() } : null return ( ) } export default AppSider