167 lines
4.3 KiB
JavaScript
167 lines
4.3 KiB
JavaScript
import { Input, Tree } from "antd";
|
|
import { useEffect, useState } from "react";
|
|
import { getTreeNodePaths } from "../../../utils";
|
|
|
|
const { Search } = Input;
|
|
|
|
/**
|
|
* 基础左侧树组件(不建议直接使用此组件,二次继承使用)
|
|
*/
|
|
const BasicLeftTree = (props) => {
|
|
const {
|
|
onSelect,
|
|
onGetNodePaths,
|
|
onGetNodePathsIsIncludeOneself = true,
|
|
expandedKeys: externalExpandedKeys,
|
|
treeData = [],
|
|
nameKey = "name",
|
|
idKey = "id",
|
|
childrenKey = "childrenList",
|
|
...restProps
|
|
} = props;
|
|
|
|
const [expandedKeys, setExpandedKeys] = useState([]);
|
|
const [searchValue, setSearchValue] = useState("");
|
|
const [autoExpandParent, setAutoExpandParent] = useState(true);
|
|
|
|
useEffect(() => {
|
|
setExpandedKeys(externalExpandedKeys);
|
|
}, [externalExpandedKeys]);
|
|
|
|
// 展开所有包含匹配项的父节点
|
|
const getAllExpandedKeys = (data, searchValue, keys = []) => {
|
|
data.forEach((node) => {
|
|
if (node[childrenKey]) {
|
|
if (node[nameKey].includes(searchValue)
|
|
|| node[childrenKey].some(child => child[nameKey].includes(searchValue))) {
|
|
keys.push(node[idKey]);
|
|
}
|
|
getAllExpandedKeys(node[childrenKey], searchValue, keys);
|
|
}
|
|
});
|
|
return keys;
|
|
};
|
|
|
|
// 过滤树数据,只保留匹配的节点
|
|
const filterTreeData = (data, searchValue) => {
|
|
if (!searchValue) {
|
|
return data;
|
|
}
|
|
|
|
return data.reduce((acc, node) => {
|
|
// 检查当前节点是否匹配
|
|
const isMatch = node[nameKey].includes(searchValue);
|
|
|
|
// 递归处理子节点
|
|
const filteredChildren = node[childrenKey] ? filterTreeData(node[childrenKey], searchValue) : [];
|
|
|
|
// 如果当前节点匹配或者有匹配的子节点,则保留该节点
|
|
if (isMatch || filteredChildren.length > 0) {
|
|
acc.push({
|
|
...node,
|
|
[childrenKey]: filteredChildren.length > 0 ? filteredChildren : undefined,
|
|
});
|
|
}
|
|
|
|
return acc;
|
|
}, []);
|
|
};
|
|
|
|
const handleExpand = (newExpandedKeys) => {
|
|
setExpandedKeys(newExpandedKeys);
|
|
setAutoExpandParent(false);
|
|
};
|
|
|
|
const handleSelect = (selectedKeys, event) => {
|
|
if (selectedKeys.length > 0) {
|
|
const selectedNodeId = selectedKeys[0];
|
|
const parentNodes = getTreeNodePaths({
|
|
data: treeData,
|
|
targetId: selectedNodeId,
|
|
idKey,
|
|
childrenKey,
|
|
isIncludeOneself: onGetNodePathsIsIncludeOneself,
|
|
});
|
|
onGetNodePaths?.(parentNodes);
|
|
}
|
|
onSelect?.(selectedKeys, event);
|
|
};
|
|
|
|
const onFilterTreeData = (value) => {
|
|
setSearchValue(value);
|
|
setAutoExpandParent(true);
|
|
|
|
if (!value) {
|
|
setExpandedKeys([]);
|
|
return;
|
|
}
|
|
|
|
const expandedKeys = getAllExpandedKeys(treeData, value);
|
|
|
|
setExpandedKeys(expandedKeys);
|
|
};
|
|
|
|
const onSearch = async (value) => {
|
|
if (value === searchValue)
|
|
return;
|
|
onFilterTreeData(value);
|
|
};
|
|
|
|
// 渲染带高亮的标题
|
|
const renderTitle = (name) => {
|
|
if (!searchValue)
|
|
return name;
|
|
|
|
const index = name.indexOf(searchValue);
|
|
if (index === -1)
|
|
return name;
|
|
|
|
const beforeStr = name.substring(0, index);
|
|
const afterStr = name.substring(index + searchValue.length);
|
|
|
|
return (
|
|
<span>
|
|
{beforeStr}
|
|
<span style={{ color: "#f50" }}>{searchValue}</span>
|
|
{afterStr}
|
|
</span>
|
|
);
|
|
};
|
|
|
|
// 递归处理树节点标题显示
|
|
const processTreeData = (data) => {
|
|
return data.map(node => ({
|
|
...node,
|
|
[nameKey]: renderTitle(node[nameKey]),
|
|
[childrenKey]: node[childrenKey] ? processTreeData(node[childrenKey]) : undefined,
|
|
}));
|
|
};
|
|
|
|
// 过滤并处理树数据
|
|
const filteredTreeData = filterTreeData(treeData, searchValue);
|
|
const processedTreeData = processTreeData(filteredTreeData);
|
|
|
|
return (
|
|
<div style={{ width: 300 }}>
|
|
<Search
|
|
style={{ marginBottom: 8 }}
|
|
placeholder="输入关键字进行过滤"
|
|
onSearch={onSearch}
|
|
/>
|
|
<Tree
|
|
onExpand={handleExpand}
|
|
onSelect={handleSelect}
|
|
autoExpandParent={autoExpandParent}
|
|
expandedKeys={expandedKeys}
|
|
treeData={processedTreeData}
|
|
fieldNames={{ title: nameKey, key: idKey, children: childrenKey }}
|
|
{...restProps}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
BasicLeftTree.displayName = "BasicLeftTree";
|
|
|
|
export default BasicLeftTree;
|