mirror of
https://github.com/massbug/judge4c.git
synced 2026-05-20 13:18:52 +00:00
feat(role): rename_guest_role_to_student
This commit is contained in:
parent
c4e7a3b6f5
commit
2cbe91d487
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- The values [GUEST] on the enum `Role` will be removed. If these variants are still used in the database, this will fail.
|
||||
|
||||
*/
|
||||
-- AlterEnum
|
||||
BEGIN;
|
||||
CREATE TYPE "Role_new" AS ENUM ('ADMIN', 'STUDENT', 'TEACHER');
|
||||
ALTER TABLE "User" ALTER COLUMN "role" DROP DEFAULT;
|
||||
ALTER TABLE "User" ALTER COLUMN "role" TYPE "Role_new" USING ("role"::text::"Role_new");
|
||||
ALTER TYPE "Role" RENAME TO "Role_old";
|
||||
ALTER TYPE "Role_new" RENAME TO "Role";
|
||||
DROP TYPE "Role_old";
|
||||
ALTER TABLE "User" ALTER COLUMN "role" SET DEFAULT 'STUDENT';
|
||||
COMMIT;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "User" ALTER COLUMN "role" SET DEFAULT 'STUDENT';
|
||||
@ -10,7 +10,7 @@ generator client {
|
||||
|
||||
enum Role {
|
||||
ADMIN
|
||||
GUEST
|
||||
STUDENT
|
||||
TEACHER
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ model User {
|
||||
password String?
|
||||
emailVerified DateTime?
|
||||
image String?
|
||||
role Role @default(GUEST)
|
||||
role Role @default(STUDENT)
|
||||
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
|
||||
@ -2999,12 +2999,12 @@ export async function main() {
|
||||
where: { email },
|
||||
update: {
|
||||
name: `学生${index + 1}`,
|
||||
role: "GUEST",
|
||||
role: "STUDENT",
|
||||
},
|
||||
create: {
|
||||
name: `学生${index + 1}`,
|
||||
email,
|
||||
role: "GUEST",
|
||||
role: "STUDENT",
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
@ -41,7 +41,7 @@ export function assertTeacherOrAdmin(actor: AuthenticatedActor) {
|
||||
}
|
||||
|
||||
export function assertStudent(actor: AuthenticatedActor) {
|
||||
if (actor.role !== "GUEST") {
|
||||
if (actor.role !== "STUDENT") {
|
||||
throw new Error("仅学生可访问");
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,7 +130,7 @@ export async function enrollStudents(courseId: string, studentIds: string[]) {
|
||||
const students = await prisma.user.findMany({
|
||||
where: {
|
||||
id: { in: uniqueStudentIds },
|
||||
role: "GUEST",
|
||||
role: "STUDENT",
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
@ -197,7 +197,7 @@ export async function listAvailableStudents() {
|
||||
assertTeacherOrAdmin(actor);
|
||||
|
||||
return prisma.user.findMany({
|
||||
where: { role: "GUEST" },
|
||||
where: { role: "STUDENT" },
|
||||
orderBy: { createdAt: "desc" },
|
||||
select: {
|
||||
id: true,
|
||||
|
||||
@ -47,16 +47,16 @@ export default async function Layout({ children }: LayoutProps) {
|
||||
return <AdminSidebar user={user} />;
|
||||
case "TEACHER":
|
||||
return <TeacherSidebar user={user} />;
|
||||
case "GUEST":
|
||||
case "STUDENT":
|
||||
default:
|
||||
// 学生(GUEST)需要查询错题数据
|
||||
// 学生(STUDENT)需要查询错题数据
|
||||
return <AppSidebar user={user} wrongProblems={[]} />;
|
||||
}
|
||||
};
|
||||
|
||||
// 只有学生才需要查询错题数据
|
||||
let wrongProblemsData: WrongProblem[] = [];
|
||||
if (fullUser.role === "GUEST") {
|
||||
if (fullUser.role === "STUDENT") {
|
||||
// 查询未完成(未AC)题目的最新一次提交
|
||||
const wrongProblems = await prisma.problem.findMany({
|
||||
where: {
|
||||
@ -98,7 +98,7 @@ export default async function Layout({ children }: LayoutProps) {
|
||||
|
||||
return (
|
||||
<SidebarProvider>
|
||||
{fullUser.role === "GUEST" ? (
|
||||
{fullUser.role === "STUDENT" ? (
|
||||
<AppSidebar user={user} wrongProblems={wrongProblemsData} />
|
||||
) : (
|
||||
renderSidebar()
|
||||
|
||||
@ -12,7 +12,7 @@ interface User {
|
||||
email: string;
|
||||
emailVerified?: Date | null;
|
||||
image: string | null;
|
||||
role: "GUEST" | "USER" | "ADMIN" | "TEACHER";
|
||||
role: "STUDENT" | "ADMIN" | "TEACHER";
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
@ -94,7 +94,7 @@ export default async function DashboardPage() {
|
||||
// 教师统计
|
||||
const [totalStudents, totalProblems, totalSubmissions, recentSubmissions] =
|
||||
await Promise.all([
|
||||
prisma.user.count({ where: { role: "GUEST" } }),
|
||||
prisma.user.count({ where: { role: "STUDENT" } }),
|
||||
prisma.problem.count({ where: { isPublished: true } }),
|
||||
prisma.submission.count(),
|
||||
prisma.submission.findMany({
|
||||
@ -204,8 +204,8 @@ export default async function DashboardPage() {
|
||||
icon: Target,
|
||||
},
|
||||
{
|
||||
label: "用户管理",
|
||||
href: "/dashboard/usermanagement/guest",
|
||||
label: "学生管理",
|
||||
href: "/dashboard/usermanagement/student",
|
||||
icon: Users,
|
||||
},
|
||||
{
|
||||
@ -246,8 +246,8 @@ export default async function DashboardPage() {
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
label: "用户管理",
|
||||
href: "/dashboard/usermanagement/guest",
|
||||
label: "学生管理",
|
||||
href: "/dashboard/usermanagement/student",
|
||||
icon: Users,
|
||||
},
|
||||
{
|
||||
@ -311,7 +311,7 @@ export default async function DashboardPage() {
|
||||
|
||||
const config = getRoleConfig();
|
||||
const completionRate =
|
||||
fullUser.role === "GUEST"
|
||||
fullUser.role === "STUDENT"
|
||||
? (stats.totalProblems || 0) > 0
|
||||
? ((stats.completedProblems || 0) / (stats.totalProblems || 1)) * 100
|
||||
: 0
|
||||
@ -349,7 +349,7 @@ export default async function DashboardPage() {
|
||||
</div>
|
||||
|
||||
{/* 学生进度条 */}
|
||||
{fullUser.role === "GUEST" && (
|
||||
{fullUser.role === "STUDENT" && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
|
||||
@ -5,5 +5,5 @@ export default async function StudentCoursesLayout({
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return <ProtectedLayout roles={["GUEST"]}>{children}</ProtectedLayout>;
|
||||
return <ProtectedLayout roles={["STUDENT"]}>{children}</ProtectedLayout>;
|
||||
}
|
||||
|
||||
@ -6,10 +6,10 @@ import { Role } from "@/generated/client";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import type { User } from "@/generated/client";
|
||||
|
||||
type UserType = "admin" | "teacher" | "guest";
|
||||
type ResourceType = "admin" | "teacher" | "student";
|
||||
|
||||
export async function createUser(
|
||||
userType: UserType,
|
||||
resourceType: ResourceType,
|
||||
data: Omit<User, "id" | "createdAt" | "updatedAt"> & { password?: string }
|
||||
) {
|
||||
let password = data.password;
|
||||
@ -17,13 +17,13 @@ export async function createUser(
|
||||
password = await bcrypt.hash(password, 10);
|
||||
}
|
||||
|
||||
const role = userType.toUpperCase() as Role;
|
||||
const role = resourceType.toUpperCase() as Role;
|
||||
await prisma.user.create({ data: { ...data, password, role } });
|
||||
revalidatePath(`/usermanagement/${userType}`);
|
||||
revalidatePath(`/usermanagement/${resourceType}`);
|
||||
}
|
||||
|
||||
export async function updateUser(
|
||||
userType: UserType,
|
||||
resourceType: ResourceType,
|
||||
id: string,
|
||||
data: Partial<Omit<User, "id" | "createdAt" | "updatedAt">>
|
||||
) {
|
||||
@ -38,10 +38,10 @@ export async function updateUser(
|
||||
}
|
||||
|
||||
await prisma.user.update({ where: { id }, data: updateData });
|
||||
revalidatePath(`/usermanagement/${userType}`);
|
||||
revalidatePath(`/usermanagement/${resourceType}`);
|
||||
}
|
||||
|
||||
export async function deleteUser(userType: UserType, id: string) {
|
||||
export async function deleteUser(resourceType: ResourceType, id: string) {
|
||||
await prisma.user.delete({ where: { id } });
|
||||
revalidatePath(`/usermanagement/${userType}`);
|
||||
revalidatePath(`/usermanagement/${resourceType}`);
|
||||
}
|
||||
|
||||
@ -2,5 +2,5 @@ import { adminConfig } from "@/features/user-management/config/admin";
|
||||
import GenericPage from "@/features/user-management/components/generic-page";
|
||||
|
||||
export default function AdminPage() {
|
||||
return <GenericPage userType="admin" config={adminConfig} />;
|
||||
return <GenericPage resourceType="admin" config={adminConfig} />;
|
||||
}
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
import { guestConfig } from "@/features/user-management/config/guest";
|
||||
import GenericPage from "@/features/user-management/components/generic-page";
|
||||
|
||||
export default function GuestPage() {
|
||||
return <GenericPage userType="guest" config={guestConfig} />;
|
||||
}
|
||||
@ -2,5 +2,5 @@ import { problemConfig } from "@/features/user-management/config/problem";
|
||||
import GenericPage from "@/features/user-management/components/generic-page";
|
||||
|
||||
export default function ProblemPage() {
|
||||
return <GenericPage userType="problem" config={problemConfig} />;
|
||||
return <GenericPage resourceType="problem" config={problemConfig} />;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import GenericLayout from "../components/GenericLayout";
|
||||
|
||||
export default function GuestLayout({
|
||||
export default function StudentLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
@ -0,0 +1,6 @@
|
||||
import { studentConfig } from "@/features/user-management/config/student";
|
||||
import GenericPage from "@/features/user-management/components/generic-page";
|
||||
|
||||
export default function StudentPage() {
|
||||
return <GenericPage resourceType="student" config={studentConfig} />;
|
||||
}
|
||||
@ -2,5 +2,5 @@ import { teacherConfig } from "@/features/user-management/config/teacher";
|
||||
import GenericPage from "@/features/user-management/components/generic-page";
|
||||
|
||||
export default function TeacherPage() {
|
||||
return <GenericPage userType="teacher" config={teacherConfig} />;
|
||||
return <GenericPage resourceType="teacher" config={teacherConfig} />;
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ interface LayoutProps {
|
||||
|
||||
const Layout = ({ children }: LayoutProps) => {
|
||||
return (
|
||||
<ProtectedLayout roles={["ADMIN", "TEACHER", "GUEST"]}>
|
||||
<ProtectedLayout roles={["ADMIN", "TEACHER", "STUDENT"]}>
|
||||
{children}
|
||||
</ProtectedLayout>
|
||||
);
|
||||
|
||||
@ -84,7 +84,7 @@ export const judge = async (
|
||||
const canAccessAssignment =
|
||||
actor.role === "ADMIN" ||
|
||||
(actor.role === "TEACHER" && isTeacherOwner) ||
|
||||
(actor.role === "GUEST" && isStudentEnrolled);
|
||||
(actor.role === "STUDENT" && isStudentEnrolled);
|
||||
|
||||
if (!canAccessAssignment) {
|
||||
await createSystemErrorSubmission("No permission for assignment", {
|
||||
@ -100,7 +100,7 @@ export const judge = async (
|
||||
return Status.SE;
|
||||
}
|
||||
|
||||
if (!assignment.published && actor.role === "GUEST") {
|
||||
if (!assignment.published && actor.role === "STUDENT") {
|
||||
await createSystemErrorSubmission("Assignment is not published", {
|
||||
assignmentId: assignment.id,
|
||||
});
|
||||
|
||||
@ -44,7 +44,7 @@ export function DynamicBreadcrumb() {
|
||||
admin: "管理后台",
|
||||
teacher: "教师平台",
|
||||
student: "学生平台",
|
||||
usermanagement: "用户管理",
|
||||
usermanagement: "账号管理",
|
||||
courses: "课程",
|
||||
assignments: "作业",
|
||||
userdashboard: "用户仪表板",
|
||||
|
||||
@ -26,7 +26,7 @@ const adminData = {
|
||||
isActive: true,
|
||||
items: [
|
||||
{ title: "管理员管理", url: "/dashboard/usermanagement/admin" },
|
||||
{ title: "用户管理", url: "/dashboard/usermanagement/guest" },
|
||||
{ title: "学生管理", url: "/dashboard/usermanagement/student" },
|
||||
{ title: "教师管理", url: "/dashboard/usermanagement/teacher" },
|
||||
{ title: "题目管理", url: "/dashboard/usermanagement/problem" },
|
||||
],
|
||||
|
||||
@ -26,8 +26,8 @@ const data = {
|
||||
isActive: true,
|
||||
items: [
|
||||
{
|
||||
title: "用户管理",
|
||||
url: "/dashboard/usermanagement/guest",
|
||||
title: "学生管理",
|
||||
url: "/dashboard/usermanagement/student",
|
||||
},
|
||||
{
|
||||
title: "题目管理",
|
||||
|
||||
@ -5,19 +5,19 @@ import { UserConfig } from "./user-table";
|
||||
import type { User, Problem } from "@/generated/client";
|
||||
|
||||
interface GenericPageProps {
|
||||
userType: "admin" | "teacher" | "guest" | "problem";
|
||||
resourceType: "admin" | "teacher" | "student" | "problem";
|
||||
config: UserConfig;
|
||||
}
|
||||
|
||||
export default async function GenericPage({
|
||||
userType,
|
||||
resourceType,
|
||||
config,
|
||||
}: GenericPageProps) {
|
||||
if (userType === "problem") {
|
||||
if (resourceType === "problem") {
|
||||
const data: Problem[] = await prisma.problem.findMany({});
|
||||
return <UserTable config={config} data={data} />;
|
||||
} else {
|
||||
const role = userType.toUpperCase() as Role;
|
||||
const role = resourceType.toUpperCase() as Role;
|
||||
const data: User[] = await prisma.user.findMany({ where: { role } });
|
||||
return <UserTable config={config} data={data} />;
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ import {
|
||||
} from "@/app/(protected)/dashboard/usermanagement/actions/problemActions";
|
||||
|
||||
export interface UserConfig {
|
||||
userType: string;
|
||||
resourceType: string;
|
||||
title: string;
|
||||
apiPath: string;
|
||||
columns: Array<{
|
||||
@ -154,7 +154,7 @@ const addProblemSchema = z.object({
|
||||
});
|
||||
|
||||
export function UserTable(props: UserTableProps) {
|
||||
const isProblem = props.config.userType === "problem";
|
||||
const isProblem = props.config.resourceType === "problem";
|
||||
const router = useRouter();
|
||||
const problemData = isProblem ? (props.data as Problem[]) : undefined;
|
||||
|
||||
@ -324,7 +324,7 @@ export function UserTable(props: UserTableProps) {
|
||||
createdAt: "",
|
||||
image: null,
|
||||
emailVerified: null,
|
||||
role: Role.GUEST,
|
||||
role: Role.STUDENT,
|
||||
},
|
||||
});
|
||||
React.useEffect(() => {
|
||||
@ -336,7 +336,7 @@ export function UserTable(props: UserTableProps) {
|
||||
createdAt: "",
|
||||
image: null,
|
||||
emailVerified: null,
|
||||
role: Role.GUEST,
|
||||
role: Role.STUDENT,
|
||||
});
|
||||
}
|
||||
}, [open, form]);
|
||||
@ -354,19 +354,19 @@ export function UserTable(props: UserTableProps) {
|
||||
...data,
|
||||
image: data.image ?? null,
|
||||
emailVerified: data.emailVerified ?? null,
|
||||
role: data.role ?? Role.GUEST,
|
||||
role: data.role ?? Role.STUDENT,
|
||||
};
|
||||
if (!submitData.name) submitData.name = "";
|
||||
if (!submitData.createdAt)
|
||||
submitData.createdAt = new Date().toISOString();
|
||||
else
|
||||
submitData.createdAt = new Date(submitData.createdAt).toISOString();
|
||||
if (props.config.userType === "admin")
|
||||
if (props.config.resourceType === "admin")
|
||||
await createUser("admin", submitData);
|
||||
else if (props.config.userType === "teacher")
|
||||
else if (props.config.resourceType === "teacher")
|
||||
await createUser("teacher", submitData);
|
||||
else if (props.config.userType === "guest")
|
||||
await createUser("guest", submitData);
|
||||
else if (props.config.resourceType === "student")
|
||||
await createUser("student", submitData);
|
||||
onOpenChange(false);
|
||||
toast.success("添加成功", { duration: 1500 });
|
||||
router.refresh();
|
||||
@ -610,7 +610,7 @@ export function UserTable(props: UserTableProps) {
|
||||
name: user.name ?? "",
|
||||
email: user.email ?? "",
|
||||
password: "",
|
||||
role: user.role ?? Role.GUEST,
|
||||
role: user.role ?? Role.STUDENT,
|
||||
createdAt: user.createdAt
|
||||
? new Date(user.createdAt).toISOString().slice(0, 16)
|
||||
: "",
|
||||
@ -625,7 +625,7 @@ export function UserTable(props: UserTableProps) {
|
||||
name: user.name ?? "",
|
||||
email: user.email ?? "",
|
||||
password: "",
|
||||
role: user.role ?? Role.GUEST,
|
||||
role: user.role ?? Role.STUDENT,
|
||||
createdAt: user.createdAt
|
||||
? new Date(user.createdAt).toISOString().slice(0, 16)
|
||||
: "",
|
||||
@ -644,15 +644,15 @@ export function UserTable(props: UserTableProps) {
|
||||
: new Date().toISOString(),
|
||||
image: data.image ?? null,
|
||||
emailVerified: data.emailVerified ?? null,
|
||||
role: data.role ?? Role.GUEST,
|
||||
role: data.role ?? Role.STUDENT,
|
||||
};
|
||||
const id = typeof submitData.id === "string" ? submitData.id : "";
|
||||
if (props.config.userType === "admin")
|
||||
if (props.config.resourceType === "admin")
|
||||
await updateUser("admin", id, submitData);
|
||||
else if (props.config.userType === "teacher")
|
||||
else if (props.config.resourceType === "teacher")
|
||||
await updateUser("teacher", id, submitData);
|
||||
else if (props.config.userType === "guest")
|
||||
await updateUser("guest", id, submitData);
|
||||
else if (props.config.resourceType === "student")
|
||||
await updateUser("student", id, submitData);
|
||||
onOpenChange(false);
|
||||
toast.success("修改成功", { duration: 1500 });
|
||||
} catch {
|
||||
@ -710,7 +710,7 @@ export function UserTable(props: UserTableProps) {
|
||||
</div>
|
||||
))}
|
||||
{/* 编辑时显示角色选择 */}
|
||||
{props.config.userType !== "problem" && (
|
||||
{props.config.resourceType !== "problem" && (
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="role" className="text-right">
|
||||
角色
|
||||
@ -725,18 +725,18 @@ export function UserTable(props: UserTableProps) {
|
||||
<SelectValue placeholder="请选择角色" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{props.config.userType === "guest" && (
|
||||
{props.config.resourceType === "student" && (
|
||||
<>
|
||||
<SelectItem value="GUEST">学生</SelectItem>
|
||||
<SelectItem value="STUDENT">学生</SelectItem>
|
||||
<SelectItem value="TEACHER">老师</SelectItem>
|
||||
</>
|
||||
)}
|
||||
{(props.config.userType === "teacher" ||
|
||||
props.config.userType === "admin") && (
|
||||
{(props.config.resourceType === "teacher" ||
|
||||
props.config.resourceType === "admin") && (
|
||||
<>
|
||||
<SelectItem value="ADMIN">管理员</SelectItem>
|
||||
<SelectItem value="TEACHER">老师</SelectItem>
|
||||
<SelectItem value="GUEST">学生</SelectItem>
|
||||
<SelectItem value="STUDENT">学生</SelectItem>
|
||||
</>
|
||||
)}
|
||||
</SelectContent>
|
||||
@ -1068,10 +1068,10 @@ export function UserTable(props: UserTableProps) {
|
||||
await deleteProblem((row.original as Problem).id);
|
||||
} else {
|
||||
await deleteUser(
|
||||
props.config.userType as
|
||||
props.config.resourceType as
|
||||
| "admin"
|
||||
| "teacher"
|
||||
| "guest",
|
||||
| "student",
|
||||
(row.original as User).id
|
||||
);
|
||||
}
|
||||
@ -1113,7 +1113,7 @@ export function UserTable(props: UserTableProps) {
|
||||
await deleteProblem((pendingDeleteItem as Problem).id);
|
||||
} else {
|
||||
await deleteUser(
|
||||
props.config.userType as "admin" | "teacher" | "guest",
|
||||
props.config.resourceType as "admin" | "teacher" | "student",
|
||||
(pendingDeleteItem as User).id
|
||||
);
|
||||
}
|
||||
|
||||
@ -95,14 +95,14 @@ export const basePagination = {
|
||||
|
||||
// 创建用户配置的工厂函数
|
||||
export function createUserConfig(
|
||||
userType: string,
|
||||
resourceType: string,
|
||||
title: string,
|
||||
addLabel: string,
|
||||
namePlaceholder: string,
|
||||
emailPlaceholder: string
|
||||
) {
|
||||
return {
|
||||
userType,
|
||||
resourceType,
|
||||
title,
|
||||
apiPath: "/api/user",
|
||||
columns: baseColumns,
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
import {
|
||||
createUserConfig,
|
||||
baseUserSchema,
|
||||
baseAddUserSchema,
|
||||
baseEditUserSchema,
|
||||
} from "./base-config";
|
||||
import { z } from "zod";
|
||||
|
||||
export const guestSchema = baseUserSchema;
|
||||
export type Guest = z.infer<typeof guestSchema>;
|
||||
|
||||
export const addGuestSchema = baseAddUserSchema;
|
||||
export type AddGuestFormData = z.infer<typeof addGuestSchema>;
|
||||
|
||||
export const editGuestSchema = baseEditUserSchema;
|
||||
export type EditGuestFormData = z.infer<typeof editGuestSchema>;
|
||||
|
||||
export const guestConfig = createUserConfig(
|
||||
"guest",
|
||||
"客户列表",
|
||||
"添加客户",
|
||||
"请输入客户姓名",
|
||||
"请输入客户邮箱"
|
||||
);
|
||||
@ -19,7 +19,7 @@ export const editProblemSchema = z.object({
|
||||
});
|
||||
|
||||
export const problemConfig = {
|
||||
userType: "problem",
|
||||
resourceType: "problem",
|
||||
title: "题目列表",
|
||||
apiPath: "/api/problem",
|
||||
columns: [
|
||||
|
||||
24
src/features/user-management/config/student.ts
Normal file
24
src/features/user-management/config/student.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import {
|
||||
createUserConfig,
|
||||
baseUserSchema,
|
||||
baseAddUserSchema,
|
||||
baseEditUserSchema,
|
||||
} from "./base-config";
|
||||
import { z } from "zod";
|
||||
|
||||
export const studentSchema = baseUserSchema;
|
||||
export type Student = z.infer<typeof studentSchema>;
|
||||
|
||||
export const addStudentSchema = baseAddUserSchema;
|
||||
export type AddStudentFormData = z.infer<typeof addStudentSchema>;
|
||||
|
||||
export const editStudentSchema = baseEditUserSchema;
|
||||
export type EditStudentFormData = z.infer<typeof editStudentSchema>;
|
||||
|
||||
export const studentConfig = createUserConfig(
|
||||
"student",
|
||||
"学生列表",
|
||||
"添加学生",
|
||||
"请输入学生姓名",
|
||||
"请输入学生邮箱"
|
||||
);
|
||||
Loading…
Reference in New Issue
Block a user