控制 Ant Design TreeSelect 的搜索与展开功能
本文探讨了优化Ant Design树选择组件(a-tree-select)搜索和选中节点展开行为的解决方案。
Antd 树选择组件中控制搜索/选中节点展开行为
在使用 Ant Design 的树选择组件(a-tree-select
)时,发现搜索的时候,有以下问题:
- 所有节点都会展开
- 无法保持树形结构的折叠状态
- 体验不佳,信息展示过于分散
- 当选中某节点,再点开选择框时,所有节点都会展开
问题描述
针对上述问题,希望实现,搜索只展示匹配上的节点,并且当选中某节点,再点开选择框时,只展开选中节点的路径。
例如:
- A公司
- 销售部
- 华东销售组
- 华南销售组
- 技术部
- 前端组
- 后端组
- 销售部
期望行为:
- 选择"华南销售组"时,只展开
['A公司', '销售部', '华南销售组']
- 其他节点如"技术部"保持折叠状态
解决方案
实现步骤
1. 树节点过滤函数
/**
* 递归过滤树节点,只保留包含搜索关键词的节点及其父路径
* @param {Array} treeData - 原始树数据
* @param {string} searchValue - 搜索关键词
* @returns {Array} 过滤后的树数据
*/
export const filterDeptTree = (treeData, searchValue) => {
if (!searchValue) return treeData;
return treeData
.map(node => {
// 递归处理子节点
const children = node.children
? filterDeptTree(node.children, searchValue)
: [];
// 如果当前节点包含关键词或有匹配的子节点,则保留
if (node.name.toLowerCase().includes(searchValue.toLowerCase()) || children.length) {
return { ...node, children };
}
return null;
})
.filter(Boolean); // 过滤掉 null 节点
};
🧠 工作原理:
1️⃣递归遍历树的每个节点
2️⃣对每个节点的子节点进行同样的过滤
3️⃣如果当前节点包含搜索关键词,或者它的子树中有匹配的节点,则保留该节点
4️⃣最终返回只包含匹配节点及其父路径的树结构
2. 获取匹配节点的路径
✅ 当用户输入搜索关键词时,我们需要:
1、过滤出匹配的树节点
2、展开匹配节点的路径
3、确保只显示相关节点及其路径
/**
* 获取包含搜索关键词的所有节点及其路径上的父节点
* @param {Array} treeData - 原始树数据
* @param {string} searchValue - 搜索关键词
* @returns {Array} 匹配节点及其路径的唯一标识数组
*/
export const getMatchedKeys = (treeData, searchValue) => {
const traverse = (data, path = []) => {
return data.reduce((acc, node) => {
const currentPath = [...path, node];
// 如果当前节点匹配关键词,将路径上所有节点的 code 加入结果
if (node.name?.toLowerCase().includes(searchValue.toLowerCase())) {
acc.push(...currentPath.map(n => n.code));
}
// 递归处理子节点
if (node.children && node.children.length > 0) {
traverse(node.children, currentPath).forEach(key => acc.push(key));
}
return acc;
}, []);
};
// 去重处理
return [...new Set(traverse(treeData))];
};
🧠 核心逻辑:
1、递归遍历树结构,记录当前遍历路径
2、当节点名称包含搜索关键词时,将路径上所有节点的code加入结果
3、处理子节点并合并结果
4、最后通过Set去重,避免重复的节点标识
3. 获取指定节点的完整路径
const getPathToNode = (treeData, targetCode) => {
const traverse = (data, path = []) => {
for (const node of data) {
const currentPath = [...path, node];
if (node.code === targetCode) {
return currentPath;
}
if (node.children) {
const result = traverse(node.children, currentPath);
if (result) return result;
}
}
return null;
};
const path = traverse(treeData, []);
return path ? path.map(n => n.code) : [];
};
🧠 这个递归函数会:
1、遍历整个树结构
2、查找目标节点(通过code匹配)
3、返回从根节点到目标节点的完整路径
组件实现与事件处理
1. 组件基础配置
<a-tree-select
v-model:value="deptCode"
v-model:treeExpandedKeys="treeExpandedKeys"
show-search
:dropdown-style="{ maxHeight: '270px', overflow: 'auto' }"
:field-names="{
children: 'children',
label: 'name',
value: 'code'
}"
class="select-dept-width"
allow-clear
placeholder="全部"
:getPopupContainer="trigger => trigger.parentNode"
:tree-data="filteredDeptTreeData"
tree-node-filter-prop="name"
:filterTreeNode="() => true"
@select="handleDeptSelect"
@search="handleDeptSearch"
@change="handleDeptChange"
/>
🧨 关键属性说明:v-model:treeExpandedKeys
:双向绑定展开的节点key数组tree-node-filter-prop
:指定搜索匹配的属性名filterTreeNode
:自定义搜索过滤方法(这里设为始终返回true)
2.搜索事件处理
const handleDeptSearch = value => {
if (!value) {
// 搜索值为空时恢复原始状态
filteredDeptTreeData.value = deptTreeData.value;
treeExpandedKeys.value = []; // 清空展开状态
} else {
// 有搜索值时:
// 1. 过滤树数据只显示匹配节点及其路径
filteredDeptTreeData.value = filterDeptTree(deptTreeData.value, value);
// 2. 自动展开匹配节点及其路径
treeExpandedKeys.value = getMatchedKeys(deptTreeData.value, value);
}
};
🔁 搜索时的工作流程
1、用户输入搜索词:触发 handleDeptSearch 函数
2、空值处理:恢复完整树结构,收起所有节点
3、有搜索值时:
- 过滤树数据(filterDeptTree 函数)
- 获取所有匹配节点及其路径节点的key
- 展开这些节点
3. 选择事件处理
// 部门选择
const handleDeptSelect = (value, node, extra) => {
console.log(value, node, extra, "部门选择");
// ....操作
filteredDeptTreeData.value = deptTreeData.value; //恢复默认
// 只展开当前选中节点的路径
treeExpandedKeys.value = getPathToNode(deptTreeData.value, value);
// 其余操作
};
🔁 功能分析
1、将过滤后的树数据重置为原始完整数据(确保下次打开能看到完整结构)
2、控制展开状态:
- 使用 getPathToNode 函数计算从根到选中节点的完整路径
- 更新 treeExpandedKeys 使只有选中节点的路径是展开的
更多推荐
所有评论(0)