需求是这样的:有一个角色设置模块,左侧为角色列表,右侧为权限树。不同的角色会有不同的权限,切换角色的时候需要准确的显示该角色拥有的权限,且有相关权限的用户可对角色权限进行修改,即增加或删除用户的某一权限。
首先,角色所拥有的权限树是通过请求接口返回的对象数组的数据,每一个权限拥有label、key、value和children。label、key、children对应tree组件里面的title、key和children,value是一个布尔值,用来判断当前权限是否被勾选(true为被勾选)。大致数据结构如下:
export const treeData = [
{
title: "全局权限",
key: "0",
value: false,
children: [
{
title: "查看手机号",
key: "1",
value: false
}
]
},
{
title: "账号管理",
key: "2",
value: false,
children: [
{
title: "新增账号",
key: "3",
value: true
},
{
title: "编辑账号",
key: "4",
value: false
},
{
title: "删除账号",
key: "5",
value: false
}
]
}
]
在拿到数据之后,需要把数据转换成树形控件,因为存在子节点的渲染,所以自然而然也会想到递归的方法,这里采用的是Ant Design文档里提供的方法:
// 组件树形控件 子节点渲染
renderTreeNodes = data =>
data.map(item => {
if (item.children) {
return (
<TreeNode title={item.title} key={item.key}>
{this.renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode title={item.title} key={item.key}></TreeNode>;
});
至此,后端返回的数据可以以树形控件的形式展示:
前面说到,后端返回的每个权限里还有一个值是用来判断当前权限是否被勾选。相当于是树形控件默认选中的节点。我这里的做法是先依次遍历每个节点,然后取出节点的value是true的值,返回一个数组,代码如下:
// 权限树获取已勾选的节点的key值(value为true)
getCheckedKeys = (data) => {
let arr = [];
for (let i = 0;i < data.length;i++) {
if (data[i].value) {
arr.push(data[i].key)
}
if (data[i].children) {
let res = this.getCheckedKeys(data[i].children)
arr = [...arr, ...res]
}
}
return arr;
}
以上,已可以将后端返回的数据以树形结构展示出来,且可以获得被勾选的节点的数组。
下一步工作就是要让默认节点被勾选上,且可以勾选或取消勾选任意节点。
首先想到的是受控组件的写法:
受控组件是依赖于state的,即不管是默认值的赋值还是值的改变都是要通过state的。但由于需求里的数据是从后端请求获取的,在render
函数里面是不允许使用setState
方法的,而在componentDidMount
这个生命周期函数里还无法获取到接口返回的数据。所以只能考虑请求接口之后得到数据再做处理。dispatch
这个函数有一个参数是回调函数,我们可以在请求接口的时候把接口返回的数据作为回调函数的参数,这样一来,我们就可以在请求接口的回调函数里拿到我们所想到的数据了,并且可以使用setState
方法,render
函数代码如下:
render() {
return (
<Tree
checkable
onCheck={this.onCheck}
checkedKeys={this.state.defaultCheckedKeys}
>
{this.renderTreeNodes(treeData)}
</Tree>
);
}
当勾选或取消勾选树节点的时候,会触发onCheck
事件。代码如下:
onCheck = (checkedKeys) => {
this.setState({
defaultCheckedKeys: checkedKeys
})
}
除了受控组件的写法,还可以采用非受控组件。不过会存在一定的性能问题,具体的下面看代码再解释。
render() {
let defaultCheckedKeys = this.getCheckedKeys(treeData);
return (
<Tree
checkable
onCheck={this.onCheck}
defaultCheckedKeys={defaultCheckedKeys}
key={this.treeIndex}
>
{this.renderTreeNodes(treeData)}
</Tree>
);
}
因为需求里是不同角色切换的时候,要显示对应的权限树,也就是说,角色切换的时候树是重新渲染的。所以在上面的代码中,我们给这个树形控件再加一个属性:key
。因为角色列表是一个数组,所以可以把key
的值跟数组下标对应起来,这样每次进行角色切换的时候,key
的值也会发生变化,树形控件也会这个key
值的变化而重新渲染。但是,由于树形控件的重新渲染,如果一旦数据比较庞大的话,可能就会出现性能问题,在角色切换的时候,权限树的显示会有一定的延迟。
相比较而言,不管数据是不是从后端获取的,受控组件的写法更好一点,且不存在性能问题,但是受控组件是依赖于state的,所以值的获取和改变都与state相关,这点需要注意,不然可能就会出现意想不到的问题。
demo链接
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。