antd可搜索可选择Tree树形组件,expandedKeys,checkedKeys属性详解
最近写react用的都是antd组件,不得不说,antd组件在使用上会比element-UI复杂点,不知道是因为react的原因不.很多组件的方法是需要自己配置的,在element-ui中可能只需要增加一个属性,最近也使用也踩了一些坑.今天总结一下Tree树形组件的使用和一些闭坑指南把.
项目源码: https://github.com/shengbid/my-react-admin这是一个react hook + antd + ts写的后台管理系统,
我的需求是: 一个可选择的tree树形列表,页面初始化展示,从后台获取数据,根据数据设置部分节点选中.顶部有搜索框,可以搜索树形列表,根据搜索高亮匹配的节点,并且展开节点.
找了下官网的例子,也有可搜索tree,拿过来用了下,套上自己的数据,结果怎么都不生效.折腾了半天,发现是我的数据的原因,后台返给我的key是Number数据,而tree设置展开和选中都需要string数据的key
所以还是要仔细看文档
然后还要注意的是expandedKeys和defaultExpandedKeys 也就是属性前有default和没有default的区别,没有default是完全控制,做任何操作都要重新设置值,否则就不会改变,有default的属性是只在初始化渲染有效,后期的操作不会影响
以defaultExpandedKeys为例,初始值设置的展开节点会生效,但是在后期搜索节点后重新设置展开节点时就不会生效了,所以如果你之后还需要对节点进行操作,就需要用没有default的完全可控的expandedKeys属性
实现可搜索的tree的步骤:
1.设置expandedKeys属性来控制搜索时的节点展开
2.对treeData数据进行处理,设置样式,对匹配的搜索项高亮
3.增加输入框,输入时,调用方法把需要展开的节点返回,重新设置expandedKeys
4.点击节点的展开收缩是也需要重新设置expandedKeys
贴代码
import React, { useEffect, useState } from 'react'; import { Tree, Input } from 'antd' import { getDeptList } from '@/services/dept' import { // getFathersById, getParentKey, } from '@/commons/utils' import { DeleteOutlined } from '@ant-design/icons' import './style.less' const Detail = () => { const [expandedKeys, setExpandedKeys] = useState(['0-0', '0-0-0']) const [treeData, setTreeData] = useState<any[]>([]) const [selectdKeys, setSelectdKeys] = useState<any[]>([]) const [checkedKeys, setCheckedKeys] = useState<any[]>([]) const [autoExpandParent, setAutoExpandParent] = useState(true) const [searchValue, setSearchValue] = useState('') // 将树形节点改为一维数组 const generateList = (data: any, dataList: any[]) => { for (let i = 0; i < data.length; i++) { const node = data[i]; const { key, title } = node; dataList.push({ key, title, }); if (node.children) { generateList(node.children, dataList); } } return dataList } // 获取树形节点数据 const getList = async() => { const res = await getDeptList() console.log(11, res) setTreeData(res.data) // handleTree(res.data) } // 搜索节点 const onChange = (e: any) => { let { value } = e.target value = String(value).trim() const dataList: any[] = generateList(treeData, []) let expandedKeys: any = dataList .map((item: any) => { if (item.title.indexOf(value) > -1) { // return getFathersById(treeData, item.key, 'key') return getParentKey(item.key, treeData) } return null; }) .filter((item: any, i: number, self: any) => item && self.indexOf(item) === i) // expandedKeys = expandedKeys.length ? expandedKeys[0] : [] console.log(26, expandedKeys) setExpandedKeys(expandedKeys) setAutoExpandParent(true) setSearchValue(value) } // 树节点展开/收缩 const onExpand = (expandedKeys: any) => { console.log(22, expandedKeys) setExpandedKeys(expandedKeys) setAutoExpandParent(false) } // 选择节点 const checkDep = (val: any, data: any) => { if (data && data.checkedNodes && data.checkedNodes.length) { let checkedNodes = [...data.checkedNodes] // console.log(1, val, data) setSelectdKeys(checkedNodes) setCheckedKeys( checkedNodes.map((subItem: any) => { return subItem.key }), ) } else { setSelectdKeys([]) setCheckedKeys([]) } } // 删除节点 const removeDep = (i: number) => { const checkedNodes = [...selectdKeys] checkedNodes.splice(i, 1) setSelectdKeys(checkedNodes) setCheckedKeys( checkedNodes.map((subItem: any) => { return subItem.key }), ) } // 处理搜索时树形数据高亮 const loop = (data: any) => data.map((item: any) => { 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 className="site-tree-search-value">{searchValue}</span> {afterStr} </span> ) : ( <span>{item.title}</span> ); if (item.children) { return { title, key: item.key, children: loop(item.children) }; } return { title, key: item.key, }; }) useEffect(() => { getList() }, []) return ( <div> <div className="tree-contanier"> <div className="left-content"> <span>可搜索可控制选择的树形组件</span> <Input style={{ marginBottom: 8 }} allowClear placeholder="Search" onChange={onChange} /> <Tree onExpand={onExpand} expandedKeys={expandedKeys} autoExpandParent={autoExpandParent} checkable defaultExpandAll onCheck={checkDep} checkedKeys={checkedKeys} checkStrictly treeData={loop(treeData)} /> </div> <div className="right-content"> <span>已选择列表</span> <ul> {selectdKeys.map((item: any, i: number) => { return <li key={item.key} className="select-item"> {item.title} <span className="remove" title="删除" onClick={() => removeDep(i)}><DeleteOutlined /></span> </li> })} </ul> </div> </div> </div> ) } export default Detail
代码中的getParentKey方法单独拿出来了
// antd tree树形匹配方法 export function getParentKey( key: number | string, tree: any): any { let parentKey for (let i = 0; i < tree.length; i++) { const node = tree[i]; if (node.children) { if (node.children.some((item: any) => item.key === key)) { parentKey = node.key; } else if (getParentKey(key, node.children)) { parentKey = getParentKey(key, node.children); } } } return parentKey; }
实现效果