185 lines
4.9 KiB
JavaScript
185 lines
4.9 KiB
JavaScript
import { Input, Tree } from "antd";
|
||
import { useEffect, useState } from "react";
|
||
|
||
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 getNodePaths = (data, targetId, idKey, childrenKey, path = [], isIncludeOneself) => {
|
||
for (let i = 0; i < data.length; i++) {
|
||
const node = data[i];
|
||
const newPath = [...path, node];
|
||
|
||
// 找到目标节点,根据isIncludeOneself决定是否包含自身
|
||
if (node[idKey] === targetId) {
|
||
if (isIncludeOneself)
|
||
return newPath; // 包含自身
|
||
else
|
||
return path; // 不包含自身,只返回父节点路径
|
||
}
|
||
|
||
// 递归查找子节点
|
||
if (node[childrenKey] && node[childrenKey].length > 0) {
|
||
const result = getNodePaths(node[childrenKey], targetId, idKey, childrenKey, newPath, isIncludeOneself);
|
||
if (result) {
|
||
return result;
|
||
}
|
||
}
|
||
}
|
||
|
||
return null;
|
||
};
|
||
|
||
const handleSelect = (selectedKeys, event) => {
|
||
if (selectedKeys.length > 0) {
|
||
const selectedNodeId = selectedKeys[0];
|
||
const parentNodes = getNodePaths(treeData, selectedNodeId, idKey, childrenKey, 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;
|