From 28df31cc37ad4a7aeebfad465f57e3776aa431e9 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 8 Sep 2025 09:52:43 +0800 Subject: [PATCH] =?UTF-8?q?reactor=EF=BC=9A=E3=80=90system=20=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E7=AE=A1=E7=90=86=E3=80=91user=20=E8=BF=9B=E4=B8=80?= =?UTF-8?q?=E6=AD=A5=E7=BB=9F=E4=B8=80=E4=BB=A3=E7=A0=81=E9=A3=8E=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web-antd/src/views/system/user/data.ts | 55 +++++++++--- apps/web-antd/src/views/system/user/index.vue | 60 +++++++------- .../src/views/system/user/modules/form.vue | 3 +- apps/web-ele/src/views/system/user/data.ts | 5 +- apps/web-ele/src/views/system/user/index.vue | 83 +++++++++++-------- .../views/system/user/modules/import-form.vue | 9 +- 6 files changed, 123 insertions(+), 92 deletions(-) diff --git a/apps/web-antd/src/views/system/user/data.ts b/apps/web-antd/src/views/system/user/data.ts index 75ea02add..369845c64 100644 --- a/apps/web-antd/src/views/system/user/data.ts +++ b/apps/web-antd/src/views/system/user/data.ts @@ -4,6 +4,7 @@ import type { SystemUserApi } from '#/api/system/user'; import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; +import { $t } from '@vben/locales'; import { handleTree } from '@vben/utils'; import { z } from '#/adapter/form'; @@ -45,6 +46,7 @@ export function useFormSchema(): VbenFormSchema[] { component: 'Input', rules: 'required', }, + // TODO @xingyu:【重要】这个字段出不来 { fieldName: 'deptId', label: '归属部门', @@ -126,31 +128,50 @@ export function useResetPasswordFormSchema(): VbenFormSchema[] { }, }, { - fieldName: 'newPassword', - label: '新密码', - component: 'InputPassword', + component: 'VbenInputPassword', componentProps: { + passwordStrength: true, placeholder: '请输入新密码', }, + dependencies: { + rules(values) { + return z + .string({ message: '请输入新密码' }) + .min(5, '密码长度不能少于 5 个字符') + .max(20, '密码长度不能超过 20 个字符') + .refine( + (value) => value !== values.oldPassword, + '新旧密码不能相同', + ); + }, + triggerFields: ['newPassword', 'oldPassword'], + }, + fieldName: 'newPassword', + label: '新密码', rules: 'required', }, { - fieldName: 'confirmPassword', - label: '确认密码', - component: 'InputPassword', + component: 'VbenInputPassword', componentProps: { - placeholder: '请再次输入新密码', + passwordStrength: true, + placeholder: $t('authentication.confirmPassword'), }, dependencies: { - rules(values: Record) { - const { newPassword } = values; + rules(values) { return z - .string() - .nonempty('确认密码不能为空') - .refine((value) => value === newPassword, '两次输入的密码不一致'); + .string({ message: '请输入确认密码' }) + .min(5, '密码长度不能少于 5 个字符') + .max(20, '密码长度不能超过 20 个字符') + .refine( + (value) => value === values.newPassword, + '新密码和确认密码不一致', + ); }, - triggerFields: ['newPassword'], + triggerFields: ['newPassword', 'confirmPassword'], }, + fieldName: 'confirmPassword', + label: '确认密码', + rules: 'required', }, ]; } @@ -207,6 +228,7 @@ export function useImportFormSchema(): VbenFormSchema[] { rules: 'required', help: '仅允许导入 xls、xlsx 格式文件', }, + // TODO @xingyu:【重要】看不到 switch 这个按钮 { fieldName: 'updateSupport', label: '是否覆盖', @@ -266,26 +288,32 @@ export function useGridColumns( { field: 'id', title: '用户编号', + minWidth: 100, }, { field: 'username', title: '用户名称', + minWidth: 120, }, { field: 'nickname', title: '用户昵称', + minWidth: 120, }, { field: 'deptName', title: '部门', + minWidth: 120, }, { field: 'mobile', title: '手机号码', + minWidth: 120, }, { field: 'status', title: '状态', + minWidth: 100, align: 'center', cellRender: { attrs: { beforeChange: onStatusChange }, @@ -299,6 +327,7 @@ export function useGridColumns( { field: 'createTime', title: '创建时间', + minWidth: 180, formatter: 'formatDateTime', }, { diff --git a/apps/web-antd/src/views/system/user/index.vue b/apps/web-antd/src/views/system/user/index.vue index 247d73be9..cf83ffea8 100644 --- a/apps/web-antd/src/views/system/user/index.vue +++ b/apps/web-antd/src/views/system/user/index.vue @@ -50,7 +50,7 @@ const [ImportModal, importModalApi] = useVbenModal({ }); /** 刷新表格 */ -function onRefresh() { +function handleRefresh() { gridApi.query(); } @@ -62,10 +62,9 @@ async function handleExport() { /** 选择部门 */ const searchDeptId = ref(undefined); - async function handleDeptSelect(dept: SystemDeptApi.Dept) { searchDeptId.value = dept.id; - onRefresh(); + handleRefresh(); } /** 创建用户 */ @@ -87,15 +86,29 @@ function handleEdit(row: SystemUserApi.User) { async function handleDelete(row: SystemUserApi.User) { const hideLoading = message.loading({ content: $t('ui.actionMessage.deleting', [row.username]), - key: 'action_key_msg', + duration: 0, }); try { await deleteUser(row.id as number); - message.success({ - content: $t('ui.actionMessage.deleteSuccess', [row.username]), - key: 'action_key_msg', - }); - onRefresh(); + message.success($t('ui.actionMessage.deleteSuccess', [row.username])); + handleRefresh(); + } finally { + hideLoading(); + } +} + +/** 批量删除用户 */ +async function handleDeleteBatch() { + await confirm($t('ui.actionMessage.deleteBatchConfirm')); + const hideLoading = message.loading({ + content: $t('ui.actionMessage.deletingBatch'), + duration: 0, + }); + try { + await deleteUserList(checkedIds.value); + checkedIds.value = []; + message.success($t('ui.actionMessage.deleteSuccess')); + handleRefresh(); } finally { hideLoading(); } @@ -110,23 +123,6 @@ function handleRowCheckboxChange({ checkedIds.value = records.map((item) => item.id!); } -/** 批量删除用户 */ -async function handleDeleteBatch() { - const hideLoading = message.loading({ - content: $t('ui.actionMessage.deleting'), - duration: 0, - key: 'action_process_msg', - }); - try { - await deleteUserList(checkedIds.value); - checkedIds.value = []; - message.success($t('ui.actionMessage.deleteSuccess')); - onRefresh(); - } finally { - hideLoading(); - } -} - /** 重置密码 */ function handleResetPassword(row: SystemUserApi.User) { resetPasswordModalApi.setData(row).open(); @@ -210,10 +206,10 @@ const [Grid, gridApi] = useVbenVxeGrid({ /> - - - - + + + +
@@ -248,11 +244,11 @@ const [Grid, gridApi] = useVbenVxeGrid({ onClick: handleImport, }, { - label: '批量删除', + label: $t('ui.actionTitle.deleteBatch'), type: 'primary', danger: true, - disabled: isEmpty(checkedIds), icon: ACTION_ICON.DELETE, + disabled: isEmpty(checkedIds), auth: ['system:user:delete'], onClick: handleDeleteBatch, }, diff --git a/apps/web-antd/src/views/system/user/modules/form.vue b/apps/web-antd/src/views/system/user/modules/form.vue index 479c71132..d4f8e7e8f 100644 --- a/apps/web-antd/src/views/system/user/modules/form.vue +++ b/apps/web-antd/src/views/system/user/modules/form.vue @@ -26,8 +26,9 @@ const [Form, formApi] = useVbenForm({ componentProps: { class: 'w-full', }, + formItemClass: 'col-span-2', + labelWidth: 80, }, - wrapperClass: 'grid-cols-2', layout: 'horizontal', schema: useFormSchema(), showDefaultActions: false, diff --git a/apps/web-ele/src/views/system/user/data.ts b/apps/web-ele/src/views/system/user/data.ts index 0d1f86d11..28d9b4ef6 100644 --- a/apps/web-ele/src/views/system/user/data.ts +++ b/apps/web-ele/src/views/system/user/data.ts @@ -282,10 +282,7 @@ export function useGridColumns( ) => PromiseLike, ): VxeTableGridOptions['columns'] { return [ - { - type: 'checkbox', - width: 40, - }, + { type: 'checkbox', width: 40 }, { field: 'id', title: '用户编号', diff --git a/apps/web-ele/src/views/system/user/index.vue b/apps/web-ele/src/views/system/user/index.vue index 2c395a05c..385caef6d 100644 --- a/apps/web-ele/src/views/system/user/index.vue +++ b/apps/web-ele/src/views/system/user/index.vue @@ -50,59 +50,66 @@ const [ImportModal, importModalApi] = useVbenModal({ }); /** 刷新表格 */ -function onRefresh() { +function handleRefresh() { gridApi.query(); } /** 导出表格 */ -async function onExport() { +async function handleExport() { const data = await exportUser(await gridApi.formApi.getValues()); downloadFileFromBlobPart({ fileName: '用户.xls', source: data }); } /** 选择部门 */ const searchDeptId = ref(undefined); -async function onDeptSelect(dept: SystemDeptApi.Dept) { +async function handleDeptSelect(dept: SystemDeptApi.Dept) { searchDeptId.value = dept.id; - onRefresh(); + handleRefresh(); } /** 创建用户 */ -function onCreate() { +function handleCreate() { formModalApi.setData(null).open(); } /** 导入用户 */ -function onImport() { +function handleImport() { importModalApi.open(); } /** 编辑用户 */ -function onEdit(row: SystemUserApi.User) { +function handleEdit(row: SystemUserApi.User) { formModalApi.setData(row).open(); } /** 删除用户 */ -async function onDelete(row: SystemUserApi.User) { +async function handleDelete(row: SystemUserApi.User) { const loadingInstance = ElLoading.service({ text: $t('ui.actionMessage.deleting', [row.username]), }); try { await deleteUser(row.id as number); ElMessage.success($t('ui.actionMessage.deleteSuccess', [row.username])); - onRefresh(); + handleRefresh(); } finally { loadingInstance.close(); } } /** 批量删除用户 */ -async function onDeleteBatch() { - await confirm('确定要批量删除该用户吗?'); - await deleteUserList(checkedIds.value); - checkedIds.value = []; - ElMessage.success($t('ui.actionMessage.deleteSuccess')); - onRefresh(); +async function handleDeleteBatch() { + await confirm($t('ui.actionMessage.deleteBatchConfirm')); + const loadingInstance = ElLoading.service({ + text: $t('ui.actionMessage.deletingBatch'), + }); + try { + await deleteUserList(checkedIds.value); + checkedIds.value = []; + ElMessage.success($t('ui.actionMessage.deleteSuccess')); + handleRefresh(); + } finally { + loadingInstance.close(); + } } const checkedIds = ref([]); @@ -115,17 +122,17 @@ function handleRowCheckboxChange({ } /** 重置密码 */ -function onResetPassword(row: SystemUserApi.User) { +function handleResetPassword(row: SystemUserApi.User) { resetPasswordModalApi.setData(row).open(); } /** 分配角色 */ -function onAssignRole(row: SystemUserApi.User) { +function handleAssignRole(row: SystemUserApi.User) { assignRoleModalApi.setData(row).open(); } /** 更新用户状态 */ -async function onStatusChange( +async function handleStatusChange( newStatus: number, row: SystemUserApi.User, ): Promise { @@ -155,7 +162,7 @@ const [Grid, gridApi] = useVbenVxeGrid({ schema: useGridFormSchema(), }, gridOptions: { - columns: useGridColumns(onStatusChange), + columns: useGridColumns(handleStatusChange), height: 'auto', keepSource: true, proxyConfig: { @@ -172,6 +179,7 @@ const [Grid, gridApi] = useVbenVxeGrid({ }, rowConfig: { keyField: 'id', + isHover: true, }, toolbarConfig: { refresh: true, @@ -196,15 +204,15 @@ const [Grid, gridApi] = useVbenVxeGrid({ /> - - - - + + + +
- +
@@ -217,21 +225,21 @@ const [Grid, gridApi] = useVbenVxeGrid({ type: 'primary', icon: ACTION_ICON.ADD, auth: ['system:user:create'], - onClick: onCreate, + onClick: handleCreate, }, { label: $t('ui.actionTitle.export'), type: 'primary', icon: ACTION_ICON.DOWNLOAD, auth: ['system:user:export'], - onClick: onExport, + onClick: handleExport, }, { label: $t('ui.actionTitle.import', ['用户']), type: 'primary', icon: ACTION_ICON.UPLOAD, auth: ['system:user:import'], - onClick: onImport, + onClick: handleImport, }, { label: $t('ui.actionTitle.deleteBatch'), @@ -239,7 +247,7 @@ const [Grid, gridApi] = useVbenVxeGrid({ icon: ACTION_ICON.DELETE, disabled: isEmpty(checkedIds), auth: ['system:user:delete'], - onClick: onDeleteBatch, + onClick: handleDeleteBatch, }, ]" /> @@ -249,35 +257,38 @@ const [Grid, gridApi] = useVbenVxeGrid({ :actions="[ { label: $t('common.edit'), - type: 'text', + type: 'primary', + link: true, icon: ACTION_ICON.EDIT, auth: ['system:user:update'], - onClick: onEdit.bind(null, row), + onClick: handleEdit.bind(null, row), }, { label: $t('common.delete'), type: 'danger', - text: true, + link: true, icon: ACTION_ICON.DELETE, auth: ['system:user:delete'], popConfirm: { title: $t('ui.actionMessage.deleteConfirm', [row.username]), - confirm: onDelete.bind(null, row), + confirm: handleDelete.bind(null, row), }, }, ]" :drop-down-actions="[ { label: '分配角色', - type: 'text', + type: 'primary', + link: true, auth: ['system:permission:assign-user-role'], - onClick: onAssignRole.bind(null, row), + onClick: handleAssignRole.bind(null, row), }, { label: '重置密码', - type: 'text', + type: 'primary', + link: true, auth: ['system:user:update-password'], - onClick: onResetPassword.bind(null, row), + onClick: handleResetPassword.bind(null, row), }, ]" /> diff --git a/apps/web-ele/src/views/system/user/modules/import-form.vue b/apps/web-ele/src/views/system/user/modules/import-form.vue index 5e8008033..c1d07565f 100644 --- a/apps/web-ele/src/views/system/user/modules/import-form.vue +++ b/apps/web-ele/src/views/system/user/modules/import-form.vue @@ -16,11 +16,8 @@ const emit = defineEmits(['success']); const [Form, formApi] = useVbenForm({ commonConfig: { - componentProps: { - class: 'w-full', - }, formItemClass: 'col-span-2', - labelWidth: 80, + labelWidth: 120, }, layout: 'horizontal', schema: useImportFormSchema(), @@ -55,7 +52,7 @@ function beforeUpload(file: UploadRawFile) { } /** 下载模版 */ -async function onDownload() { +async function handleDownload() { const data = await importUserTemplate(); downloadFileFromBlobPart({ fileName: '用户导入模板.xls', source: data }); } @@ -79,7 +76,7 @@ async function onDownload() {