|
|
@@ -5,32 +5,30 @@
|
|
|
<el-form :model="searchForm" inline data-id="search-form">
|
|
|
<el-form-item>
|
|
|
<el-input
|
|
|
- v-model.trim="searchForm.username"
|
|
|
- :placeholder="t('用户名')"
|
|
|
+ v-model.trim="searchForm.keyword"
|
|
|
+ :placeholder="t('用户名/昵称')"
|
|
|
clearable
|
|
|
+ data-id="search-keyword"
|
|
|
@keyup.enter="handleSearch"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
<el-form-item>
|
|
|
- <el-input v-model.trim="searchForm.realName" :placeholder="t('姓名')" clearable @keyup.enter="handleSearch" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item>
|
|
|
- <el-select v-model="searchForm.status" :placeholder="t('状态')" clearable>
|
|
|
- <el-option :label="t('全部')" value="" />
|
|
|
- <el-option :label="t('启用')" value="enabled" />
|
|
|
- <el-option :label="t('禁用')" value="disabled" />
|
|
|
+ <el-select v-model="searchForm.enabled" :placeholder="t('状态')" clearable data-id="search-status">
|
|
|
+ <el-option :label="t('全部')" :value="undefined" />
|
|
|
+ <el-option :label="t('启用')" :value="true" />
|
|
|
+ <el-option :label="t('禁用')" :value="false" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
<el-form-item>
|
|
|
- <el-button type="primary" @click="handleSearch">
|
|
|
+ <el-button type="primary" data-id="btn-search" @click="handleSearch">
|
|
|
<Icon icon="mdi:magnify" width="16" height="16" style="margin-right: 4px" />
|
|
|
{{ t('查询') }}
|
|
|
</el-button>
|
|
|
- <el-button type="info" @click="handleReset">
|
|
|
+ <el-button type="info" data-id="btn-reset" @click="handleReset">
|
|
|
<Icon icon="mdi:refresh" width="16" height="16" style="margin-right: 4px" />
|
|
|
{{ t('重置') }}
|
|
|
</el-button>
|
|
|
- <el-button type="primary" @click="handleAdd">
|
|
|
+ <el-button type="primary" data-id="btn-add" @click="handleAdd">
|
|
|
<Icon icon="mdi:plus" width="16" height="16" style="margin-right: 4px" />
|
|
|
{{ t('新增') }}
|
|
|
</el-button>
|
|
|
@@ -47,41 +45,46 @@
|
|
|
stripe
|
|
|
size="default"
|
|
|
height="100%"
|
|
|
+ data-id="user-table"
|
|
|
@sort-change="handleSortChange"
|
|
|
>
|
|
|
<el-table-column prop="id" :label="t('ID')" width="80" />
|
|
|
<el-table-column prop="username" :label="t('用户名')" min-width="120" sortable="custom" show-overflow-tooltip />
|
|
|
- <el-table-column prop="realName" :label="t('姓名')" min-width="120" show-overflow-tooltip />
|
|
|
- <el-table-column prop="email" :label="t('邮箱')" min-width="180" show-overflow-tooltip />
|
|
|
- <el-table-column prop="phone" :label="t('手机号')" min-width="130" show-overflow-tooltip />
|
|
|
- <el-table-column :label="t('角色')" min-width="120">
|
|
|
+ <el-table-column prop="nickname" :label="t('昵称')" min-width="120" show-overflow-tooltip />
|
|
|
+ <el-table-column :label="t('角色')" min-width="150">
|
|
|
<template #default="{ row }">
|
|
|
- <el-tag v-for="role in row.roles" :key="role" size="small" style="margin-right: 4px">
|
|
|
- {{ role }}
|
|
|
+ <el-tag v-for="role in row.roles" :key="role.id" size="small" style="margin-right: 4px">
|
|
|
+ {{ role.name }}
|
|
|
</el-tag>
|
|
|
+ <span v-if="!row.roles?.length" class="text-placeholder">-</span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column :label="t('状态')" width="100" align="center">
|
|
|
<template #default="{ row }">
|
|
|
- <el-tag :type="row.status === 'enabled' ? 'success' : 'danger'" size="small">
|
|
|
- {{ row.status === 'enabled' ? t('启用') : t('禁用') }}
|
|
|
+ <el-tag :type="row.enabled ? 'success' : 'danger'" size="small">
|
|
|
+ {{ row.enabled ? t('启用') : t('禁用') }}
|
|
|
</el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
+ <el-table-column :label="t('最后登录')" min-width="160">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ row.lastLoginAt ? formatTime(row.lastLoginAt) : '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
<el-table-column :label="t('创建时间')" min-width="160">
|
|
|
<template #default="{ row }">
|
|
|
{{ formatTime(row.createdAt) }}
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column :label="t('操作')" width="150" align="center" fixed="right">
|
|
|
+ <el-table-column :label="t('操作')" width="160" align="center" fixed="right">
|
|
|
<template #default="{ row }">
|
|
|
- <el-button type="primary" link @click="handleEdit(row)">
|
|
|
+ <el-button type="primary" link data-id="btn-edit" @click="handleEdit(row)">
|
|
|
<Icon icon="mdi:note-edit-outline" width="20" height="20" />
|
|
|
</el-button>
|
|
|
- <el-button type="primary" link @click="handleResetPassword(row)">
|
|
|
- <Icon icon="mdi:lock-reset" width="20" height="20" />
|
|
|
+ <el-button type="warning" link data-id="btn-assign-role" @click="handleAssignRole(row)">
|
|
|
+ <Icon icon="mdi:account-key" width="20" height="20" />
|
|
|
</el-button>
|
|
|
- <el-button type="danger" link @click="handleDelete(row)">
|
|
|
+ <el-button type="danger" link data-id="btn-delete" @click="handleDelete(row)">
|
|
|
<Icon icon="mdi:delete" width="20" height="20" />
|
|
|
</el-button>
|
|
|
</template>
|
|
|
@@ -98,6 +101,7 @@
|
|
|
:total="total"
|
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
|
background
|
|
|
+ data-id="pagination"
|
|
|
@size-change="handleSizeChange"
|
|
|
@current-change="handleCurrentChange"
|
|
|
/>
|
|
|
@@ -111,49 +115,110 @@
|
|
|
size="500px"
|
|
|
:close-on-click-modal="false"
|
|
|
destroy-on-close
|
|
|
+ data-id="user-drawer"
|
|
|
>
|
|
|
<el-form ref="formRef" :model="form" :rules="rules" label-width="auto">
|
|
|
<div class="role-form-container">
|
|
|
<el-form-item :label="t('用户名')" prop="username">
|
|
|
- <el-input v-model="form.username" :disabled="isEdit" :placeholder="t('请输入用户名')" />
|
|
|
+ <el-input
|
|
|
+ v-model="form.username"
|
|
|
+ :disabled="isEdit"
|
|
|
+ :placeholder="t('请输入用户名')"
|
|
|
+ data-id="input-username"
|
|
|
+ />
|
|
|
</el-form-item>
|
|
|
- <el-form-item :label="t('姓名')" prop="realName">
|
|
|
- <el-input v-model="form.realName" :placeholder="t('请输入姓名')" />
|
|
|
+ <el-form-item :label="t('昵称')" prop="nickname">
|
|
|
+ <el-input v-model="form.nickname" :placeholder="t('请输入昵称')" data-id="input-nickname" />
|
|
|
</el-form-item>
|
|
|
- <el-form-item :label="t('邮箱')" prop="email">
|
|
|
- <el-input v-model="form.email" :placeholder="t('请输入邮箱')" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item :label="t('手机号')" prop="phone">
|
|
|
- <el-input v-model="form.phone" :placeholder="t('请输入手机号')" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item :label="t('角色')" prop="roles">
|
|
|
- <el-select v-model="form.roles" multiple :placeholder="t('请选择角色')" style="width: 100%">
|
|
|
- <el-option v-for="role in roleOptions" :key="role.value" :label="role.label" :value="role.value" />
|
|
|
+ <el-form-item :label="t('角色')" prop="roleIds">
|
|
|
+ <el-select
|
|
|
+ v-model="form.roleIds"
|
|
|
+ multiple
|
|
|
+ :placeholder="t('请选择角色')"
|
|
|
+ style="width: 100%"
|
|
|
+ data-id="select-roles"
|
|
|
+ >
|
|
|
+ <el-option v-for="role in roleOptions" :key="role.id" :label="role.name" :value="role.id" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
<el-form-item v-if="!isEdit" :label="t('密码')" prop="password">
|
|
|
- <el-input v-model="form.password" type="password" show-password :placeholder="t('请输入密码')" />
|
|
|
+ <el-input
|
|
|
+ v-model="form.password"
|
|
|
+ type="password"
|
|
|
+ show-password
|
|
|
+ :placeholder="t('请输入密码')"
|
|
|
+ data-id="input-password"
|
|
|
+ />
|
|
|
</el-form-item>
|
|
|
- <el-form-item :label="t('状态')" prop="status">
|
|
|
- <el-radio-group v-model="form.status">
|
|
|
- <el-radio value="enabled">{{ t('启用') }}</el-radio>
|
|
|
- <el-radio value="disabled">{{ t('禁用') }}</el-radio>
|
|
|
- </el-radio-group>
|
|
|
+ <el-form-item v-if="isEdit" :label="t('新密码')" prop="password">
|
|
|
+ <el-input
|
|
|
+ v-model="form.password"
|
|
|
+ type="password"
|
|
|
+ show-password
|
|
|
+ :placeholder="t('留空则不修改密码')"
|
|
|
+ data-id="input-new-password"
|
|
|
+ />
|
|
|
</el-form-item>
|
|
|
- <el-form-item :label="t('备注')" prop="remark">
|
|
|
- <el-input v-model="form.remark" type="textarea" :rows="3" :placeholder="t('请输入备注')" />
|
|
|
+ <el-form-item :label="t('状态')" prop="enabled">
|
|
|
+ <el-radio-group v-model="form.enabled" data-id="radio-enabled">
|
|
|
+ <el-radio :value="true">{{ t('启用') }}</el-radio>
|
|
|
+ <el-radio :value="false">{{ t('禁用') }}</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
</el-form-item>
|
|
|
</div>
|
|
|
</el-form>
|
|
|
<template #footer>
|
|
|
<div class="drawer-footer">
|
|
|
- <el-button @click="drawerVisible = false">{{ t('取消') }}</el-button>
|
|
|
- <el-button type="primary" :loading="submitting" @click="handleSubmit">
|
|
|
+ <el-button data-id="btn-cancel" @click="drawerVisible = false">{{ t('取消') }}</el-button>
|
|
|
+ <el-button type="primary" :loading="submitting" data-id="btn-submit" @click="handleSubmit">
|
|
|
{{ isEdit ? t('更新') : t('添加') }}
|
|
|
</el-button>
|
|
|
</div>
|
|
|
</template>
|
|
|
</el-drawer>
|
|
|
+
|
|
|
+ <!-- 分配角色对话框 -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="roleDialogVisible"
|
|
|
+ :title="t('分配角色')"
|
|
|
+ width="500px"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ destroy-on-close
|
|
|
+ data-id="role-dialog"
|
|
|
+ >
|
|
|
+ <div class="role-dialog-content">
|
|
|
+ <div class="user-info">
|
|
|
+ <span class="label">{{ t('用户') }}:</span>
|
|
|
+ <span class="value">{{ assignRoleUser?.username }} ({{ assignRoleUser?.nickname || '-' }})</span>
|
|
|
+ </div>
|
|
|
+ <el-form label-width="80px">
|
|
|
+ <el-form-item :label="t('角色')">
|
|
|
+ <el-select
|
|
|
+ v-model="assignRoleId"
|
|
|
+ :placeholder="t('请选择角色')"
|
|
|
+ style="width: 100%"
|
|
|
+ clearable
|
|
|
+ data-id="assign-role-select"
|
|
|
+ >
|
|
|
+ <el-option v-for="role in roleOptions" :key="role.id" :label="role.name" :value="role.id" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button data-id="btn-role-cancel" @click="roleDialogVisible = false">{{ t('取消') }}</el-button>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ :loading="assigningRole"
|
|
|
+ data-id="btn-role-confirm"
|
|
|
+ @click="handleConfirmAssignRole"
|
|
|
+ >
|
|
|
+ {{ t('确定') }}
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -164,84 +229,26 @@ import { Icon } from '@iconify/vue'
|
|
|
import type { FormInstance, FormRules } from 'element-plus'
|
|
|
import { useI18n } from 'vue-i18n'
|
|
|
import { formatTime } from '@/utils/dayjs'
|
|
|
+import {
|
|
|
+ listAccounts,
|
|
|
+ addAccount,
|
|
|
+ updateAccount,
|
|
|
+ deleteAccount,
|
|
|
+ type AdminDTO,
|
|
|
+ type AccountListRequest
|
|
|
+} from '@/api/admin-account'
|
|
|
+import { listAllRoles, type RoleSimpleDTO } from '@/api/admin-role'
|
|
|
|
|
|
const { t } = useI18n({ useScope: 'global' })
|
|
|
|
|
|
-// Mock 数据
|
|
|
-interface User {
|
|
|
- id: number
|
|
|
- username: string
|
|
|
- realName: string
|
|
|
- email: string
|
|
|
- phone: string
|
|
|
- roles: string[]
|
|
|
- status: 'enabled' | 'disabled'
|
|
|
- createdAt: string
|
|
|
- remark?: string
|
|
|
-}
|
|
|
-
|
|
|
-const mockUsers: User[] = [
|
|
|
- {
|
|
|
- id: 1,
|
|
|
- username: 'admin',
|
|
|
- realName: '系统管理员',
|
|
|
- email: 'admin@example.com',
|
|
|
- phone: '13800138000',
|
|
|
- roles: ['管理员'],
|
|
|
- status: 'enabled',
|
|
|
- createdAt: '2024-01-01T10:00:00Z'
|
|
|
- },
|
|
|
- {
|
|
|
- id: 2,
|
|
|
- username: 'operator',
|
|
|
- realName: '张三',
|
|
|
- email: 'zhangsan@example.com',
|
|
|
- phone: '13800138001',
|
|
|
- roles: ['操作员'],
|
|
|
- status: 'enabled',
|
|
|
- createdAt: '2024-01-15T14:30:00Z'
|
|
|
- },
|
|
|
- {
|
|
|
- id: 3,
|
|
|
- username: 'viewer',
|
|
|
- realName: '李四',
|
|
|
- email: 'lisi@example.com',
|
|
|
- phone: '13800138002',
|
|
|
- roles: ['查看者'],
|
|
|
- status: 'enabled',
|
|
|
- createdAt: '2024-02-01T09:00:00Z'
|
|
|
- },
|
|
|
- {
|
|
|
- id: 4,
|
|
|
- username: 'test',
|
|
|
- realName: '王五',
|
|
|
- email: 'wangwu@example.com',
|
|
|
- phone: '13800138003',
|
|
|
- roles: ['操作员', '查看者'],
|
|
|
- status: 'disabled',
|
|
|
- createdAt: '2024-02-15T16:00:00Z'
|
|
|
- },
|
|
|
- {
|
|
|
- id: 5,
|
|
|
- username: 'user001',
|
|
|
- realName: '赵六',
|
|
|
- email: 'zhaoliu@example.com',
|
|
|
- phone: '13800138004',
|
|
|
- roles: ['查看者'],
|
|
|
- status: 'enabled',
|
|
|
- createdAt: '2024-03-01T11:30:00Z'
|
|
|
- }
|
|
|
-]
|
|
|
-
|
|
|
const loading = ref(false)
|
|
|
-const userList = ref<User[]>([])
|
|
|
+const userList = ref<AdminDTO[]>([])
|
|
|
const tableRef = ref()
|
|
|
|
|
|
// 搜索表单
|
|
|
-const searchForm = reactive({
|
|
|
- username: '',
|
|
|
- realName: '',
|
|
|
- status: '' as '' | 'enabled' | 'disabled'
|
|
|
+const searchForm = reactive<AccountListRequest>({
|
|
|
+ keyword: '',
|
|
|
+ enabled: undefined
|
|
|
})
|
|
|
|
|
|
// 分页
|
|
|
@@ -260,68 +267,74 @@ const drawerVisible = ref(false)
|
|
|
const isEdit = ref(false)
|
|
|
const submitting = ref(false)
|
|
|
const formRef = ref<FormInstance>()
|
|
|
-const currentUser = ref<User | null>(null)
|
|
|
+const currentUser = ref<AdminDTO | null>(null)
|
|
|
|
|
|
// 角色选项
|
|
|
-const roleOptions = [
|
|
|
- { label: '管理员', value: '管理员' },
|
|
|
- { label: '操作员', value: '操作员' },
|
|
|
- { label: '查看者', value: '查看者' }
|
|
|
-]
|
|
|
+const roleOptions = ref<RoleSimpleDTO[]>([])
|
|
|
+
|
|
|
+// 分配角色对话框
|
|
|
+const roleDialogVisible = ref(false)
|
|
|
+const assignRoleUser = ref<AdminDTO | null>(null)
|
|
|
+const assignRoleId = ref<number | undefined>(undefined)
|
|
|
+const assigningRole = ref(false)
|
|
|
|
|
|
// 表单
|
|
|
const form = reactive({
|
|
|
username: '',
|
|
|
- realName: '',
|
|
|
- email: '',
|
|
|
- phone: '',
|
|
|
- roles: [] as string[],
|
|
|
+ nickname: '',
|
|
|
+ roleIds: [] as number[],
|
|
|
password: '',
|
|
|
- status: 'enabled' as 'enabled' | 'disabled',
|
|
|
- remark: ''
|
|
|
+ enabled: true
|
|
|
})
|
|
|
|
|
|
// 表单验证规则
|
|
|
const rules = computed<FormRules>(() => ({
|
|
|
- username: [{ required: true, message: t('请输入用户名'), trigger: 'blur' }],
|
|
|
- realName: [{ required: true, message: t('请输入姓名'), trigger: 'blur' }],
|
|
|
- email: [{ type: 'email', message: t('请输入正确的邮箱'), trigger: 'blur' }],
|
|
|
- roles: [{ required: true, message: t('请选择角色'), trigger: 'change' }],
|
|
|
- password: [{ required: !isEdit.value, message: t('请输入密码'), trigger: 'blur' }]
|
|
|
+ username: [
|
|
|
+ { required: true, message: t('请输入用户名'), trigger: 'blur' },
|
|
|
+ { max: 50, message: t('用户名最多50个字符'), trigger: 'blur' }
|
|
|
+ ],
|
|
|
+ nickname: [{ max: 50, message: t('昵称最多50个字符'), trigger: 'blur' }],
|
|
|
+ password: [
|
|
|
+ { required: !isEdit.value, message: t('请输入密码'), trigger: 'blur' },
|
|
|
+ { max: 100, message: t('密码最多100个字符'), trigger: 'blur' }
|
|
|
+ ]
|
|
|
}))
|
|
|
|
|
|
+// 加载角色选项
|
|
|
+async function loadRoleOptions() {
|
|
|
+ try {
|
|
|
+ const res = await listAllRoles()
|
|
|
+ if (res.success && res.data) {
|
|
|
+ roleOptions.value = res.data
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Failed to load roles:', error)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// 获取列表
|
|
|
async function getList() {
|
|
|
loading.value = true
|
|
|
try {
|
|
|
- // 模拟 API 延迟
|
|
|
- await new Promise((resolve) => setTimeout(resolve, 300))
|
|
|
-
|
|
|
- let filtered = [...mockUsers]
|
|
|
-
|
|
|
- // 搜索过滤
|
|
|
- if (searchForm.username) {
|
|
|
- filtered = filtered.filter((u) => u.username.includes(searchForm.username))
|
|
|
- }
|
|
|
- if (searchForm.realName) {
|
|
|
- filtered = filtered.filter((u) => u.realName.includes(searchForm.realName))
|
|
|
- }
|
|
|
- if (searchForm.status) {
|
|
|
- filtered = filtered.filter((u) => u.status === searchForm.status)
|
|
|
+ const params: AccountListRequest = {
|
|
|
+ page: currentPage.value,
|
|
|
+ size: pageSize.value,
|
|
|
+ keyword: searchForm.keyword || undefined,
|
|
|
+ enabled: searchForm.enabled,
|
|
|
+ sortBy: sortState.sortBy || undefined,
|
|
|
+ sortDir: sortState.sortDir || undefined
|
|
|
}
|
|
|
|
|
|
- // 排序
|
|
|
- if (sortState.sortBy) {
|
|
|
- filtered.sort((a, b) => {
|
|
|
- const aVal = a[sortState.sortBy as keyof User] as string
|
|
|
- const bVal = b[sortState.sortBy as keyof User] as string
|
|
|
- return sortState.sortDir === 'ASC' ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal)
|
|
|
- })
|
|
|
+ const res = await listAccounts(params)
|
|
|
+ if (res.success && res.data) {
|
|
|
+ userList.value = res.data.list
|
|
|
+ total.value = res.data.total
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.errMessage || t('获取数据失败'))
|
|
|
}
|
|
|
-
|
|
|
- total.value = filtered.length
|
|
|
- const start = (currentPage.value - 1) * pageSize.value
|
|
|
- userList.value = filtered.slice(start, start + pageSize.value)
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Failed to load accounts:', error)
|
|
|
+ ElMessage.error(t('获取数据失败'))
|
|
|
} finally {
|
|
|
loading.value = false
|
|
|
}
|
|
|
@@ -333,9 +346,8 @@ function handleSearch() {
|
|
|
}
|
|
|
|
|
|
function handleReset() {
|
|
|
- searchForm.username = ''
|
|
|
- searchForm.realName = ''
|
|
|
- searchForm.status = ''
|
|
|
+ searchForm.keyword = ''
|
|
|
+ searchForm.enabled = undefined
|
|
|
currentPage.value = 1
|
|
|
getList()
|
|
|
}
|
|
|
@@ -359,13 +371,10 @@ function handleCurrentChange(val: number) {
|
|
|
|
|
|
function resetForm() {
|
|
|
form.username = ''
|
|
|
- form.realName = ''
|
|
|
- form.email = ''
|
|
|
- form.phone = ''
|
|
|
- form.roles = []
|
|
|
+ form.nickname = ''
|
|
|
+ form.roleIds = []
|
|
|
form.password = ''
|
|
|
- form.status = 'enabled'
|
|
|
- form.remark = ''
|
|
|
+ form.enabled = true
|
|
|
formRef.value?.clearValidate()
|
|
|
}
|
|
|
|
|
|
@@ -376,16 +385,14 @@ function handleAdd() {
|
|
|
drawerVisible.value = true
|
|
|
}
|
|
|
|
|
|
-function handleEdit(row: User) {
|
|
|
+function handleEdit(row: AdminDTO) {
|
|
|
isEdit.value = true
|
|
|
currentUser.value = row
|
|
|
form.username = row.username
|
|
|
- form.realName = row.realName
|
|
|
- form.email = row.email
|
|
|
- form.phone = row.phone
|
|
|
- form.roles = [...row.roles]
|
|
|
- form.status = row.status
|
|
|
- form.remark = row.remark || ''
|
|
|
+ form.nickname = row.nickname || ''
|
|
|
+ form.roleIds = row.roles?.map((r) => r.id) || []
|
|
|
+ form.password = ''
|
|
|
+ form.enabled = row.enabled
|
|
|
drawerVisible.value = true
|
|
|
}
|
|
|
|
|
|
@@ -397,47 +404,100 @@ async function handleSubmit() {
|
|
|
|
|
|
submitting.value = true
|
|
|
try {
|
|
|
- await new Promise((resolve) => setTimeout(resolve, 500))
|
|
|
- ElMessage.success(isEdit.value ? t('更新成功') : t('添加成功'))
|
|
|
- drawerVisible.value = false
|
|
|
- getList()
|
|
|
+ if (isEdit.value && currentUser.value) {
|
|
|
+ const res = await updateAccount({
|
|
|
+ id: currentUser.value.id,
|
|
|
+ nickname: form.nickname || undefined,
|
|
|
+ password: form.password || undefined,
|
|
|
+ enabled: form.enabled,
|
|
|
+ roleIds: form.roleIds.length > 0 ? form.roleIds : undefined
|
|
|
+ })
|
|
|
+ if (res.success) {
|
|
|
+ ElMessage.success(t('更新成功'))
|
|
|
+ drawerVisible.value = false
|
|
|
+ getList()
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.errMessage || t('更新失败'))
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const res = await addAccount({
|
|
|
+ username: form.username,
|
|
|
+ password: form.password,
|
|
|
+ nickname: form.nickname || undefined,
|
|
|
+ roleIds: form.roleIds.length > 0 ? form.roleIds : undefined
|
|
|
+ })
|
|
|
+ if (res.success) {
|
|
|
+ ElMessage.success(t('添加成功'))
|
|
|
+ drawerVisible.value = false
|
|
|
+ getList()
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.errMessage || t('添加失败'))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Submit error:', error)
|
|
|
+ ElMessage.error(t('操作失败'))
|
|
|
} finally {
|
|
|
submitting.value = false
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-async function handleDelete(row: User) {
|
|
|
+function handleAssignRole(row: AdminDTO) {
|
|
|
+ assignRoleUser.value = row
|
|
|
+ assignRoleId.value = row.roles?.[0]?.id
|
|
|
+ roleDialogVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+async function handleConfirmAssignRole() {
|
|
|
+ if (!assignRoleUser.value) return
|
|
|
+
|
|
|
+ assigningRole.value = true
|
|
|
+ try {
|
|
|
+ const res = await updateAccount({
|
|
|
+ id: assignRoleUser.value.id,
|
|
|
+ roleIds: assignRoleId.value ? [assignRoleId.value] : []
|
|
|
+ })
|
|
|
+ if (res.success) {
|
|
|
+ ElMessage.success(t('角色分配成功'))
|
|
|
+ roleDialogVisible.value = false
|
|
|
+ getList()
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.errMessage || t('角色分配失败'))
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Assign role error:', error)
|
|
|
+ ElMessage.error(t('角色分配失败'))
|
|
|
+ } finally {
|
|
|
+ assigningRole.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function handleDelete(row: AdminDTO) {
|
|
|
try {
|
|
|
await ElMessageBox.confirm(
|
|
|
- `你确定要删除这个用户吗?<br/><br/>用户名:${row.username}<br/>姓名:${row.realName}`,
|
|
|
+ `${t('确定要删除用户吗?')}<br/><br/>${t('用户名')}:${row.username}<br/>${t('昵称')}:${row.nickname || '-'}`,
|
|
|
t('提示'),
|
|
|
{
|
|
|
type: 'warning',
|
|
|
dangerouslyUseHTMLString: true
|
|
|
}
|
|
|
)
|
|
|
- await new Promise((resolve) => setTimeout(resolve, 300))
|
|
|
- ElMessage.success(t('删除成功'))
|
|
|
- getList()
|
|
|
- } catch {
|
|
|
- // 取消
|
|
|
- }
|
|
|
-}
|
|
|
|
|
|
-async function handleResetPassword(row: User) {
|
|
|
- try {
|
|
|
- await ElMessageBox.confirm(`确定要重置用户 "${row.username}" 的密码吗?`, t('提示'), {
|
|
|
- type: 'warning'
|
|
|
- })
|
|
|
- await new Promise((resolve) => setTimeout(resolve, 300))
|
|
|
- ElMessage.success(t('密码已重置为默认密码'))
|
|
|
+ const res = await deleteAccount(row.id)
|
|
|
+ if (res.success) {
|
|
|
+ ElMessage.success(t('删除成功'))
|
|
|
+ getList()
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.errMessage || t('删除失败'))
|
|
|
+ }
|
|
|
} catch {
|
|
|
// 取消
|
|
|
}
|
|
|
}
|
|
|
|
|
|
onMounted(() => {
|
|
|
+ loadRoleOptions()
|
|
|
getList()
|
|
|
})
|
|
|
</script>
|
|
|
@@ -457,7 +517,7 @@ onMounted(() => {
|
|
|
flex-shrink: 0;
|
|
|
margin-bottom: 16px;
|
|
|
padding: 16px 16px 4px 16px;
|
|
|
- background: #f5f7fa;
|
|
|
+ background: var(--bg-hover);
|
|
|
|
|
|
:deep(.el-form-item) {
|
|
|
margin-bottom: 12px;
|
|
|
@@ -487,5 +547,34 @@ onMounted(() => {
|
|
|
display: flex;
|
|
|
justify-content: flex-end;
|
|
|
gap: 12px;
|
|
|
+ padding: 0 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.text-placeholder {
|
|
|
+ color: var(--text-placeholder);
|
|
|
+}
|
|
|
+
|
|
|
+.role-dialog-content {
|
|
|
+ .user-info {
|
|
|
+ margin-bottom: 20px;
|
|
|
+ padding: 12px 16px;
|
|
|
+ background: var(--bg-hover);
|
|
|
+ border-radius: 4px;
|
|
|
+
|
|
|
+ .label {
|
|
|
+ color: var(--text-secondary);
|
|
|
+ margin-right: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .value {
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.dialog-footer {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ gap: 12px;
|
|
|
}
|
|
|
</style>
|