Sidebar.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. "use client";
  2. import {
  3. GraduationCap,
  4. LayoutDashboard,
  5. BarChart2,
  6. Users,
  7. Settings,
  8. } from "lucide-react";
  9. import Image from "next/image";
  10. import Link from "next/link";
  11. import { usePathname } from "next/navigation";
  12. import { useLayout } from "@/components/LayoutProvider";
  13. export default function Sidebar() {
  14. const pathname = usePathname();
  15. const { mobileMenuOpen, setMobileMenuOpen } = useLayout();
  16. return (
  17. <>
  18. {/* Mobile backdrop */}
  19. {mobileMenuOpen && (
  20. <div
  21. className="fixed inset-0 bg-background/80 backdrop-blur-sm z-30 lg:hidden"
  22. onClick={() => setMobileMenuOpen(false)}
  23. />
  24. )}
  25. <aside
  26. 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 ${
  27. mobileMenuOpen ? "translate-x-0" : "-translate-x-full lg:translate-x-0"
  28. }`}
  29. >
  30. <div>
  31. <div className="h-20 flex items-center justify-center px-4 lg:px-6 w-full border-b border-outline-variant/30">
  32. <Link href="/" className="flex items-center justify-center hover:opacity-80 transition-opacity w-full">
  33. <Image
  34. src="/banner.png"
  35. alt="SEEC Metrics Banner"
  36. width={160}
  37. height={40}
  38. className="object-contain mx-auto"
  39. priority
  40. />
  41. </Link>
  42. </div>
  43. <nav className="mt-4 px-3 space-y-2">
  44. {[
  45. { name: "总览面板", href: "/", icon: LayoutDashboard },
  46. { name: "能力分析", href: "/analysis", icon: BarChart2 },
  47. { name: "学生管理", href: "/students", icon: Users },
  48. { name: "系统设置", href: "/settings", icon: Settings },
  49. ].map((item) => {
  50. const isActive = pathname === item.href || (pathname?.startsWith("/metrics") && item.href === "/analysis");
  51. const activeClass = isActive
  52. ? "bg-primary-container text-on-primary-container"
  53. : "text-secondary hover:bg-surface-container-high hover:text-on-surface";
  54. return (
  55. <Link
  56. key={item.name}
  57. className={`flex items-center px-4 py-3 rounded-full group transition-colors ${activeClass}`}
  58. href={item.href}
  59. >
  60. <item.icon className="w-5 h-5 shrink-0" />
  61. <span className="ml-3 font-medium text-sm">
  62. {item.name}
  63. </span>
  64. </Link>
  65. )
  66. })}
  67. </nav>
  68. </div>
  69. <div className="p-4 lg:p-6 mt-auto">
  70. <Link
  71. href="/profile"
  72. className="flex items-center justify-start p-3 rounded-xl bg-surface-container border border-outline-variant/30 hover:bg-surface-container-high cursor-pointer transition-colors group"
  73. onClick={() => setMobileMenuOpen(false)}
  74. >
  75. <div className="relative shrink-0">
  76. <Image
  77. alt="User Profile"
  78. className="h-10 w-10 rounded-lg object-contain ring-2 ring-surface group-hover:ring-primary/50 transition-all bg-white"
  79. src="/images/profile-avatar.png"
  80. width={40}
  81. height={40}
  82. referrerPolicy="no-referrer"
  83. />
  84. <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>
  85. </div>
  86. <div className="ml-3">
  87. <p className="text-sm font-medium text-on-surface group-hover:text-primary transition-colors">刘钦</p>
  88. <p className="text-xs text-outline group-hover:text-secondary transition-colors">指导教师</p>
  89. </div>
  90. </Link>
  91. </div>
  92. </aside>
  93. </>
  94. );
  95. }