"use client" import * as React from "react" import Link from "next/link" import {usePathname, useRouter} from "next/navigation" import packageJson from "../../package.json" import {toast} from "sonner" import {Button} from "@/components/ui/button" import {Spinner} from "@/components/ui/spinner" import {AnimateIcon} from "@/components/animate-ui/icons/icon" import {ChevronLeft} from "@/components/animate-ui/icons/chevron-left" import {ChevronRight} from "@/components/animate-ui/icons/chevron-right" import {Avatar, AvatarFallback, AvatarImage} from "@/components/ui/avatar" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import {getCurrentTheme} from "@/components/layout/avater-style/registry" import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, useSidebar, } from "@/components/ui/sidebar" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog" import { ArrowUpRight, ChevronDown, CreditCard, FileQuestionMark, FileText, FolderOpen, Home, Layers, LogOut, Palette, Settings, ShieldCheck, Terminal, UserRound, } from "lucide-react" import {useUser} from "@/contexts/user-context" import {usePublicConfig} from "@/hooks/use-public-config" /* 导航数据 */ const data = { navMain: [ { title: "首页", url: "/home", icon: Home }, ], admin: [ { title: "用户管理", url: "/admin/users", icon: UserRound }, { title: "文件管理", url: "/admin/files", icon: FolderOpen }, { title: "任务管理", url: "/admin/tasks", icon: Layers }, { title: "系统配置", url: "/admin/system", icon: ShieldCheck }, { title: "系统日志", url: "/admin/logs", icon: Terminal }, { title: "系统设置", url: "/admin/settings", icon: Settings }, ], document: [ { title: "组件库", url: "/components", icon: Palette }, { title: "接口文档", url: "/docs/api", icon: CreditCard, external: true }, { title: "使用文档", url: "/docs/how-to-use", icon: FileText, external: true }, ], } /** * 应用侧边栏组件 * 显示应用侧边栏 * * @example * ```tsx * * ``` * @returns {React.ReactNode} 应用侧边栏组件 */ export function AppSidebar({ ...props }: React.ComponentProps) { const { toggleSidebar, state, isMobile, setOpenMobile } = useSidebar() const { user, logout } = useUser() const { config } = usePublicConfig() const [showLogoutDialog, setShowLogoutDialog] = React.useState(false) const [isLoggingOut, setIsLoggingOut] = React.useState(false) const pathname = usePathname() const router = useRouter() const currentTheme = getCurrentTheme() const { MainDecoration, InteractionDecoration, BackgroundEffect, smallMainDecorationConfig, largeMainDecorationConfig, interactionDecorationConfig } = currentTheme const hasAvatarDecoration = currentTheme.key !== "none" const [showEffect, setShowEffect] = React.useState(false) const [userMenuOpen, setUserMenuOpen] = React.useState(false) const userMenuLastToggle = React.useRef(0) const handleUserMenuOpenChange = React.useCallback((open: boolean) => { const now = Date.now() if (now - userMenuLastToggle.current < 65) return userMenuLastToggle.current = now setUserMenuOpen(open) }, []) const handleCloseSidebar = React.useCallback(() => { if (isMobile) { setOpenMobile(false) } }, [isMobile, setOpenMobile]) const handleLogout = async () => { setIsLoggingOut(true) try { await logout() setShowLogoutDialog(false) } catch (error) { toast.error("登出失败", { description: error instanceof Error ? error.message : "登出时发生错误,请重试" }) setIsLoggingOut(false) } } const navMainFiltered = React.useMemo(() => { const displayConfig = config?.menu_display_config || {} return data.navMain.filter((item) => displayConfig[item.url] !== false) }, [config]) const adminFiltered = React.useMemo(() => { const displayConfig = config?.menu_display_config || {} return data.admin.filter((item) => displayConfig[item.url] !== false) }, [config]) const documentFiltered = React.useMemo(() => { const displayConfig = config?.menu_display_config || {} return data.document.filter((item) => displayConfig[item.url] !== false) }, [config]) return ( <> {hasAvatarDecoration && showEffect && BackgroundEffect && } {state === "expanded" ? ( ) : ( )} {hasAvatarDecoration && } {user?.nickname?.charAt(0)?.toUpperCase() || "L"} {user?.nickname || user?.username || "Unknown User"} {user?.is_admin ? "系统管理员" : "普通用户"} setShowEffect(!showEffect) : undefined} > {hasAvatarDecoration && ( )} {hasAvatarDecoration && } {user?.nickname?.charAt(0)?.toUpperCase() || "L"} {user?.nickname || user?.username || "Unknown User"} {user?.is_admin ? "系统管理员" : "普通用户"} { router.push("/settings/profile") handleCloseSidebar() }}> 我的资料 { router.push("/settings") handleCloseSidebar() }}> 设置 { router.push("/docs/how-to-use") handleCloseSidebar() }}> 使用帮助 setShowLogoutDialog(true)}> 退出登录 {navMainFiltered.length > 0 && ( {navMainFiltered.map((item) => ( {item.icon && } {item.title} ))} )} {user?.is_admin && adminFiltered.length > 0 && ( 管理 {adminFiltered.map((item) => ( {item.icon && } {item.title} ))} )} {documentFiltered.length > 0 && ( 文档库 {documentFiltered.map((item) => ( {item.external ? ( {item.icon && } {item.title} ) : ( {item.icon && } {item.title} )} ))} )} Version {packageJson.version} Build At {packageJson.buildDate} !isLoggingOut && setShowLogoutDialog(open)}> 确认登出 {isLoggingOut ? '正在登出,请稍候...' : '您确定要登出当前账户吗?'} 取消 {isLoggingOut && } {isLoggingOut ? '登出中...' : '确认登出'} > ) }