refactor(user-management): 优化用户管理功能和界面

- 为 toast 消息添加 1500ms 持续时间
- 修复管理员、学生和教师表单的字段和验证规则
- 优化问题表格的列配置
- 调整用户表格的样式和布局
This commit is contained in:
liguang 2025-06-19 17:52:52 +08:00
parent 42e576876e
commit db8051d1d8
5 changed files with 83 additions and 86 deletions

View File

@ -293,11 +293,11 @@ export function UserTable({ config, data: initialData }: UserTableProps) {
if (isProblem) {
problemApi.getProblems()
.then(setData)
.catch(() => toast.error('获取数据失败'))
.catch(() => toast.error('获取数据失败', { duration: 1500 }))
} else {
userApi.getUsers(config.userType)
.then(setData)
.catch(() => toast.error('获取数据失败'))
.catch(() => toast.error('获取数据失败', { duration: 1500 }))
}
}, [config.userType])
@ -335,9 +335,9 @@ export function UserTable({ config, data: initialData }: UserTableProps) {
await userApi.createUser(config.userType, submitData)
userApi.getUsers(config.userType).then(setData)
onOpenChange(false)
toast.success('添加成功')
toast.success('添加成功', { duration: 1500 })
} catch {
toast.error("添加失败")
toast.error('添加失败', { duration: 1500 })
} finally {
setIsLoading(false)
}
@ -410,9 +410,9 @@ export function UserTable({ config, data: initialData }: UserTableProps) {
await problemApi.createProblem(submitData)
problemApi.getProblems().then(setData)
onOpenChange(false)
toast.success('添加成功')
toast.success('添加成功', { duration: 1500 })
} catch {
toast.error("添加失败")
toast.error('添加失败', { duration: 1500 })
} finally {
setIsLoading(false)
}
@ -485,9 +485,9 @@ export function UserTable({ config, data: initialData }: UserTableProps) {
await userApi.updateUser(config.userType, submitData)
userApi.getUsers(config.userType).then(setData)
onOpenChange(false)
toast.success('修改成功')
toast.success('修改成功', { duration: 1500 })
} catch {
toast.error("修改失败")
toast.error('修改失败', { duration: 1500 })
} finally {
setIsLoading(false)
}
@ -558,9 +558,9 @@ export function UserTable({ config, data: initialData }: UserTableProps) {
await problemApi.updateProblem(submitData)
problemApi.getProblems().then(setData)
onOpenChange(false)
toast.success('修改成功')
toast.success('修改成功', { duration: 1500 })
} catch {
toast.error("修改失败")
toast.error('修改失败', { duration: 1500 })
} finally {
setIsLoading(false)
}
@ -577,26 +577,21 @@ export function UserTable({ config, data: initialData }: UserTableProps) {
</DialogHeader>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<div className="grid gap-4 py-4">
{config.formFields.map((field) => (
<div key={field.key} className="grid grid-cols-4 items-center gap-4">
<Label htmlFor={field.key} className="text-right">
{field.label}
</Label>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="displayId" className="text-right"></Label>
<Input
id={field.key}
type={field.type}
{...form.register(field.key as 'displayId' | 'difficulty', field.key === 'displayId' ? { valueAsNumber: true } : {})}
id="displayId"
type="number"
{...form.register('displayId', { valueAsNumber: true })}
className="col-span-3"
placeholder={field.placeholder}
disabled={field.key === 'id'}
placeholder="请输入题目编号"
/>
{form.formState.errors[field.key as keyof typeof form.formState.errors]?.message && (
{form.formState.errors.displayId?.message && (
<p className="col-span-3 col-start-2 text-sm text-red-500">
{form.formState.errors[field.key as keyof typeof form.formState.errors]?.message as string}
{form.formState.errors.displayId?.message as string}
</p>
)}
</div>
))}
</div>
<DialogFooter>
<Button type="submit" disabled={isLoading}>
@ -644,6 +639,8 @@ export function UserTable({ config, data: initialData }: UserTableProps) {
password: "密码",
createdAt: "创建时间",
actions: "操作",
displayId: "题目编号",
difficulty: "难度",
}
return (
<DropdownMenuCheckboxItem
@ -660,6 +657,7 @@ export function UserTable({ config, data: initialData }: UserTableProps) {
})}
</DropdownMenuContent>
</DropdownMenu>
{config.actions.add && (
<Button
variant="outline"
size="sm"
@ -669,6 +667,7 @@ export function UserTable({ config, data: initialData }: UserTableProps) {
<PlusIcon className="h-4 w-4" />
{config.actions.add.label}
</Button>
)}
<Button
variant="destructive"
size="sm"
@ -830,11 +829,11 @@ export function UserTable({ config, data: initialData }: UserTableProps) {
</div>
{/* 添加用户对话框 */}
{isProblem ? (
{isProblem && config.actions.add ? (
<AddUserDialogProblem open={isAddDialogOpen} onOpenChange={setIsAddDialogOpen} />
) : (
) : !isProblem && config.actions.add ? (
<AddUserDialogUser open={isAddDialogOpen} onOpenChange={setIsAddDialogOpen} />
)}
) : null}
{/* 编辑用户对话框 */}
{isProblem && editingUser ? (
@ -874,7 +873,7 @@ export function UserTable({ config, data: initialData }: UserTableProps) {
userApi.getUsers(config.userType).then(setData)
}
}
toast.success(`成功删除 ${selectedRows.length} 条记录`)
toast.success(`成功删除 ${selectedRows.length} 条记录`, { duration: 1500 })
} else if (deleteTargetId) {
if (isProblem) {
await problemApi.deleteProblem(deleteTargetId)
@ -883,11 +882,11 @@ export function UserTable({ config, data: initialData }: UserTableProps) {
await userApi.deleteUser(config.userType, deleteTargetId)
userApi.getUsers(config.userType).then(setData)
}
toast.success('删除成功')
toast.success('删除成功', { duration: 1500 })
}
setDeleteDialogOpen(false)
} catch {
toast.error("删除失败")
toast.error('删除失败', { duration: 1500 })
}
}}
>

View File

@ -15,18 +15,18 @@ export type Admin = z.infer<typeof adminSchema>
// 添加管理员表单校验 schema
export const addAdminSchema = z.object({
name: z.string().optional(),
name: z.string().min(1, "姓名为必填项"),
email: z.string().email("请输入有效的邮箱地址"),
password: z.string().optional(),
password: z.string().min(8, "密码长度8-32位").max(32, "密码长度8-32位"),
createdAt: z.string(),
})
// 编辑管理员表单校验 schema
export const editAdminSchema = z.object({
id: z.string(),
name: z.string().optional(),
name: z.string().min(1, "姓名为必填项"),
email: z.string().email("请输入有效的邮箱地址"),
password: z.string().optional(),
password: z.string().min(8, "密码长度8-32位").max(32, "密码长度8-32位"),
createdAt: z.string(),
})
@ -77,8 +77,8 @@ export const adminConfig = {
key: "name",
label: "姓名",
type: "text",
placeholder: "请输入管理员姓名(选填)",
required: false,
placeholder: "请输入管理员姓名",
required: true,
},
{
key: "email",
@ -91,8 +91,8 @@ export const adminConfig = {
key: "password",
label: "密码",
type: "password",
placeholder: "请输入密码(选填)",
required: false,
placeholder: "请输入8-32位密码",
required: true,
},
{
key: "createdAt",

View File

@ -26,14 +26,12 @@ export const problemConfig = {
{ key: "id", label: "ID", sortable: true },
{ key: "displayId", label: "题目编号", sortable: true, searchable: true, placeholder: "搜索编号" },
{ key: "difficulty", label: "难度", sortable: true, searchable: true, placeholder: "搜索难度" },
{ key: "createdAt", label: "创建时间", sortable: true },
],
formFields: [
{ key: "displayId", label: "题目编号", type: "number", required: true },
{ key: "difficulty", label: "难度", type: "text", required: true },
],
actions: {
add: { label: "添加题目", icon: "PlusIcon" },
edit: { label: "编辑", icon: "PencilIcon" },
delete: { label: "删除", icon: "TrashIcon" },
batchDelete: { label: "批量删除", icon: "TrashIcon" },

View File

@ -11,17 +11,17 @@ export const studentSchema = z.object({
});
export const addStudentSchema = z.object({
name: z.string().optional(),
name: z.string().min(1, "姓名为必填项"),
email: z.string().email("请输入有效的邮箱地址"),
password: z.string().optional(),
password: z.string().min(8, "密码长度8-32位").max(32, "密码长度8-32位"),
createdAt: z.string(),
});
export const editStudentSchema = z.object({
id: z.string(),
name: z.string().optional(),
name: z.string().min(1, "姓名为必填项"),
email: z.string().email("请输入有效的邮箱地址"),
password: z.string().optional(),
password: z.string().min(8, "密码长度8-32位").max(32, "密码长度8-32位"),
createdAt: z.string(),
});
@ -37,9 +37,9 @@ export const studentConfig = {
{ key: "createdAt", label: "创建时间", sortable: true },
],
formFields: [
{ key: "name", label: "姓名", type: "text", placeholder: "请输入学生姓名(选填)", required: false },
{ key: "name", label: "姓名", type: "text", placeholder: "请输入学生姓名", required: true },
{ key: "email", label: "邮箱", type: "email", placeholder: "请输入学生邮箱", required: true },
{ key: "password", label: "密码", type: "password", placeholder: "请输入密码(选填)", required: false },
{ key: "password", label: "密码", type: "password", placeholder: "请输入8-32位密码", required: true },
{ key: "createdAt", label: "创建时间", type: "datetime-local", required: true },
],
actions: {

View File

@ -11,17 +11,17 @@ export const teacherSchema = z.object({
});
export const addTeacherSchema = z.object({
name: z.string().optional(),
name: z.string().min(1, "姓名为必填项"),
email: z.string().email("请输入有效的邮箱地址"),
password: z.string().optional(),
password: z.string().min(8, "密码长度8-32位").max(32, "密码长度8-32位"),
createdAt: z.string(),
});
export const editTeacherSchema = z.object({
id: z.string(),
name: z.string().optional(),
name: z.string().min(1, "姓名为必填项"),
email: z.string().email("请输入有效的邮箱地址"),
password: z.string().optional(),
password: z.string().min(8, "密码长度8-32位").max(32, "密码长度8-32位"),
createdAt: z.string(),
});
@ -37,9 +37,9 @@ export const teacherConfig = {
{ key: "createdAt", label: "创建时间", sortable: true },
],
formFields: [
{ key: "name", label: "姓名", type: "text", placeholder: "请输入教师姓名(选填)", required: false },
{ key: "name", label: "姓名", type: "text", placeholder: "请输入教师姓名", required: true },
{ key: "email", label: "邮箱", type: "email", placeholder: "请输入教师邮箱", required: true },
{ key: "password", label: "密码", type: "password", placeholder: "请输入密码(选填)", required: false },
{ key: "password", label: "密码", type: "password", placeholder: "请输入8-32位密码", required: true },
{ key: "createdAt", label: "创建时间", type: "datetime-local", required: true },
],
actions: {