import { Menu, Moon, Sun, ChevronLeft, Home, Settings, LogOut, FileText, Server, Boxes, Smile, MessageSquare, UserCircle, FileSearch, Package, BookOpen, Search, Sliders, Network, Hash, LayoutGrid, Database, Activity, PieChart } from 'lucide-react' import { useState, useEffect } from 'react' import { Link, useMatchRoute } from '@tanstack/react-router' import { useTheme, toggleThemeWithTransition } from './use-theme' import { useAuthGuard } from '@/hooks/use-auth' import { logout } from '@/lib/fetch-with-auth' import { Button } from '@/components/ui/button' import { Kbd } from '@/components/ui/kbd' import { SearchDialog } from '@/components/search-dialog' import { ScrollArea } from '@/components/ui/scroll-area' import { HttpWarningBanner } from '@/components/http-warning-banner' import { BackToTop } from '@/components/back-to-top' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip' import { cn } from '@/lib/utils' import { formatVersion } from '@/lib/version' import type { ReactNode, ComponentType } from 'react' import type { LucideProps } from 'lucide-react' interface LayoutProps { children: ReactNode } interface MenuItem { icon: ComponentType label: string path: string tourId?: string } interface MenuSection { title: string items: MenuItem[] } export function Layout({ children }: LayoutProps) { const { checking } = useAuthGuard() // 检查认证状态 const [sidebarOpen, setSidebarOpen] = useState(true) const [mobileMenuOpen, setMobileMenuOpen] = useState(false) const [searchOpen, setSearchOpen] = useState(false) const [tooltipsEnabled, setTooltipsEnabled] = useState(false) // 控制 tooltip 启用状态 const { theme, setTheme } = useTheme() const matchRoute = useMatchRoute() // 侧边栏状态变化时,延迟启用/禁用 tooltip useEffect(() => { if (sidebarOpen) { // 侧边栏展开时,立即禁用 tooltip setTooltipsEnabled(false) } else { // 侧边栏收起时,等待动画完成后再启用 tooltip const timer = setTimeout(() => { setTooltipsEnabled(true) }, 350) // 稍大于 CSS transition duration (300ms) return () => clearTimeout(timer) } }, [sidebarOpen]) // 搜索快捷键监听(Cmd/Ctrl + K) useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if ((e.metaKey || e.ctrlKey) && e.key === 'k') { e.preventDefault() setSearchOpen(true) } } window.addEventListener('keydown', handleKeyDown) return () => window.removeEventListener('keydown', handleKeyDown) }, []) // 认证检查中,显示加载状态 if (checking) { return (
正在验证登录状态...
) } // 菜单项配置 - 分块结构 const menuSections: MenuSection[] = [ { title: '概览', items: [ { icon: Home, label: '首页', path: '/' }, ], }, { title: '麦麦配置编辑', items: [ { icon: FileText, label: '麦麦主程序配置', path: '/config/bot' }, { icon: Server, label: 'AI模型厂商配置', path: '/config/modelProvider', tourId: 'sidebar-model-provider' }, { icon: Boxes, label: '模型管理与分配', path: '/config/model', tourId: 'sidebar-model-management' }, { icon: Sliders, label: '麦麦适配器配置', path: '/config/adapter' }, ], }, { title: '麦麦资源管理', items: [ { icon: Smile, label: '表情包管理', path: '/resource/emoji' }, { icon: MessageSquare, label: '表达方式管理', path: '/resource/expression' }, { icon: Hash, label: '黑话管理', path: '/resource/jargon' }, { icon: UserCircle, label: '人物信息管理', path: '/resource/person' }, { icon: Network, label: '知识库图谱可视化', path: '/resource/knowledge-graph' }, { icon: Database, label: '麦麦知识库管理', path: '/resource/knowledge-base' }, ], }, { title: '扩展与监控', items: [ { icon: Package, label: '插件市场', path: '/plugins' }, { icon: LayoutGrid, label: '配置模板市场', path: '/config/pack-market' }, { icon: Sliders, label: '插件配置', path: '/plugin-config' }, { icon: FileSearch, label: '日志查看器', path: '/logs' }, { icon: Activity, label: '计划器&回复器监控', path: '/planner-monitor' }, { icon: MessageSquare, label: '本地聊天室', path: '/chat' }, ], }, { title: '系统', items: [ { icon: Settings, label: '系统设置', path: '/settings' }, ], }, ] // 获取实际应用的主题(处理 system 情况) const getActualTheme = () => { if (theme === 'system') { return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' } return theme } const actualTheme = getActualTheme() // 登出处理 const handleLogout = async () => { await logout() } return (
{/* Sidebar */} {/* Mobile overlay */} {mobileMenuOpen && (
setMobileMenuOpen(false)} /> )} {/* Main content */}
{/* HTTP 安全警告横幅 */} {/* Topbar */}
{/* 移动端菜单按钮 */} {/* 桌面端侧边栏收起/展开按钮 */}
{/* 年度总结入口 */} {/* 搜索框 */} {/* 搜索对话框 */} {/* 麦麦文档链接 */} {/* 主题切换按钮 */} {/* 分隔线 */}
{/* 登出按钮 */}
{/* Page content */}
{children}
{/* Back to Top Button */}
) }