'use client' import Link from 'next/link' import { usePathname, useRouter } from 'next/navigation' import { useEffect, useMemo, useState } from 'react' import { BarChart3, Bot, LayoutDashboard, LogOut, Settings } from 'lucide-react' import { Button } from '@/components/ui/button' import { cn } from '@/lib/utils' import { ADMIN_UNAUTHORIZED_EVENT } from '@/lib/admin-api' const API_BASE = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8030' export default function AdminLayout({ children, }: { children: React.ReactNode }) { const router = useRouter() const pathname = usePathname() const [authChecked, setAuthChecked] = useState(false) const [adminName, setAdminName] = useState('') const navItems = useMemo( () => [ { href: '/admin', label: '概览', icon: LayoutDashboard }, { href: '/admin/providers', label: 'AI 配置', icon: Bot }, { href: '/admin/stats', label: '统计', icon: BarChart3 }, { href: '/admin/settings', label: '设置', icon: Settings }, ], [] ) const getToken = () => localStorage.getItem('admin_token') || '' const fetchMe = async () => { const token = getToken() if (!token) return try { const res = await fetch(`${API_BASE}/api/v1/admin/me`, { headers: { Authorization: `Bearer ${token}` }, }) if (res.status === 401) throw new Error('unauthorized') if (!res.ok) return const data = await res.json() setAdminName(data.username || data.sub || '') } catch { localStorage.removeItem('admin_token') router.replace('/login') } } useEffect(() => { const token = getToken() if (!token) { router.replace('/login') return } setAuthChecked(true) fetchMe() // eslint-disable-next-line react-hooks/exhaustive-deps }, []) useEffect(() => { const onUnauthorized = () => { localStorage.removeItem('admin_token') router.replace('/login') } window.addEventListener(ADMIN_UNAUTHORIZED_EVENT, onUnauthorized) return () => window.removeEventListener(ADMIN_UNAUTHORIZED_EVENT, onUnauthorized) }, [router]) const handleLogout = () => { localStorage.removeItem('admin_token') router.replace('/login') } if (!authChecked) { return (
加载中...
) } return (
{/* Sidebar */} {/* Main */}
管理后台
{navItems.map((item) => { const active = pathname === item.href || (item.href !== '/admin' && pathname.startsWith(item.href)) return ( {item.label} ) })}
{adminName && (
{adminName}
)}
{children}
) }