独书先生 Menu

All items for 5月, 2021

Tree树形控件展示当前路径

问题

现有一个树形结构的菜单,支持无限的嵌套层级,需要根据用户点击的某个子菜单,获取到这个子菜单所在节点的路径,类似于面包屑效果。

一个合格的树形菜单数据结构

const menu = [{
          id:'1',
          label: '一级 1',
          children: [{
            id:'11',
            label: '二级 1-1',
            children: [{
              id:'111',
              label: '三级 1-1-1'
            }]
          }]
        }, {
          id:'2',
          label: '一级 2',
          children: [{
            id:'21',
            label: '二级 2-1',
            children: [{
              id:'211',
              label: '三级 2-1-1'
            }]
          }, {
            id:'22',
            label: '二级 2-2',
            children: [{
              id:'221',
              label: '三级 2-2-1'
            }]
          }]
        }, {
          id:'3',
          label: '一级 3',
          children: [{
            id:'31',
            label: '二级 3-1',
            children: [{
              id:'311',
              label: '三级 3-1-1'
            }]
          }, {
            id:'32',
            label: '二级 3-2',
            children: [{
              id:'321',
              label: '三级 3-2-1'
            }]
          }]
        }]

解决方案

思路:

递归整个树形菜单,记录下每次循环时候的当前路径名称,匹配到之后再把所有节点名称拼接起来。

其中有一个关键处理点是每次循环到同级数组,需要重新开始记录路径,否则会出现路径重复的情况

代码:

/**
 * @param {array[][]} data 整个菜单数组
 * @param {object[][]} v 当前节点对象
 * @return {string[]} 当前节点对象路径
 */

function findPath(data, v){
            var find = false; //找到id后标记,结束递归
            var paths = [];

            findId(data, v.id);

            return paths.join('/');

            function findId(root, vid) {

                var currentPathsLength = paths.length;

                root.forEach((ele, i, root) => {
                    // 后面不再循环找了
                    if (find) {
                        return
                    }

                    // 每次循环到同级数组,重新开始记录路径
                    paths = paths.slice(0,currentPathsLength);
                    paths.push(ele.label)


                    // 找到即返回
                    if (ele.id === vid) {
                        find = true;
                        return;
                    } else if (Array.isArray(ele.children) && ele.children.length > 0) { //有子节点继续寻找
                        return findId(ele.children, vid)
                    }

                });

            }
        }

在线演示:

See the Pen
zYKeVJe
by alex (@dushusir)
on CodePen.