From dff0515dbbc9fc8743a685f5c6fd99a51f56eb7c Mon Sep 17 00:00:00 2001
From: Asuka <15019597+asuka-civil@user.noreply.gitee.com>
Date: Fri, 20 Jun 2025 20:18:13 +0800
Subject: [PATCH] =?UTF-8?q?=E6=9A=82=E6=97=B6=E4=BF=9D=E5=AD=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../management/actions/changePassword.ts | 42 +++++
.../(app)/management/actions/getUserInfo.ts | 19 +++
src/app/(app)/management/actions/index.ts | 4 +
.../management/actions/updateUserInfo.ts | 25 +++
.../(app)/management/change-password/page.tsx | 125 +++++++++++++++
src/app/(app)/management/page.tsx | 90 +++++++++++
src/app/(app)/management/profile/page.tsx | 150 ++++++++++++++++++
.../dashboard/layout.tsx} | 34 ++--
src/app/(protected)/dashboard/page.tsx | 3 +
.../management-sidebar/manage-form.tsx | 28 ++++
.../management-sidebar/manage-sidebar.tsx | 97 +++++++++++
.../management-sidebar/manage-switcher.tsx | 64 ++++++++
src/components/nav-user.tsx | 21 ++-
src/components/sidebar/admin-sidebar.tsx | 108 +++++++++++++
src/components/sidebar/app-sidebar.tsx | 84 +++++++---
src/components/sidebar/teacher-sidebar.tsx | 8 +-
src/components/ui/donut-chart.tsx | 58 -------
src/lib/auth.ts | 5 +
18 files changed, 856 insertions(+), 109 deletions(-)
create mode 100644 src/app/(app)/management/actions/changePassword.ts
create mode 100644 src/app/(app)/management/actions/getUserInfo.ts
create mode 100644 src/app/(app)/management/actions/index.ts
create mode 100644 src/app/(app)/management/actions/updateUserInfo.ts
create mode 100644 src/app/(app)/management/change-password/page.tsx
create mode 100644 src/app/(app)/management/page.tsx
create mode 100644 src/app/(app)/management/profile/page.tsx
rename src/app/{dashboard/page.tsx => (protected)/dashboard/layout.tsx} (61%)
create mode 100644 src/app/(protected)/dashboard/page.tsx
create mode 100644 src/components/management-sidebar/manage-form.tsx
create mode 100644 src/components/management-sidebar/manage-sidebar.tsx
create mode 100644 src/components/management-sidebar/manage-switcher.tsx
create mode 100644 src/components/sidebar/admin-sidebar.tsx
delete mode 100644 src/components/ui/donut-chart.tsx
diff --git a/src/app/(app)/management/actions/changePassword.ts b/src/app/(app)/management/actions/changePassword.ts
new file mode 100644
index 0000000..018db7a
--- /dev/null
+++ b/src/app/(app)/management/actions/changePassword.ts
@@ -0,0 +1,42 @@
+// changePassword.ts
+"use server";
+
+import prisma from "@/lib/prisma";
+import bcrypt from "bcryptjs";
+
+export async function changePassword(formData: FormData) {
+ const oldPassword = formData.get("oldPassword") as string;
+ const newPassword = formData.get("newPassword") as string;
+
+ if (!oldPassword || !newPassword) {
+ throw new Error("旧密码和新密码不能为空");
+ }
+
+ try {
+ const user = await prisma.user.findUnique({
+ where: { id: '1' },
+ });
+
+ if (!user) throw new Error("用户不存在");
+
+ if (!user.password) {
+ throw new Error("用户密码未设置");
+ }
+
+ const passwordHash: string = user.password as string;
+ const isMatch = await bcrypt.compare(oldPassword, passwordHash);
+ if (!isMatch) throw new Error("旧密码错误");
+
+ const hashedPassword = await bcrypt.hash(newPassword, 10);
+
+ await prisma.user.update({
+ where: { id: '1' },
+ data: { password: hashedPassword },
+ });
+
+ return { success: true };
+ } catch (error) {
+ console.error("修改密码失败:", error);
+ throw new Error("修改密码失败");
+ }
+}
\ No newline at end of file
diff --git a/src/app/(app)/management/actions/getUserInfo.ts b/src/app/(app)/management/actions/getUserInfo.ts
new file mode 100644
index 0000000..dd83390
--- /dev/null
+++ b/src/app/(app)/management/actions/getUserInfo.ts
@@ -0,0 +1,19 @@
+// getUserInfo.ts
+"use server";
+
+import prisma from "@/lib/prisma";
+
+export async function getUserInfo() {
+ try {
+ const user = await prisma.user.findUnique({
+ where: { id: 'user_001' },
+ });
+
+ if (!user) throw new Error("用户不存在");
+
+ return user;
+ } catch (error) {
+ console.error("获取用户信息失败:", error);
+ throw new Error("获取用户信息失败");
+ }
+}
\ No newline at end of file
diff --git a/src/app/(app)/management/actions/index.ts b/src/app/(app)/management/actions/index.ts
new file mode 100644
index 0000000..5c599e5
--- /dev/null
+++ b/src/app/(app)/management/actions/index.ts
@@ -0,0 +1,4 @@
+// index.ts
+export { getUserInfo } from "./getUserInfo";
+export { updateUserInfo } from "./updateUserInfo";
+export { changePassword } from "./changePassword";
\ No newline at end of file
diff --git a/src/app/(app)/management/actions/updateUserInfo.ts b/src/app/(app)/management/actions/updateUserInfo.ts
new file mode 100644
index 0000000..201284e
--- /dev/null
+++ b/src/app/(app)/management/actions/updateUserInfo.ts
@@ -0,0 +1,25 @@
+// updateUserInfo.ts
+"use server";
+
+import prisma from "@/lib/prisma";
+
+export async function updateUserInfo(formData: FormData) {
+ const name = formData.get("name") as string;
+ const email = formData.get("email") as string;
+
+ if (!name || !email) {
+ throw new Error("缺少必要字段:name, email");
+ }
+
+ try {
+ const updatedUser = await prisma.user.update({
+ where: { id: 'user_001' },
+ data: { name, email },
+ });
+
+ return updatedUser;
+ } catch (error) {
+ console.error("更新用户信息失败:", error);
+ throw new Error("更新用户信息失败");
+ }
+}
\ No newline at end of file
diff --git a/src/app/(app)/management/change-password/page.tsx b/src/app/(app)/management/change-password/page.tsx
new file mode 100644
index 0000000..3b9b770
--- /dev/null
+++ b/src/app/(app)/management/change-password/page.tsx
@@ -0,0 +1,125 @@
+// src/app/(app)/management/change-password/page.tsx
+"use client";
+
+import { useState } from "react";
+import { changePassword } from "@/app/(app)/management/actions";
+
+export default function ChangePasswordPage() {
+ const [oldPassword, setOldPassword] = useState("");
+ const [newPassword, setNewPassword] = useState("");
+ const [confirmPassword, setConfirmPassword] = useState("");
+ const [showSuccess, setShowSuccess] = useState(false);
+
+ const getPasswordStrength = (password: string) => {
+ if (password.length < 6) return "weak";
+ if (/[A-Za-z]/.test(password) && /\d/.test(password)) return "medium";
+ return "strong";
+ };
+
+ const strengthText = getPasswordStrength(newPassword);
+ let strengthColor = "";
+ let strengthLabel = "";
+
+ switch (strengthText) {
+ case "weak":
+ strengthColor = "bg-red-500";
+ strengthLabel = "弱";
+ break;
+ case "medium":
+ strengthColor = "bg-yellow-500";
+ strengthLabel = "中等";
+ break;
+ case "strong":
+ strengthColor = "bg-green-500";
+ strengthLabel = "强";
+ break;
+ }
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ if (newPassword !== confirmPassword) {
+ alert("两次输入的密码不一致!");
+ return;
+ }
+
+ const formData = new FormData();
+ formData.append("oldPassword", oldPassword);
+ formData.append("newPassword", newPassword);
+
+ try {
+ await changePassword(formData);
+ setShowSuccess(true);
+ setTimeout(() => setShowSuccess(false), 3000);
+ } catch (error: any) {
+ alert(error.message);
+ }
+ };
+
+ return (
+
+
+
+ {showSuccess && (
+
+ ✅ 密码修改成功!
+
+ )}
+
+ );
+}
\ No newline at end of file
diff --git a/src/app/(app)/management/page.tsx b/src/app/(app)/management/page.tsx
new file mode 100644
index 0000000..03a8f8c
--- /dev/null
+++ b/src/app/(app)/management/page.tsx
@@ -0,0 +1,90 @@
+"use client"
+import React, { useState } from "react"
+import { AppSidebar } from "@/components/management-sidebar/manage-sidebar"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/components/ui/breadcrumb"
+import { Separator } from "@/components/ui/separator"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/components/ui/sidebar"
+import ProfilePage from "./profile/page"
+import ChangePasswordPage from "./change-password/page"
+
+// 模拟菜单数据
+const menuItems = [
+ { title: "登录信息", key: "profile" },
+ { title: "修改密码", key: "change-password" },
+]
+
+export default function ManagementDefaultPage() {
+ const [activePage, setActivePage] = useState("profile")
+ const [isCollapsed, setIsCollapsed] = useState(false)
+
+ const renderContent = () => {
+ switch (activePage) {
+ case "profile":
+ return
+ case "change-password":
+ return
+ default:
+ return
+ }
+ }
+
+ const toggleSidebar = () => {
+ setIsCollapsed((prev) => !prev)
+ }
+
+ return (
+
+
+ {/* 左侧侧边栏 */}
+ {!isCollapsed && (
+
+ )}
+
+ {/* 右侧主内容区域 */}
+
+
+ {/* 折叠按钮 */}
+
+
+
+ {/* 面包屑导航 */}
+
+
+
+ 管理面板
+
+
+
+
+ {menuItems.find((item) => item.key === activePage)?.title}
+
+
+
+
+
+ {/* 主体内容:根据 isCollapsed 切换样式 */}
+
+ {renderContent()}
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/(app)/management/profile/page.tsx b/src/app/(app)/management/profile/page.tsx
new file mode 100644
index 0000000..4659aba
--- /dev/null
+++ b/src/app/(app)/management/profile/page.tsx
@@ -0,0 +1,150 @@
+// src/app/(app)/management/profile/page.tsx
+"use client";
+
+import { useEffect, useState } from "react";
+import { getUserInfo, updateUserInfo } from "@/app/(app)/management/actions";
+
+interface User {
+ id: string; // TEXT 类型
+ name: string | null; // 可能为空
+ email: string; // NOT NULL
+ emailVerified: Date | null; // TIMESTAMP 转换为字符串
+ image: string | null;
+ role: "GUEST" | "USER" | "ADMIN"; // 枚举类型
+ createdAt: Date; // TIMESTAMP 转换为字符串
+ updatedAt: Date; // TIMESTAMP 转换为字符串
+}
+
+export default function ProfilePage() {
+ const [user, setUser] = useState(null);
+ const [isEditing, setIsEditing] = useState(false);
+
+ useEffect(() => {
+ async function fetchUser() {
+ try {
+ const data = await getUserInfo();
+ setUser(data);
+ } catch (error) {
+ console.error("获取用户信息失败:", error);
+ }
+ }
+
+ fetchUser();
+ }, []);
+
+ const handleSave = async () => {
+ const nameInput = document.getElementById("name") as HTMLInputElement | null;
+ const emailInput = document.getElementById("email") as HTMLInputElement | null;
+
+ if (!nameInput || !emailInput) {
+ alert("表单元素缺失");
+ return;
+ }
+
+ const formData = new FormData();
+ formData.append("name", nameInput.value);
+ formData.append("email", emailInput.value);
+
+ try {
+ const updatedUser = await updateUserInfo(formData);
+ setUser(updatedUser);
+ setIsEditing(false);
+ } catch (error: any) {
+ alert(error.message);
+ }
+};
+
+ if (!user) return 加载中...
;
+
+ return (
+
+
+
用户信息
+
+
+
+
+ {isEditing ? (
+
+ ) : (
+
{user?.name || "未提供"}
+ )}
+
角色:{user?.role}
+
邮箱验证时间:{user.emailVerified ? new Date(user.emailVerified).toLocaleString() : "未验证"}
+
+
+
+
+
+
+
+
+
+
+ {isEditing ? (
+
+ ) : (
+
{user.email}
+ )}
+
+
+
+
+
{new Date(user.createdAt).toLocaleString()}
+
+
+
+
+
{new Date(user.updatedAt).toLocaleString()}
+
+
+
+
+ {isEditing ? (
+ <>
+
+
+ >
+ ) : (
+
+ )}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/app/dashboard/page.tsx b/src/app/(protected)/dashboard/layout.tsx
similarity index 61%
rename from src/app/dashboard/page.tsx
rename to src/app/(protected)/dashboard/layout.tsx
index a6876ff..565b2a7 100644
--- a/src/app/dashboard/page.tsx
+++ b/src/app/(protected)/dashboard/layout.tsx
@@ -1,4 +1,4 @@
-import { AppSidebar } from "@/components/sidebar/app-sidebar"
+import { AppSidebar } from "@/components/sidebar/app-sidebar";
import {
Breadcrumb,
BreadcrumbItem,
@@ -6,18 +6,29 @@ import {
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
-} from "@/components/ui/breadcrumb"
-import { Separator } from "@/components/ui/separator"
+} from "@/components/ui/breadcrumb";
+import { Separator } from "@/components/ui/separator";
import {
SidebarInset,
SidebarProvider,
SidebarTrigger,
-} from "@/components/ui/sidebar"
+} from "@/components/ui/sidebar";
+import { auth } from "@/lib/auth";
+import { notFound } from "next/navigation";
-export default function Page() {
+interface LayoutProps {
+ children: React.ReactNode;
+}
+
+export default async function Layout({ children }: LayoutProps) {
+ const session = await auth();
+ const user = session?.user;
+ if (!user) {
+ notFound();
+ }
return (
-
+
-
+ {children}
- )
+ );
}
diff --git a/src/app/(protected)/dashboard/page.tsx b/src/app/(protected)/dashboard/page.tsx
new file mode 100644
index 0000000..80efbfb
--- /dev/null
+++ b/src/app/(protected)/dashboard/page.tsx
@@ -0,0 +1,3 @@
+export default function Page() {
+ return Dashboard
+}
diff --git a/src/components/management-sidebar/manage-form.tsx b/src/components/management-sidebar/manage-form.tsx
new file mode 100644
index 0000000..a120602
--- /dev/null
+++ b/src/components/management-sidebar/manage-form.tsx
@@ -0,0 +1,28 @@
+import { Search } from "lucide-react"
+
+import { Label } from "@/components/ui/label"
+import {
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarInput,
+} from "@/components/ui/sidebar"
+
+export function SearchForm({ ...props }: React.ComponentProps<"form">) {
+ return (
+
+ )
+}
\ No newline at end of file
diff --git a/src/components/management-sidebar/manage-sidebar.tsx b/src/components/management-sidebar/manage-sidebar.tsx
new file mode 100644
index 0000000..fb026d3
--- /dev/null
+++ b/src/components/management-sidebar/manage-sidebar.tsx
@@ -0,0 +1,97 @@
+import * as React from "react";
+import { ChevronRight } from "lucide-react";
+
+import { VersionSwitcher } from "@/components//management-sidebar/manage-switcher";
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/components/ui/collapsible";
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarHeader,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarRail,
+} from "@/components/ui/sidebar";
+
+// 自定义数据:包含用户相关菜单项
+const data = {
+ versions: ["1.0.1", "1.1.0-alpha", "2.0.0-beta1"],
+ navUser: [
+ {
+ title: "个人中心",
+ url: "#",
+ items: [
+ { title: "登录信息", url: "#", key: "profile" },
+ { title: "修改密码", url: "#", key: "change-password" },
+ ],
+ },
+ ],
+};
+
+// 显式定义 props 类型
+interface AppSidebarProps {
+ onItemClick?: (key: string) => void;
+}
+
+export function AppSidebar({ onItemClick = (key: string) => {}, ...props }: AppSidebarProps) {
+ return (
+
+
+
+
+
+ {/* 渲染用户相关的侧边栏菜单 */}
+ {data.navUser.map((item) => (
+
+
+
+
+ {item.title}
+
+
+
+
+
+
+ {item.items.map((subItem) => (
+
+ {
+ e.preventDefault();
+ onItemClick(subItem.key);
+ }}
+ >
+ {subItem.title}
+
+
+ ))}
+
+
+
+
+
+ ))}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/management-sidebar/manage-switcher.tsx b/src/components/management-sidebar/manage-switcher.tsx
new file mode 100644
index 0000000..054995b
--- /dev/null
+++ b/src/components/management-sidebar/manage-switcher.tsx
@@ -0,0 +1,64 @@
+"use client"
+
+import * as React from "react"
+import { Check, ChevronsUpDown, GalleryVerticalEnd } from "lucide-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu"
+import {
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/components/ui/sidebar"
+
+export function VersionSwitcher({
+ versions,
+ defaultVersion,
+}: {
+ versions: string[]
+ defaultVersion: string
+}) {
+ const [selectedVersion, setSelectedVersion] = React.useState(defaultVersion)
+
+ return (
+
+
+
+
+
+
+
+
+
+ Documentation
+ v{selectedVersion}
+
+
+
+
+
+ {versions.map((version) => (
+ setSelectedVersion(version)}
+ >
+ v{version}{" "}
+ {version === selectedVersion && }
+
+ ))}
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/components/nav-user.tsx b/src/components/nav-user.tsx
index ec6f1f6..1fa7a82 100644
--- a/src/components/nav-user.tsx
+++ b/src/components/nav-user.tsx
@@ -43,6 +43,19 @@ export function NavUser({
const { isMobile } = useSidebar()
const router = useRouter()
+ async function handleLogout() {
+ await fetch("/api/auth/signout", { method: "POST" });
+ router.replace("/sign-in");
+ }
+
+ function handleAccount() {
+ if (user && user.email) {
+ router.replace("/user/profile");
+ } else {
+ router.replace("/sign-in");
+ }
+ }
+
return (
@@ -90,11 +103,11 @@ export function NavUser({
-
+
Account
- router.push("/sign-in")}>
+ router.push("/sign-in")}>
Switch User
@@ -104,9 +117,7 @@ export function NavUser({
- {
- router.replace("/");
- }}>
+
Log out
diff --git a/src/components/sidebar/admin-sidebar.tsx b/src/components/sidebar/admin-sidebar.tsx
new file mode 100644
index 0000000..b5ead92
--- /dev/null
+++ b/src/components/sidebar/admin-sidebar.tsx
@@ -0,0 +1,108 @@
+"use client"
+import { siteConfig } from "@/config/site"
+import * as React from "react"
+import {
+ LifeBuoy,
+ Send,
+ Shield,
+} from "lucide-react"
+
+import { NavMain } from "@/components/nav-main"
+import { NavProjects } from "@/components/nav-projects"
+import { NavSecondary } from "@/components/nav-secondary"
+import { NavUser } from "@/components/nav-user"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarFooter,
+ SidebarHeader,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/components/ui/sidebar"
+
+import { useEffect, useState } from "react"
+
+const adminData = {
+ navMain: [
+ {
+ title: "管理面板",
+ url: "#",
+ icon: Shield,
+ isActive: true,
+ items: [
+ { title: "管理员管理", url: "/usermanagement/admin" },
+ { title: "用户管理", url: "/usermanagement/guest" },
+ { title: "教师管理", url: "/usermanagement/teacher" },
+ { title: "题目管理", url: "/usermanagement/problem" },
+ ],
+ },
+
+ ],
+ navSecondary: [
+ { title: "帮助", url: "/", icon: LifeBuoy },
+ { title: "反馈", url: siteConfig.url.repo.github, icon: Send },
+ ],
+ wrongProblems: [],
+}
+
+async function fetchCurrentUser() {
+ try {
+ const res = await fetch("/api/auth/session");
+ if (!res.ok) return null;
+ const session = await res.json();
+ return {
+ name: session?.user?.name ?? "未登录管理员",
+ email: session?.user?.email ?? "",
+ avatar: session?.user?.image ?? "/avatars/default.jpg",
+ };
+ } catch {
+ return {
+ name: "未登录管理员",
+ email: "",
+ avatar: "/avatars/default.jpg",
+ };
+ }
+}
+
+export function AdminSidebar(props: React.ComponentProps) {
+ const [user, setUser] = useState({
+ name: "未登录管理员",
+ email: "",
+ avatar: "/avatars/default.jpg",
+ });
+
+ useEffect(() => {
+ fetchCurrentUser().then(u => u && setUser(u));
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ Admin
+ 管理后台
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/components/sidebar/app-sidebar.tsx b/src/components/sidebar/app-sidebar.tsx
index ff41c19..9038c9e 100644
--- a/src/components/sidebar/app-sidebar.tsx
+++ b/src/components/sidebar/app-sidebar.tsx
@@ -1,6 +1,7 @@
-"use client"
-import { siteConfig } from "@/config/site"
-import * as React from "react"
+"use client";
+
+import { siteConfig } from "@/config/site";
+import * as React from "react";
import {
BookOpen,
Command,
@@ -8,12 +9,12 @@ import {
Send,
Settings2,
SquareTerminal,
-} from "lucide-react"
+} from "lucide-react";
-import { NavMain } from "@/components/nav-main"
-import { NavProjects } from "@/components/nav-projects"
-import { NavSecondary } from "@/components/nav-secondary"
-import { NavUser } from "@/components/nav-user"
+import { NavMain } from "@/components/nav-main";
+import { NavProjects } from "@/components/nav-projects";
+import { NavSecondary } from "@/components/nav-secondary";
+import { NavUser } from "@/components/nav-user";
import {
Sidebar,
SidebarContent,
@@ -22,15 +23,13 @@ import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
-} from "@/components/ui/sidebar"
+} from "@/components/ui/sidebar";
+import { User } from "next-auth";
+// import { useEffect, useState } from "react"
+// import { auth, signIn } from "@/lib/auth"
const data = {
- user: {
- name: "shadcn",
- email: "m@example.com",
- avatar: "/avatars/shadcn.jpg",
- },
navMain: [
{
title: "页面",
@@ -52,7 +51,7 @@ const data = {
},
],
},
-
+
{
title: "已完成事项",
url: "#",
@@ -66,7 +65,7 @@ const data = {
title: "错题集",
url: "#",
},
- {
+ {
title: "收藏集",
url: "#",
},
@@ -77,10 +76,6 @@ const data = {
url: "#",
icon: Settings2,
items: [
- {
- title: "一般设置",
- url: "#",
- },
{
title: "语言",
url: "#",
@@ -117,11 +112,50 @@ const data = {
status: "TLE",
},
],
+};
+
+// // 获取当前登录用户信息的 API
+// async function fetchCurrentUser() {
+// try {
+// const res = await fetch("/api/auth/session");
+// if (!res.ok) return null;
+// const session = await res.json();
+// return {
+// name: session?.user?.name ?? "未登录用户",
+// email: session?.user?.email ?? "",
+// avatar: session?.user?.image ?? "/avatars/default.jpg",
+// };
+// } catch {
+// return {
+// name: "未登录用户",
+// email: "",
+// avatar: "/avatars/default.jpg",
+// };
+// }
+// }
+
+interface AppSidebarProps{
+ user:User
}
-export function AppSidebar({ ...props }: React.ComponentProps) {
+export function AppSidebar({ user, ...props }: AppSidebarProps) {
+ // const [user, setUser] = useState({
+ // name: "未登录用户",
+ // email: "",
+ // avatar: "/avatars/default.jpg",
+ // });
+
+ // useEffect(() => {
+ // fetchCurrentUser().then(u => u && setUser(u));
+ // }, []);
+ const userInfo = {
+ name: user.name ?? "",
+ email: user.email ?? "",
+ avatar: user.image ?? "",
+ };
+
return (
-
+
@@ -145,8 +179,8 @@ export function AppSidebar({ ...props }: React.ComponentProps) {
-
+
- )
-}
\ No newline at end of file
+ );
+}
diff --git a/src/components/sidebar/teacher-sidebar.tsx b/src/components/sidebar/teacher-sidebar.tsx
index 49babac..2eecd71 100644
--- a/src/components/sidebar/teacher-sidebar.tsx
+++ b/src/components/sidebar/teacher-sidebar.tsx
@@ -36,10 +36,6 @@ const data = {
icon: SquareTerminal,
isActive: true,
items: [
- {
- title: "课程管理",
- url: "/teacher/courses",
- },
{
title: "学生管理",
url: "/teacher/students",
@@ -56,11 +52,11 @@ const data = {
icon: PieChart,
items: [
{
- title: "成绩统计",
+ title: "完成情况",
url: "/teacher/statistics/grades",
},
{
- title: "错题分析",
+ title: "错题统计",
url: "/teacher/statistics/activity",
},
],
diff --git a/src/components/ui/donut-chart.tsx b/src/components/ui/donut-chart.tsx
deleted file mode 100644
index c6f3296..0000000
--- a/src/components/ui/donut-chart.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-// 简单的环形图组件,使用 SVG 实现
-import React from "react";
-
-interface DonutChartProps {
- percent: number; // 完成比例 0-100
- size?: number; // 图表直径
- strokeWidth?: number; // 圆环宽度
- color?: string; // 完成部分颜色
- bgColor?: string; // 未完成部分颜色
-}
-
-export function DonutChart({
- percent,
- size = 120,
- strokeWidth = 16,
- color = "#3b82f6",
- bgColor = "#e5e7eb",
-}: DonutChartProps) {
- const radius = (size - strokeWidth) / 2;
- const circumference = 2 * Math.PI * radius;
- const offset = circumference * (1 - percent / 100);
-
- return (
-
- );
-}
diff --git a/src/lib/auth.ts b/src/lib/auth.ts
index 0643271..96ba401 100644
--- a/src/lib/auth.ts
+++ b/src/lib/auth.ts
@@ -247,3 +247,8 @@ export const { auth, handlers, signIn, signOut } = NextAuth({
},
},
});
+
+export const getCurrentUser=async ()=>{
+ const session=await auth();
+ return session?.user
+}