前言:
最近再用Tree组件做项目,记录一下使用方法。
一、预期的效果
antd-tree带搜索框的示例(官网):官网只是标红
我预期的效果是搜索并且标红,只保留包含搜索的值。
二、antd-tree组件带搜索框的使用及踩坑(只保留搜索的内容并且标红)
一、方案一
这个类似于官网的例子,但官网的例子,不是很清晰,代码很多。
import React, { Component } from 'react';
import { Tree, Input } from 'antd';
import styles from '../../assets/index.css';
const { TreeNode } = Tree;
const { Search } = Input;
class Index extends Component {
constructor(props) {
super(props)
this.state = {
data: [
{
title: "十三中学",
key: 1,
children: [
{
title: "初一年级",
key: 2,
children: [
{
title: "一班",
key: 3,
},
{
title: "二班",
key: 4,
}
]
},
{
title: "初二年级",
key: 20,
}
]
},
{
title: "八十中学",
key: 5,
children: [
{
title: "初一年级",
key: 6,
children: [
{
title: "一班",
key: 7,
children: [
]
},
{
title: "二班",
key: 8,
children: [
{
title: "一组",
key: 9,
children: []
},
{
title: "vv组",
key: 999,
children: []
}
]
}
]
},
{
title: "初二年级",
key: 55,
children: []
}
]
}
],
expandedKeys: [], //展开的key值
autoExpandParent: true,
checkedKeys: [], //选中的key,
searchValue: '',
searchTreeKey: [] //搜索得key
}
}
//给子级添加父级Key
addParentKeyWrapper = (tree) => {
//深度克隆
const data = JSON.parse(JSON.stringify(tree));
function addParentKey(data, parentKey) {
data.forEach(ele => {
const { children, key } = ele;
ele.parent_key = parentKey;
if (children) {//如果唯一标识不是code可以自行改变
addParentKey(children, key)
}
})
}
addParentKey(data, null); //一开始为null,根节点没有父级
return data;
}
componentDidMount() {
this.expandedKeysFn();
this.setState({
data: this.addParentKeyWrapper(this.state.data) //调用添加父元素key
})
}
onChange = (e) => { //search变化
const { value } = e.target;
console.log(value)
const dataList = [];
let searchElement = [];
let allParentkey = [];
if (value) {
const generateList = data => { //tree树片扁平化
for (let i = 0; i < data.length; i++) {
const node = data[i];
const { key, title, parent_key } = node;
dataList.push({ key, title: title, parent_key: parent_key });
if (node.children) {
generateList(node.children);
}
}
};
generateList(this.state.data);
const getParentKey = (key, tree) => { //获取父元素可以
let parentKey;
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (node.children) {
if (node.children.some(item => item.key === key)) {
parentKey = node.key;
} else if (getParentKey(key, node.children)) {
parentKey = getParentKey(key, node.children);
}
}
}
return parentKey;
};
//上级元素
const getParentElementKey = (searchElement, dataList) => {
for (let i = 0; i < searchElement.length; i++) {
for (let j = 0; j < dataList.length; j++) {
if (searchElement[i] == dataList[j].key) {
allParentkey.push(dataList[j].key);
getParentElementKey([dataList[j].parent_key], dataList);
}
}
}
}
const expandedKeys = dataList
.map(item => {
if (item.title.indexOf(value) > -1) {
searchElement.push(item.key);
return getParentKey(item.key, this.state.data);
}
return null;
})
.filter((item, i, self) => item && self.indexOf(item) === i);
getParentElementKey(searchElement, dataList);
this.setState({
expandedKeys: expandedKeys,
searchValue: value,
autoExpandParent: true,
searchTreeKey: allParentkey
})
} else {
this.expandedKeysFn() //重置展开key
this.setState({
searchValue: value,
autoExpandParent: true,
searchTreeKey: allParentkey
})
}
}
renderTreeNode = (data) => { //生成树结构函数
if (data.length == 0) {
return
}
let { expandedKeys, searchValue, searchTreeKey } = this.state;
return data.map((item) => {
const index = item.title.indexOf(searchValue);
const beforeStr = item.title.substr(0, index);
const afterStr = item.title.substr(index + searchValue.length);
const title =
index > -1 ? (
<span>
{beforeStr}
<span style={{ color: "red" }}>{searchValue}</span>
{afterStr}
</span>
) : (
<span>{item.title}</span>
);
if (item.children && item.children.length > 0) {
//className={searchTreeKey.indexOf(item.key) > -1 ? styles.yes : styles.no}
return <TreeNode className={searchTreeKey.length > 0 ? (searchTreeKey.indexOf(item.key) > -1 ? styles.yes : styles.no) : styles.yes} title={title} key={item.key} >
{
this.renderTreeNode(item.children)
}
</TreeNode>
}
return <TreeNode className={searchTreeKey.length > 0 ? (searchTreeKey.indexOf(item.key) > -1 ? styles.yes : styles.no) : styles.yes} title={title} ></TreeNode>
})
}
expandedKeysFn = () => {
let { data } = this.state;
let arr = [];
let loop = (data) => {
data.map((item, index) => {
arr.push(item.key);
if (item.children && item.children.length > 0) {
loop(item.children)
}
})
}
loop(data);
this.setState({
expandedKeys: arr
})
}
onExpand = expandedKeys => {
console.log('onExpand', expandedKeys);
this.setState({
expandedKeys,
autoExpandParent: false
});
};
onCheck = (checkedKeys) => {
this.setState({ checkedKeys })
}
render() {
let { data, expandedKeys, autoExpandParent, checkedKeys } = this.state;
return (
<div>
<Search style={{ marginBottom: 8 }} placeholder="Search" onChange={this.onChange} />
<Tree
checkable
expandedKeys={expandedKeys} //默认展开的key
onExpand={this.onExpand} //展开事件
autoExpandParent={autoExpandParent} //是否自动展开父节点
checkedKeys={checkedKeys} //选中的key
onCheck={this.onCheck} //选中事件
>
{this.renderTreeNode(data)}
</Tree>
</div>
);
}
}
export default Index
index.css
.yes{
display: block;
}
.no{
display: none;
}
二、 我自己写的(2021-09-09补充)
import React, { Component } from 'react';
import { Form, Input, Tree } from 'antd';
const TreeNode=Tree.TreeNode;
import styles from './index.less';
const { Search } = Input;
const treeData = [
{
title: '0-0',
key: '0-0',
children: [
{
title: '0-0-0',
key: '0-0-0',
children: [
{ title: '0-0-0-0-88', key: '0-0-0-0' },
{ title: '0-0-0-1', key: '0-0-0-1' },
{ title: '0-0-0-2', key: '0-0-0-2' },
],
},
{
title: '0-0-1',
key: '0-0-1',
children: [
{ title: '0-0-1-0', key: '0-0-1-0' },
{ title: '0-0-1-1', key: '0-0-1-1' },
{ title: '0-0-1-2', key: '0-0-1-2' },
],
},
{
title: '0-0-2',
key: '0-0-2',
},
],
},
{
title: '99',
key: '0-1',
children: [
{ title: '0-1-0-0', key: '0-1-0-0' },
{ title: '9988', key: '0-1-0-1' },
{ title: '0-1-0-2-99988', key: '0-1-0-2' },
],
},
{
title: '0-2',
key: '0-2',
},
];
class Index extends Component {
constructor(props) {
super(props);
this.state = {
expandedKeys: [],//树节点展开key
treeData: [],
copyTree: [],//备份 treeData
copyExpandedKeys: [], //备份 展开key
searchValue: ""
}
}
componentDidMount() {
let a = this.expandedKeysFun(treeData); //展开key
let cp = JSON.stringify(treeData); //这个是最简单的 深拷贝
this.setState({
treeData: treeData,
expandedKeys: a,
copyTree: cp,
copyExpandedKeys: a
})
}
arrayTreeFilter = (data, predicate, filterText) => {
const nodes = data;
// 如果已经没有节点了,结束递归
if (!(nodes && nodes.length)) {
return;
}
const newChildren = [];
for (const node of nodes) {
if (predicate(node, filterText)) {
// 如果自己(节点)符合条件,直接加入到新的节点集
newChildren.push(node);
// 并接着处理其 children,(因为父节点符合,子节点一定要在,所以这一步就不递归了)
node.children = this.arrayTreeFilter(node.children, predicate, filterText);
} else {
// 如果自己不符合条件,需要根据子集来判断它是否将其加入新节点集
// 根据递归调用 arrayTreeFilter() 的返回值来判断
const subs = this.arrayTreeFilter(node.children, predicate, filterText);
// 以下两个条件任何一个成立,当前节点都应该加入到新子节点集中
// 1. 子孙节点中存在符合条件的,即 subs 数组中有值
// 2. 自己本身符合条件
if ((subs && subs.length) || predicate(node, filterText)) {
node.children = subs;
newChildren.push(node);
}
}
}
return newChildren;
}
filterFn = (data, filterText) => { //过滤函数
if (!filterText) {
return true;
}
return (
new RegExp(filterText, "i").test(data.title) //我是一title过滤 ,你可以根据自己需求改动
);
}
flatTreeFun = (treeData) => { //扁平化 tree
let arr = [];
const flatTree = (treeData) => {
treeData.map((item, index) => {
arr.push(item);
if (item.children && item.children.length > 0) {
flatTree(item.children);
item.children = [];
}
})
}
flatTree(treeData);
return arr;
}
expandedKeysFun = (treeData) => { //展开 key函数
if (treeData && treeData.length == 0) {
return [];
}
//console.log(treeData)
let arr = [];
const expandedKeysFn = (treeData) => {
treeData.map((item, index) => {
arr.push(item.key);
if (item.children && item.children.length > 0) {
expandedKeysFn(item.children);
}
})
}
expandedKeysFn(treeData);
return arr;
}
onChange = (e) => { //搜索框 change事件
let value = e.target.value;
if (value == "") { //为空时要回到最初 的树节点
let { copyTree, copyExpandedKeys } = this.state;
// let res = this.arrayTreeFilter(JSON.parse(copyTree), this.filterFn, value);
// let expkey = this.expandedKeysFun(res);
this.setState({
treeData: JSON.parse(copyTree),
expandedKeys: copyExpandedKeys
})
} else {
let res = this.arrayTreeFilter(treeData, this.filterFn, value);
let expkey = this.expandedKeysFun(res);
this.setState({
treeData: res,
expandedKeys: expkey,
searchValue:value
})
}
}
renderTreeNode = (data) => { //生成树结构函数
if (data.length == 0) {
return
}
let { expandedKeys, searchValue } = this.state;
return data.map((item) => {
const index = item.title.indexOf(searchValue);
const beforeStr = item.title.substr(0, index);
const afterStr = item.title.substr(index + searchValue.length);
const title =
index > -1 ? (
<span>
{beforeStr}
<span style={{ color: "red" }}>{searchValue}</span>
{afterStr}
</span>
) : (
<span>{item.title}</span>
);
if (item.children && item.children.length > 0) {
return <TreeNode key={item.key} title={title} >
{
this.renderTreeNode(item.children)
}
</TreeNode>
}
return <TreeNode key={item.key} title={title} ></TreeNode>
})
}
onExpand = expandedKeys => {
this.setState({
expandedKeys,
});
};
render() {
let { expandedKeys, treeData } = this.state;
return (
<div>
<Search style={{ marginBottom: 8 }} placeholder="Search" onChange={this.onChange} />
<Tree
checkable
onExpand={this.onExpand}
expandedKeys={expandedKeys}
autoExpandParent={true}
// treeData={treeData}
>
{
this.renderTreeNode(treeData)
}
</Tree>
</div>
)
}
}
export default Index
数据量大造成的卡顿
可以给Tree加一个height属性:使用 height
属性则切换为虚拟滚动。
<Tree height={500}/>
这样可以缓解卡顿现象
总结:
想要antd-tree组件带搜索框的使用及踩坑(只标红)可以看这篇。
我自己实现的antd Tree带搜索