From 768bde6e1475fe48e2a0eb41c7690c5c3167d880 Mon Sep 17 00:00:00 2001 From: sunlei Date: Wed, 3 Jun 2026 16:06:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web-antdv-next/src/api/core/menu.ts | 4 + apps/web-antdv-next/src/api/system/index.ts | 1 + apps/web-antdv-next/src/api/system/user.ts | 74 ++++ .../src/locales/langs/en-US/system.json | 17 + .../src/locales/langs/zh-CN/system.json | 17 + .../src/router/routes/modules/system.ts | 9 + .../src/views/system/user/data.ts | 150 ++++++++ .../src/views/system/user/list.vue | 353 ++++++++++++++++++ .../src/views/system/user/modules/form.vue | 77 ++++ 9 files changed, 702 insertions(+) create mode 100644 apps/web-antdv-next/src/api/system/user.ts create mode 100644 apps/web-antdv-next/src/views/system/user/data.ts create mode 100644 apps/web-antdv-next/src/views/system/user/list.vue create mode 100644 apps/web-antdv-next/src/views/system/user/modules/form.vue diff --git a/apps/web-antdv-next/src/api/core/menu.ts b/apps/web-antdv-next/src/api/core/menu.ts index 75e6841..58e0172 100644 --- a/apps/web-antdv-next/src/api/core/menu.ts +++ b/apps/web-antdv-next/src/api/core/menu.ts @@ -63,6 +63,10 @@ const SUPPORTED_ADMIN_MENU_NAMES = new Set([ 'SystemRoleCreate', 'SystemRoleDelete', 'SystemRoleEdit', + 'SystemUser', + 'SystemUserCreate', + 'SystemUserDelete', + 'SystemUserEdit', ]); export function isSupportedAdminMenuName(name?: null | string | symbol) { diff --git a/apps/web-antdv-next/src/api/system/index.ts b/apps/web-antdv-next/src/api/system/index.ts index f2a248f..1b28fb6 100644 --- a/apps/web-antdv-next/src/api/system/index.ts +++ b/apps/web-antdv-next/src/api/system/index.ts @@ -1,3 +1,4 @@ export * from './dept'; export * from './menu'; export * from './role'; +export * from './user'; diff --git a/apps/web-antdv-next/src/api/system/user.ts b/apps/web-antdv-next/src/api/system/user.ts new file mode 100644 index 0000000..833962b --- /dev/null +++ b/apps/web-antdv-next/src/api/system/user.ts @@ -0,0 +1,74 @@ +import type { Recordable } from '@vben/types'; + +import { requestClient } from '#/api/request'; + +export namespace SystemUserApi { + export interface SystemUser { + [key: string]: any; + createTime?: string; + dept?: null | { + id: string; + name: string; + }; + deptId?: null | string; + deptName?: string; + homePath: string; + id: string; + password?: string; + realName: string; + roleIds: string[]; + roleNames: string[]; + roles?: Array<{ + id: string; + name: string; + roleCode: string; + status: 0 | 1; + }>; + status: 0 | 1; + timezone: string; + updateTime?: string; + username: string; + } + + export type SystemUserInput = Partial> & { + roleIds?: string[]; + }; +} + +/** + * 获取用户列表数据 + * @param params 用户查询参数 + */ +async function getUserList(params: Recordable) { + return requestClient.get>( + '/system/user/list', + { params }, + ); +} + +/** + * 创建用户 + * @param data 用户数据 + */ +async function createUser(data: SystemUserApi.SystemUserInput) { + return requestClient.post('/system/user', data); +} + +/** + * 更新用户 + * @param id 用户 ID + * @param data 用户数据 + */ +async function updateUser(id: string, data: SystemUserApi.SystemUserInput) { + return requestClient.put(`/system/user/${id}`, data); +} + +/** + * 删除用户 + * @param id 用户 ID + */ +async function deleteUser(id: string) { + return requestClient.delete(`/system/user/${id}`); +} + +export { createUser, deleteUser, getUserList, updateUser }; diff --git a/apps/web-antdv-next/src/locales/langs/en-US/system.json b/apps/web-antdv-next/src/locales/langs/en-US/system.json index 031820b..0803c7a 100644 --- a/apps/web-antdv-next/src/locales/langs/en-US/system.json +++ b/apps/web-antdv-next/src/locales/langs/en-US/system.json @@ -1,5 +1,22 @@ { "title": "System Management", + "user": { + "allDept": "All", + "createTime": "Create Time", + "dept": "Department", + "deptTree": "Department Tree", + "homePath": "Home Path", + "list": "User List", + "name": "User", + "password": "Password", + "passwordPlaceholder": "Leave blank when editing to keep the current password; blank new users default to 123456", + "realName": "Real Name", + "roles": "Roles", + "status": "Status", + "timezone": "Timezone", + "title": "User Management", + "username": "Username" + }, "dept": { "name": "Department", "title": "Department Management", diff --git a/apps/web-antdv-next/src/locales/langs/zh-CN/system.json b/apps/web-antdv-next/src/locales/langs/zh-CN/system.json index 6576be3..00043d6 100644 --- a/apps/web-antdv-next/src/locales/langs/zh-CN/system.json +++ b/apps/web-antdv-next/src/locales/langs/zh-CN/system.json @@ -1,5 +1,22 @@ { "title": "系统管理", + "user": { + "allDept": "全部", + "createTime": "创建时间", + "dept": "所属部门", + "deptTree": "部门树", + "homePath": "首页路径", + "list": "用户列表", + "name": "用户", + "password": "密码", + "passwordPlaceholder": "编辑时留空则不修改密码;新增留空默认 123456", + "realName": "真实姓名", + "roles": "角色", + "status": "状态", + "timezone": "时区", + "title": "用户管理", + "username": "用户名" + }, "dept": { "list": "部门列表", "createTime": "创建时间", diff --git a/apps/web-antdv-next/src/router/routes/modules/system.ts b/apps/web-antdv-next/src/router/routes/modules/system.ts index 96f348c..371d235 100644 --- a/apps/web-antdv-next/src/router/routes/modules/system.ts +++ b/apps/web-antdv-next/src/router/routes/modules/system.ts @@ -12,6 +12,15 @@ const routes: RouteRecordRaw[] = [ name: 'System', path: '/system', children: [ + { + path: '/system/user', + name: 'SystemUser', + meta: { + icon: 'mdi:account', + title: $t('system.user.title'), + }, + component: () => import('#/views/system/user/list.vue'), + }, { path: '/system/role', name: 'SystemRole', diff --git a/apps/web-antdv-next/src/views/system/user/data.ts b/apps/web-antdv-next/src/views/system/user/data.ts new file mode 100644 index 0000000..bfb0ab1 --- /dev/null +++ b/apps/web-antdv-next/src/views/system/user/data.ts @@ -0,0 +1,150 @@ +import type { VbenFormSchema } from '#/adapter/form'; + +import { z } from '#/adapter/form'; +import { getDeptList } from '#/api/system/dept'; +import { getRoleList } from '#/api/system/role'; +import { $t } from '#/locales'; + +const statusOptions = [ + { label: $t('common.enabled'), value: 1 }, + { label: $t('common.disabled'), value: 0 }, +]; + +async function getRoleOptions() { + const res = await getRoleList({ + page: 1, + pageSize: 1000, + status: 1, + }); + const items = (res as any)?.items || []; + return items.map((role: any) => ({ + label: role.name, + value: role.id, + })); +} + +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'username', + label: $t('system.user.username'), + rules: z + .string() + .min(2, $t('ui.formRules.minLength', [$t('system.user.username'), 2])) + .max( + 30, + $t('ui.formRules.maxLength', [$t('system.user.username'), 30]), + ), + }, + { + component: 'Input', + componentProps: { + placeholder: $t('system.user.passwordPlaceholder'), + }, + fieldName: 'password', + label: $t('system.user.password'), + }, + { + component: 'Input', + fieldName: 'realName', + label: $t('system.user.realName'), + rules: z + .string() + .min(2, $t('ui.formRules.minLength', [$t('system.user.realName'), 2])) + .max( + 30, + $t('ui.formRules.maxLength', [$t('system.user.realName'), 30]), + ), + }, + { + component: 'ApiSelect', + componentProps: { + api: getRoleOptions, + mode: 'multiple', + }, + fieldName: 'roleIds', + label: $t('system.user.roles'), + rules: 'required', + }, + { + component: 'ApiTreeSelect', + componentProps: { + allowClear: true, + api: getDeptList, + childrenField: 'children', + labelField: 'name', + valueField: 'id', + }, + fieldName: 'deptId', + label: $t('system.user.dept'), + }, + { + component: 'Input', + componentProps: { + placeholder: '/workspace', + }, + defaultValue: '/workspace', + fieldName: 'homePath', + label: $t('system.user.homePath'), + }, + { + component: 'Input', + componentProps: { + placeholder: 'Asia/Shanghai', + }, + defaultValue: 'Asia/Shanghai', + fieldName: 'timezone', + label: $t('system.user.timezone'), + }, + { + component: 'RadioGroup', + componentProps: { + buttonStyle: 'solid', + options: statusOptions, + optionType: 'button', + }, + defaultValue: 1, + fieldName: 'status', + label: $t('system.user.status'), + }, + ]; +} + +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'username', + label: $t('system.user.username'), + }, + { + component: 'Input', + fieldName: 'realName', + label: $t('system.user.realName'), + }, + { + component: 'Select', + componentProps: { + allowClear: true, + options: statusOptions, + }, + fieldName: 'status', + label: $t('system.user.status'), + }, + { + component: 'ApiSelect', + componentProps: { + allowClear: true, + api: getRoleOptions, + }, + fieldName: 'roleId', + label: $t('system.user.roles'), + }, + { + component: 'RangePicker', + fieldName: 'createTime', + label: $t('system.user.createTime'), + }, + ]; +} diff --git a/apps/web-antdv-next/src/views/system/user/list.vue b/apps/web-antdv-next/src/views/system/user/list.vue new file mode 100644 index 0000000..ecbc259 --- /dev/null +++ b/apps/web-antdv-next/src/views/system/user/list.vue @@ -0,0 +1,353 @@ + + + + + diff --git a/apps/web-antdv-next/src/views/system/user/modules/form.vue b/apps/web-antdv-next/src/views/system/user/modules/form.vue new file mode 100644 index 0000000..3c48e8a --- /dev/null +++ b/apps/web-antdv-next/src/views/system/user/modules/form.vue @@ -0,0 +1,77 @@ + + + + +