Selaa lähdekoodia

✨ feat(user): 添加用户类型功能

- 新增UserType枚举定义老师和学生两种用户类型
- 在用户实体中添加user_type字段,默认为学生
- 更新用户列表页,添加用户类型列并使用标签显示
- 在用户创建和编辑表单中添加用户类型选择框
- 完善用户相关DTO,添加用户类型字段验证和默认值
- 编辑用户时设置默认用户类型,避免空值问题
yourname 4 kuukautta sitten
vanhempi
sitoutus
4f268c9a46

+ 29 - 1
src/client/admin/pages/Users.tsx

@@ -7,6 +7,7 @@ import { useQuery } from '@tanstack/react-query';
 import dayjs from 'dayjs';
 import { roleClient, userClient } from '@/client/api';
 import type { InferResponseType, InferRequestType } from 'hono/client';
+import { UserType } from '@/server/modules/users/user.enum';
 
 type UserListResponse = InferResponseType<typeof userClient.$get, 200>;
 type RoleListResponse = InferResponseType<typeof roleClient.$get, 200>;
@@ -84,7 +85,10 @@ export const UsersPage = () => {
   const showEditModal = (user: any) => {
     setModalTitle('编辑用户');
     setEditingUser(user);
-    form.setFieldsValue(user);
+    form.setFieldsValue({
+      ...user,
+      userType: user.userType || UserType.STUDENT
+    });
     setModalVisible(true);
   };
 
@@ -161,6 +165,16 @@ export const UsersPage = () => {
       dataIndex: 'name',
       key: 'name',
     },
+    {
+      title: '用户类型',
+      dataIndex: 'userType',
+      key: 'userType',
+      render: (userType: string) => (
+        <Tag color={userType === UserType.TEACHER ? 'blue' : 'green'}>
+          {userType === UserType.TEACHER ? '老师' : '学生'}
+        </Tag>
+      ),
+    },
     {
       title: '角色',
       dataIndex: 'role',
@@ -324,11 +338,25 @@ export const UsersPage = () => {
             </Form.Item>
           )}
 
+          <Form.Item
+            name="userType"
+            label="用户类型"
+            required
+            rules={[{ required: true, message: '请选择用户类型' }]}
+            initialValue={UserType.STUDENT}
+          >
+            <Select placeholder="请选择用户类型">
+              <Select.Option value={UserType.TEACHER}>老师</Select.Option>
+              <Select.Option value={UserType.STUDENT}>学生</Select.Option>
+            </Select>
+          </Form.Item>
+
           <Form.Item
             name="isDisabled"
             label="状态"
             required
             rules={[{ required: true, message: '请选择状态' }]}
+            initialValue={0}
           >
             <Select placeholder="请选择状态">
               <Select.Option value={0}>启用</Select.Option>

+ 95 - 0
src/server/modules/users/user.entity.ts

@@ -2,6 +2,8 @@ import { Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable, CreateDa
 import { Role, RoleSchema } from './role.entity';
 import { z } from '@hono/zod-openapi';
 import { DeleteStatus, DisabledStatus } from '@/share/types';
+import { UserType } from './user.enum';
+
 
 @Entity({ name: 'users' })
 export class UserEntity {
@@ -35,6 +37,15 @@ export class UserEntity {
   @Column({ name: 'is_deleted', type: 'int', default: DeleteStatus.NOT_DELETED, comment: '是否删除(0:未删除,1:已删除)' })
   isDeleted!: DeleteStatus;
 
+  @Column({
+    name: 'user_type',
+    type: 'enum',
+    enum: UserType,
+    default: UserType.STUDENT,
+    comment: '用户类型(teacher:老师,student:学生)'
+  })
+  userType!: UserType;
+
   @ManyToMany(() => Role)
   @JoinTable()
   roles!: Role[];
@@ -94,6 +105,90 @@ export const UserSchema = z.object({
     ],
     description: '用户角色列表'
   }),
+  userType: z.enum([UserType.TEACHER, UserType.STUDENT]).default(UserType.STUDENT).openapi({
+    example: UserType.STUDENT,
+    description: '用户类型(teacher:老师,student:学生)'
+  }),
   createdAt: z.date().openapi({ description: '创建时间' }),
   updatedAt: z.date().openapi({ description: '更新时间' })
+});
+
+export const CreateUserDto = z.object({
+  username: z.string().min(3).max(255).openapi({
+    example: 'admin',
+    description: '用户名,3-255个字符'
+  }),
+  password: z.string().min(6).max(255).openapi({
+    example: 'password123',
+    description: '密码,最少6位'
+  }),
+  phone: z.string().max(255).nullable().optional().openapi({
+    example: '13800138000',
+    description: '手机号'
+  }),
+  email: z.string().email().max(255).nullable().optional().openapi({
+    example: 'user@example.com',
+    description: '邮箱'
+  }),
+  nickname: z.string().max(255).nullable().optional().openapi({
+    example: '昵称',
+    description: '用户昵称'
+  }),
+  name: z.string().max(255).nullable().optional().openapi({
+    example: '张三',
+    description: '真实姓名'
+  }),
+  avatar: z.string().max(255).nullable().optional().openapi({
+    example: 'https://example.com/avatar.jpg',
+    description: '用户头像'
+  }),
+  isDisabled: z.number().int().min(0).max(1).default(DisabledStatus.ENABLED).openapi({
+    example: DisabledStatus.ENABLED,
+    description: '是否禁用(0:启用,1:禁用)'
+  }),
+  userType: z.enum([UserType.TEACHER, UserType.STUDENT]).default(UserType.STUDENT).openapi({
+    example: UserType.STUDENT,
+    description: '用户类型(teacher:老师,student:学生)'
+  }),
+  createdAt: z.date().openapi({ description: '创建时间' }),
+  updatedAt: z.date().openapi({ description: '更新时间' })
+});
+
+export const UpdateUserDto = z.object({
+  username: z.string().min(3).max(255).optional().openapi({
+    example: 'admin',
+    description: '用户名,3-255个字符'
+  }),
+  password: z.string().min(6).max(255).optional().openapi({
+    example: 'password123',
+    description: '密码,最少6位'
+  }),
+  phone: z.string().max(255).nullable().optional().openapi({
+    example: '13800138000',
+    description: '手机号'
+  }),
+  email: z.string().email().max(255).nullable().optional().openapi({
+    example: 'user@example.com',
+    description: '邮箱'
+  }),
+  nickname: z.string().max(255).nullable().optional().openapi({
+    example: '昵称',
+    description: '用户昵称'
+  }),
+  name: z.string().max(255).nullable().optional().openapi({
+    example: '张三',
+    description: '真实姓名'
+  }),
+  avatar: z.string().max(255).nullable().optional().openapi({
+    example: 'https://example.com/avatar.jpg',
+    description: '用户头像'
+  }),
+  isDisabled: z.number().int().min(0).max(1).optional().openapi({
+    example: DisabledStatus.ENABLED,
+    description: '是否禁用(0:启用,1:禁用)'
+  }),
+  userType: z.enum([UserType.TEACHER, UserType.STUDENT]).optional().openapi({
+    example: UserType.STUDENT,
+    description: '用户类型(teacher:老师,student:学生)'
+  })
 });

+ 5 - 0
src/server/modules/users/user.enum.ts

@@ -0,0 +1,5 @@
+
+export enum UserType {
+    TEACHER = 'teacher',
+    STUDENT = 'student'
+  }