最近在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:;
}
.tree .childNode .checkBox {
position: absolute;
width: 16px;
left: 20px;
top:;
z-index:;
margin: 7px 10px 0;
height: 16px;
box-sizing: border-box;
border: 1px solid #d2d2d2;
vertical-align: text-bottom;
font-size:;
border-radius: 2px;
cursor: pointer;
}
.tree .childNode .checkBox:hover {
cursor: pointer;
border-color: #5bb976;
}
.tree .childNode .checkBox.checked {
border:;
background: url(../images/icon-check-green.png) no-repeat center center;
background-size: 100% 100%;
background: none\9;
filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='./images/icon-check-green.png', sizingMethod='scale') \9;
}
.tree .childNode .checkBox.partChecked {
border:;
background: url(../images/part-checked.png) no-repeat center center;
background-size: 100% 100%;
background: none\9;
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:;
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: none\9;
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: none\9;
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:;
margin: 5px 10px 0;
z-index:;
}
.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:;
}
.tree .childNode:after {
position: absolute;
bottom: -12px;
left: 7px;
width: 1px;
height: 30px;
z-index:;
background-color: #ffffff;
content: '';
}
.tree .childNode.parentNode:before {
border-top: none;
}
.tree .childNode .openNode {
position: absolute;
z-index:;
left:;
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: none\9;
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:;
background-color: #ffffff;
}

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

github项目地址

react树状组件的更多相关文章

  1. Java Swing 树状组件JTree的使用方法(转)

    树中特定的节点可以由 TreePath(封装节点及其所有祖先的对象)标识,或由其显示行(其中显示区域中的每一行都显示一个节点)标识.展开 节点是一个非叶节点(由返回 false 的 TreeModel ...

  2. react 使用antd的TreeSelect树选择组件实现多个树选择循环

    需求说明,一个帐号角色可以设置管理多个项目的菜单权限 且菜单接口每次只能查询特定项目的菜单数据[无法查全部] 开发思路: 1,获取项目接口数组,得到项目数据 2,循环项目数据,以此为参数递归查询菜单数 ...

  3. 聊聊React高阶组件(Higher-Order Components)

    使用 react已经有不短的时间了,最近看到关于 react高阶组件的一篇文章,看了之后顿时眼前一亮,对于我这种还在新手村晃荡.一切朝着打怪升级看齐的小喽啰来说,像这种难度不是太高同时门槛也不是那么低 ...

  4. EasyUI + ajax + treegrid/datagrid 接收 json 数据,显示树状/网状表结构

    最后一更了,时间间隔有点久了~~ EasyUI作为一个成熟的前端框架,封装了ajax,对于数据的处理配合datagrid组件的使用,使其非常适合后台管理界面的开发(目前来说界面有点过时了). 通过aj ...

  5. vue 树状图数据的循环 递归循环

    在main.js中注册一个子组件 在父组件中引用 树状图的数据格式 绑定一个数据传入子组件,子组件props接收数据 子组件中循环调用组件,就实现了递归循环

  6. Android中的树状(tree)列表

    树状列表前端挺常用的,还有人专门写过Ztree,Android中有的时候也需要使用到树状列表,上篇文章写了一下ExpandableListView,ExpandableListView最多支持两级结构 ...

  7. Tkinter 之TreeView表格与树状标签

    一.TreeView介绍 TreeView组件是一个树状结构和表格的结合体.第一列是树状结构,后几列是列表.每一行表示一个item,树的item可以分级,每个item有子item,名称对应text标签 ...

  8. 手把手教学~基于element封装tree树状下拉框

    在日常项目开发中,树状下拉框的需求还是比较常见的,但是element并没有这种组件以供使用.在这里,小编就基于element如何封装一个树状下拉框做个详细的介绍. 通过这篇文章,你可以了解学习到一个树 ...

  9. jquery-treegrid树状表格的使用(.Net平台)

    上一篇介绍了DataTable,这一篇在DT的基础之上再使用jquery的一款插件:treegrid,官网地址:http://maxazan.github.io/jquery-treegrid/ 一. ...

随机推荐

  1. 浏览器Quirksmode(怪异模式)与标准模式

    由于历史的原因,各个浏览器在对页面的渲染上存在差异,甚至同一浏览器在不同版本中,对页面的渲染也不同.在W3C标准出台以前,浏览器在对页面的渲染上没有统一规范,产生了差异(Quirks mode或者称为 ...

  2. [USACO1.4]等差数列 Arithmetic Progressions

    题目描述 一个等差数列是一个能表示成a, a+b, a+2b,..., a+nb (n=0,1,2,3,...)的数列. 在这个问题中a是一个非负的整数,b是正整数.写一个程序来找出在双平方数集合(双 ...

  3. 调试JMETER脚本的5种方法

    如果你曾经设计过JMeter脚本,我敢打赌你至少有一次弄清楚Json Extractor无法正常工作的原因.你猜怎么着?我去过那儿! 你知道为什么最好的JMeter Performance Engin ...

  4. Django 01 django基本介绍及环境搭建

    Django 01 django基本介绍及环境搭建 #http服务器 #用来接收用户请求,并将请求转发给web应用框架进行处理 #Web应用框架 #处理完请求后在发送给http服务器,http服务器在 ...

  5. 转 mysql 下载 以及安装

    https://blog.csdn.net/kerafan/article/details/78001849 一.MySQL 各个版本区别 ,(都需要编译的 ) 1.MySQL Community S ...

  6. (转)CentOS 7常见的基础命令和配置

    CentOS 7常见的基础命令和配置 原文:http://blog.51cto.com/hujiangtao/1973566 管理服务 命令格式:systemctl COMMAND name.serv ...

  7. 利用rand7()构造rand10()

    题意 已知有个rand7()的函数,返回1到7随机自然数,让利用这个rand7()构造rand10() 随机1~10 参考代码 int rand7() { srand((int)time(NULL)) ...

  8. Java基础重点指南

    Java基础重点指南 每个人都有自己的学习方法,下面是我学完Java后总结的一些要点: 一:(了解)Java诞生于1995年,由Sun公司开发.它把源代码编译成“中间码”,然后通过“Java虚拟机”把 ...

  9. 动态页面技术----JSP技术

    1995年java诞生,没有jsp,只有Servlet, Servlet开发:Servlet上嵌套html代码,非常繁琐. 1998年,出现jsp,方便写html代码,并且可以在html代码中嵌套ja ...

  10. C# 连接 postgres失败

    小程序完成了,要发布到客户的服务器上: 我的测试机:win10  64             数据库:PG9.6.5     64 客户服务器:win7    32             数据库: ...