import { useState, useCallback, useEffect, useMemo } from 'react' import { Outlet, useNavigate, useLocation } from 'react-router-dom' import { DashboardOutlined, TeamOutlined, CloudServerOutlined, BarChartOutlined, SwapOutlined, SettingOutlined, FileTextOutlined, MessageOutlined, RobotOutlined, LogoutOutlined, MenuFoldOutlined, MenuUnfoldOutlined, SunOutlined, MoonOutlined, ApiOutlined, } from '@ant-design/icons' import { Avatar, Dropdown, Tooltip, Drawer } from 'antd' import { useAuthStore } from '@/stores/authStore' import { useThemeStore, setThemeMode } from '@/stores/themeStore' import type { ReactNode } from 'react' // ============================================================ // Navigation Configuration // ============================================================ interface NavItem { path: string name: string icon: ReactNode permission?: string group: string } const navItems: NavItem[] = [ { path: '/', name: '仪表盘', icon: , group: '核心' }, { path: '/accounts', name: '账号管理', icon: , permission: 'account:admin', group: '资源管理' }, { path: '/model-services', name: '模型服务', icon: , permission: 'provider:manage', group: '资源管理' }, { path: '/agent-templates', name: 'Agent 模板', icon: , permission: 'model:read', group: '资源管理' }, { path: '/api-keys', name: 'API 密钥', icon: , permission: 'provider:manage', group: '资源管理' }, { path: '/usage', name: '用量统计', icon: , permission: 'admin:full', group: '运维' }, { path: '/relay', name: '中转任务', icon: , permission: 'relay:use', group: '运维' }, { path: '/logs', name: '操作日志', icon: , permission: 'admin:full', group: '运维' }, { path: '/config', name: '系统配置', icon: , permission: 'config:read', group: '系统' }, { path: '/prompts', name: '提示词管理', icon: , permission: 'prompt:read', group: '系统' }, ] // ============================================================ // Sidebar Component // ============================================================ function Sidebar({ collapsed, onNavigate, activePath, }: { collapsed: boolean onNavigate: (path: string) => void activePath: string }) { const { hasPermission } = useAuthStore() const visibleItems = navItems.filter( (item) => !item.permission || hasPermission(item.permission), ) const groups = useMemo(() => { const map = new Map() for (const item of visibleItems) { const list = map.get(item.group) || [] list.push(item) map.set(item.group, list) } return map }, [visibleItems]) return ( ) } // ============================================================ // Mobile Drawer Sidebar // ============================================================ function MobileDrawer({ open, onClose, onNavigate, activePath, }: { open: boolean onClose: () => void onNavigate: (path: string) => void activePath: string }) { return ( ) } // ============================================================ // Breadcrumb // ============================================================ const breadcrumbMap: Record = { '/': '仪表盘', '/accounts': '账号管理', '/model-services': '模型服务', '/providers': '模型服务', '/models': '模型服务', '/api-keys': 'API 密钥', '/agent-templates': 'Agent 模板', '/usage': '用量统计', '/relay': '中转任务', '/config': '系统配置', '/prompts': '提示词管理', '/logs': '操作日志', } // ============================================================ // Main Layout // ============================================================ export default function AdminLayout() { const navigate = useNavigate() const location = useLocation() const { account, logout } = useAuthStore() const themeState = useThemeStore() const [collapsed, setCollapsed] = useState(false) const [mobileOpen, setMobileOpen] = useState(false) const [isMobile, setIsMobile] = useState(false) // Responsive detection useEffect(() => { const mq = window.matchMedia('(max-width: 768px)') setIsMobile(mq.matches) const handler = (e: MediaQueryListEvent) => setIsMobile(e.matches) mq.addEventListener('change', handler) return () => mq.removeEventListener('change', handler) }, []) const handleNavigate = useCallback( (path: string) => { navigate(path) setMobileOpen(false) }, [navigate], ) const handleLogout = useCallback(() => { logout() navigate('/login', { replace: true }) }, [logout, navigate]) const toggleTheme = useCallback(() => { setThemeMode(themeState.resolved === 'dark' ? 'light' : 'dark') }, [themeState.resolved]) const currentPage = breadcrumbMap[location.pathname] || '页面' return (
{/* Desktop Sidebar */} {!isMobile && ( )} {/* Mobile Drawer */} {isMobile && ( setMobileOpen(false)} onNavigate={handleNavigate} activePath={location.pathname} /> )} {/* Main Area */}
{/* Header */}
{/* Mobile menu button */} {isMobile && ( )} {/* Collapse toggle (desktop) */} {!isMobile && ( )} {/* Breadcrumb */}
ZCLAW / {currentPage}
{/* Right actions */}
{/* Theme toggle */} {/* User avatar */} , label: '退出登录', onClick: handleLogout, }, ], }} >
{/* Content */}
) }