rct-tree
Version:
A tree component based on ReactJS and Typescript.
263 lines (258 loc) • 10.4 kB
text/typescript
import { ITreeNodeParams } from './type'
/**
* @description class Tree 是一个 tree 内置的构造器,里面主要存一些操作 Tree 组件的工具类
* @author jazzyXie
*/
class Tree {
public flatData: ITreeNodeParams[] = []
public idCounter: number = 0 // 记录历史上所存在的所有 id 数量
constructor(treeData: ITreeNodeParams[]) {
this.PreProcessTreeToFlat(treeData)
}
/**
* @description processTreeData 用来预处理数据,将用户传进来的数据转换成扁平的一维数据
* @param { ITreeNodeParams } treeData - 树形数据
* @returns 没有返回值
*/
public PreProcessTreeToFlat = (treeData: ITreeNodeParams[], parent_id?: number) => {
treeData.map((el: ITreeNodeParams) => {
el.id = ++this.idCounter // 为每一项数据添加id号
el.halfChecked = false // 默认半选为 false ,halfChecked 触发的前置条件是 checked 为 false
el.selected = false // 默认 title 不被选中,选中则置为 true
el.hide = false
if (parent_id) {
el.parent_id = parent_id
} else {
el.parent_id = -1 // 最外层元素的parent_id为-1
}
this.flatData.push(el)
if (el.children && el.children.length > 0) {
this.PreProcessTreeToFlat(el.children, el.id)
}
})
}
/**
* @description getNodeByID 根据节点的 id 查找节点
* @param { number } id - 节点的id
* @returns { ITreeNodeParams } node - filter 返回一个新的数组,这里取数组的下标值为 0 的第一项;
* 当 id 为 -1 ,这种情况触发的情况是 parent_id 为 -1 ,不存在其父节点
*/
public getNodeByID = (id: number) => {
if (id === -1) {
return {}
}
return this.flatData.filter((node: ITreeNodeParams, index: number) => {
if (id == node.id) {
return node
}
})[0]
}
/**
* @description getSelectedNodes 获取所有选中的数据
* @param { boolean } [selectedFlag] selected 字段为 true/false
* @returns { ITreeNodeParams[] } [ el, el, el, ...] 根据参数 selectedFlag 返回 selected 字段为 true/false的数据,无传入参数,默认返回selected 字段为 true 的数据
*/
public getSelectedNodes(selectedFlag?: boolean) {
if (arguments.length == 0 || arguments[0] === undefined) {
selectedFlag = true
}
if (selectedFlag === true) {
return this.flatData.filter((el: ITreeNodeParams) => {
return el.selected
})
} else if (selectedFlag === false) {
return this.flatData.filter((el: ITreeNodeParams) => {
return !el.selected
})
} else {
throw new TypeError(`Function getSelectedNodes accepts param type boolean only ~`)
}
}
/**
* @description getCheckedNodes 获取所有选中的数据
* @param { boolean } [checkedFlag] checked 字段为 true/false
* @returns { ITreeNodeParams[] } [ el, el, el, ...] 根据参数 checkedFlag 返回 checked 字段为 true/false 的数据,无传入参数,默认返回 checked 字段为 true 的数据
*/
public getCheckedNodes(checkedFlag?: boolean) {
if (arguments.length == 0 || arguments[0] === undefined) {
checkedFlag = true
}
if (checkedFlag === true) {
return this.flatData.filter((el: ITreeNodeParams) => {
return el.checked
})
} else if (checkedFlag === false) {
return this.flatData.filter((el: ITreeNodeParams) => {
return !el.checked
})
} else {
throw new TypeError(`Function getCheckedNodes accepts param type boolean only ~`)
}
}
public getExpandNodes(expandFlag?: boolean) {
this.flatData.forEach((el: ITreeNodeParams) => { })
}
/**
* @description checkedAll 该方法将设置节点的 checked 字段 全为 true/false (全选/反选)
* @param { boolean } [checkedFlag] checked 字段设置为 true/false
*/
public checkedAll(checkedFlag?: boolean) {
if (arguments.length == 0 || arguments[0] === undefined) {
checkedFlag = true
}
if (checkedFlag === true) {
this.flatData.map((el: ITreeNodeParams) => {
el.checked = true
})
} else if (checkedFlag === false) {
this.flatData.map((el: ITreeNodeParams) => {
el.checked = false
})
} else {
throw new TypeError(`Function checkedAll accepts param type boolean only ~`)
}
}
/**
* @description getIndexByID 根据节点的 id 查找节点
* @param { number } id - 节点的id
* @returns { number } idx 返回节点的索引
*/
public getIndexByID = (id: number) => {
let idx = -1 // 下标初始值为-1
this.flatData.forEach((item, index) => {
if (item.id == id) {
idx = index
}
})
return idx
}
/**
* @description 收缩联动子节点
*/
toggleLinkage = (currentNode: ITreeNodeParams) => {
let childList: number[] = []
function pushNode(node: ITreeNodeParams) {
node && node.children && node.children.forEach((el: any) => {
childList.push(el.id)
if (el.children && el.children.length) {
pushNode(el)
}
})
}
pushNode(currentNode)
return childList
}
/**
* @description 联动父子节点
*/
public nodeLinkage = (currentNode: ITreeNodeParams) => {
this.childrenNodeLinkage(currentNode) // 联动子节点
this.parentNodeLinkage(currentNode) // 联动父节点
}
/**
* @description 联动子节点,子节点状态跟父节点状态保持一致
*/
public childrenNodeLinkage = (currentNode: ITreeNodeParams) => {
if (currentNode.children && currentNode.children.length) {
currentNode.children.map((child: ITreeNodeParams) => {
child.checked = currentNode.checked
if (!currentNode.checked) {
child.halfChecked = false
}
this.childrenNodeLinkage(child)
})
}
}
/**
* @description 联动父节点
*/
public parentNodeLinkage = (currentNode: ITreeNodeParams) => {
let isCheckedAll: boolean
let isCheckedNull: boolean
const _this = this
// let { treeConstructor } = this.state
// let { getNodeByID } = treeConstructor
const parent_id = currentNode.parent_id
if (parent_id) {
// 有父节点
const parentNode = _this.getNodeByID(parent_id)
// 父节点有 children
if (parentNode && parentNode.children) {
// isCheckedAll 表示当前节点的直属父节点的所有子节点是否全都为选中状态
isCheckedAll = parentNode.children.every((child: any) => {
return child.checked == true
})
// isCheckedNull 表示当前节点的直属父节点的所有子节点是否全都为未选中状态
isCheckedNull = parentNode.children.every((child: any) => {
return child.checked == false
})
let parentAll,
parentNull
// 此处利用了立即执行函数形成闭包 引用了 parentNodeLinkage 上的全局变量 isCheckedAll 和 isCheckedNull
// isCheckedAll 在 recursionChangeParentNode 上 用于判断 是否 将当前节点的所有父节点都置为选中状态
// isCheckedNull 在 recursionChangeParentNode 上 用于判断 是否 将当前节点的所有父节点都置为为选中状态
; (function recursionChangeParentNode(isAll: boolean, isNull: boolean, currentNode: ITreeNodeParams) {
if (currentNode.parent_id) {
const parentNode = _this.getNodeByID(currentNode.parent_id)
const hasParent = parentNode && JSON.stringify(parentNode) != '{}' ? true : false
if (hasParent && parentNode.children) {
// parentAll 表示当前遍历的节点的父节点的所有子节点是否都为选中状态
parentAll = parentNode.children.every((child: any) => {
return child.checked == true
})
// parentNull 表示当前遍历的节点的父节点的所有子节点是否都为未选中状态
// !parentNull 表示半选状态
parentNull = parentNode.children.every((child: any) => {
return child.checked == false && child.halfChecked == false
})
// 当前节点所在层级的所有节点均为选中状态
if (isAll) {
// 当前节点的父节点为选中状态,其所有父节点都置为选中状态
if (isAll == parentAll) {
parentNode.checked = true
parentNode.halfChecked = false
} else {
// 当前节点的任意一个父节点链上有任意一个父节点不为选中状态,将该节点置为半选状态
parentNode.checked = false
parentNode.halfChecked = true
}
} else {
// 此处为非全选中状态
parentNode.checked = false
parentNode.halfChecked = false
// 为半选状态
if (!parentNull) {
parentNode.halfChecked = true
}
}
recursionChangeParentNode(isAll, isNull, parentNode)
}
}
})(isCheckedAll, isCheckedNull, currentNode)
}
}
}
/**
* @description 每次更新 flatData 的时候,在视图渲染的时候都会重新构建一棵树
*/
public rebuildTree = (visibleData: ITreeNodeParams[]) => {
const map: any = {}
const tree: any = []
visibleData.forEach((el: any) => {
if (el.children) {
delete el.children
}
map[el.id] = el
const parent = map[el.parent_id]
if (parent) {
// 有父节点,则push到父节点children字段,此处children字段默认为空数组
; (parent.children || (parent.children = [])).push(el)
} else {
// 没有父节点,则直接push到tree最外层
tree.push(el)
}
})
return tree
}
}
export default Tree