React 组件封装之 Tree 树形控件

  • 一、Tree 树形结构
  • 二、使用案例
  • 三、API 使用指南
  • 四、源代码
  • 五、总结

一、Tree 树形结构

组件说明:
实现树形控件,适用于组织架构、文章列表等链表结构的功能。

效果展示:

  1. 默认样式

    2.自定义样式

    3.用于组织架构的自定义样式

二、使用案例

  1. 默认样式案例

index.js

import React from 'react';
import Tree from './Tree';
export default class MyTree extends React.Component{constructor(props){super(props);this.state = {treeData:[{title: 'parent 1',key: '0-0',children: [{title: 'parent 1-0',key: '0-0-0',children: [{title: 'leaf',key: '0-0-0-0',},{title: 'leaf',key: '0-0-0-1',},{title: 'leaf',key: '0-0-0-2',},],},{title: 'parent 1-1',key: '0-0-1',children: [{title: 'leaf',key: '0-0-1-0',},],},{title: 'parent 1-2',key: '0-0-2',children: [{title: 'leaf',key: '0-0-2-0',},{title: 'leaf',key: '0-0-2-1',},],},],},]
}}onSelect(){}render(){      const {treeData} = this.state;return (<TreetreeData={treeData}       onSelect={()=>this.onSelect()}defaultExpandedKeys={['0-0-0']}/>)}
}
  1. 自定义样式
import React from 'react';
import {Avatar} from 'antd'
import Tree from './Tree';
import defaultAvatar from '../../assets/home/default-avatar.png';
import redBadge from '../../assets/detail/red-badge.png';
import grayBadge from '../../assets/detail/grar-badge.png';
import blueBadge from '../../assets/detail/blue-badge.png';
const { TreeNode } = Tree;
function loop(arr,keys) {return arr.map((item)=>{return  <TreeNode title={item.Job_Grade?<div className="cm-flex cm-ai-c header-language-hover"><div className="cm-position-relative"><Avatar src={item.avatar?item.avatar:defaultAvatar} size={50}/><img src={changeBadge(item.badge)} alt="" className="detail-badge"/></div><div className="cm-ml-01  cm-mtb-01 cm-flex-1"><span className="cm-text-ellipsis cm-c-333 cm-fw-bold">{item[keys.name]}({item.English_Name})</span><div className="cm-c-999 cm-text-nowrap cm-fs-012 cm-lh-initial">{item.PM_Job_Classification}{item.Job_Grade}</div><div className="cm-c-999 cm-text-nowrap cm-fs-012 cm-lh-initial">{item.company}</div></div></div>: <span className="cm-text-ellipsis cm-cursor-not">{item[keys.name]}</span>} key={item[keys.id]} flag={item.Job_Grade}>{item.children&&item.children.length>0?loop.call(this,item.children,keys):null}</TreeNode>})
}
function changeBadge(badge) {if(badge === "左上"||badge === "左中"||badge === "左下"){return redBadge;}else if(badge === "中上"||badge === "中中"||badge === "中下"){return blueBadge;}else {return grayBadge;}
}
const generate = (arr,topId,keys,data=[])=>{for(let i = 0;i<arr.length;i++){let item =  arr[i];if(item[keys.parentId]===topId){data.push(item);for(let j = i+1;j<arr.length;j++){let item1 =  arr[j];if(item[keys.id] === item1[keys.parentId]){item.children = [];generate(arr,item1[keys.parentId],keys,item.children);}}}}return data;
}
class MyTree extends React.Component{constructor(props){super(props);this.state = {dataSource:[{deptId:10,deptName:"XX集团",parentDeptId:1},{deptId:11,deptName:"XXXX有限公司",parentDeptId:1},{deptId:2,deptName:"开发部",parentDeptId:10},{deptId:4,deptName:"销售部",parentDeptId:11},{deptId:5,deptName:"招聘部",parentDeptId:11},{deptId:14,deptName:"小红",parentDeptId:4,badge:"左上",English_Name:"zhu dan",Job_Grade:'P-10',PM_Job_Classification:"部门经理",company:'XXXX有限公司',},{deptId:15,deptName:"张三",parentDeptId:14,Job_Grade:'P-11',PM_Job_Classification:"部门经理",company:'XXXX有限公司',English_Name:"zhang san",badge:"中上",},{deptId:16,deptName:"李四",parentDeptId:15,Job_Grade:'P-11',PM_Job_Classification:"部门经理",badge:"右上",English_Name:"li si",company:'XXXX有限公司'},{deptId:17,deptName:"王五",parentDeptId:16,Job_Grade:'P-11',PM_Job_Classification:"XXXX有限公司",badge:"中下",English_Name:"wang wu",company:'XXXX有限公司'},],}}onSelect(){}render(){const {dataSource} = this.state;let keys = {id:"key",parentId:"parentDeptId",topId:1,name:"title"};let newData = generate(dataSource,keys.topId,keys);return (<TreeonSelect={()=>this.onSelect()}defaultExpandedKeys={['11']}>{loop.call(this,newData,keys)}</Tree>)}
}export { MyTree }

