修改工单: 右键 给角色分配权限
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m45s

This commit is contained in:
sexygoat
2026-02-26 10:06:11 +08:00
parent dccad819cf
commit 3570b062a1
7 changed files with 302 additions and 80 deletions

View File

@@ -120,8 +120,11 @@
:props="{ children: 'children', label: 'label' }"
node-key="id"
show-checkbox
check-strictly
:filter-node-method="filterNode"
:default-expand-all="false"
:check-on-click-node="false"
@check="handleLeftTreeCheck"
class="permission-tree"
>
<template #default="{ node, data }">
@@ -130,9 +133,6 @@
<ElTag :type="data.perm_type === 1 ? 'info' : 'success'" size="small">
{{ data.perm_type === 1 ? '菜单' : '按钮' }}
</ElTag>
<ElTag :type="data.status === 1 ? 'success' : 'info'" size="small">
{{ data.status === 1 ? '启用' : '禁用' }}
</ElTag>
</span>
</template>
</ElTree>
@@ -180,15 +180,12 @@
<ElTag :type="data.perm_type === 1 ? 'info' : 'success'" size="small">
{{ data.perm_type === 1 ? '菜单' : '按钮' }}
</ElTag>
<ElTag :type="data.status === 1 ? 'success' : 'info'" size="small">
{{ data.status === 1 ? '启用' : '禁用' }}
</ElTag>
</span>
<ElButton
type="danger"
size="small"
link
@click="removeSinglePermission(data.id)"
@click="removeSinglePermission(data)"
>
移除
</ElButton>
@@ -255,6 +252,7 @@
const allPermissionsMap = ref<Map<number, any>>(new Map()) // 所有权限的映射表
const leftTreeFilter = ref('') // 左侧树搜索关键字
const rightTreeFilter = ref('') // 右侧树搜索关键字
const isHandlingCheck = ref(false) // 标志位:是否正在处理勾选事件
// 搜索表单初始值
const initialSearchState = {
@@ -456,15 +454,134 @@
return data.label.toLowerCase().includes(value.toLowerCase())
}
// 构建权限树数据结构
// 获取节点的所有子节点ID(包括子菜单和按钮)
const getAllChildrenIds = (node: any): number[] => {
const ids: number[] = []
const traverse = (n: any) => {
if (n.children && n.children.length > 0) {
n.children.forEach((child: any) => {
ids.push(child.id)
traverse(child)
})
}
}
traverse(node)
return ids
}
// 在树数据中查找节点
const findNodeInTree = (treeData: any[], nodeId: number): any => {
for (const node of treeData) {
if (node.id === nodeId) return node
if (node.children && node.children.length > 0) {
const found = findNodeInTree(node.children, nodeId)
if (found) return found
}
}
return null
}
// 更新父节点的勾选状态(计算应该半选的父节点)
const updateParentCheckStatus = (checkedKeys: number[]) => {
const checkedSet = new Set(checkedKeys)
const parentIdsToHalfCheck = new Set<number>()
// 递归检查节点的子节点勾选状态
const checkNode = (node: any): { allChecked: boolean; someChecked: boolean } => {
if (!node.children || node.children.length === 0) {
// 叶子节点
return {
allChecked: checkedSet.has(node.id),
someChecked: checkedSet.has(node.id)
}
}
// 有子节点的节点
let allChecked = true
let someChecked = false
for (const child of node.children) {
const childStatus = checkNode(child)
if (!childStatus.allChecked) {
allChecked = false
}
if (childStatus.someChecked) {
someChecked = true
}
}
// 当前节点被勾选
const currentNodeChecked = checkedSet.has(node.id)
// 判断父节点应该显示的状态:
// 1. 如果有子节点被勾选(someChecked=true),但不是全部子节点被勾选或当前节点未被勾选 -> 半选
// 2. 如果所有子节点都被勾选且当前节点也被勾选 -> 全选
if (someChecked) {
if (!allChecked || !currentNodeChecked) {
// 子节点部分被勾选,或者当前节点未被勾选 -> 半选
parentIdsToHalfCheck.add(node.id)
}
// 如果 allChecked && currentNodeChecked,则为全选,不需要设置半选
}
return {
allChecked: allChecked && currentNodeChecked,
someChecked: someChecked || currentNodeChecked
}
}
// 检查所有顶层节点
availablePermissions.value.forEach(node => checkNode(node))
return Array.from(parentIdsToHalfCheck)
}
// 处理左侧树的勾选事件
const handleLeftTreeCheck = (data: any, checked: any) => {
if (isHandlingCheck.value) return
isHandlingCheck.value = true
try {
// 获取当前勾选的keys
const currentChecked = checked.checkedKeys as number[]
// 计算应该半选的父节点
const halfCheckedIds = updateParentCheckStatus(currentChecked)
// 使用内部API直接操作树的半选状态
nextTick(() => {
if (leftTreeRef.value && leftTreeRef.value.store) {
// 清除所有半选状态
Object.values(leftTreeRef.value.store.nodesMap).forEach((node: any) => {
node.indeterminate = false
})
// 设置应该半选的节点
halfCheckedIds.forEach((id: number) => {
const node = leftTreeRef.value.store.nodesMap[id]
if (node) {
node.indeterminate = true
}
})
}
})
} finally {
isHandlingCheck.value = false
}
}
// 构建权限树数据结构(只包含启用的权限)
const buildTreeData = (treeNodes: PermissionTreeNode[]): any[] => {
return treeNodes.map((node) => ({
id: node.id,
label: node.perm_name,
perm_type: node.perm_type,
status: node.status ?? 1,
children: node.children && node.children.length > 0 ? buildTreeData(node.children) : undefined
}))
return treeNodes
.filter((node) => node.status === 1) // 只显示启用的权限
.map((node) => ({
id: node.id,
label: node.perm_name,
perm_type: node.perm_type,
status: node.status ?? 1,
children: node.children && node.children.length > 0 ? buildTreeData(node.children) : undefined
}))
}
// 构建权限映射表(包括所有节点和子节点)
@@ -582,12 +699,50 @@
return leftTreeRef.value.getCheckedKeys(false)
}
// 获取左侧树勾选的节点(包括半选节点,用于提交服务器)
// 获取节点的所有父节点ID
const getParentNodeIds = (nodeId: number): number[] => {
const parentIds: number[] = []
// 在原始权限树中递归查找父节点
const findInTree = (treeNodes: PermissionTreeNode[], targetId: number, parentId?: number): number | null => {
for (const node of treeNodes) {
if (node.id === targetId) {
return parentId || null
}
if (node.children && node.children.length > 0) {
const found = findInTree(node.children, targetId, node.id)
if (found !== null) return found
}
}
return null
}
// 递归向上查找所有父节点
const findAllParents = (currentId: number) => {
const parentId = findInTree(originalPermissionTree.value, currentId)
if (parentId) {
parentIds.push(parentId)
findAllParents(parentId)
}
}
findAllParents(nodeId)
return parentIds
}
// 获取左侧树勾选的节点(包括必要的父节点,用于提交服务器)
const getLeftCheckedKeysWithHalf = (): number[] => {
if (!leftTreeRef.value) return []
const checkedKeys = leftTreeRef.value.getCheckedKeys(false)
const halfCheckedKeys = leftTreeRef.value.getHalfCheckedKeys()
return [...checkedKeys, ...halfCheckedKeys]
const parentIds = new Set<number>()
// 为每个勾选的节点找到所有父节点
checkedKeys.forEach((key: number) => {
const parents = getParentNodeIds(key)
parents.forEach(parentId => parentIds.add(parentId))
})
return [...checkedKeys, ...Array.from(parentIds)]
}
// 添加权限
@@ -625,8 +780,19 @@
}
}
// 检查节点是否有子节点(子菜单或按钮)
const hasChildren = (data: any): boolean => {
return data.children && data.children.length > 0
}
// 移除单个权限
const removeSinglePermission = async (permId: number) => {
const removeSinglePermission = async (data: any) => {
// 检查是否有子菜单或按钮
if (hasChildren(data)) {
ElMessage.warning('该权限下还有子菜单或按钮,请先移除子项后再移除此权限')
return
}
try {
// 保存右侧树的展开节点
const expandedKeys = rightTreeRef.value?.store?.nodesMap
@@ -635,10 +801,10 @@
.map((key) => Number(key))
: []
await RoleService.removePermission(currentRoleId.value, permId)
await RoleService.removePermission(currentRoleId.value, data.id)
// 更新已选权限列表
selectedPermissions.value = selectedPermissions.value.filter((id) => id !== permId)
selectedPermissions.value = selectedPermissions.value.filter((id) => id !== data.id)
// 重新构建左右两侧树
const fullTreeData = buildTreeData(originalPermissionTree.value)