• react树状组件


    最近在react项目中需要一个树状组件,但是又不想因为这个去引入一套UI组件,故自己封装了一个基于react的树状组件,

    个人认为比较难得部分在于数据的处理,话不多说直接上代码:

    下面是tree.js

    import React, {Component} from 'react';
    import './tree.css';
    import Stack from '../utils/util';
    
    class Tree extends Component {
      constructor(props) {
        super(props)
        this.state = {
          treeData: {},
          treeArray: [],
          treeObj: {},
          type: 'tree',
          parentId: 'pid',
          id: 'id',
          value: 'value',
          label: 'label',
          children: 'children',
          checkBox: false
        }
        this.checkMap = {
          2: 'checked',
          1: 'partChecked',
          0: ''
        }
      }
    
      componentWillMount() {
        if (this.props.config.type.toLowerCase() === 'tree') {
          this.setState({
            treeData: this.props.treeData,
            ...this.props.config
          })
        } else {
          this.setState({
            treeArray: this.props.treeData,
            ...this.props.config
          })
        }
      }
    
      componentDidMount() {
        if (this.state.type.toLowerCase() !== 'tree') {
          this.factoryArrayData()
        } else {
          this.factoryTreeData()
        }
      }
    
      componentDidUpdate() {
    
      }
    
      componentWillUnmount() {
    
      }
    
      factoryArrayData() {
        let data = this.state.treeArray, obj = {}, rootId = null;
        data.map((v, i) => {
          if (v[this.state.parentId] || v[this.state.parentId] === 0) {
            if (obj[v[this.state.parentId]]) {
              if (obj[v[this.state.parentId]].children) {
                obj[v[this.state.parentId]].children.push(v)
              } else {
                obj[v[this.state.parentId]].children = [v]
              }
            } else {
              obj[v[this.state.parentId]] = {
                children: [v]
              }
            }
          } else {
            rootId = v[this.state.id]
          }
          if (obj[v[this.state.id]]) {
            v.children = obj[v[this.state.id]].children
          }
          obj[v[this.state.id]] = v
        })
        this.setState({
          treeData: obj[rootId],
          treeObj: obj
        })
      }
    
      factoryTreeData() {
        let data = this.state.treeData
        let stack = new Stack();
        let obj = {};
        stack.push(data);
        while (stack.top) {
          let node = stack.pop();
          for (let i in node.children) {
            stack.push(node.children[i])
          }
          obj[node[this.state.id]] = node
        }
        this.setState({
          treeObj: obj
        })
      }
    
      openNode (e, data) {
        if (e.stopPropagation) {
          e.stopPropagation();
        } else {
          window.event.cancelBubble = true;
        }
        data.open = !data.open
        this.forceUpdate()
      }
    
      selectNode (e, data) {
        if (e.stopPropagation) {
          e.stopPropagation();
        } else {
          window.event.cancelBubble = true;
        }
        this.setState({
          selectVal: data[this.state.value]
        }, () => {
          if (this.props.nodeClick) {
            this.props.nodeClick(data[this.state.value])
          }
        })
      }
    
      selectCheckBox (e, data) {
        if (e.stopPropagation) {
          e.stopPropagation();
        } else {
          window.event.cancelBubble = true;
        }
        let check = data.checked
        if (data.children && data.children.length) {
          let stack = new Stack();
          stack.push(data);
          while(stack.top) {
            let node = stack.pop()
            for (let i in node.children) {
              stack.push(node.children[i])
            }
            if (check === 2) {
              node.checked = 0;
            } else {
              node.checked = 2
            }
          }
        } else {
          if (check === 2) {
            data.checked = 0;
          } else {
            data.checked = 2
          }
        }
        if (data[this.state.parentId] || data[this.state.parentId] === 0) {
          this.updateParentNode(data)
        } else {
          this.forceUpdate()
          if (this.props.selectChange) {
            this.getCheckedItems()
          }
        }
      }
    
      updateParentNode (data) {
        let par = this.state.treeObj[data[this.state.parentId]], checkLen = 0, partChecked = false;
        for (let i in par.children) {
          if (par.children[i].checked === 2) {
            checkLen++;
          } else if (par.children[i].checked === 1) {
            partChecked = true;
            break;
          }
        }
        if (checkLen === par.children.length) {
          par.checked = 2
        } else if (partChecked || (checkLen < par.children.length && checkLen > 0)) {
          par.checked = 1;
        } else {
          par.checked = 0;
        }
        if (this.state.treeObj[par[this.state.parentId]] || this.state.treeObj[par[this.state.parentId]] == 0) {
          this.updateParentNode(par)
        } else {
          this.forceUpdate()
          if (this.props.selectChange) {
            this.getCheckedItems()
          }
        }
      }
    
      getCheckedItems() {
        let stack = new Stack ();
        let checkedArr = [];
        stack.push(this.state.treeData);
        while (stack.top) {
          let node = stack.pop();
          for (let i in node.children) {
            stack.push(node.children[i])
          }
          if (node.checked === 2) {
            checkedArr.push(node[this.state.value])
          }
        }
        this.props.selectChange(checkedArr)
      }
    
      renderTreeParent() {
        let data = this.state.treeData
        return (
          <div className={`parentNode childNode ${data.open?'open':'close'} ${data.children && data.children.length?'':'noChildren'}`}>
            <span onClick={(e) => this.openNode(e, data)} className="openNode"></span>
            {
              this.state.checkBox?
                <div className={`checkBox ${this.checkMap[data.checked]}`} onClick={(e) => this.selectCheckBox(e, data)}></div>:
                <div className="fileBox">
                  <img src="./images/file-icon.png" alt=""/>
                </div>
            }
            <div className={`nodeName ${this.state.selectVal === data[this.state.value]?'active':''}`} onClick={(e) => this.selectNode(e, data)}>
              {data[this.state.label]}
            </div>
            {
              this.state.treeData.children ?
                <div className="childList">
                  {this.renderTreeNode(data)}
                </div> : null
            }
          </div>
        )
      }
    
      renderTreeNode(data) {
        return data.children.map((val, ind) => {
          return (
            <div key={ind} className={`childNode ${val.open?'open':'close'} ${val.children && val.children.length?'':'noChildren'}`}>
              <span onClick={(e) => this.openNode(e, val)} className="openNode"></span>
              {
                this.state.checkBox?
                  <div className={`checkBox ${this.checkMap[val.checked]}`} onClick={(e) => this.selectCheckBox(e, val)}></div>:
                  <div className="fileBox">
                    <img src="./images/file-icon.png" alt=""/>
                  </div>
              }
              {ind === data.children.length - 1?
                  <span className="lastNode"></span>:null
              }
              <div className={`nodeName ${this.state.selectVal === val[this.state.value]?'active':''}`} onClick={(e) => this.selectNode(e, val)}>
                {val[this.state.label]}
              </div>
              {
                val.children ?
                  <div className="childList">
                    {this.renderTreeNode(val)}
                  </div> : null
              }
            </div>
          )
        })
      }
    
      render() {
        return (
          <div className="tree">
            {this.renderTreeParent()}
          </div>
        )
      }
    }
    
    export default Tree

    下面是tree.css

    .tree {
      text-align: left;
    }
    .tree .childNode {
      padding-left: 20px;
      position: relative;
      background-color: #ffffff;
      z-index: 1;
    }
    .tree .childNode .checkBox {
      position: absolute;
      width: 16px;
      left: 20px;
      top: 0;
      z-index: 2;
      margin: 7px 10px 0;
      height: 16px;
      box-sizing: border-box;
      border: 1px solid #d2d2d2;
      vertical-align: text-bottom;
      font-size: 0;
      border-radius: 2px;
      cursor: pointer;
    }
    .tree .childNode .checkBox:hover {
      cursor: pointer;
      border-color: #5bb976;
    }
    .tree .childNode .checkBox.checked {
      border: 0;
      background: url(../images/icon-check-green.png) no-repeat center center;
      background-size: 100% 100%;
      background: none9;
      filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='./images/icon-check-green.png', sizingMethod='scale') 9;
    }
    .tree .childNode .checkBox.partChecked {
      border: 0;
      background: url(../images/part-checked.png) no-repeat center center;
      background-size: 100% 100%;
      background: none9;
      filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='./images/part-checked.png', sizingMethod='scale') 9;
    }
    .tree .childNode .nodeName {
      padding-left: 36px;
      font-size: 14px;
      color: #333333;
      white-space: nowrap;
      overflow: hidden;
      line-height: 30px;
      height: 30px;
      text-overflow: ellipsis;
      position: relative;
      z-index: 1;
      display: inline-block;
      padding-right: 10px;
    }
    .tree .childNode .nodeName.active {
      background-color: #DEF1FF;
    }
    .tree .childNode .nodeName:hover {
      text-decoration: underline;
      cursor: pointer;
    }
    .tree .childNode.open .openNode {
      background: url(../images/department-close.png) no-repeat center center;
      background-size: 100% 100%;
      background: none9;
      filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='./images/department-close.png', sizingMethod='scale') 9;
    }
    .tree .childNode.open .childList {
      display: block;
    }
    .tree .childNode.close .openNode {
      background: url(../images/depart-open.png) no-repeat center center;
      background-size: 100% 100%;
      background: none9;
      filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='./images/depart-open.png', sizingMethod='scale') 9;
    }
    .tree .childNode.close .childList {
      display: none;
    }
    .tree .childNode .fileBox {
      position: absolute;
      width: 16px;
      left: 20px;
      top: 0;
      margin: 5px 10px 0;
      z-index: 2;
    }
    .tree .childNode .fileBox:hover {
      cursor: pointer;
    }
    .tree .childNode .fileBox img {
      width: 16px;
    }
    .tree .childNode:before {
      position: absolute;
      left: -13px;
      top: 15px;
      width: 20px;
      height: 100%;
      border-top: 1px solid #CFCFCF;
      border-right: 1px solid #CFCFCF;
      content: '';
      z-index: 1;
    }
    .tree .childNode:after {
      position: absolute;
      bottom: -12px;
      left: 7px;
      width: 1px;
      height: 30px;
      z-index: 3;
      background-color: #ffffff;
      content: '';
    }
    .tree .childNode.parentNode:before {
      border-top: none;
    }
    .tree .childNode .openNode {
      position: absolute;
      z-index: 5;
      left: 0;
      top: 8px;
      width: 14px;
      height: 14px;
    }
    .tree .childNode .openNode:hover {
      cursor: pointer;
    }
    .tree .childNode.noChildren .openNode {
      width: 10px;
      height: 10px;
      top: 10px;
      left: 7px;
      background: url(../images/no-child.png) no-repeat center center;
      background-size: 100% 100%;
      background: none9;
      filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='./images/no-child.png', sizingMethod='scale') 9;
    }
    .tree .childNode.noChildren .openNode:hover {
      cursor: default;
    }
    .tree .childNode .lastNode {
      position: absolute;
      bottom: -15px;
      left: -13px;
      width: 1px;
      height: 100%;
      z-index: 4;
      background-color: #ffffff;
    }

    utils里面是封装了一个stack栈,关于js栈的使用请移步js遍历树状数据文章。

    github项目地址

  • 相关阅读:
    SpringBoot结合ShardingSphere实现分库分表、读写分离
    SpringBoot结合ShardingSphere实现主从读写分离
    使用Sentinel实现Spring Cloud Gateway网关流量控制
    使用Sentinel实现热点参数限流
    对比学习UIKit和AppKit--入门级
    UIViewController
    C++的异常处理之一:throw是个一无是处的东西
    About Closure
    理解Objective C 中id
    关于文件压缩的一些小知识
  • 原文地址:https://www.cnblogs.com/marvey/p/10796175.html
Copyright © 2020-2023  润新知