2023-09-04 18:13:20 +08:00

409 lines
10 KiB
Vue

<script setup>
import { h, onMounted, ref, resolveDirective, withDirectives } from 'vue'
import {
NButton,
NCheckbox,
NCheckboxGroup,
NForm,
NFormItem,
NImage,
NInput,
NSpace,
NSwitch,
NTag,
NPopconfirm,
} from 'naive-ui'
import CommonPage from '@/components/page/CommonPage.vue'
import QueryBarItem from '@/components/query-bar/QueryBarItem.vue'
import CrudModal from '@/components/table/CrudModal.vue'
import CrudTable from '@/components/table/CrudTable.vue'
import { formatDate, renderIcon } from '@/utils'
import { useCRUD } from '@/composables'
// import { loginTypeMap, loginTypeOptions } from '@/constant/data'
import api from '@/api'
import TheIcon from '@/components/icon/TheIcon.vue'
import { useUserStore } from '@/store'
defineOptions({ name: '用户列表' })
const $table = ref(null)
const queryItems = ref({})
const vPermission = resolveDirective('permission')
const {
modalVisible,
modalTitle,
modalAction,
modalLoading,
handleSave,
modalForm,
modalFormRef,
handleEdit,
handleDelete,
handleAdd,
} = useCRUD({
name: '用户',
initForm: {},
doCreate: api.createUser,
doUpdate: api.updateUser,
doDelete: api.deleteUser,
refresh: () => $table.value?.handleSearch(),
})
const roleOption = ref([])
onMounted(() => {
$table.value?.handleSearch()
api.getRoleList({ page: 1, page_size: 9999 }).then((res) => (roleOption.value = res.data))
})
const columns = [
{
title: '头像',
key: 'avatar',
width: 50,
align: 'center',
render(row) {
return h(NImage, {
height: 50,
imgProps: { style: { 'border-radius': '3px' } },
src: row.avatar,
'fallback-src': 'http://dummyimage.com/400x400', // 加载失败
'show-toolbar-tooltip': true,
})
},
},
{
title: '名称',
key: 'username',
width: 60,
align: 'center',
ellipsis: { tooltip: true },
},
{
title: '邮箱',
key: 'email',
width: 60,
align: 'center',
ellipsis: { tooltip: true },
},
{
title: '用户角色',
key: 'role',
width: 60,
align: 'center',
render(row) {
const roles = row.roles ?? []
const group = []
for (let i = 0; i < roles.length; i++)
group.push(
h(NTag, { type: 'info', style: { margin: '2px 3px' } }, { default: () => roles[i].name })
)
return h('span', group)
},
},
{
title: '超级用户',
key: 'is_superuser',
align: 'center',
width: 40,
render(row) {
return h(
NTag,
{ type: 'info', style: { margin: '2px 3px' } },
{ default: () => (row.is_superuser ? '是' : '否') }
)
},
},
{
title: '上次登录时间',
key: 'last_login',
align: 'center',
width: 80,
ellipsis: { tooltip: true },
render(row) {
return h(
NButton,
{ size: 'small', type: 'text', ghost: true },
{
default: () => (row.last_login !== null ? formatDate(row.last_login) : null),
icon: renderIcon('mdi:update', { size: 16 }),
}
)
},
},
{
title: '禁用',
key: 'is_active',
width: 50,
align: 'center',
render(row) {
return h(NSwitch, {
size: 'small',
rubberBand: false,
value: row.is_active,
loading: !!row.publishing,
checkedValue: false,
uncheckedValue: true,
onUpdateValue: () => handleUpdateDisable(row),
})
},
},
{
title: '操作',
key: 'actions',
width: 80,
align: 'center',
fixed: 'right',
render(row) {
return [
withDirectives(
h(
NButton,
{
size: 'small',
type: 'primary',
style: 'margin-right: 8px;',
onClick: () => {
// roles => role_ids
handleEdit(row)
modalForm.value.roles = row.roles.map((e) => (e = e.id))
},
},
{
default: () => '编辑',
icon: renderIcon('material-symbols:edit', { size: 16 }),
}
),
[[vPermission, 'post/api/v1/user/update']]
),
h(
NPopconfirm,
{
onPositiveClick: () => handleDelete({ user_id: row.id }, false),
onNegativeClick: () => {},
},
{
trigger: () =>
withDirectives(
h(
NButton,
{
size: 'small',
type: 'error',
},
{
default: () => '删除',
icon: renderIcon('material-symbols:delete-outline', { size: 16 }),
}
),
[[vPermission, 'delete/api/v1/user/delete']]
),
default: () => h('div', {}, '确定删除该用户吗?'),
}
),
]
},
},
]
// 修改用户禁用状态
async function handleUpdateDisable(row) {
if (!row.id) return
const userStore = useUserStore()
if (userStore.userId === row.id) {
$message.error('当前登录用户不可禁用!')
return
}
row.publishing = true
row.is_active = row.is_active === false ? true : false
row.publishing = false
const role_ids = []
row.roles.forEach((e) => {
role_ids.push(e.id)
})
row.roles = role_ids
try {
await api.updateUser(row)
$message?.success(row.is_active ? '已取消禁用该用户' : '已禁用该用户')
$table.value?.handleSearch()
} catch (err) {
// 有异常恢复原来的状态
row.is_active = row.is_active === false ? true : false
} finally {
row.publishing = false
}
}
const validateAddUser = {
username: [
{
required: true,
message: '请输入名称',
trigger: ['input', 'blur'],
},
],
email: [
{
required: true,
message: '请输入邮箱地址',
trigger: ['input', 'change'],
},
{
trigger: ['blur'],
validator: (rule, value, callback) => {
const re = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/
if (!re.test(modalForm.value.email)) {
callback('邮箱格式错误')
return
}
callback()
},
},
],
password: [
{
required: true,
message: '请输入密码',
trigger: ['input', 'blur', 'change'],
},
],
confirmPassword: [
{
required: true,
message: '请再次输入密码',
trigger: ['input'],
},
{
trigger: ['blur'],
validator: (rule, value, callback) => {
if (value !== modalForm.value.password) {
callback('两次密码输入不一致')
return
}
callback()
},
},
],
roles: [
{
type: 'array',
required: true,
message: '请至少选择一个角色',
trigger: ['blur', 'change'],
},
],
}
</script>
<template>
<!-- 业务页面 -->
<CommonPage show-footer title="用户列表">
<template #action>
<NButton v-permission="'post/api/v1/user/create'" type="primary" @click="handleAdd">
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" />新建用户
</NButton>
</template>
<!-- 表格 -->
<CrudTable
ref="$table"
v-model:query-items="queryItems"
:columns="columns"
:get-data="api.getUserList"
>
<template #queryBar>
<QueryBarItem label="名称" :label-width="40">
<NInput
v-model:value="queryItems.username"
clearable
type="text"
placeholder="请输入用户名称"
@keypress.enter="$table?.handleSearch"
/>
</QueryBarItem>
<QueryBarItem label="邮箱" :label-width="40">
<NInput
v-model:value="queryItems.email"
clearable
type="text"
placeholder="请输入邮箱"
@keypress.enter="$table?.handleSearch"
/>
</QueryBarItem>
</template>
</CrudTable>
<!-- 新增/编辑 弹窗 -->
<CrudModal
v-model:visible="modalVisible"
:title="modalTitle"
:loading="modalLoading"
@save="handleSave"
>
<NForm
ref="modalFormRef"
label-placement="left"
label-align="left"
:label-width="80"
:model="modalForm"
:rules="validateAddUser"
>
<NFormItem label="用户名称" path="username">
<NInput v-model:value="modalForm.username" clearable placeholder="请输入用户名称" />
</NFormItem>
<NFormItem label="邮箱" path="email">
<NInput v-model:value="modalForm.email" clearable placeholder="请输入邮箱" />
</NFormItem>
<NFormItem v-if="modalAction === 'add'" label="密码" path="password">
<NInput
v-model:value="modalForm.password"
show-password-on="mousedown"
type="password"
clearable
placeholder="请输入密码"
/>
</NFormItem>
<NFormItem v-if="modalAction === 'add'" label="确认密码" path="confirmPassword">
<NInput
v-model:value="modalForm.confirmPassword"
show-password-on="mousedown"
type="password"
clearable
placeholder="请确认密码"
/>
</NFormItem>
<NFormItem label="角色" path="roles">
<NCheckboxGroup v-model:value="modalForm.roles">
<NSpace item-style="display: flex;">
<NCheckbox
v-for="item in roleOption"
:key="item.id"
:value="item.id"
:label="item.name"
/>
</NSpace>
</NCheckboxGroup>
</NFormItem>
<NFormItem label="超级用户" path="is_superuser">
<NSwitch
v-model:value="modalForm.is_superuser"
size="small"
:checked-value="true"
:unchecked-value="false"
></NSwitch>
</NFormItem>
<NFormItem label="禁用" path="is_active">
<NSwitch
v-model:value="modalForm.is_active"
:checked-value="false"
:unchecked-value="true"
:default-value="true"
/>
</NFormItem>
</NForm>
</CrudModal>
</CommonPage>
</template>