<template>
|
<basic-container>
|
<!-- 左侧部门树 -->
|
<el-row :gutter="16" class="user-container">
|
<el-col :span="4">
|
<div class="tree-box">
|
<el-scrollbar>
|
<el-tree
|
:data="deptTree"
|
:props="deptTreeProps"
|
node-key="id"
|
:load="loadDeptTree"
|
lazy
|
@node-click="handleDeptNodeClick"
|
/>
|
</el-scrollbar>
|
</div>
|
</el-col>
|
<!-- 右侧列表区 -->
|
<el-col :span="20">
|
<!-- 搜索表单 -->
|
<el-form ref="queryParamsRef" :model="searchParams">
|
<el-row :gutter="16">
|
<el-col :span="5">
|
<el-form-item label="登录账号" prop="account">
|
<el-input v-model="searchParams.account" placeholder="请输入" clearable @clear="handleSearch" />
|
</el-form-item>
|
</el-col>
|
<el-col :span="5">
|
<el-form-item label="用户姓名" prop="realName">
|
<el-input v-model="searchParams.realName" placeholder="请输入" clearable @clear="handleSearch" />
|
</el-form-item>
|
</el-col>
|
<el-col :span="4">
|
<el-form-item>
|
<el-button @click="resetForm">重置</el-button>
|
<el-button type="primary" @click="handleSearch">查询</el-button>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
</el-form>
|
|
<!-- 操作按钮 -->
|
<div class="btn-group">
|
<el-button v-if="permission.user_add && !auditMode" type="primary" @click="openForm('add')">新增</el-button>
|
<el-button
|
v-if="permission.user_delete && !auditMode"
|
type="danger"
|
:disabled="!selectedIds.length"
|
@click="handleDelete()"
|
>
|
删除
|
</el-button>
|
<el-button
|
v-if="userInfo.role_name?.includes('admin') && auditMode"
|
type="success"
|
:disabled="!selectedIds.length"
|
@click="handleAuditPass"
|
>
|
通过
|
</el-button>
|
<el-button
|
v-if="userInfo.role_name?.includes('admin') && auditMode"
|
type="danger"
|
:disabled="!selectedIds.length"
|
@click="handleAuditRefuse"
|
>
|
拒绝
|
</el-button>
|
<el-button
|
v-if="userInfo.role_name?.includes('admin') && auditMode"
|
type="primary"
|
plain
|
@click="handleAuditBack"
|
>
|
返回
|
</el-button>
|
<el-button
|
v-if="permission.user_role && !auditMode"
|
type="info"
|
plain
|
:disabled="!selectedIds.length"
|
@click="handleGrant"
|
>
|
角色配置
|
</el-button>
|
<el-button
|
v-if="permission.user_reset && !auditMode"
|
type="info"
|
plain
|
:disabled="!selectedIds.length"
|
@click="handleReset"
|
>
|
密码重置
|
</el-button>
|
<el-button
|
v-if="userInfo.role_name?.includes('admin') && !auditMode"
|
type="info"
|
plain
|
:disabled="!selectedIds.length"
|
@click="handleLock"
|
>
|
账号解封
|
</el-button>
|
<el-button
|
v-if="userInfo.role_name?.includes('admin') && !auditMode"
|
type="success"
|
plain
|
@click="handleImport"
|
>
|
导入
|
</el-button>
|
<el-button
|
v-if="userInfo.role_name?.includes('admin') && !auditMode"
|
type="warning"
|
plain
|
@click="handleExport"
|
>
|
导出
|
</el-button>
|
</div>
|
|
<!-- 用户列表 -->
|
<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
|
<el-table-column type="selection" width="55" />
|
<el-table-column type="index" width="60" label="序号" />
|
<el-table-column prop="account" label="登录账号" />
|
<el-table-column prop="realName" label="用户姓名" />
|
<el-table-column prop="roleName" label="所属角色">
|
<template v-slot="{ row }">
|
<el-tag v-if="row.roleName">{{ row.roleName }}</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column prop="deptName" label="所属部门">
|
<template v-slot="{ row }">
|
<el-tag v-if="row.deptName">{{ row.deptName }}</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column prop="phone" label="手机号码" />
|
<el-table-column label="操作" width="180">
|
<template v-slot="{ row }">
|
<el-link v-if="permission.user_view" @click="openForm('view', row)" type="primary">查看</el-link>
|
<el-link v-if="permission.user_edit && !auditMode" @click="openForm('edit', row)" type="warning">
|
编辑
|
</el-link>
|
<el-link v-if="permission.user_delete && !auditMode" @click="handleDelete(row)" type="danger">
|
删除
|
</el-link>
|
</template>
|
</el-table-column>
|
</el-table>
|
|
<!-- 分页 -->
|
<div>
|
<el-pagination
|
v-model:current-page="searchParams.current"
|
v-model:page-size="searchParams.size"
|
:total="total"
|
@change="getList"
|
/>
|
</div>
|
</el-col>
|
</el-row>
|
|
<!-- 用户表单弹框 -->
|
<FormDiaLog v-if="dialogVisible" v-model="dialogVisible" ref="dialogRef" @success="getList" />
|
|
<!-- 角色配置弹框 -->
|
<el-dialog title="用户角色配置" append-to-body v-model="roleBox" width="345px">
|
<el-tree
|
:data="roleGrantList"
|
show-checkbox
|
check-strictly
|
default-expand-all
|
node-key="id"
|
ref="treeRoleRef"
|
:default-checked-keys="roleTreeObj"
|
:props="roleProps"
|
></el-tree>
|
<template #footer>
|
<span class="dialog-footer">
|
<el-button @click="roleBox = false">取 消</el-button>
|
<el-button type="primary" @click="submitRole">确 定</el-button>
|
</span>
|
</template>
|
</el-dialog>
|
|
<!-- 导入弹框 -->
|
<el-dialog title="用户数据导入" append-to-body v-model="excelBox" width="555px">
|
<el-form :model="excelForm" label-width="100px">
|
<el-form-item label="数据覆盖">
|
<el-switch v-model="excelForm.isCovered" :active-value="1" :inactive-value="0" />
|
</el-form-item>
|
<el-form-item label="模板上传">
|
<el-upload
|
:action="uploadAction"
|
:on-success="handleUploadSuccess"
|
accept=".xls,.xlsx"
|
:show-file-list="false"
|
>
|
<el-button type="primary">点击上传</el-button>
|
</el-upload>
|
<div class="upload-tip">请上传 .xls,.xlsx 标准格式文件</div>
|
</el-form-item>
|
<el-form-item label="模板下载">
|
<el-button type="primary" @click="handleTemplate">点击下载</el-button>
|
</el-form-item>
|
</el-form>
|
</el-dialog>
|
</basic-container>
|
</template>
|
|
<script setup>
|
import { computed, nextTick, onMounted, ref } from 'vue'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { useStore } from 'vuex'
|
import FormDiaLog from './FormDiaLog.vue'
|
import { getList as getListApi, remove, grant, resetPassword, unlock, auditPass, auditRefuse } from '@/api/system/user'
|
import { exportBlob } from '@/api/common'
|
import { getDeptTree, getDeptLazyTree } from '@/api/system/dept'
|
import { getRoleTree } from '@/api/system/role'
|
import website from '@/config/website'
|
import { getToken } from '@/utils/auth'
|
import { downloadXls } from '@/utils/util'
|
import NProgress from 'nprogress'
|
import 'nprogress/nprogress.css'
|
import func from '@/utils/func'
|
|
const store = useStore()
|
const userInfo = computed(() => store.getters.userInfo)
|
const permission = computed(() => store.getters.permission)
|
|
// 初始化搜索参数
|
const initSearchParams = () => ({
|
account: '',
|
realName: '',
|
current: 1,
|
size: 10,
|
})
|
|
const searchParams = ref(initSearchParams())
|
const total = ref(0)
|
const loading = ref(false)
|
const list = ref([])
|
const selectedIds = ref([])
|
const selectionList = ref([])
|
const queryParamsRef = ref(null)
|
const dialogRef = ref(null)
|
const dialogVisible = ref(false)
|
const auditMode = ref(false)
|
const treeDeptId = ref('')
|
|
// 部门树
|
const deptTree = ref([])
|
const deptTreeProps = {
|
label: 'title',
|
children: 'children',
|
isLeaf: 'leaf',
|
}
|
|
// 角色配置
|
const roleBox = ref(false)
|
const roleGrantList = ref([])
|
const roleTreeObj = ref([])
|
const treeRoleRef = ref(null)
|
const roleProps = {
|
label: 'title',
|
value: 'key',
|
}
|
|
// 导入相关
|
const excelBox = ref(false)
|
const excelForm = ref({
|
isCovered: 0,
|
})
|
const uploadAction = computed(() => `/blade-system/user/import-user?isCovered=${excelForm.value.isCovered}`)
|
|
provide('treeDeptId', treeDeptId)
|
|
// 加载部门树节点
|
async function loadDeptTree(node, resolve) {
|
const parentId = node.level === 0 ? 0 : node.data.id
|
const param = {
|
parentId: parentId,
|
level: node.level,
|
}
|
const res = await getDeptLazyTree(param)
|
resolve(
|
res.data.data.map(item => ({
|
...item,
|
leaf: !item.hasChildren,
|
}))
|
)
|
}
|
|
// 部门节点点击
|
function handleDeptNodeClick(data) {
|
treeDeptId.value = data.id
|
searchParams.value.current = 1
|
getList()
|
}
|
|
// 获取列表
|
async function getList() {
|
loading.value = true
|
try {
|
const res = await getListApi(
|
searchParams.value.current,
|
searchParams.value.size,
|
{
|
account: searchParams.value.account,
|
realName: searchParams.value.realName,
|
status: auditMode.value ? 0 : 1,
|
},
|
treeDeptId.value
|
)
|
list.value = res?.data?.data?.records ?? []
|
total.value = res?.data?.data?.total ?? 0
|
} finally {
|
loading.value = false
|
}
|
}
|
|
// 查询
|
function handleSearch() {
|
searchParams.value.current = 1
|
getList()
|
}
|
|
// 重置查询
|
function resetForm() {
|
queryParamsRef.value?.resetFields()
|
searchParams.value = initSearchParams()
|
treeDeptId.value = ''
|
getList()
|
}
|
|
// 删除
|
async function handleDelete(row) {
|
const tips = row ? '该条' : '选中的项'
|
await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { type: 'warning' })
|
const ids = row ? row.id : selectedIds.value.join(',')
|
await remove(ids)
|
ElMessage.success('删除成功')
|
selectedIds.value = []
|
getList()
|
}
|
|
// 勾选值设置
|
function handleSelectionChange(rows) {
|
selectionList.value = rows
|
selectedIds.value = rows.map(item => item.id)
|
}
|
|
// 角色配置
|
async function handleGrant() {
|
if (selectedIds.value.length === 0) {
|
ElMessage.warning('请选择至少一条数据')
|
return
|
}
|
roleTreeObj.value = []
|
if (selectionList.value.length === 1) {
|
roleTreeObj.value = selectionList.value[0].roleId?.split(',') || []
|
}
|
const res = await getRoleTree()
|
roleGrantList.value = res.data.data
|
roleBox.value = true
|
}
|
|
// 提交角色配置
|
async function submitRole() {
|
const roleList = treeRoleRef.value.getCheckedKeys().join(',')
|
await grant(selectedIds.value.join(','), roleList)
|
roleBox.value = false
|
ElMessage.success('操作成功')
|
getList()
|
}
|
|
// 密码重置
|
async function handleReset() {
|
if (selectedIds.value.length === 0) {
|
ElMessage.warning('请选择至少一条数据')
|
return
|
}
|
await ElMessageBox.confirm('确定将选择账号密码重置为123456?', '提示', { type: 'warning' })
|
await resetPassword(selectedIds.value.join(','))
|
ElMessage.success('操作成功')
|
}
|
|
// 账号解封
|
async function handleLock() {
|
if (selectedIds.value.length === 0) {
|
ElMessage.warning('请选择至少一条数据')
|
return
|
}
|
await ElMessageBox.confirm('确定将选择账号解封?', '提示', { type: 'warning' })
|
await unlock(selectedIds.value.join(','))
|
ElMessage.success('操作成功')
|
}
|
|
// 审核通过
|
async function handleAuditPass() {
|
if (selectedIds.value.length === 0) {
|
ElMessage.warning('请选择至少一条数据')
|
return
|
}
|
await ElMessageBox.confirm('确定将选择数据通过审核?', '提示', { type: 'warning' })
|
await auditPass(selectedIds.value.join(','))
|
ElMessage.success('操作成功')
|
getList()
|
}
|
|
// 审核拒绝
|
async function handleAuditRefuse() {
|
if (selectedIds.value.length === 0) {
|
ElMessage.warning('请选择至少一条数据')
|
return
|
}
|
await ElMessageBox.confirm('确定将选择数据拒绝审核?', '提示', { type: 'warning' })
|
await auditRefuse(selectedIds.value.join(','))
|
ElMessage.success('操作成功')
|
getList()
|
}
|
|
// 返回正常模式
|
function handleAuditBack() {
|
auditMode.value = false
|
getList()
|
}
|
|
// 导入
|
function handleImport() {
|
excelBox.value = true
|
}
|
|
// 上传成功
|
function handleUploadSuccess() {
|
excelBox.value = false
|
getList()
|
ElMessage.success('导入成功')
|
}
|
|
// 导出
|
function handleExport() {
|
const account = func.toStr(searchParams.value.account)
|
const realName = func.toStr(searchParams.value.realName)
|
ElMessageBox.confirm('是否导出用户数据?', '提示', { type: 'warning' }).then(() => {
|
NProgress.start()
|
exportBlob(
|
`/blade-system/user/export-user?${website.tokenHeader}=${getToken()}&account=${account}&realName=${realName}`
|
).then(res => {
|
downloadXls(res.data, `用户数据表${new Date().toISOString().slice(0, 10)}.xlsx`)
|
NProgress.done()
|
})
|
})
|
}
|
|
// 下载模板
|
function handleTemplate() {
|
exportBlob(`/blade-system/user/export-template?${website.tokenHeader}=${getToken()}`).then(res => {
|
downloadXls(res.data, '用户数据模板.xlsx')
|
})
|
}
|
|
// 新增/编辑/查看 弹框
|
function openForm(mode, row) {
|
dialogVisible.value = true
|
nextTick(() => {
|
dialogRef.value?.open({ mode, row })
|
})
|
}
|
|
onMounted(() => {
|
getList()
|
})
|
</script>
|
|
<style scoped lang="scss">
|
.user-container {
|
height: 100%;
|
}
|
|
.tree-box {
|
height: 100%;
|
padding: 10px;
|
border-radius: 5px;
|
background-color: #fff;
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
:deep(.el-scrollbar) {
|
height: 100%;
|
}
|
}
|
|
.btn-group {
|
margin-bottom: 16px;
|
}
|
|
.upload-tip {
|
color: #999;
|
font-size: 12px;
|
margin-top: 8px;
|
}
|
</style>
|