前言:笔者在前端开发过程中遇到关于树的问题,把antd官方的Tree组件直接拿下来用了,刚开始其他方法都还好,当需求中写个节点的删除功能的时候,因为算法功底不好,导致此功能实现不了,所以有了此文
背景
前端项目是react+umi+antd,这个用过的都知道好,不做赘述,笔者先找到了这个antd官方提供的Tree组件
export default class MyTree extends React.Component {
state = {
treeData: [
{
key:'1',
title:"第一部分",
children:[
{key:'1-1',title:"一、二级标题",},
{key:'1-2',title:"二、二级标题",},
{key:'1-3',title:"三、二级标题",},
{key:'1-4',title:"四、二级标题",}
]
},
{
key:'2',
title:"第二部分",
children:[
{
key:'2-1',
title:"一、二级标题",
children:[
{key:'2-1-1',title:"(一)三级标题",},
{key:'2-1-2',title:"(二)三级标题",},
{key:'2-1-3',title:"(三)三级标题",},
]
},
{
key:'2-2',
title:"二、二级标题",
children:[
{key:'2-2-1',title:"(一)三级标题",},
{key:'2-2-2',title:"(二)三级标题",}
]
},
{
key:'2-3',
title:"三、二级标题",
children:[
{key:'2-3-1',title:"(一)三级标题",},
{key:'2-3-2',title:"(二)三级标题",},
]
},
{
key:'2-4',
title:"四、二级标题",
children:[
{key:'2-4-1',title:"(一)三级标题",},
{key:'2-4-2',title:"(二)三级标题",},
]
},
{
key:'2-5',
title:"五、二级标题",
children:[
{key:'2-5-1',title:"(一)三级标题",},
{key:'2-5-2',title:"(二)三级标题",},
]
},
]
},
{
key:'3',
title:"第三部分",
children:[
{
key:'3-1',
title:"一、二级标题",
children:[
{key:'3-1-1',title:"(一)综合医院",},
{key:'3-1-2',title:"(二)重点专科医院",},
]
},
{
key:'3-2',
title:"二、二级标题",
children:[
{key:'3-2-1',title:"(一)三级标题",},
{key:'3-2-2',title:"(二)三级标题",},
]
},
{
key:'3-3',
title:"三、二级标题",
children:[
{key:'3-3-1',title:"(一)三级标题",},
{key:'3-3-2',title:"(二)三级标题",},
]
},
{
key:'3-4',
title:"四、二级标题",
children:[
{key:'3-4-1',title:"(一)三级标题",},
{key:'3-4-2',title:"(二)三级标题",},
]
},
]
}
]
}
renderTreeNodes = data => data.map((item) => {
if (item.children) {
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{this.renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode {...item} dataRef={item} />;
})
render() {
return (
<Tree>
{this.renderTreeNodes(this.state.treeData)}
</Tree>
);
}
}
通过这个结构,render出来的结果,和https://ant.design/components/tree-cn/中的效果
寻找删除之道
这个时候又行业大佬提供了以下思路
1:递归去找到然后删除
2:把key值不等于这个得,重新放入一个新数组,最后重新设置state
3:把key值直接设置为className,找dom数然后删除
其实三种方法都可以进行探索,网上找了N多资料,都不得解,最终晚上灵光一闪,得出以下思路:
如果把data数组,都放在最顶层,取消children数组,然后给每个都加上一个parentId,
1:这样满足了render的时候可以进行递归,
2:要删除的时候,或者更改的时候,遍历data,就能找打key,进行删除,当然,如果删除的是父级,同时也可以删除所有子级节点
以下是代码
export default class MyTree extends React.Component {
state = {
data:[
{key:'1',title:'第一部分',parentId:'-1'},
{key:'1-1',title:'一、二级标题',parentId:'1'},
{key:'1-2',title:'二、二级标题',parentId:'1'},
{key:'1-3',title:'三、二级标题',parentId:'1'},
{key:'1-4',title:'四、二级标题',parentId:'1'},
{key:'2',title:'第二部分',parentId:'-1'},
{key:'2-1',title:'一、二级标题',parentId:'2'},
{key:'2-1-1',title:'(一)三级标题',parentId:'2-1'},
{key:'2-1-2',title:'(二)三级标题',parentId:'2-1'},
{key:'2-1-3',title:'(三)三级标题',parentId:'2-1'},
{key:'2-2',title:'二、二级标题',parentId:'2'},
{key:'2-2-1',title:'(一)三级标题',parentId:'2-2'},
{key:'2-2-2',title:'(二)三级标题',parentId:'2-2'},
{key:'2-3',title:'三、二级标题',parentId:'2'},
{key:'2-3-1',title:'(一)三级标题',parentId:'2-3'},
{key:'2-3-2',title:'(二)三级标题',parentId:'2-3'},
{key:'2-4',title:'四、二级标题',parentId:'2'},
{key:'2-4-1',title:'(一)三级标题',parentId:'2-4'},
{key:'2-4-2',title:'(二)三级标题',parentId:'2-4'},
{key:'2-5',title:'五、二级标题',parentId:'2'},
{key:'2-5-1',title:'(一)三级标题',parentId:'2-5'},
{key:'2-5-2',title:'(二)三级标题',parentId:'2-5'},
{key:'3',title:'第三部分',parentId:'-1'},
{key:'3-1',title:'一、二级标题',parentId:'3'},
{key:'3-1-1',title:'(一)三级标题',parentId:'3-1'},
{key:'3-1-2',title:'(二)三级标题',parentId:'3-1'},
{key:'3-2',title:'二、二级标题',parentId:'3'},
{key:'3-2-1',title:'(一)三级标题',parentId:'3-2'},
{key:'3-2-2',title:'(二)三级标题',parentId:'3-2'},
{key:'3-3',title:'三、二级标题',parentId:'3'},
{key:'3-3-1',title:'(一)三级标题',parentId:'3-3'},
{key:'3-3-2',title:'(二)三级标题',parentId:'3-3'},
{key:'3-4',title:'四、二级标题',parentId:'3'},
{key:'3-4-1',title:'(一)三级标题',parentId:'3-4'},
{key:'3-4-2',title:'(二)三级标题',parentId:'3-4'},
],
}
// 根据父级找到所有子级节点
getByParentId(parentId){
return this.state.data.filter(item => {
return item.parentId === parentId;
})
}
renderTreeNode = (parentId) => {
// 先找到子级节点
var tmp = this.getByParentId(parentId);
if(tmp.length > 0){
// 遍历铺页面,如果数组长度不为0则证明子级不为空
return tmp.map(item =>{
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{this.renderTreeNode(item.key)}
</TreeNode>
);
})
}
}
render() {
return (
<Tree>
{/*先找到所有parentId为-1的顶级节点*/}
{this.renderTreeNode("-1")}
</Tree>
);
}
}
接下来就好办了,删除方法和右键响应方法
//简单处理,右键删除
handleRightClick = (e) =>{
let key = e.node.props.dataRef.key;
this.handleDelete(key)
}
//根据key删除节点
handleDelete = (key) => {
let {data} = this.state;
data.splice(data.findIndex(item => item.key === key),1)
this.setState(data);
}
Tree绑定右键处理事件
<Tree onRightClick={this.handleRightClick}>
{/*先找到所有parentId为-1的顶级节点*/}
{this.renderTreeNode("-1")}
</Tree>
效果:
同理,要对节点进行其他遍历操作就更方便,不会像第一次那样做各种递归处理,给不同节点增加不同样式,修改内容等等,方便很多
鉴于楼下有个小伙伴,提问如何更换位置,今日有空,也实现出来,大家多多指导
class MyTree extends React.Component {
state = {
currentNode: null,
data:[
{key:'1',title:'第一部分',parentId:'-1'},
{key:'2',title:'第二部分',parentId:'-1'},
{key:'3',title:'第三部分',parentId:'-1'},
{key:'1-1',title:'一、二级标题',parentId:'1'},
{key:'1-2',title:'二、二级标题',parentId:'1'},
{key:'1-3',title:'三、二级标题',parentId:'1'},
{key:'1-4',title:'四、二级标题',parentId:'1'},
{key:'2-1',title:'一、二级标题',parentId:'2'},
{key:'2-2',title:'二、二级标题',parentId:'2'},
{key:'2-3',title:'三、二级标题',parentId:'2'},
{key:'2-4',title:'四、二级标题',parentId:'2'},
{key:'2-5',title:'五、二级标题',parentId:'2'},
{key:'3-1',title:'一、二级标题',parentId:'3'},
{key:'3-2',title:'二、二级标题',parentId:'3'},
{key:'3-3',title:'三、二级标题',parentId:'3'},
{key:'3-4',title:'四、二级标题',parentId:'3'},
{key:'2-1-1',title:'(一)三级标题',parentId:'2-1'},
{key:'2-1-2',title:'(二)三级标题',parentId:'2-1'},
{key:'2-1-3',title:'(三)三级标题',parentId:'2-1'},
{key:'2-2-1',title:'(一)三级标题',parentId:'2-2'},
{key:'2-2-2',title:'(二)三级标题',parentId:'2-2'},
{key:'2-3-1',title:'(一)三级标题',parentId:'2-3'},
{key:'2-3-2',title:'(二)三级标题',parentId:'2-3'},
{key:'2-4-1',title:'(一)三级标题',parentId:'2-4'},
{key:'2-4-2',title:'(二)三级标题',parentId:'2-4'},
{key:'2-5-1',title:'(一)三级标题',parentId:'2-5'},
{key:'2-5-2',title:'(二)三级标题',parentId:'2-5'},
{key:'3-1-1',title:'(一)三级标题',parentId:'3-1'},
{key:'3-1-2',title:'(二)三级标题',parentId:'3-1'},
{key:'3-2-1',title:'(一)三级标题',parentId:'3-2'},
{key:'3-2-2',title:'(二)三级标题',parentId:'3-2'},
{key:'3-3-1',title:'(一)三级标题',parentId:'3-3'},
{key:'3-3-2',title:'(二)三级标题',parentId:'3-3'},
{key:'3-4-1',title:'(一)三级标题',parentId:'3-4'},
{key:'3-4-2',title:'(二)三级标题',parentId:'3-4'},
],
}
// 根据父级找到所有子级节点
getByParentId(parentId){
return this.state.data.filter(item => {
return item.parentId === parentId;
})
}
renderTreeNode = (parentId) => {
// 先找到子级节点
let tmp = this.getByParentId(parentId);
if(tmp.length > 0){
// 遍历铺页面,如果数组长度不为0则证明子级不为空
return tmp.map(item =>{
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{this.renderTreeNode(item.key)}
</TreeNode>
);
})
}
}
handleRightClick = ({event,node}) =>{
let key = node.props.dataRef.key;
this.handleDelete(key)
}
/**
* 上下移动
*/
handleMoveOn=()=> {
let data = this.state.data;
for(let i = 0; i< data.length; i++){
if(data[i].key == this.state.currentNode && i >= 1){
let tmp = data[i];
data[i] = data[i-1]
data[i-1] = tmp;
}
}
this.setState(data)
}
handleClick=(e)=>{
console.log(1)
this.setState({currentNode:e})
}
//根据key删除节点
handleDelete = (key) => {
let {data} = this.state;
data.splice(data.findIndex(item => item.key === key),1)
this.setState(data);
}
render() {
return (
<div>
<div className={styles.btn}>
<Button onClick={this.handleMoveOn} disabled={this.state.currentNode==null}>上移</Button>
</div>
<Tree onRightClick={this.handleRightClick} className={styles.normal} onSelect={this.handleClick}>
{/*先找到所有parentId为-1的顶级节点*/}
{this.renderTreeNode("-1")}
</Tree>
</div>
);
}
}
鸣谢:感谢antd、umi 提供优秀的前端框架
版权声明:本文原创,转载请声明出处,谢谢