/**
* AdminLayout - 管理后台布局
*
* 布局:侧栏 248px + 顶栏 64px + 内容区
*/
import { useState, useEffect } from 'react'
import { Layout, Menu, Avatar, Dropdown, Spin, Drawer, Button, type MenuProps } from 'antd'
import {
DashboardOutlined,
SafetyOutlined,
FileTextOutlined,
ApiOutlined,
UserOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined,
MenuOutlined,
LogoutOutlined,
SettingOutlined,
} from '@ant-design/icons'
import type { ReactNode } from 'react'
import { Outlet, useLocation, useNavigate } from 'react-router-dom'
import { useAuth } from '@/app/providers/auth-context'
import { useBreadcrumbs } from '@/lib/hooks/useBreadcrumbs'
import styles from './AdminLayout.module.css'
const { Sider, Header, Content } = Layout
// 管理员菜单配置
const adminMenuItems: MenuProps['items'] = [
{
key: '/dashboard',
icon: ,
label: '总览',
},
{
key: 'access-control',
icon: ,
label: '访问控制',
children: [
{ key: '/users', label: '用户管理' },
{ key: '/roles', label: '角色管理' },
{ key: '/permissions', label: '权限管理' },
],
},
{
key: 'logs',
icon: ,
label: '审计日志',
children: [
{ key: '/logs/login', label: '登录日志' },
{ key: '/logs/operation', label: '操作日志' },
],
},
{
key: 'integration',
icon: ,
label: '集成能力',
children: [
{ key: '/webhooks', label: 'Webhooks' },
{ key: '/import-export', label: '导入导出' },
],
},
{
key: 'profile',
icon: ,
label: '我的账户',
children: [
{ key: '/profile', label: '个人资料' },
{ key: '/profile/security', label: '安全设置' },
],
},
]
// 非管理员菜单配置(只有 Webhooks 和个人中心)
const userMenuItems: MenuProps['items'] = [
{
key: '/webhooks',
icon: ,
label: 'Webhooks',
},
{
key: 'profile',
icon: ,
label: '我的账户',
children: [
{ key: '/profile', label: '个人资料' },
{ key: '/profile/security', label: '安全设置' },
],
},
]
interface AdminLayoutProps {
children?: ReactNode
}
export function AdminLayout({ children }: AdminLayoutProps) {
const [collapsed, setCollapsed] = useState(false)
const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false)
const [isMobile, setIsMobile] = useState(false)
const location = useLocation()
const navigate = useNavigate()
const { user, isAdmin, logout, isLoading } = useAuth()
const breadcrumbItems = useBreadcrumbs()
// 检测移动端
useEffect(() => {
const checkMobile = () => {
setIsMobile(window.innerWidth < 768)
}
checkMobile()
window.addEventListener('resize', checkMobile)
return () => window.removeEventListener('resize', checkMobile)
}, [])
// 移动端切换侧边栏
const toggleMobileDrawer = () => {
setMobileDrawerOpen(!mobileDrawerOpen)
}
// 移动端菜单点击后关闭抽屉
const handleMobileMenuClick: MenuProps['onClick'] = (info) => {
navigate(info.key)
setMobileDrawerOpen(false)
}
// 根据是否为管理员选择菜单
const menuItems = isAdmin ? adminMenuItems : userMenuItems
// 当前选中的菜单
const selectedKeys = [location.pathname]
// 当前展开的菜单组(根据路径决定哪个分组展开)
const openKeys = collapsed
? []
: [
...(location.pathname.startsWith('/users') ||
location.pathname.startsWith('/roles') ||
location.pathname.startsWith('/permissions')
? ['access-control']
: []),
...(location.pathname.startsWith('/logs') ? ['logs'] : []),
...(location.pathname.startsWith('/webhooks') ||
location.pathname.startsWith('/import-export')
? ['integration']
: []),
...(location.pathname.startsWith('/profile') ? ['profile'] : []),
]
const handleMenuClick: MenuProps['onClick'] = (info) => {
navigate(info.key)
}
// 处理面包屑点击
const handleBreadcrumbClick = (path: string) => {
navigate(path)
}
// 处理登出
const handleLogout = () => {
void logout()
}
// 用户下拉菜单
const userDropdownItems: MenuProps['items'] = [
{
key: 'profile',
icon: ,
label: '个人资料',
onClick: () => navigate('/profile'),
},
{
key: 'security',
icon: ,
label: '安全设置',
onClick: () => navigate('/profile/security'),
},
{ type: 'divider' },
{
key: 'logout',
icon: ,
label: '退出登录',
danger: true,
onClick: handleLogout,
},
]
// 加载中状态
if (isLoading) {
return (
)
}
return (
{/* 跳过链接 - 便于键盘用户快速跳转到主要内容 */}
跳转到主要内容
{/* 侧边栏 */}
{/* Logo 区域 */}
{collapsed ? 'UMS' : '用户管理系统'}
{/* 导航菜单 */}
{/* 右侧主体 */}
{/* 顶栏 */}
{/* 内容区 */}
{children || }
{/* 移动端抽屉式导航 */}
{collapsed ? 'UMS' : '用户管理系统'}
}
placement="left"
onClose={toggleMobileDrawer}
open={mobileDrawerOpen}
size="default"
className={styles.mobileDrawer}
styles={{ body: { padding: 0 } }}
>
)
}