| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- "use client";
- import {
- GraduationCap,
- LayoutDashboard,
- BarChart2,
- Users,
- Settings,
- LogOut
- } from "lucide-react";
- import Image from "next/image";
- import Link from "next/link";
- import { usePathname } from "next/navigation";
- import { useLayout } from "@/components/LayoutProvider";
- import { useAuth } from "@/components/AuthProvider";
- export default function Sidebar() {
- const pathname = usePathname();
- const { mobileMenuOpen, setMobileMenuOpen } = useLayout();
- const { user, logout } = useAuth();
- const navItems = [
- { name: "总览面板", href: "/", icon: LayoutDashboard, roles: ["stu", "teacher"] },
- { name: "能力分析", href: "/analysis", icon: BarChart2, roles: ["stu"] },
- { name: "学生管理", href: "/students", icon: Users, roles: ["teacher"] },
- { name: "系统设置", href: "/settings", icon: Settings, roles: ["teacher"] },
- ].filter(item => item.roles.includes(user?.role || "stu"));
- return (
- <>
- {/* Mobile backdrop */}
- {mobileMenuOpen && (
- <div
- className="fixed inset-0 bg-background/80 backdrop-blur-sm z-30 lg:hidden"
- onClick={() => setMobileMenuOpen(false)}
- />
- )}
-
- <aside
- className={`fixed inset-y-0 left-0 bg-surface border-r border-outline-variant flex flex-col justify-between z-40 w-64 lg:w-72 lg:static shrink-0 transition-transform duration-300 ease-in-out ${
- mobileMenuOpen ? "translate-x-0" : "-translate-x-full lg:translate-x-0"
- }`}
- >
- <div>
- <div className="h-20 flex items-center justify-center px-4 lg:px-6 w-full border-b border-outline-variant/30">
- <Link href="/" className="flex items-center justify-center hover:opacity-80 transition-opacity w-full">
- <Image
- src="/banner.png"
- alt="SEEC Analysis Banner"
- width={160}
- height={40}
- className="object-contain mx-auto"
- priority
- />
- </Link>
- </div>
- <nav className="mt-4 px-3 space-y-2">
- {navItems.map((item) => {
- const isActive = pathname === item.href || (pathname?.startsWith("/metrics") && item.href === "/analysis");
- const activeClass = isActive
- ? "bg-primary-container text-on-primary-container"
- : "text-secondary hover:bg-surface-container-high hover:text-on-surface";
-
- return (
- <Link
- key={item.name}
- className={`flex items-center px-4 py-3 rounded-full group transition-colors ${activeClass}`}
- href={item.href}
- >
- <item.icon className="w-5 h-5 shrink-0" />
- <span className="ml-3 font-medium text-sm">
- {item.name}
- </span>
- </Link>
- )
- })}
- </nav>
- </div>
- <div className="p-4 lg:p-6 mt-auto">
- <div className="flex items-center justify-between p-3 rounded-xl bg-surface-container border border-outline-variant/30 hover:bg-surface-container-high transition-colors group">
- <Link
- href="/profile"
- className="flex items-center flex-1 cursor-pointer"
- onClick={() => setMobileMenuOpen(false)}
- >
- <div className="relative shrink-0">
- <Image
- alt="User Profile"
- className="h-10 w-10 rounded-lg object-contain ring-2 ring-surface group-hover:ring-primary/50 transition-all bg-white"
- src={user?.avatar || "/images/profile-avatar.png"}
- width={40}
- height={40}
- referrerPolicy="no-referrer"
- />
- <span className="absolute bottom-0 right-0 h-2.5 w-2.5 bg-green-400 rounded-full ring-2 ring-surface-container group-hover:ring-surface-container-high transition-colors"></span>
- </div>
- <div className="ml-3">
- <p className="text-sm font-medium text-on-surface group-hover:text-primary transition-colors">{user?.name || "未知用户"}</p>
- <p className="text-xs text-outline group-hover:text-secondary transition-colors">{user?.role === 'teacher' ? '指导教师' : '学生'}</p>
- </div>
- </Link>
- <button
- onClick={() => {
- setMobileMenuOpen(false);
- logout();
- }}
- className="p-2 text-outline hover:text-metric-low hover:bg-metric-low/10 rounded-lg transition-colors ml-2"
- title="退出登录"
- >
- <LogOut className="w-5 h-5" />
- </button>
- </div>
- </div>
- </aside>
- </>
- );
- }
|