/** * AdminLayout - 管理后台布局 * * 布局:侧栏 248px + 顶栏 64px + 内容区 */ import { useEffect, useState } from 'react' import { Avatar, Button, Drawer, Dropdown, Layout, Menu, Spin, type MenuProps } from 'antd' import { ApiOutlined, DashboardOutlined, FileTextOutlined, LogoutOutlined, MenuFoldOutlined, MenuOutlined, MenuUnfoldOutlined, SafetyOutlined, SettingOutlined, UserOutlined, } 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 { Content, Header, Sider } = Layout const menuLabel = (testId: string, text: string) => ( {text} ) const adminMenuItems: MenuProps['items'] = [ { key: '/dashboard', icon: , label: menuLabel('nav-dashboard', '总览'), }, { key: 'access-control', icon: , label: menuLabel('nav-group-access-control', '访问控制'), children: [ { key: '/users', label: menuLabel('nav-users', '用户管理') }, { key: '/roles', label: menuLabel('nav-roles', '角色管理') }, { key: '/permissions', label: menuLabel('nav-permissions', '权限管理') }, ], }, { key: 'logs', icon: , label: menuLabel('nav-group-logs', '审计日志'), children: [ { key: '/logs/login', label: menuLabel('nav-login-logs', '登录日志') }, { key: '/logs/operation', label: menuLabel('nav-operation-logs', '操作日志') }, ], }, { key: 'integration', icon: , label: menuLabel('nav-group-integration', '集成能力'), children: [ { key: '/webhooks', label: menuLabel('nav-webhooks', 'Webhooks') }, { key: '/import-export', label: menuLabel('nav-import-export', '导入导出') }, ], }, { key: 'profile', icon: , label: menuLabel('nav-group-profile', '我的账户'), children: [ { key: '/profile', label: menuLabel('nav-profile', '个人资料') }, { key: '/profile/security', label: menuLabel('nav-profile-security', '安全设置') }, ], }, ] const userMenuItems: MenuProps['items'] = [ { key: '/webhooks', icon: , label: menuLabel('nav-webhooks', 'Webhooks'), }, { key: 'profile', icon: , label: menuLabel('nav-group-profile', '我的账户'), children: [ { key: '/profile', label: menuLabel('nav-profile', '个人资料') }, { key: '/profile/security', label: menuLabel('nav-profile-security', '安全设置') }, ], }, ] 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 = () => { const nextIsMobile = window.innerWidth < 768 setIsMobile(nextIsMobile) if (!nextIsMobile) { setMobileDrawerOpen(false) } } checkMobile() window.addEventListener('resize', checkMobile) return () => window.removeEventListener('resize', checkMobile) }, []) const openMobileDrawer = () => { setMobileDrawerOpen(true) } const closeMobileDrawer = () => { setMobileDrawerOpen(false) } const handleMobileMenuClick: MenuProps['onClick'] = (info) => { navigate(info.key) closeMobileDrawer() } 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 ( 跳转到主要内容
{collapsed ? 'UMS' : '用户管理系统'}
{isMobile ? ( )} {breadcrumbItems && breadcrumbItems.length > 0 ? (
{breadcrumbItems.map((item, index) => ( {item.path ? ( handleBreadcrumbClick(item.path as string)} > {item.title} ) : ( {item.title} )} {index < breadcrumbItems.length - 1 ? ( / ) : null} ))}
) : null}
} src={user?.avatar || null} style={{ backgroundColor: user?.avatar ? undefined : 'var(--color-primary)' }} /> {user?.nickname || user?.username || '用户'}
{children || }
{collapsed ? 'UMS' : '用户管理系统'}} placement="left" onClose={closeMobileDrawer} open={mobileDrawerOpen} size="default" className={styles.mobileDrawer} styles={{ body: { padding: 0 } }} > ) }