三、API 使用指南

属性 说明 类型 默认值
defaultExpandedKeys 默认展开指定的树节点 string[]
onSelect 点击某一行触发的事件 Function
treeData 链表结构的数据源 Array
key 唯一key,不可重复 Array
title 标题 string
children 子节点 Array

四、源代码

Tree.js

import React from 'react';
import arrowDownGray from '../../assets/home/arraw-down-gray.png';
import arrowUpGray from '../../assets/home/arraw-up-gray.png';
class Tree extends React.Component{constructor(props){super(props);this.state = {keys:[]}}componentWillMount(){const {defaultExpandedKeys,treeData,children} = this.props;//处理默认样式if(treeData && treeData.length>0){if(Array.isArray(defaultExpandedKeys) && defaultExpandedKeys.length>0) {treeData.map((item) => {if (defaultExpandedKeys.includes(String(item.key))) {item.isOpenChild = true;//展开子节点}});}}//处理自定义样式if(children && children.length>0){let keys = [];children.map((item)=>{keys.push(item.key)});if(Array.isArray(defaultExpandedKeys) && defaultExpandedKeys.length>0){children.map((item1)=>{if(defaultExpandedKeys.includes(item1.key)){if(Array.isArray(item1.props.children) && item1.props.children.length>0){item1.props.children.map((item2)=>{keys.push(item2.key);});}}});}this.setState({keys});}}onTrigger(e,item){e.stopPropagation();let keys = this.state.keys;if(item.props.children && item.props.children.length>0){item.props.children.map((item1)=>{//如果没有子节点就添加if(!keys.includes(item1.key)){keys.push(item1.key);}else {//否则过滤子节点keys = keys.filter(item2=>item2!=item1.key);}});}this.setState({keys})}onSelect(e,item){const {onSelect} = this.props;if(onSelect){onSelect(item);}}onTrigger1(e,item){e.stopPropagation();item.isOpenChild = !item.isOpenChild;this.setState({isRefresh:!this.state.isRefresh})}loopTree(arr){return  arr.map((item,index)=>{return <div key={index}>{<div className="cm-flex cm-jc-sb cm-c-333  cm-ai-c cm-cursor-p"><div className="cm-flex-1 cm-hover-bc-eee cm-p-01" onClick={(e)=>this.onSelect(e,item)}>{item.title}</div>{item.children?<div onClick={(e)=>this.onTrigger1(e,item)} className="cm-ml-01 cm-p-005"><img src={item.isOpenChild?arrowUpGray:arrowDownGray}className="cm-img-01" alt="" /></div> :null}</div>}{item.children&&item.children.length&&item.isOpenChild?<div className={item.isOpenChild?"cm-display-block ":"cm-display-none"}><div className="cm-ml-02" >{this.loopTree(item.children)}</div></div> :null}</div>})}loopChild(arr){const {keys} = this.state;return  arr.map((item,index)=>{return <div key={index} className={keys.includes(item.key)?"cm-display-block":"cm-display-none"}>{<div className="cm-flex cm-jc-sb cm-p-01 cm-ai-c cm-cursor-p"><div className="cm-flex-1" onClick={(e)=>this.onSelect(e,item)}>{item}</div>{item.props.children?<div onClick={(e)=>this.onTrigger(e,item)} className="cm-ml-01 cm-p-005"><img src={keys.includes(item.props.children[0].key)?arrowUpGray:arrowDownGray}className="cm-img-01" alt="" /></div> :null}</div>}{item.props.children&&item.props.children.length?<div className="cm-ml-02">{this.loopChild(item.props.children)}</div>:null}</div>})}render(){const {treeData,children} = this.props;return (<>{treeData && treeData.length>0?this.loopTree(treeData):this.loopChild(children)}</>)}
}
class TreeNode extends React.Component{constructor(props){super(props);}render(){const {title} = this.props;return  <div className="cm-c-333">{title}</div>}
}
Tree.TreeNode = TreeNode;
export default Tree;

五、总结

树形控件分为默认控件和自定义控件。

  1. 默认控件

【用户】
只需要把数据源处理成链表结构即可。

【处理方式】
使用递归将子节点一层层铺开。初始铺开的是第一层节点,然后通过点击向上/向下箭头,分别展开/收起子节点。实现的逻辑是给对象添加一个布尔类型的 isOpenChild 属性,然后通过 setState 更新页面。

  1. 自定义控件

【用户】
用户通过递归 TreeNode 子节点,可自定义 title 属性,实现自定义列表样式。

【处理方式】

自定义控件主要是将 title 属性暴露给了用户,但是逻辑控制还是通过组件来完成。

由于自定义样式接收的是虚拟 DOM 结构,所以没法像默认控件那样通过改变对象的属性来展开/收起子节点,所以需要一个 keys 集合。

初始时将第一层节点的 key 添加到 keys,然后通过点击向上/向下箭头,分别添加/过滤子节点 key,子节点根据 key 是否存在于 keys 来判断是否展开/收起。

React 组件封装之 Tree 树形控件相关推荐

