前言:笔者在前端开发过程中遇到关于树的问题,把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 提供优秀的前端框架
版权声明:本文原创,转载请声明出处,谢谢
文章转载于:https://www.jianshu.com/p/70660590db2d
原著是一个有趣的人,若有侵权,请通知删除
评论前必须登录!
立即登录