import { useState, useEffect } from 'react' import { useNavigate, useLocation } from 'react-router-dom' import { DashboardOutlined, DesktopOutlined, GlobalOutlined, CloudServerOutlined, UserOutlined, AppstoreOutlined, SettingOutlined, BlockOutlined, FolderOutlined, FileTextOutlined, SafetyOutlined, TeamOutlined, ProjectOutlined, RocketOutlined, ReadOutlined, BookOutlined, } from '@ant-design/icons' import { message } from 'antd' import { getUserMenus } from '@/api/menu' import useUserStore from '@/stores/userStore' import ModernSidebar from '../ModernSidebar/ModernSidebar' // 图标映射 const iconMap = { DashboardOutlined: , DesktopOutlined: , GlobalOutlined: , CloudServerOutlined: , UserOutlined: , AppstoreOutlined: , SettingOutlined: , BlockOutlined: , FolderOutlined: , FileTextOutlined: , SafetyOutlined: , TeamOutlined: , ProjectOutlined: , ReadOutlined: , BookOutlined: , } 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) message.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 icon = typeof child.icon === 'string' ? (iconMap[child.icon] || ) : child.icon return { key: child.menu_code, label: child.menu_name, icon: icon, path: child.path } }) groups.push({ title: item.menu_name, // e.g. "系统管理" items: groupItems }) } else { // 一级菜单是叶子节点,放入默认组 const icon = typeof item.icon === 'string' ? (iconMap[item.icon] || ) : item.icon defaultGroup.items.push({ key: item.menu_code, label: item.menu_name, icon: icon, path: item.path }) } }) // 如果默认组有内容,放在最前面 if (defaultGroup.items.length > 0) { groups.unshift(defaultGroup) } setMenuGroups(groups) } 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