文章目录
- 前言
- 一、遇到的交互场景
- el-tree 中 check-strictly 属性
- 二、处理父级的半选中以及选中交互
- el-tree
- check,check-change 事件
- 编辑进来,父级的半选状态处理
- 总结
前言
在开发后台管理系统的时候,用户的权限控制是一个常见的需求。这里需要探讨下按钮的级别的权限控制,以及实现中使用element-plus tree 组件的使用细节。
一、遇到的交互场景
基于原有的基础上实现按钮级别的权限控制,原有的如下图每一个菜单都有一个唯一ID,PID(父级ID),现在需要扩展的功能,就是添加一个button按钮控制,其中主要的细节交互:
- 取消选中子级menu/button,父级不关联取消;
- 选中/取消父级catalog/menu,子级全部选中/取消;
- 选中/取消部分子级menu/button,父级关联半选中状态(indeterminate=true);
其中我用的是el-tree 组件,我用button:xxx业务按钮;这样的方式来命名,后端也给了区分类型的字段type:catalog(目录),menu(菜单),button(按钮)。
el-tree 中 check-strictly 属性
el-tree 中 check-strictly 属性,为了实现取消选中子级menu/button,父级不关联取消,这里check-strictly设置为true;设置后点击父级也不关联子级选中了,用户用起来不方便,这里就需要我们自己处理
二、处理父级的半选中以及选中交互
el-tree
<el-tree
ref="treeRef"
:check-strictly="true"
:data="treeData"
show-checkbox
default-expand-all
node-key="id"
highlight-current
:default-checked-keys="variable.roleForm.featureIds"
:props="defaultProps"
@check="hanleCheck"
@check-change="checkChange"
/>
const defaultProps = {
children: 'children',
label: function (treeData: any, treeNode: any) {
return treeData?.name
}
}
check,check-change 事件
这里主要用的事件 check,check-change
const hanleCheck = (data:any, node:any) => {
console.log(data, node);
// 获取当前节点是否被选中
const isChecked = treeRef.value!.getNode(data).checked
// 如果当前节点被选中,则遍历下级子节点并选中,如果当前节点取消选中,则遍历下级节点并取消
if (isChecked) {
// 判断该节点是否有下级节点,如果有那么遍历设置下级节点为选中
data.children && data.children.length > 0 && setChildreChecked(data.children, true)
} else {
// 如果节点取消选中,则取消该节点下的子节点选中
data.children && data.children.length > 0 && setChildreChecked(data.children, false)
}
function setChildreChecked(node:any, isChecked:boolean) {
node.forEach((item:any) => {
item.children && item.children.length > 0 && setChildreChecked(item.children, isChecked)
// // 修改勾选状态
treeRef.value!.setChecked(item.id, isChecked,false)
})
}
}
const checkChange = (data, checked, indeterminate) => {
// console.log(data, checked, indeterminate);
// 选中全部子节点,父节点也默认选中,但是子节点再次取消勾选或者全部子节点取消勾选也不会影响父节点勾选状态
let checkNode = treeRef.value!.getNode(data)//获取当前节点
// 勾选部分子节点,父节点变为半选状态
if (checkNode.parent && checkNode.parent.childNodes.some(ele => ele.checked)) {
checkNode.parent.indeterminate = true
}
// 勾选全部子节点,父节点变为全选状态
if (checkNode.parent && checkNode.parent.childNodes.every(ele => ele.checked)) {
checkNode.parent.checked = true
checkNode.parent.indeterminate = false
}
// 如果取消所有第二节点的勾选状态,则第一层父节点也取消勾选
if (checkNode.level == 2 && checkNode.parent.childNodes.every(ele => !ele.checked)) {
checkNode.parent.checked = false
checkNode.parent.indeterminate = false
}
}
其中,上面用的
treeRef.value!.setChecked(item.id, isChecked,false)
看文档说第三个属性是是否要递归,我觉得是设置了true后,应该帮我递归选中/取消关联的节点,但没有生效,所以还是自己递归下;(有大佬能指出是我的用法有问题吗?劳烦指出)
编辑进来,父级的半选状态处理
上述方法实现后,我们就可以实现上述交互了,但这里要注意的一点,就是点击编辑进入的,父级的半选状态没有选中,也就是说 checkNode.parent.indeterminate 这个没有被设置为true;下面打个补丁。
const editRole = async (row: any) => {
const { permissionIds} = await detailApi({ id: row.id })
await nextTick()
setCheckedKeys(permissionIds) // 返回用户的权限id后,根据id选中树节点
permissionIds.forEach((id:any) => {
const nodeData = treeRef.value!.getNode(id) // 根据权限id,获取每个选中的树节点
console.log('nodeData',nodeData)
console.log('nodeData.parent?.id',nodeData.parent?.id)
// 反显,上面setCheckedKeys后,如果父级没有选中,则把半选状态设置true
if(nodeData?.parent?.id != 0 && !nodeData?.parent?.checked){
nodeData.parent.indeterminate = true
}
});
}
总结
后台管理系统常见的权限控制需求,这里讲button实现交互细节处理,
- 取消选中子级menu/button,父级不关联取消;
- 选中/取消父级catalog/menu,子级全部选中/取消;
- 选中/取消部分子级menu/button,父级关联半选中状态(indeterminate=true);
下篇再补上button 的根据权限控制显示,用vue3 的指令实现,谢谢关注