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 = (

{!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