添加菜单 权限 等页面

This commit is contained in:
浅念
2025-08-13 19:27:49 +08:00
parent e797cb24de
commit f5deb7b8f7
15 changed files with 2297 additions and 20 deletions
+328 -13
View File
@@ -3,16 +3,16 @@
<!-- 顶部操作栏 -->
<div class="customer-toolbar boxshadow">
<div class="customer-search-group">
<el-input v-model="keyword" placeholder="请输入关键字" />
<el-select
v-model="selectValue"
style="min-width: 120px"
filterable
placeholder="请选择用户"
>
<el-option v-for="v in userOptions" :key="v.id" :label="v.name" :value="v.id">
</el-option>
</el-select>
<el-form-item label="关键字:" label-width="70px">
<el-input style="min-width: 120px" v-model="keyword" placeholder="请输入关键字" />
</el-form-item>
<el-form-item label="状态:" label-width="50px">
<el-select v-model="selectValue" style="min-width: 120px">
<el-option label="全部" value="" />
<el-option label="正常" value="1" />
<el-option label="离职" value="2" />
</el-select>
</el-form-item>
</div>
</div>
<div class="boxshadow">
@@ -57,6 +57,24 @@
<el-table-column prop="customer_count" label="客户数量" align="center" />
<el-table-column fixed="right" label="操作" width="200px" align="center">
<template #default="scope">
<el-link
type="success"
underline="never"
style="margin-right: 8px"
@click="showPermissionDialog(scope.row)"
v-if="hasAuth('user@direct_perm')"
>
<i class="iconfont-sys">&#xe723;</i>直接授权
</el-link>
<el-link
type="warning"
underline="never"
style="margin-right: 8px"
@click="showroleDialog(scope.row)"
v-if="hasAuth('user@role_system')"
>
<i class="iconfont-sys">&#xe830;</i>角色&系统
</el-link>
<el-link
type="primary"
underline="never"
@@ -185,7 +203,6 @@
</div>
</el-form>
</el-dialog>
<!-- 图片预览组件 -->
<ElImageViewer
v-if="showImageViewer"
@@ -193,6 +210,97 @@
:hide-on-click-modal="true"
@close="showImageViewer = false"
/>
<ElDialog
v-model="permissionDialog"
title="菜单权限"
width="520px"
align-center
class="el-dialog-border"
>
<ElScrollbar height="70vh">
<ElTree
ref="treeRef"
:data="processedMenuList"
show-checkbox
node-key="id"
:default-expand-all="isExpandAll"
:props="defaultProps"
@check="handleTreeCheck"
>
<template #default="{ data }">
<div style="display: flex; align-items: center">
<span v-if="data.isAuth">
{{ data.label }}
</span>
<span v-else>{{ defaultProps.label(data) }}</span>
</div>
</template>
</ElTree>
</ElScrollbar>
<template #footer>
<div class="dialog-footer">
<ElButton @click="toggleExpandAll">{{ isExpandAll ? '全部收起' : '全部展开' }}</ElButton>
<ElButton @click="toggleSelectAll" style="margin-left: 8px">{{
isSelectAll ? '取消全选' : '全部选择'
}}</ElButton>
<ElButton type="primary" @click="savePermission">保存</ElButton>
</div>
</template>
</ElDialog>
<el-dialog
title="角色&系统设置"
v-model="roleDialogVisible"
width="700px"
:close-on-click-modal="false"
>
<el-form :model="roleForm" ref="addFormRef" label-width="80px" class="add-form">
<el-row :gutter="16">
<el-col :span="24">
<el-form-item label="角色" prop="status">
<ElSelect
v-model="roleForm.role_ids"
multiple
class="account-select"
placeholder="请选择角色"
>
<ElOption
v-for="account in roleOptions"
:key="account.id"
:label="account.name"
:value="account.id"
>
<span>{{ account.name }}</span>
</ElOption>
</ElSelect>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="系统" prop="status">
<ElSelect
v-model="roleForm.system_ids"
multiple
class="account-select"
placeholder="请选择系统"
>
<ElOption
v-for="account in systemsList"
:key="account.id"
:label="account.name"
:value="account.id"
>
<span>{{ account.name }}</span>
</ElOption>
</ElSelect>
</el-form-item>
</el-col>
</el-row>
<div class="form-submit-row">
<el-button type="primary" size="large" style="width: 70%" @click="handleRoleSubmit"
>提交</el-button
>
</div>
</el-form>
</el-dialog>
</template>
<script setup lang="ts">
@@ -200,6 +308,9 @@
import { ElImageViewer } from 'element-plus'
import { manageService } from '@/api/adminApi'
import { useAuth } from '@/composables/useAuth'
import { systemService } from '@/api/systemApi'
import { formatMenuTitle } from '@/router/utils/utils'
import { UserService } from '@/api/usersApi'
const { hasAuth } = useAuth()
const selectValue = ref<string[]>([])
const keyword = ref<string>('')
@@ -208,6 +319,13 @@
const detailDialogVisible = ref(false)
const plzyDialogVisible = ref(false)
const AddDialogVisible = ref(false)
const permissionDialog = ref(false)
const roleDialogVisible = ref(false)
// 权限树数据(来自接口 getPermissionMenuList)
const permissionMenuList = ref<any[]>([])
const treeRef = ref()
const isExpandAll = ref(true)
const isSelectAll = ref(false)
const pagination = reactive({
currentPage: 1,
pageSize: 10,
@@ -232,7 +350,24 @@
phone: '',
status: ''
})
const roleForm = reactive({
user_id: '',
role_ids: '',
system_ids: ''
})
const userOptions = ref<any>()
//系统
const systemsList = ref<any>()
const getsystemsList = async () => {
const response = await UserService.getsystemsList()
systemsList.value = response || []
}
//角色下拉框
const roleOptions = ref<any>()
const getroleOptions = async () => {
const response = await systemService.getRoleSelectList()
roleOptions.value = response || []
}
const getuserList = async () => {
// 这里调用获取用户列表的接口
const res = await manageService.presetusers()
@@ -260,7 +395,7 @@
size: pageSize
}
if (selectValue.value) {
params.id = selectValue.value
params.status = selectValue.value
}
if (keyword.value) {
params.keyword = keyword.value
@@ -327,6 +462,187 @@
zyForm.customer_id = row.id
detailDialogVisible.value = true
}
// 显示角色&系统对话框
const showroleDialog = (row: any) => {
getsystemsList()
getroleOptions()
roleDialogVisible.value = true
roleForm.user_id = row.id
roleForm.role_ids = row.role_ids || []
roleForm.system_ids = row.system_ids || []
}
// 提交角色&系统
const handleRoleSubmit = async () => {
const res = await manageService.userSetRole(roleForm)
ElMessage.success(res?.message || '角色&系统设置成功!')
roleDialogVisible.value = false
gettableList()
}
// 显示权限对话框
const userId = ref('')
const showPermissionDialog = async (row: any) => {
permissionDialog.value = true
userId.value = row.id
try {
const res = await systemService.getPermissionMenuList({ user_id: row.id })
// 兼容不同返回结构(data/records/直接数组)
const list = (res as any)?.data ?? (res as any)?.records ?? (res as any)
permissionMenuList.value = Array.isArray(list) ? list : []
// 根据接口返回的 selected 字段设置默认选中
nextTick(() => {
const keys = buildDefaultCheckedKeys(permissionMenuList.value)
treeRef.value?.setCheckedKeys(keys)
handleTreeCheck()
})
} catch (e) {
permissionMenuList.value = []
}
}
// 构建默认选中的 key 列表(与 node-key="id" 一致)
const buildDefaultCheckedKeys = (source: any[]): Array<string | number> => {
const keys: Array<string | number> = []
const walk = (node: any) => {
if (node?.meta?.selected) {
keys.push(node.id)
}
if (Array.isArray(node?.meta?.authList)) {
node.meta.authList.forEach((auth: any) => {
if (auth?.selected) {
keys.push(`${node.id}__auth__${auth.authMark}`)
}
})
}
if (Array.isArray(node?.children)) node.children.forEach(walk)
}
source.forEach(walk)
return keys
}
// 处理菜单数据,将 authList 转换为子节点
const processedMenuList = computed(() => {
const processNode = (node: any) => {
// 规范化 id,确保树节点唯一键与 node-key 对应
const processed: any = { ...node, id: node.id }
// 如果有 authList,将其转换为子节点
if (node.meta && node.meta.authList && node.meta.authList.length) {
const authNodes = node.meta.authList.map((auth: any) => ({
// 为权限子节点提供稳定唯一 id(数字+字符串),与 node-key="id" 匹配
id: `${node.id}__auth__${auth.authMark}`,
name: `${node.name}_${auth.authMark}`,
label: auth.title,
auth_mark: auth.authMark,
auth_id: auth.id,
isAuth: true,
checked: auth.selected || false
}))
processed.children = processed.children ? [...processed.children, ...authNodes] : authNodes
}
// 递归处理子节点
if (processed.children) {
processed.children = processed.children.map(processNode)
}
return processed
}
const source = permissionMenuList.value || []
return source.map(processNode)
})
const defaultProps = {
children: 'children',
label: (data: any) => formatMenuTitle(data.meta?.title) || ''
}
const toggleExpandAll = () => {
const tree = treeRef.value
if (!tree) return
// 使用store.nodesMap直接控制所有节点的展开状态
const nodes = tree.store.nodesMap
for (const node in nodes) {
nodes[node].expanded = !isExpandAll.value
}
isExpandAll.value = !isExpandAll.value
}
const toggleSelectAll = () => {
const tree = treeRef.value
if (!tree) return
if (!isSelectAll.value) {
// 全选:获取所有节点的key并设置为选中
const allKeys = getAllNodeKeys(processedMenuList.value)
tree.setCheckedKeys(allKeys)
} else {
// 取消全选:清空所有选中
tree.setCheckedKeys([])
}
isSelectAll.value = !isSelectAll.value
}
const getAllNodeKeys = (nodes: any[]): Array<string | number> => {
const keys: Array<string | number> = []
const traverse = (nodeList: any[]) => {
nodeList.forEach((node) => {
if (node.id !== undefined && node.id !== null) {
keys.push(node.id)
}
if (node.children && node.children.length > 0) {
traverse(node.children)
}
})
}
traverse(nodes)
return keys
}
const handleTreeCheck = () => {
const tree = treeRef.value
if (!tree) return
// 使用树组件的getCheckedKeys方法获取选中的节点
const checkedKeys = tree.getCheckedKeys()
const allKeys = getAllNodeKeys(processedMenuList.value)
// 判断是否全选:选中的节点数量等于总节点数量
isSelectAll.value = checkedKeys.length === allKeys.length && allKeys.length > 0
}
const getSelectedIds = () => {
const tree = treeRef.value
if (!tree) return { menuIds: [], authIds: [] }
const checkedNodes = tree.getCheckedNodes?.(false) || []
const halfCheckedNodes = tree.getHalfCheckedNodes?.() || []
const allNodes = [...checkedNodes, ...halfCheckedNodes]
const menuIds = Array.from(
new Set(
allNodes
.filter((n: any) => !n.isAuth && n.id !== undefined && n.id !== null)
.map((n: any) => n.id)
)
)
const authIds = Array.from(
new Set(
checkedNodes.filter((n: any) => n.isAuth && n.auth_id != null).map((n: any) => n.auth_id)
)
)
const ids = Array.from(new Set([...(menuIds as any[]), ...(authIds as any[])]))
return { menuIds, authIds, ids }
}
const savePermission = async () => {
const { ids } = getSelectedIds()
const res = await manageService.userMenuAuth({
user_id: userId.value,
resources: ids
})
ElMessage.success(res?.message || '权限保存成功')
permissionDialog.value = false
}
// 选中的行数据
const selectedRows = ref<any[]>([])
// 处理表格行选择变化
@@ -390,7 +706,6 @@
height: 40px;
border-radius: 50%;
transition: opacity 0.2s;
&:hover {
opacity: 0.8;
}