zy-react-library/components/LeftTree/Basic/index.js

185 lines
4.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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;