mirror of
https://github.com/cfngc4594/monaco-editor-lsp-next.git
synced 2025-07-04 09:20:53 +00:00
refactor(user-management): 重构用户管理功能
- 合并 adminActions、teacherActions 和 guestActions 到统一的 userActions - 创建通用的用户配置和页面组件 - 优化用户表单验证逻辑 - 调整布局和页面结构
This commit is contained in:
parent
03ccd285be
commit
7cdf5fe6b8
@ -1,34 +0,0 @@
|
||||
'use server'
|
||||
import prisma from '@/lib/prisma'
|
||||
import { revalidatePath } from 'next/cache'
|
||||
import bcrypt from 'bcryptjs'
|
||||
import type { User } from '@/generated/client'
|
||||
|
||||
export async function createAdmin(data: Omit<User, 'id'|'createdAt'|'updatedAt'> & { password?: string }) {
|
||||
let password = data.password
|
||||
if (password) {
|
||||
password = await bcrypt.hash(password, 10)
|
||||
}
|
||||
await prisma.user.create({ data: { ...data, password, role: 'ADMIN' } })
|
||||
revalidatePath('/usermanagement/admin')
|
||||
}
|
||||
|
||||
export async function updateAdmin(id: string, data: Partial<Omit<User, 'id'|'createdAt'|'updatedAt'>>) {
|
||||
let updateData = { ...data }
|
||||
|
||||
// 如果包含密码字段且不为空,则进行加密
|
||||
if (data.password && data.password.trim() !== '') {
|
||||
updateData.password = await bcrypt.hash(data.password, 10)
|
||||
} else {
|
||||
// 如果密码为空,则从更新数据中移除密码字段,保持原密码不变
|
||||
delete updateData.password
|
||||
}
|
||||
|
||||
await prisma.user.update({ where: { id }, data: updateData })
|
||||
revalidatePath('/usermanagement/admin')
|
||||
}
|
||||
|
||||
export async function deleteAdmin(id: string) {
|
||||
await prisma.user.delete({ where: { id } })
|
||||
revalidatePath('/usermanagement/admin')
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
'use server'
|
||||
import prisma from '@/lib/prisma'
|
||||
import { revalidatePath } from 'next/cache'
|
||||
import bcrypt from 'bcryptjs'
|
||||
import type { User } from '@/generated/client'
|
||||
|
||||
export async function createGuest(data: Omit<User, 'id'|'createdAt'|'updatedAt'> & { password?: string }) {
|
||||
let password = data.password
|
||||
if (password) {
|
||||
password = await bcrypt.hash(password, 10)
|
||||
}
|
||||
await prisma.user.create({ data: { ...data, password, role: 'GUEST' } })
|
||||
revalidatePath('/usermanagement/guest')
|
||||
}
|
||||
|
||||
export async function updateGuest(id: string, data: Partial<Omit<User, 'id'|'createdAt'|'updatedAt'>>) {
|
||||
let updateData = { ...data }
|
||||
|
||||
// 如果包含密码字段且不为空,则进行加密
|
||||
if (data.password && data.password.trim() !== '') {
|
||||
updateData.password = await bcrypt.hash(data.password, 10)
|
||||
} else {
|
||||
// 如果密码为空,则从更新数据中移除密码字段,保持原密码不变
|
||||
delete updateData.password
|
||||
}
|
||||
|
||||
await prisma.user.update({ where: { id }, data: updateData })
|
||||
revalidatePath('/usermanagement/guest')
|
||||
}
|
||||
|
||||
export async function deleteGuest(id: string) {
|
||||
await prisma.user.delete({ where: { id } })
|
||||
revalidatePath('/usermanagement/guest')
|
||||
}
|
@ -8,11 +8,6 @@ export async function createProblem(data: Omit<Problem, 'id'|'createdAt'|'update
|
||||
revalidatePath('/usermanagement/problem')
|
||||
}
|
||||
|
||||
export async function updateProblem(id: string, data: Partial<Omit<Problem, 'id'|'createdAt'|'updatedAt'>>) {
|
||||
await prisma.problem.update({ where: { id }, data })
|
||||
revalidatePath('/usermanagement/problem')
|
||||
}
|
||||
|
||||
export async function deleteProblem(id: string) {
|
||||
await prisma.problem.delete({ where: { id } })
|
||||
revalidatePath('/usermanagement/problem')
|
||||
|
@ -1,34 +0,0 @@
|
||||
'use server'
|
||||
import prisma from '@/lib/prisma'
|
||||
import { revalidatePath } from 'next/cache'
|
||||
import bcrypt from 'bcryptjs'
|
||||
import type { User } from '@/generated/client'
|
||||
|
||||
export async function createTeacher(data: Omit<User, 'id'|'createdAt'|'updatedAt'> & { password?: string }) {
|
||||
let password = data.password
|
||||
if (password) {
|
||||
password = await bcrypt.hash(password, 10)
|
||||
}
|
||||
await prisma.user.create({ data: { ...data, password, role: 'TEACHER' } })
|
||||
revalidatePath('/usermanagement/teacher')
|
||||
}
|
||||
|
||||
export async function updateTeacher(id: string, data: Partial<Omit<User, 'id'|'createdAt'|'updatedAt'>>) {
|
||||
let updateData = { ...data }
|
||||
|
||||
// 如果包含密码字段且不为空,则进行加密
|
||||
if (data.password && data.password.trim() !== '') {
|
||||
updateData.password = await bcrypt.hash(data.password, 10)
|
||||
} else {
|
||||
// 如果密码为空,则从更新数据中移除密码字段,保持原密码不变
|
||||
delete updateData.password
|
||||
}
|
||||
|
||||
await prisma.user.update({ where: { id }, data: updateData })
|
||||
revalidatePath('/usermanagement/teacher')
|
||||
}
|
||||
|
||||
export async function deleteTeacher(id: string) {
|
||||
await prisma.user.delete({ where: { id } })
|
||||
revalidatePath('/usermanagement/teacher')
|
||||
}
|
46
src/app/(app)/usermanagement/_actions/userActions.ts
Normal file
46
src/app/(app)/usermanagement/_actions/userActions.ts
Normal file
@ -0,0 +1,46 @@
|
||||
'use server'
|
||||
import prisma from '@/lib/prisma'
|
||||
import { revalidatePath } from 'next/cache'
|
||||
import bcrypt from 'bcryptjs'
|
||||
import type { User } from '@/generated/client'
|
||||
import { Role } from '@/generated/client'
|
||||
|
||||
type UserType = 'admin' | 'teacher' | 'guest'
|
||||
|
||||
export async function createUser(
|
||||
userType: UserType,
|
||||
data: Omit<User, 'id'|'createdAt'|'updatedAt'> & { password?: string }
|
||||
) {
|
||||
let password = data.password
|
||||
if (password) {
|
||||
password = await bcrypt.hash(password, 10)
|
||||
}
|
||||
|
||||
const role = userType.toUpperCase() as Role
|
||||
await prisma.user.create({ data: { ...data, password, role } })
|
||||
revalidatePath(`/usermanagement/${userType}`)
|
||||
}
|
||||
|
||||
export async function updateUser(
|
||||
userType: UserType,
|
||||
id: string,
|
||||
data: Partial<Omit<User, 'id'|'createdAt'|'updatedAt'>>
|
||||
) {
|
||||
const updateData = { ...data }
|
||||
|
||||
// 如果包含密码字段且不为空,则进行加密
|
||||
if (data.password && data.password.trim() !== '') {
|
||||
updateData.password = await bcrypt.hash(data.password, 10)
|
||||
} else {
|
||||
// 如果密码为空,则从更新数据中移除密码字段,保持原密码不变
|
||||
delete updateData.password
|
||||
}
|
||||
|
||||
await prisma.user.update({ where: { id }, data: updateData })
|
||||
revalidatePath(`/usermanagement/${userType}`)
|
||||
}
|
||||
|
||||
export async function deleteUser(userType: UserType, id: string) {
|
||||
await prisma.user.delete({ where: { id } })
|
||||
revalidatePath(`/usermanagement/${userType}`)
|
||||
}
|
10
src/app/(app)/usermanagement/_components/GenericLayout.tsx
Normal file
10
src/app/(app)/usermanagement/_components/GenericLayout.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import ProtectedLayout from "./ProtectedLayout";
|
||||
|
||||
interface GenericLayoutProps {
|
||||
children: React.ReactNode;
|
||||
allowedRoles: string[];
|
||||
}
|
||||
|
||||
export default function GenericLayout({ children, allowedRoles }: GenericLayoutProps) {
|
||||
return <ProtectedLayout allowedRoles={allowedRoles}>{children}</ProtectedLayout>;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import ProtectedLayout from "../_components/ProtectedLayout";
|
||||
import GenericLayout from "../_components/GenericLayout";
|
||||
|
||||
export default function AdminLayout({ children }: { children: React.ReactNode }) {
|
||||
return <ProtectedLayout allowedRoles={["ADMIN"]}>{children}</ProtectedLayout>;
|
||||
return <GenericLayout allowedRoles={["ADMIN"]}>{children}</GenericLayout>;
|
||||
}
|
@ -1,9 +1,6 @@
|
||||
import { UserTable } from '@/features/user-management/components/user-table'
|
||||
import GenericPage from '@/features/user-management/components/generic-page'
|
||||
import { adminConfig } from '@/features/user-management/config/admin'
|
||||
import prisma from '@/lib/prisma'
|
||||
import type { User } from '@/generated/client'
|
||||
|
||||
export default async function AdminPage() {
|
||||
const data: User[] = await prisma.user.findMany({ where: { role: 'ADMIN' } })
|
||||
return <UserTable config={adminConfig} data={data} />
|
||||
export default function AdminPage() {
|
||||
return <GenericPage userType="admin" config={adminConfig} />
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import ProtectedLayout from "../_components/ProtectedLayout";
|
||||
import GenericLayout from "../_components/GenericLayout";
|
||||
|
||||
export default function GuestLayout({ children }: { children: React.ReactNode }) {
|
||||
return <ProtectedLayout allowedRoles={["ADMIN", "TEACHER"]}>{children}</ProtectedLayout>;
|
||||
return <GenericLayout allowedRoles={["ADMIN", "TEACHER"]}>{children}</GenericLayout>;
|
||||
}
|
@ -1,9 +1,6 @@
|
||||
import { UserTable } from '@/features/user-management/components/user-table'
|
||||
import GenericPage from '@/features/user-management/components/generic-page'
|
||||
import { guestConfig } from '@/features/user-management/config/guest'
|
||||
import prisma from '@/lib/prisma'
|
||||
import type { User } from '@/generated/client'
|
||||
|
||||
export default async function GuestPage() {
|
||||
const data: User[] = await prisma.user.findMany({ where: { role: 'GUEST' } })
|
||||
return <UserTable config={guestConfig} data={data} />
|
||||
export default function GuestPage() {
|
||||
return <GenericPage userType="guest" config={guestConfig} />
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import ProtectedLayout from "../_components/ProtectedLayout";
|
||||
import GenericLayout from "../_components/GenericLayout";
|
||||
|
||||
export default function ProblemLayout({ children }: { children: React.ReactNode }) {
|
||||
return <ProtectedLayout allowedRoles={["ADMIN", "TEACHER"]}>{children}</ProtectedLayout>;
|
||||
return <GenericLayout allowedRoles={["ADMIN", "TEACHER"]}>{children}</GenericLayout>;
|
||||
}
|
@ -1,9 +1,6 @@
|
||||
import { UserTable } from '@/features/user-management/components/user-table'
|
||||
import GenericPage from '@/features/user-management/components/generic-page'
|
||||
import { problemConfig } from '@/features/user-management/config/problem'
|
||||
import prisma from '@/lib/prisma'
|
||||
import type { Problem } from '@/generated/client'
|
||||
|
||||
export default async function ProblemPage() {
|
||||
const data: Problem[] = await prisma.problem.findMany({})
|
||||
return <UserTable config={problemConfig} data={data} />
|
||||
export default function ProblemPage() {
|
||||
return <GenericPage userType="problem" config={problemConfig} />
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import ProtectedLayout from "../_components/ProtectedLayout";
|
||||
import GenericLayout from "../_components/GenericLayout";
|
||||
|
||||
export default function TeacherLayout({ children }: { children: React.ReactNode }) {
|
||||
return <ProtectedLayout allowedRoles={["ADMIN"]}>{children}</ProtectedLayout>;
|
||||
return <GenericLayout allowedRoles={["ADMIN"]}>{children}</GenericLayout>;
|
||||
}
|
@ -1,9 +1,6 @@
|
||||
import { UserTable } from '@/features/user-management/components/user-table'
|
||||
import GenericPage from '@/features/user-management/components/generic-page'
|
||||
import { teacherConfig } from '@/features/user-management/config/teacher'
|
||||
import prisma from '@/lib/prisma'
|
||||
import type { User } from '@/generated/client'
|
||||
|
||||
export default async function TeacherPage() {
|
||||
const data: User[] = await prisma.user.findMany({ where: { role: 'TEACHER' } })
|
||||
return <UserTable config={teacherConfig} data={data} />
|
||||
export default function TeacherPage() {
|
||||
return <GenericPage userType="teacher" config={teacherConfig} />
|
||||
}
|
21
src/features/user-management/components/generic-page.tsx
Normal file
21
src/features/user-management/components/generic-page.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { UserTable } from './user-table'
|
||||
import { UserConfig } from './user-table'
|
||||
import prisma from '@/lib/prisma'
|
||||
import type { User, Problem } from '@/generated/client'
|
||||
import { Role } from '@/generated/client'
|
||||
|
||||
interface GenericPageProps {
|
||||
userType: 'admin' | 'teacher' | 'guest' | 'problem'
|
||||
config: UserConfig
|
||||
}
|
||||
|
||||
export default async function GenericPage({ userType, config }: GenericPageProps) {
|
||||
if (userType === 'problem') {
|
||||
const data: Problem[] = await prisma.problem.findMany({})
|
||||
return <UserTable config={config} data={data} />
|
||||
} else {
|
||||
const role = userType.toUpperCase() as Role
|
||||
const data: User[] = await prisma.user.findMany({ where: { role } })
|
||||
return <UserTable config={config} data={data} />
|
||||
}
|
||||
}
|
@ -69,9 +69,7 @@ import {
|
||||
Tabs,
|
||||
} from "@/components/ui/tabs"
|
||||
|
||||
import { createAdmin, updateAdmin, deleteAdmin } from '@/app/(app)/usermanagement/_actions/adminActions'
|
||||
import { createTeacher, updateTeacher } from '@/app/(app)/usermanagement/_actions/teacherActions'
|
||||
import { createGuest, updateGuest } from '@/app/(app)/usermanagement/_actions/guestActions'
|
||||
import { createUser, updateUser, deleteUser } from '@/app/(app)/usermanagement/_actions/userActions'
|
||||
import { createProblem, deleteProblem } from '@/app/(app)/usermanagement/_actions/problemActions'
|
||||
import type { User, Problem } from '@/generated/client'
|
||||
import { Difficulty, Role } from '@/generated/client'
|
||||
@ -128,7 +126,7 @@ type AddUserForm = Omit<UserForm, 'id'>
|
||||
const addUserSchema = z.object({
|
||||
name: z.string(),
|
||||
email: z.string().email(),
|
||||
password: z.string(),
|
||||
password: z.string().min(1, "密码不能为空").min(8, "密码长度至少8位"),
|
||||
createdAt: z.string(),
|
||||
image: z.string().nullable(),
|
||||
emailVerified: z.date().nullable(),
|
||||
@ -312,6 +310,13 @@ export function UserTable(props: UserTableProps) {
|
||||
async function onSubmit(data: AddUserForm) {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
|
||||
// 验证必填字段
|
||||
if (!data.password || data.password.trim() === '') {
|
||||
toast.error('密码不能为空', { duration: 1500 })
|
||||
return
|
||||
}
|
||||
|
||||
const submitData = {
|
||||
...data,
|
||||
image: data.image ?? null,
|
||||
@ -321,9 +326,9 @@ export function UserTable(props: UserTableProps) {
|
||||
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') await createAdmin(submitData)
|
||||
else if (props.config.userType === 'teacher') await createTeacher(submitData)
|
||||
else if (props.config.userType === 'guest') await createGuest(submitData)
|
||||
if (props.config.userType === 'admin') await createUser('admin', submitData)
|
||||
else if (props.config.userType === 'teacher') await createUser('teacher', submitData)
|
||||
else if (props.config.userType === 'guest') await createUser('guest', submitData)
|
||||
onOpenChange(false)
|
||||
toast.success('添加成功', { duration: 1500 })
|
||||
router.refresh()
|
||||
@ -526,9 +531,9 @@ export function UserTable(props: UserTableProps) {
|
||||
role: data.role ?? Role.GUEST,
|
||||
}
|
||||
const id = typeof submitData.id === 'string' ? submitData.id : ''
|
||||
if (props.config.userType === 'admin') await updateAdmin(id, submitData)
|
||||
else if (props.config.userType === 'teacher') await updateTeacher(id, submitData)
|
||||
else if (props.config.userType === 'guest') await updateGuest(id, submitData)
|
||||
if (props.config.userType === 'admin') await updateUser('admin', id, submitData)
|
||||
else if (props.config.userType === 'teacher') await updateUser('teacher', id, submitData)
|
||||
else if (props.config.userType === 'guest') await updateUser('guest', id, submitData)
|
||||
onOpenChange(false)
|
||||
toast.success('修改成功', { duration: 1500 })
|
||||
} catch {
|
||||
@ -898,7 +903,7 @@ export function UserTable(props: UserTableProps) {
|
||||
if (isProblem) {
|
||||
await deleteProblem((row.original as Problem).id)
|
||||
} else {
|
||||
await deleteAdmin((row.original as User).id)
|
||||
await deleteUser(props.config.userType as 'admin' | 'teacher' | 'guest', (row.original as User).id)
|
||||
}
|
||||
}
|
||||
toast.success(`成功删除 ${selectedRows.length} 条记录`, { duration: 1500 })
|
||||
@ -930,7 +935,7 @@ export function UserTable(props: UserTableProps) {
|
||||
if (isProblem) {
|
||||
await deleteProblem((pendingDeleteItem as Problem).id)
|
||||
} else {
|
||||
await deleteAdmin((pendingDeleteItem as User).id)
|
||||
await deleteUser(props.config.userType as 'admin' | 'teacher' | 'guest', (pendingDeleteItem as User).id)
|
||||
}
|
||||
toast.success('删除成功', { duration: 1500 })
|
||||
router.refresh()
|
||||
|
@ -1,126 +1,23 @@
|
||||
import { z } from "zod"
|
||||
import { createUserConfig, baseUserSchema, baseAddUserSchema, baseEditUserSchema } from './base-config'
|
||||
|
||||
// 管理员数据校验 schema
|
||||
export const adminSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string().optional(),
|
||||
email: z.string(),
|
||||
password: z.string().optional(),
|
||||
role: z.string().optional(),
|
||||
createdAt: z.string(),
|
||||
updatedAt: z.string().optional(),
|
||||
})
|
||||
|
||||
export const adminSchema = baseUserSchema
|
||||
export type Admin = z.infer<typeof adminSchema>
|
||||
|
||||
// 添加管理员表单校验 schema
|
||||
export const addAdminSchema = z.object({
|
||||
name: z.string().min(1, "姓名为必填项"),
|
||||
email: z.string().email("请输入有效的邮箱地址"),
|
||||
password: z.string().min(8, "密码长度8-32位").max(32, "密码长度8-32位"),
|
||||
createdAt: z.string(),
|
||||
})
|
||||
export const addAdminSchema = baseAddUserSchema
|
||||
export type AddAdminFormData = z.infer<typeof addAdminSchema>
|
||||
|
||||
// 编辑管理员表单校验 schema
|
||||
export const editAdminSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string().min(1, "姓名为必填项"),
|
||||
email: z.string().email("请输入有效的邮箱地址"),
|
||||
password: z.string().min(8, "密码长度8-32位").max(32, "密码长度8-32位"),
|
||||
createdAt: z.string(),
|
||||
})
|
||||
|
||||
export type AddAdminFormData = z.infer<typeof addAdminSchema>
|
||||
export const editAdminSchema = baseEditUserSchema
|
||||
export type EditAdminFormData = z.infer<typeof editAdminSchema>
|
||||
|
||||
// 管理员配置
|
||||
export const adminConfig = {
|
||||
userType: "admin",
|
||||
title: "管理员列表",
|
||||
apiPath: "/api/user",
|
||||
|
||||
// 表格列配置
|
||||
columns: [
|
||||
{
|
||||
key: "id",
|
||||
label: "ID",
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
key: "name",
|
||||
label: "姓名",
|
||||
sortable: true,
|
||||
searchable: true,
|
||||
placeholder: "搜索姓名",
|
||||
},
|
||||
{
|
||||
key: "email",
|
||||
label: "邮箱",
|
||||
sortable: true,
|
||||
searchable: true,
|
||||
placeholder: "搜索邮箱",
|
||||
},
|
||||
{
|
||||
key: "createdAt",
|
||||
label: "创建时间",
|
||||
sortable: true,
|
||||
},
|
||||
],
|
||||
|
||||
// 表单字段配置
|
||||
formFields: [
|
||||
{
|
||||
key: "name",
|
||||
label: "姓名",
|
||||
type: "text",
|
||||
placeholder: "请输入管理员姓名",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: "email",
|
||||
label: "邮箱",
|
||||
type: "email",
|
||||
placeholder: "请输入管理员邮箱",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: "password",
|
||||
label: "密码",
|
||||
type: "password",
|
||||
placeholder: "请输入8-32位密码",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: "createdAt",
|
||||
label: "创建时间",
|
||||
type: "datetime-local",
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
|
||||
// 操作按钮配置
|
||||
actions: {
|
||||
add: {
|
||||
label: "添加管理员",
|
||||
icon: "PlusIcon",
|
||||
},
|
||||
edit: {
|
||||
label: "编辑",
|
||||
icon: "PencilIcon",
|
||||
},
|
||||
delete: {
|
||||
label: "删除",
|
||||
icon: "TrashIcon",
|
||||
},
|
||||
batchDelete: {
|
||||
label: "批量删除",
|
||||
icon: "TrashIcon",
|
||||
},
|
||||
},
|
||||
|
||||
// 分页配置
|
||||
pagination: {
|
||||
pageSizes: [10, 50, 100, 500],
|
||||
defaultPageSize: 10,
|
||||
},
|
||||
}
|
||||
export const adminConfig = createUserConfig(
|
||||
"admin",
|
||||
"管理员列表",
|
||||
"添加管理员",
|
||||
"请输入管理员姓名",
|
||||
"请输入管理员邮箱"
|
||||
)
|
86
src/features/user-management/config/base-config.ts
Normal file
86
src/features/user-management/config/base-config.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import { z } from "zod"
|
||||
|
||||
// 基础用户 schema
|
||||
export const baseUserSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string().optional(),
|
||||
email: z.string(),
|
||||
password: z.string().optional(),
|
||||
role: z.string().optional(),
|
||||
createdAt: z.string(),
|
||||
updatedAt: z.string().optional(),
|
||||
})
|
||||
|
||||
// 基础添加用户 schema
|
||||
export const baseAddUserSchema = z.object({
|
||||
name: z.string().min(1, "姓名为必填项"),
|
||||
email: z.string().email("请输入有效的邮箱地址"),
|
||||
password: z.string().min(8, "密码长度8-32位").max(32, "密码长度8-32位"),
|
||||
createdAt: z.string(),
|
||||
})
|
||||
|
||||
// 基础编辑用户 schema
|
||||
export const baseEditUserSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string().min(1, "姓名为必填项"),
|
||||
email: z.string().email("请输入有效的邮箱地址"),
|
||||
password: z.string().min(8, "密码长度8-32位").max(32, "密码长度8-32位"),
|
||||
createdAt: z.string(),
|
||||
})
|
||||
|
||||
// 基础表格列配置
|
||||
export const baseColumns = [
|
||||
{ key: "id", label: "ID", sortable: true },
|
||||
{ key: "name", label: "姓名", sortable: true, searchable: true, placeholder: "搜索姓名" },
|
||||
{ key: "email", label: "邮箱", sortable: true, searchable: true, placeholder: "搜索邮箱" },
|
||||
{ key: "createdAt", label: "创建时间", sortable: true },
|
||||
]
|
||||
|
||||
// 基础表单字段配置
|
||||
export const baseFormFields = [
|
||||
{ key: "name", label: "姓名", type: "text", placeholder: "请输入姓名", required: true },
|
||||
{ key: "email", label: "邮箱", type: "email", placeholder: "请输入邮箱", required: true },
|
||||
{ key: "password", label: "密码", type: "password", placeholder: "请输入8-32位密码", required: true },
|
||||
{ key: "createdAt", label: "创建时间", type: "datetime-local", required: false },
|
||||
]
|
||||
|
||||
// 基础操作配置
|
||||
export const baseActions = {
|
||||
add: { label: "添加", icon: "PlusIcon" },
|
||||
edit: { label: "编辑", icon: "PencilIcon" },
|
||||
delete: { label: "删除", icon: "TrashIcon" },
|
||||
batchDelete: { label: "批量删除", icon: "TrashIcon" },
|
||||
}
|
||||
|
||||
// 基础分页配置
|
||||
export const basePagination = {
|
||||
pageSizes: [10, 50, 100, 500],
|
||||
defaultPageSize: 10,
|
||||
}
|
||||
|
||||
// 创建用户配置的工厂函数
|
||||
export function createUserConfig(
|
||||
userType: string,
|
||||
title: string,
|
||||
addLabel: string,
|
||||
namePlaceholder: string,
|
||||
emailPlaceholder: string
|
||||
) {
|
||||
return {
|
||||
userType,
|
||||
title,
|
||||
apiPath: "/api/user",
|
||||
columns: baseColumns,
|
||||
formFields: baseFormFields.map(field => ({
|
||||
...field,
|
||||
placeholder: field.key === 'name' ? namePlaceholder :
|
||||
field.key === 'email' ? emailPlaceholder :
|
||||
field.placeholder
|
||||
})),
|
||||
actions: {
|
||||
...baseActions,
|
||||
add: { ...baseActions.add, label: addLabel }
|
||||
},
|
||||
pagination: basePagination,
|
||||
}
|
||||
}
|
@ -1,51 +1,19 @@
|
||||
import { z } from "zod";
|
||||
import { createUserConfig, baseUserSchema, baseAddUserSchema, baseEditUserSchema } from './base-config'
|
||||
|
||||
export const guestSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string().optional(),
|
||||
email: z.string(),
|
||||
password: z.string().optional(),
|
||||
role: z.string().optional(),
|
||||
createdAt: z.string(),
|
||||
updatedAt: z.string().optional(),
|
||||
});
|
||||
export const guestSchema = baseUserSchema;
|
||||
export type Guest = z.infer<typeof guestSchema>;
|
||||
|
||||
export const addGuestSchema = z.object({
|
||||
name: z.string().min(1, "姓名为必填项"),
|
||||
email: z.string().email("请输入有效的邮箱地址"),
|
||||
password: z.string().min(8, "密码长度8-32位").max(32, "密码长度8-32位"),
|
||||
createdAt: z.string(),
|
||||
});
|
||||
export const addGuestSchema = baseAddUserSchema;
|
||||
export type AddGuestFormData = z.infer<typeof addGuestSchema>;
|
||||
|
||||
export const editGuestSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string().min(1, "姓名为必填项"),
|
||||
email: z.string().email("请输入有效的邮箱地址"),
|
||||
password: z.string().min(8, "密码长度8-32位").max(32, "密码长度8-32位"),
|
||||
createdAt: z.string(),
|
||||
});
|
||||
export const editGuestSchema = baseEditUserSchema;
|
||||
export type EditGuestFormData = z.infer<typeof editGuestSchema>;
|
||||
|
||||
export const guestConfig = {
|
||||
userType: "guest",
|
||||
title: "客户列表",
|
||||
apiPath: "/api/user",
|
||||
columns: [
|
||||
{ key: "id", label: "ID", sortable: true },
|
||||
{ key: "name", label: "姓名", sortable: true, searchable: true, placeholder: "搜索姓名" },
|
||||
{ key: "email", label: "邮箱", sortable: true, searchable: true, placeholder: "搜索邮箱" },
|
||||
{ key: "createdAt", label: "创建时间", sortable: true },
|
||||
],
|
||||
formFields: [
|
||||
{ key: "name", label: "姓名", type: "text", placeholder: "请输入客户姓名", required: true },
|
||||
{ key: "email", label: "邮箱", type: "email", placeholder: "请输入客户邮箱", required: true },
|
||||
{ key: "password", label: "密码", type: "password", placeholder: "请输入8-32位密码", required: true },
|
||||
{ key: "createdAt", label: "创建时间", type: "datetime-local", required: false },
|
||||
],
|
||||
actions: {
|
||||
add: { label: "添加客户", icon: "PlusIcon" },
|
||||
edit: { label: "编辑", icon: "PencilIcon" },
|
||||
delete: { label: "删除", icon: "TrashIcon" },
|
||||
batchDelete: { label: "批量删除", icon: "TrashIcon" },
|
||||
},
|
||||
pagination: { pageSizes: [10, 50, 100, 500], defaultPageSize: 10 },
|
||||
};
|
||||
export const guestConfig = createUserConfig(
|
||||
"guest",
|
||||
"客户列表",
|
||||
"添加客户",
|
||||
"请输入客户姓名",
|
||||
"请输入客户邮箱"
|
||||
);
|
@ -1,51 +1,19 @@
|
||||
import { z } from "zod";
|
||||
import { createUserConfig, baseUserSchema, baseAddUserSchema, baseEditUserSchema } from './base-config'
|
||||
|
||||
export const teacherSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string().optional(),
|
||||
email: z.string(),
|
||||
password: z.string().optional(),
|
||||
role: z.string().optional(),
|
||||
createdAt: z.string(),
|
||||
updatedAt: z.string().optional(),
|
||||
});
|
||||
export const teacherSchema = baseUserSchema;
|
||||
export type Teacher = z.infer<typeof teacherSchema>;
|
||||
|
||||
export const addTeacherSchema = z.object({
|
||||
name: z.string().min(1, "姓名为必填项"),
|
||||
email: z.string().email("请输入有效的邮箱地址"),
|
||||
password: z.string().min(8, "密码长度8-32位").max(32, "密码长度8-32位"),
|
||||
createdAt: z.string(),
|
||||
});
|
||||
export const addTeacherSchema = baseAddUserSchema;
|
||||
export type AddTeacherFormData = z.infer<typeof addTeacherSchema>;
|
||||
|
||||
export const editTeacherSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string().min(1, "姓名为必填项"),
|
||||
email: z.string().email("请输入有效的邮箱地址"),
|
||||
password: z.string().min(8, "密码长度8-32位").max(32, "密码长度8-32位"),
|
||||
createdAt: z.string(),
|
||||
});
|
||||
export const editTeacherSchema = baseEditUserSchema;
|
||||
export type EditTeacherFormData = z.infer<typeof editTeacherSchema>;
|
||||
|
||||
export const teacherConfig = {
|
||||
userType: "teacher",
|
||||
title: "教师列表",
|
||||
apiPath: "/api/user",
|
||||
columns: [
|
||||
{ key: "id", label: "ID", sortable: true },
|
||||
{ key: "name", label: "姓名", sortable: true, searchable: true, placeholder: "搜索姓名" },
|
||||
{ key: "email", label: "邮箱", sortable: true, searchable: true, placeholder: "搜索邮箱" },
|
||||
{ key: "createdAt", label: "创建时间", sortable: true },
|
||||
],
|
||||
formFields: [
|
||||
{ key: "name", label: "姓名", type: "text", placeholder: "请输入教师姓名", required: true },
|
||||
{ key: "email", label: "邮箱", type: "email", placeholder: "请输入教师邮箱", required: true },
|
||||
{ key: "password", label: "密码", type: "password", placeholder: "请输入8-32位密码", required: true },
|
||||
{ key: "createdAt", label: "创建时间", type: "datetime-local", required: false },
|
||||
],
|
||||
actions: {
|
||||
add: { label: "添加教师", icon: "PlusIcon" },
|
||||
edit: { label: "编辑", icon: "PencilIcon" },
|
||||
delete: { label: "删除", icon: "TrashIcon" },
|
||||
batchDelete: { label: "批量删除", icon: "TrashIcon" },
|
||||
},
|
||||
pagination: { pageSizes: [10, 50, 100, 500], defaultPageSize: 10 },
|
||||
};
|
||||
export const teacherConfig = createUserConfig(
|
||||
"teacher",
|
||||
"教师列表",
|
||||
"添加教师",
|
||||
"请输入教师姓名",
|
||||
"请输入教师邮箱"
|
||||
);
|
Loading…
Reference in New Issue
Block a user