"use client" import * as React from "react" import Link from "next/link" import {usePathname, useRouter} from "next/navigation" import {APP_BUILD_DATE, APP_VERSION} from "@/lib/app-info" 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, Database, 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/database", icon: Database }, { title: "系统日志", url: "/admin/logs", icon: Terminal }, { title: "系统配置", url: "/admin/system", icon: ShieldCheck }, { 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 }, ], } function parseMenuDisplayConfig(raw: string | undefined): Record { if (!raw) return {} try { const parsed: unknown = JSON.parse(raw) if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {} return Object.entries(parsed).reduce>((result, [key, value]) => { if (typeof value === "boolean") { result[key] = value } return result }, {}) } catch { return {} } } /** * 应用侧边栏组件 * 显示应用侧边栏 * * @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 = parseMenuDisplayConfig(config?.menu_display_config) return data.navMain.filter((item) => displayConfig[item.url] !== false) }, [config]) const adminFiltered = React.useMemo(() => { const displayConfig = parseMenuDisplayConfig(config?.menu_display_config) return data.admin.filter((item) => displayConfig[item.url] !== false) }, [config]) const documentFiltered = React.useMemo(() => { const displayConfig = parseMenuDisplayConfig(config?.menu_display_config) return data.document.filter((item) => displayConfig[item.url] !== false) }, [config]) return ( <> {hasAvatarDecoration && showEffect && BackgroundEffect && }
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 {APP_VERSION}
{APP_BUILD_DATE &&
Build At {APP_BUILD_DATE}
}
!isLoggingOut && setShowLogoutDialog(open)}> 确认登出 {isLoggingOut ? '正在登出,请稍候...' : '您确定要登出当前账户吗?'} 取消 {isLoggingOut && } {isLoggingOut ? '登出中...' : '确认登出'} ) }