| New file |
| | |
| | | <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> |