如何用react封装一款Tree 树形组件

2,477 阅读2分钟

这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战

TIP 👉 呜呼!楚虽三户能亡秦,岂有堂堂中国空无人!____陆游《金错刀行》

前言

Web Component是前端界一直非常热衷的一个领域,用第三方组件化的框架去实现的话,你需要依赖框架本身很多东西,很多时候我们只是简单的几个组件,不是很大,也不是很多,所以为了保证组件的`轻量,简单`,其实这个时候我们并不想采用第三方的框架。

Tree 树形控件

import

建议用tree2数据,通用后端给的json数据
import Tree from '@/components/Tree/Tree2';

Props

1. treeData
  • 类型:array (必填)
  • 默认值:[]
  • 说明:树形数据
2. iconFolder
  • 类型:boolean (必填)
  • 默认值:true / false
  • 说明:是否有文件夹的折起标志
3. iconStyle
  • 类型:boolean (必填)
  • 默认值:true / false
  • 说明:是否有加减的折起标志
4. canSelected
  • 类型:boolean (必填)
  • 默认值:true / false
  • 说明:是否可勾选
5. clickNodeGetInfo
  • 类型:func
  • 默认值:无
  • 说明:点击触发回调函数,入参如:
    • {obj} node 触发树的某个节点
6. parentCodeName
  • 类型:string (必填)
  • 默认值:'parentCode'
  • 说明:参照后端数据查找父节点值字段,不固定值
7. codeName
  • 类型:string (必填)
  • 默认值:'code'
  • 说明:参照后端数据查找子节点值字段,不固定值

实现Tree.js

import React from 'react';
import './Tree.scss';

/**
 * Tree                   组件所传属性描述
 * treeList               树的所有数据 arr
 * onClick                点击树的执行方法   函数
 * loadDataAction         动态加载数据   函数
 */
class Tree extends React.Component {
    constructor(props){
        super(props);
        this.handleClick      = this.handleClick.bind(this);

        this.state = {
            node: props.treeList,
        }
    }

    handleClick(e,item) {
        let Oi = e.target.parentNode.childNodes[0]
        ;
        // Oi.style.transform = Oi.style.transform == "rotate(-90deg)" ? "rotate(0deg)" : "rotate(-90deg)";
        this.digui(this.state.node,item);
        this.props.onClick && this.props.onClick(item);
        this.props.loadDataAction && this.props.loadDataAction(item);
    }

    checkAction(item){
        item.checked = !item.checked;
        this.setState({
            node : this.state.node
        });
        // console.log(item);
    }

    digui(node,item){
        let newNode = node;
        for(let i=0; i<newNode.length; i++){
            if(newNode[i].key == item.key){
                if(newNode[i].child){
                    if(item.open){
                        newNode[i].open = false;
                    }else{
                        newNode[i].open = true;
                    }
                }
            }else{
                if(newNode[i].child){
                    this.digui(newNode[i].child,item);
                }
            }
            console.log(newNode);
            this.setState({
                node : newNode
            });
        }
    }

    itemTitle(item){
        // 这个是返回title,因为有时候是点击一个链接,所以设置了两种情况,如果node节点里面有component这个节点,那就设置成可以点击跳转
        if(item.component){
            return (<Link to={ item.component } >
                <span className="" onClick={this.handleClick.bind(this)}>{item.title}</span>
            </Link>)
        }else{
            return (
                <span className="tree-span-title" onClick={(e)=>{this.handleClick(e,item)}}>{item.title}</span>
            )
        }
    }

    tree(child){
        let treeItem;
        // 如果有子元素
        if(child){
            // 子元素是数组的形式,把所有的子元素循环出来
            treeItem = child.map((item, key) => {
                // 同理,设置样式
                let itemStyle = {
                    paddingLeft: 20*parseInt(item.level.slice(5)-1)+'px',
                    position:'relative'
                };
                let wrapperStyle = {
                    left: 5*parseInt(item.level.slice(5)-1)+'px',
                };
                // 同理,设置➡️
                let iconChevron;
                if(item.child){
                    if(item.open){
                        iconChevron =  'icon icon-xiangxiazuocedaohang';
                    }else{
                        iconChevron =  'icon icon-xiangyouzuocedaohang';
                    }
                }else{
                    iconChevron = 'icon tree-icon';
                }
                return  (
                    <ul key={key}>
                        <li className={item.level} style={itemStyle}>
                            {   this.props.loadDataAction ?
                                (
                                    (!(item.child && item.child.length)) ?
                                        <i className={iconChevron} onClick={(e)=>{this.handleClick(e,item)}}></i> :
                                        (
                                            this.props.notice ?
                                                <i className="tree-empty">
                                                    <div className="wrapper" data-anim="base wrapper">
                                                        <div className="circle" data-anim="base left"></div>
                                                        <div className="circle" data-anim="base right"></div>
                                                    </div>
                                                </i> :
                                                <i className={iconChevron} onClick={(e)=>{this.handleClick(e,item)}}></i>
                                        )
                                ):
                                <i className={iconChevron} onClick={(e)=>{this.handleClick(e,item)}}></i>
                            }
                            {
                                this.props.type == 'check' &&
                                <em className={!item.checked ? "tree-checkbox" : "tree-checkbox checkedbox"} onClick={this.checkAction.bind(this,item)}>
                                    <i className={!item.checked ? "tree-i" : "tree-i checkedi"}></i>
                                </em>
                            }
                            {this.itemTitle(item)}
                        </li>
                        {/* 如果当前子元素还有子元素,就递归使用tree方法,把当前子元素的子元素渲染出来 */}
                        {item.open && this.tree(item.child)}
                    </ul>

                )
            })
        }
        return treeItem;
    }

    render() {
        return (
            <div className="tree">
                {/*{this.tree(this.state.node)}*/}
                {this.tree(this.props.treeList)}
            </div>
        );
    }
}

export default Tree;

样式这块就先不放了

「欢迎在评论区讨论」

希望看完的朋友可以给个赞,鼓励一下