website-01/components/admin/AdminSidebar.tsx

112 lines
3.3 KiB
TypeScript
Raw Permalink Normal View History

2026-06-22 14:43:46 +08:00
'use client';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import {
LayoutDashboard,
Package,
Newspaper,
Users,
MessageSquare,
Settings,
UserCog,
BookOpen,
} from 'lucide-react';
import { cn } from '@/lib/utils';
import { useAdminStore } from '@/store/adminStore';
interface NavItem {
href: string;
label: string;
icon: React.ComponentType<{ className?: string }>;
/** 仅超级管理员可见 */
superAdminOnly?: boolean;
}
interface NavGroup {
label: string;
items: NavItem[];
}
const NAV_GROUPS: NavGroup[] = [
{
label: '概览',
items: [
{ href: '/admin/dashboard', label: '仪表盘', icon: LayoutDashboard },
],
},
{
label: '内容管理',
items: [
{ href: '/admin/product', label: '产品管理', icon: Package },
{ href: '/admin/news', label: '新闻管理', icon: Newspaper },
{ href: '/admin/team', label: '团队管理', icon: Users },
{ href: '/admin/message', label: '留言管理', icon: MessageSquare },
{ href: '/admin/manual', label: '使用手册', icon: BookOpen },
],
},
{
label: '系统',
items: [
{ href: '/admin/site-config', label: '网站配置', icon: Settings },
{
href: '/admin/admin-user',
label: '管理员账号',
icon: UserCog,
superAdminOnly: true,
},
],
},
];
export function AdminSidebar() {
const pathname = usePathname();
const isSuperAdmin = useAdminStore((s) => s.admin?.role) === 'super_admin';
return (
<aside className="flex h-screen w-60 flex-col bg-slate-900 text-slate-200">
<nav className="flex-1 overflow-y-auto px-2 py-2">
{NAV_GROUPS.map((group, gIdx) => {
const visibleItems = group.items.filter(
(item) => !item.superAdminOnly || isSuperAdmin,
);
if (visibleItems.length === 0) return null;
return (
<div key={group.label} className={gIdx === 0 ? '' : 'mt-4'}>
<p className="px-3 pb-1 pt-3 text-xs font-semibold uppercase tracking-wider text-slate-500">
{group.label}
</p>
<div className="space-y-0.5">
{visibleItems.map((item) => {
const active =
pathname === item.href ||
pathname.startsWith(item.href + '/');
const Icon = item.icon;
return (
<Link
key={item.href}
href={item.href}
className={cn(
'relative flex items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors duration-150',
active
? 'bg-slate-800/60 text-white'
: 'text-slate-300 hover:bg-slate-800/40 hover:text-white',
)}
>
{active && (
<span className="absolute bottom-2 left-0 top-2 w-[3px] rounded-r bg-brand-500" />
)}
<Icon className="h-[18px] w-[18px] shrink-0" />
{item.label}
</Link>
);
})}
</div>
</div>
);
})}
</nav>
</aside>
);
}