实现树形组件 点击展开节点,动态加载数据,以及搜索
Demo
import { PageContainer } from '@ant-design/pro-components';
import { Input, Tree } from 'antd';
import { useRequest } from "ahooks"
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { getTreeList, getChildrenNodes } from './mock'
interface DataNode {
title: string;
key: string;
isLeaf?: boolean;
children?: DataNode[];
}
const TreeDemo = () => {
const [treeData, setTreeData] = useState([]);
const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
const [autoExpandParent, setAutoExpandParent] = useState(true);
const onExpand = (newExpandedKeys: React.Key[]) => {
setExpandedKeys(newExpandedKeys);
setAutoExpandParent(false);
};
const [searchValue, setSearchValue] = useState('');
// 格式化数据
const formatTreeData = useMemo(() => {
return (data) => {
if (data.length > 0) {
return data.map((item) => {
let titleDom = <span>{item?.name}</span>
const strTitle = item.name;
if (searchValue) {
const index = strTitle.indexOf(searchValue);
const beforeStr = strTitle.substring(0, index);
const afterStr = strTitle.slice(index + searchValue.length);
if (index > -1) {
titleDom = (<span>
{beforeStr}
<span style={{ color: '#1890FF' }}>{searchValue}</span>
{afterStr}
</span>)
}
}
if (item.children) {
return {
title: titleDom,
key: item.code,
children: formatTreeData(item.children),
// isLeaf:false
};
}
return {
...item,
title: titleDom,
key: item.code,
// isLeaf: false
};
});
}
}
}, [searchValue])
// 加载列表接口
const { loading, run: loadTreeData } = useRequest(getTreeList, {
manual: true,
onSuccess: (res) => {
setTreeData(formatTreeData(res?.data))
}
})
useEffect(() => {
loadTreeData()
}, [])
const findExpandedKeys = (value, tree) => {
let keys: any[] = [];
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (node.children) {
if (node.children.some((item: { name: string }) => item.name?.includes(value))) {
keys.push(node.parentCode, node.key)
}
keys = keys.concat(findExpandedKeys(value, node.children))
}
}
return keys;
}
// 搜索框
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.target;
const newExpandedKeys = findExpandedKeys(value, treeData)
setExpandedKeys(newExpandedKeys);
setSearchValue(value);
// setAutoExpandParent(true);
};
const updateTreeData = (list: DataNode[], key: React.Key, children: DataNode[]): DataNode[] => {
// const children1== children.map((item) => {
// if (item.isLeaf) {
// item.children = []
// }
// return item
// })
return list.map((node) => {
if (node.key === key) {
return {
...node,
children,
};
}
if (node.children) {
return {
...node,
children: updateTreeData(node.children, key, children),
};
}
return node;
});
}
const onLoadData = async ({ key, children }: any) => {
let result: any[];
if (!children) {
const res = await getChildrenNodes(key);
result = formatTreeData(res?.data) || []
}
return new Promise<void>((resolve) => {
if (children) {
resolve();
return;
}
setTimeout(() => {
setTreeData((origin) =>
updateTreeData(origin, key, result),
);
resolve();
}, 1000);
});
}
return (
<PageContainer
ghost
header={{
title: '树形控件',
}}
>
<Input.Search style={{ marginBottom: 8 }} placeholder="Search" onChange={onChange} />
<Tree
treeData={formatTreeData(treeData)}
loadData={onLoadData}
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
/>
</PageContainer>
);
};
export default TreeDemo;
mock.ts
import Mock from 'mockjs'
// 模拟 树列表
function getTreeList(): Promise<string> {
return new Promise((resolve) => {
setTimeout(() => {
resolve(
Mock.mock({
'data|3': [
{
'code|+1': ['1', '2', '3'],//+1 自增
'name|+1': [
'架构域1',
'架构域2',
'架构域3',
],
'parentCode|+1': ['0', '0', '0']
}
]
})
)
}, 0)
})
}
function getChildrenNodes(parentCode): Promise<string> {
return new Promise((resolve) => {
setTimeout(() => {
resolve(
Mock.mock({
'data|1-3': [
{
'code|+1': [
`${parentCode}-1`,
`${parentCode}-2`,
`${parentCode}-3`,
],
'name|+1': [
`架构域${parentCode}-1`,
`架构域${parentCode}-2`,
`架构域${parentCode}-3`,
],
parentCode: parentCode
}
]
})
)
}, 200)
})
}
export { getTreeList, getChildrenNodes }