|
|
|
|
@@ -21,6 +21,18 @@
|
|
|
|
|
@keyup.enter.native="onSearch"
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item :label="$t(key('status'))">
|
|
|
|
|
<el-select
|
|
|
|
|
v-model="search.status"
|
|
|
|
|
:placeholder="$t(key('select_status'))"
|
|
|
|
|
clearable
|
|
|
|
|
style="width:140px"
|
|
|
|
|
@change="onSearch"
|
|
|
|
|
>
|
|
|
|
|
<el-option value="1" :label="$t(key('enable'))" />
|
|
|
|
|
<el-option value="0" :label="$t(key('disable'))" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item>
|
|
|
|
|
<el-button type="primary" icon="el-icon-search" @click="onSearch">
|
|
|
|
|
{{ $t(key('search')) }}
|
|
|
|
|
@@ -46,7 +58,7 @@
|
|
|
|
|
@selection-change="onSelect"
|
|
|
|
|
>
|
|
|
|
|
<template #col-status="{ row }">
|
|
|
|
|
<span v-if="row.status === 1" style="color: #67c23a;">
|
|
|
|
|
<span v-if="String(row.status) === '1'" style="color: #67c23a;">
|
|
|
|
|
<i class="el-icon-circle-check" />
|
|
|
|
|
{{ $t(key('enable')) }}
|
|
|
|
|
</span>
|
|
|
|
|
@@ -79,7 +91,6 @@
|
|
|
|
|
import { useTableColumns } from '@/composables/useTableColumns'
|
|
|
|
|
import { useTableButtons } from '@/composables/useTableButtons'
|
|
|
|
|
import { i18nMixin } from '@/composables/useI18n'
|
|
|
|
|
import { confirmMixin } from '@/composables/useConfirmHandle'
|
|
|
|
|
import { getRoleAll } from '@/api/system-administration/role'
|
|
|
|
|
import {
|
|
|
|
|
getUserList,
|
|
|
|
|
@@ -99,10 +110,43 @@ const ownUserId = () => localStorage.getItem('user_id')
|
|
|
|
|
export default {
|
|
|
|
|
name: 'system-administration-user',
|
|
|
|
|
components: { PageTable, PageDialogForm },
|
|
|
|
|
mixins: [i18nMixin('page.system_administration.user_management.user'), confirmMixin],
|
|
|
|
|
mixins: [i18nMixin('page.system_administration.user_management.user')],
|
|
|
|
|
data () {
|
|
|
|
|
const key = this.key.bind(this)
|
|
|
|
|
const $t = this.$t.bind(this)
|
|
|
|
|
const normalizeList = res => {
|
|
|
|
|
const data = res && res.data !== undefined ? res.data : res
|
|
|
|
|
if (Array.isArray(data)) return data
|
|
|
|
|
if (data && Array.isArray(data.list)) return data.list
|
|
|
|
|
if (data && Array.isArray(data.data)) return data.data
|
|
|
|
|
if (data && data.data && Array.isArray(data.data.data)) return data.data.data
|
|
|
|
|
return []
|
|
|
|
|
}
|
|
|
|
|
const validateUsernameUnique = async (rule, value, callback) => {
|
|
|
|
|
const username = String(value || '').trim()
|
|
|
|
|
if (!username || username.length < 3) {
|
|
|
|
|
callback()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
const res = await getUserList({ username, page_no: 1, page_size: 10000 })
|
|
|
|
|
const exists = normalizeList(res).some(item => {
|
|
|
|
|
const sameName = String(item.username || '') === username
|
|
|
|
|
const sameUser = this.handleType === 'edit' && String(item.user_id) === String(this.editId)
|
|
|
|
|
return sameName && !sameUser
|
|
|
|
|
})
|
|
|
|
|
callback(exists ? new Error($t(key('username_exists'))) : undefined)
|
|
|
|
|
} catch {
|
|
|
|
|
callback()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const validatePasswordConfirm = (rule, value, callback) => {
|
|
|
|
|
if (this.handleType === 'create' && value && value !== this.formData.password) {
|
|
|
|
|
callback(new Error($t(key('password_not_match'))))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
callback()
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
|
loading: false,
|
|
|
|
|
submitting: false,
|
|
|
|
|
@@ -112,12 +156,21 @@ export default {
|
|
|
|
|
dialogTitle: '',
|
|
|
|
|
editId: '',
|
|
|
|
|
handleType: 'create',
|
|
|
|
|
search: { username: '', nickname: '' },
|
|
|
|
|
search: { username: '', nickname: '', status: '' },
|
|
|
|
|
pagination: { current: 1, size: 10, total: 0 },
|
|
|
|
|
roleOptions: [],
|
|
|
|
|
columns: [],
|
|
|
|
|
toolbarButtons: [],
|
|
|
|
|
rowButtons: [],
|
|
|
|
|
formData: {
|
|
|
|
|
username: '',
|
|
|
|
|
password: '',
|
|
|
|
|
password_confirm: '',
|
|
|
|
|
role_id: '',
|
|
|
|
|
nickname: '',
|
|
|
|
|
pass_number: '',
|
|
|
|
|
status: '1'
|
|
|
|
|
},
|
|
|
|
|
baseFormCols: {
|
|
|
|
|
create: [
|
|
|
|
|
[{ type: 'input', prop: 'username', label: key('username'), placeholder: key('enter_username'), clearable: true, style: { width: '90%' } }],
|
|
|
|
|
@@ -139,7 +192,8 @@ export default {
|
|
|
|
|
baseRules: {
|
|
|
|
|
username: [
|
|
|
|
|
{ required: true, message: key('enter_username'), trigger: 'blur' },
|
|
|
|
|
{ min: 3, max: 20, message: key('username_length'), trigger: 'blur' }
|
|
|
|
|
{ min: 3, max: 20, message: key('username_length'), trigger: 'blur' },
|
|
|
|
|
{ validator: validateUsernameUnique, trigger: 'blur' }
|
|
|
|
|
],
|
|
|
|
|
password: [
|
|
|
|
|
{ required: true, message: key('enter_password'), trigger: 'blur' },
|
|
|
|
|
@@ -147,10 +201,14 @@ export default {
|
|
|
|
|
],
|
|
|
|
|
password_confirm: [
|
|
|
|
|
{ required: true, message: key('enter_confirm_password'), trigger: 'blur' },
|
|
|
|
|
{ min: 6, max: 64, message: key('password_length'), trigger: 'blur' }
|
|
|
|
|
{ min: 6, max: 64, message: key('password_length'), trigger: 'blur' },
|
|
|
|
|
{ validator: validatePasswordConfirm, trigger: ['blur', 'change'] }
|
|
|
|
|
],
|
|
|
|
|
role_id: [
|
|
|
|
|
{ required: true, message: key('select_user_group'), trigger: 'change' }
|
|
|
|
|
],
|
|
|
|
|
status: [
|
|
|
|
|
{ required: true, message: key('select_status'), trigger: 'change' }
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -168,7 +226,8 @@ export default {
|
|
|
|
|
if (this.handleType === 'edit') {
|
|
|
|
|
return {
|
|
|
|
|
username: this.baseRules.username,
|
|
|
|
|
role_id: this.baseRules.role_id
|
|
|
|
|
role_id: this.baseRules.role_id,
|
|
|
|
|
status: this.baseRules.status
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return this.baseRules
|
|
|
|
|
@@ -255,10 +314,18 @@ export default {
|
|
|
|
|
async initRoleOptions () {
|
|
|
|
|
try {
|
|
|
|
|
const res = await getRoleAll()
|
|
|
|
|
const data = Array.isArray(res) ? res : (res.data || [])
|
|
|
|
|
const data = this.normalizeResponse(res).list
|
|
|
|
|
this.roleOptions = data.map(item => ({ value: item.id, label: item.name }))
|
|
|
|
|
} catch { /* 忽略 */ }
|
|
|
|
|
},
|
|
|
|
|
normalizeResponse (res) {
|
|
|
|
|
const data = res && res.data !== undefined ? res.data : res
|
|
|
|
|
if (Array.isArray(data)) return { list: data, total: data.length }
|
|
|
|
|
if (data && Array.isArray(data.list)) return { list: data.list, total: Number(data.count || data.total || data.list.length) }
|
|
|
|
|
if (data && Array.isArray(data.data)) return { list: data.data, total: Number(data.count || data.total || data.data.length) }
|
|
|
|
|
if (data && data.data && Array.isArray(data.data.data)) return { list: data.data.data, total: Number(data.data.count || data.data.total || data.data.data.length) }
|
|
|
|
|
return { list: [], total: 0 }
|
|
|
|
|
},
|
|
|
|
|
async fetchData () {
|
|
|
|
|
this.loading = true
|
|
|
|
|
try {
|
|
|
|
|
@@ -267,10 +334,9 @@ export default {
|
|
|
|
|
page_no: this.pagination.current,
|
|
|
|
|
page_size: this.pagination.size
|
|
|
|
|
})
|
|
|
|
|
const list = Array.isArray(res) ? res : (res.data || [])
|
|
|
|
|
const total = Array.isArray(res) ? res.length : (res.count || 0)
|
|
|
|
|
this.tableData = list
|
|
|
|
|
this.pagination.total = total
|
|
|
|
|
const data = this.normalizeResponse(res)
|
|
|
|
|
this.tableData = data.list
|
|
|
|
|
this.pagination.total = data.total
|
|
|
|
|
} finally {
|
|
|
|
|
this.loading = false
|
|
|
|
|
}
|
|
|
|
|
@@ -280,7 +346,7 @@ export default {
|
|
|
|
|
this.fetchData()
|
|
|
|
|
},
|
|
|
|
|
onReset () {
|
|
|
|
|
this.search = { username: '', nickname: '' }
|
|
|
|
|
this.search = { username: '', nickname: '', status: '' }
|
|
|
|
|
this.pagination.current = 1
|
|
|
|
|
this.fetchData()
|
|
|
|
|
},
|
|
|
|
|
@@ -377,16 +443,34 @@ export default {
|
|
|
|
|
} catch { /* 拦截器已处理 */ }
|
|
|
|
|
}).catch(() => {})
|
|
|
|
|
},
|
|
|
|
|
async confirmAction (message, action) {
|
|
|
|
|
try {
|
|
|
|
|
await this.$confirm(
|
|
|
|
|
this.$t(message),
|
|
|
|
|
this.$t(this.key('prompt')),
|
|
|
|
|
{
|
|
|
|
|
confirmButtonText: this.$t(this.key('confirm')),
|
|
|
|
|
cancelButtonText: this.$t(this.key('cancel')),
|
|
|
|
|
type: 'warning',
|
|
|
|
|
closeOnClickModal: false
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
} catch {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
await action()
|
|
|
|
|
return true
|
|
|
|
|
},
|
|
|
|
|
async handleDelete (row) {
|
|
|
|
|
if (String(row.user_id) === ownUserId()) {
|
|
|
|
|
this.$message.warning(this.$t(this.key('cannot_delete_self')))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const cancelled = await this.$confirmAction(
|
|
|
|
|
{ message: this.key('confirm_delete'), title: this.key('prompt') },
|
|
|
|
|
const ok = await this.confirmAction(
|
|
|
|
|
this.key('confirm_delete'),
|
|
|
|
|
() => deleteUser({ id: [row.user_id] })
|
|
|
|
|
)
|
|
|
|
|
if (cancelled) return
|
|
|
|
|
if (!ok) return
|
|
|
|
|
this.$message.success(this.$t(this.key('operation_success')))
|
|
|
|
|
this.pagination.current = Math.min(
|
|
|
|
|
this.pagination.current,
|
|
|
|
|
@@ -405,11 +489,11 @@ export default {
|
|
|
|
|
this.$message.warning(this.$t(this.key('select_rows_first')))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const cancelled = await this.$confirmAction(
|
|
|
|
|
{ message: this.key('confirm_batch_delete'), title: this.key('prompt') },
|
|
|
|
|
const ok = await this.confirmAction(
|
|
|
|
|
this.key('confirm_batch_delete'),
|
|
|
|
|
() => batchDeleteUser({ id: rows.map(row => row.user_id) })
|
|
|
|
|
)
|
|
|
|
|
if (cancelled) return
|
|
|
|
|
if (!ok) return
|
|
|
|
|
this.$message.success(this.$t(this.key('operation_success')))
|
|
|
|
|
this.fetchData()
|
|
|
|
|
},
|
|
|
|
|
|