'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, Bell, ChevronDown, Database, FileQuestionMark, FileText, FolderOpen, Layers, LogOut, Settings, ShieldCheck, Terminal, UserRound, } from 'lucide-react'; import { useUser } from '@/contexts/user-context'; import { usePublicConfig } from '@/hooks/use-public-config'; import { OpenFlareSidebarMenu } from '@/components/layout/openflare-sidebar-menu'; import { openflareSidebarNav } from '@/lib/navigation/openflare-nav'; /* 导航数据 */ const data = { admin: [ { title: '用户管理', url: '/admin/users', icon: UserRound }, { title: '任务管理', url: '/admin/tasks', icon: Layers }, { title: '存储管理', url: '/admin/files', icon: FolderOpen }, { title: '数据管理', url: '/admin/database', icon: Database }, { title: '通知推送', url: '/admin/push', icon: Bell }, { title: '系统日志', url: '/admin/logs', icon: Terminal }, { title: '系统配置', url: '/admin/system', icon: ShieldCheck }, { title: '系统设置', url: '/admin/settings', icon: Settings }, ], document: [ { title: '使用文档', url: 'https://open-flare.pages.dev/', 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, loading: userLoading } = 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 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'}
{userLoading ? '加载中...' : user?.nickname || user?.username || '未登录'} {userLoading ? '正在同步账户' : user?.is_admin ? '系统管理员' : '普通用户'}
{ router.push('/settings/profile'); handleCloseSidebar(); }} > 我的资料 { router.push('/settings'); handleCloseSidebar(); }} > 设置 { window.open( 'https://open-flare.pages.dev/', '_blank', 'noopener,noreferrer', ); handleCloseSidebar(); }} > 使用帮助 setShowLogoutDialog(true)} > 退出登录
{openflareSidebarNav.length > 0 && ( )} {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 ? '登出中...' : '确认登出'} ); }