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 使只有选中节点的路径是展开的
Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