From c3c84483433cd65c78661d8d4c9974bf463263db Mon Sep 17 00:00:00 2001 From: qian Date: Sun, 17 May 2026 11:42:48 +0800 Subject: [PATCH] =?UTF-8?q?fix(user):=E9=87=8D=E7=BD=AE=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=91=98=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/admin/src/views/system/UsersPage.vue | 86 +++++++++++++++++++++--- 1 file changed, 75 insertions(+), 11 deletions(-) diff --git a/web/admin/src/views/system/UsersPage.vue b/web/admin/src/views/system/UsersPage.vue index 938b003..58194a0 100644 --- a/web/admin/src/views/system/UsersPage.vue +++ b/web/admin/src/views/system/UsersPage.vue @@ -4,6 +4,7 @@ import { Tag, Button, Space, Popconfirm, Message, Modal, } from '@arco-design/web-vue' import type { TableColumnData, FormInstance } from '@arco-design/web-vue' +import { IconSync } from '@arco-design/web-vue/es/icon' import PageHeader from '@/components/PageHeader.vue' import request from '@/utils/request' import { useExport } from '@/composables/useExport' @@ -24,6 +25,11 @@ const formRef = ref(null) const formLoading = ref(false) const editingId = ref(null) +const showResetModal = ref(false) +const resetTarget = ref(null) +const resetPasswordValue = ref('') +const resetLoading = ref(false) + const form = reactive({ username: '', email: '', @@ -79,10 +85,7 @@ const columns: TableColumnData[] = [ content: `确定${record.status === 1 ? '禁用' : '启用'}该用户?`, onOk: () => toggleStatus(record), }, () => h(Button, { size: 'small' }, () => record.status === 1 ? '禁用' : '启用')), - h(Popconfirm, { - content: '确定重置该用户的密码?', - onOk: () => resetPassword(record), - }, () => h(Button, { size: 'small' }, () => '重置密码')), + h(Button, { size: 'small', onClick: () => openResetPassword(record) }, () => '重置密码'), h(Popconfirm, { content: '确定删除该用户?此操作不可撤销。', onOk: () => deleteUser(record), @@ -166,16 +169,50 @@ async function toggleStatus(row: any) { } } -async function resetPassword(row: any) { +function generateRandomPassword(length = 16) { + const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789!@#$%' + let password = '' + for (let i = 0; i < length; i++) { + password += chars.charAt(Math.floor(Math.random() * chars.length)) + } + return password +} + +function openResetPassword(row: any) { + resetTarget.value = row + resetPasswordValue.value = '' + showResetModal.value = true +} + +function fillRandomPassword() { + resetPasswordValue.value = generateRandomPassword() +} + +async function handleResetPassword(done: () => void) { + if (!resetPasswordValue.value || resetPasswordValue.value.length < 8) { + Message.warning('请输入新密码(至少8位)') + return false + } + resetLoading.value = true try { - const res: any = await request.put(`/admin/users/${row.id}/reset-password`) - Modal.info({ - title: '密码已重置', - content: `新密码:${res.data?.password || '请查看邮箱'}`, - okText: '确定', + await request.put(`/admin/users/${resetTarget.value.id}/reset-password`, { + new_password: resetPasswordValue.value, + }) + const pwd = resetPasswordValue.value + done() + Modal.success({ + title: '密码重置成功', + content: `用户 ${resetTarget.value.username} 的新密码:${pwd}`, + okText: '复制并关闭', + async onOk() { + await navigator.clipboard.writeText(pwd) + Message.success('已复制到剪贴板') + }, }) } catch { - // error handled by interceptor + return false + } finally { + resetLoading.value = false } } @@ -268,6 +305,33 @@ const { exporting, exportFile } = useExport({ + + + + + + + + + + +