  1. 理解Vue递归组件,实现Tree树形控件实例~

    思考了两天时间,准备仿照ant-design-vue实现一个基于vue的树形控件.主要用到了vue递归组件思想.input的CheckBox类型输入框的使用. 需求 能够将传入的Json数据生成树形目 ...

  2. vue 树形控件可编辑_vue.js element-ui组件改iview 第一期 tree树形控件

    此为第一期修改,后期也适配了其他组件,更多查看我得文章 element-ui组件的tree树形控件修改源码改为iview组件 实现原理 修改了element-ui源码,把源码里面的tree模块提取出来 ...

  3. vue+element tree(树形控件数据格式)组件(1)

    vue+element tree(树形控件数据格式)组件(1), 最近做了第一个组内可以使用的组件,虽然是最简版,也废了不少力.各位前辈帮我解决问题,才勉强搞定.让我来记录这个树形组件的编写过程和期间 ...

  4. vue+element-ui之tree树形控件有关子节点和父节点之间的各种选中关系详解

    做后端管理系统,永远是最蛋疼.最复杂也最欠揍的事情,也永远是前端开发人员最苦逼.最无奈也最尿性的时刻.蛋疼的是需求变幻无穷,如同二师兄的三十六般变化:复杂的是开发难度寸步难行,如同蜀道难,难于上青天: ...

  5. Element ui tree树形控件获取当前节点id和父节点id

    低版本Element ui tree树形控件获取当前节点id和父节点id的方法:点击查看 最新版本Element ui tree树形控件获取当前节点id和父节点id教程: 1.找到node_modul ...

  6. 《实用VC编程之玩转控件》第15课:Tree树形控件

    本文转载自:VC驿站 https://www.cctry.com/thread-297465-1-1.html 1.控件简介: Tree树形控件也是我们编程过程中比较常用的一个控件,而且在其他软件中也 ...

  7. elementui tree获取父节点_vue_elementUI_ tree树形控件 获取选中的父节点ID

    一,  vue_elementUI_ tree树形控件 1.1默认点击tree节点的第一个(注意不是checked选中) :expand-on-click-node="false" ...

  8. Element Tree 树形控件自定义显示样式与hover事件绑定实现添加、删除和修改

    Element Tree 树形控件自定义显示样式与hover事件绑定实现添加.删除和修改 最近在搞erp项目对应后台的管理功能,为了加速开发使用了 vue-element-admin 开发,使用的是e ...

  9. Element-UI实现Tree 树形控件节点添加图标

    Element-UI实现Tree 树形控件节点添加图标: 属于自定义节点内容,可以通过两种方法进行树节点内容的自定义:render-content 和  scoped slot. 1.scoped s ...

最新文章

  1. Nature重磅:管轶等发现穿山甲是SARS-CoV-2的中间宿主
  2. 从算法+数据结构到MVC
  3. nginx启动报错:Job for nginx.service failed. See 'systemctl status nginx.service' and 'journalctl -xn' fo
  4. sdut 活动选择问题
  5. 查看oracle 锁定用户名,oracle用户名被锁定
  6. 美国空军学习编程,为五角大楼节省了上百万!
  7. JavaScript高级使用(一)--参数Arguments对象
  8. DB9串口和RJ45串口
  9. mysql unzip下载_zip unzip 命令
  10. 【烈日炎炎战后端】Redis(6.1万字)
  11. idea 怎么快速创建类的快捷键_Idea 常用快捷键整理
  12. 400错误,The server cannot or will not process the request due to something that is perceived to be a c
  13. html文字发光效果,css文字发光效果
  14. 史上最全maven教程
  15. android minui fb显示相关函数
  16. 灰狼优化算法训练多层感知神经网络研究(Matlab代码实现)
  17. Alibaba Cloud Toolkit —— 项目一键部署工具
  18. 红帽linux挂载光盘_linux挂载光盘
  19. GIT面试题——分支
  20. 学生HTML个人网页作业作品 HTML+CSS贝聿铭人物介绍 6页 (1)

热门文章

  1. 概率 插空法和捆绑法
  2. 高中计算机听课总结,信息技术优质课听课心得体会
  3. 一级建造师考试报名需要的材料,点击收藏!
  4. 测绘专业计算机二级,测绘本科毕业,几年具有考二级注册结构师资格?
  5. 2019亚洲蓝牙大会成果盘点
  6. 数学建模论文写作学习——数模论文概述
  7. 嵌入式设备上如何通过端口号定位到进程
  8. 【手把手制作三阶魔方模拟器】用MATLAB让你的魔方动起来
  9. 个人邮箱|如何群发邮件?3秒教你搞定
  10. ECC有关DER文件的解析(Java)