树形结构实战

127 阅读2分钟

之前讲过树形结构的扁平化,那么在项目中究竟会有哪些场景呢?

下面我们以 antd 组件的 TreeSelect 组件为例:

我们采用的比较手段比较简单粗暴,初次加载时,把一二级(因为这个案例的需求是最多两层数据)数据拿到,先请求第一个接口,拿到第一个请求的结果后,这里使用 promise.all() 静态方法,用第一次请求的列表数据作为参数,之后返回一个 promise 实例,promise.all() 的用法之后会详细讲解,通过传入一个参数列表,列表的每一项都是一个 promise , 只有当所有的 promise 都请求成功后才会进入下面的 .then , 否则会进入 catch , 在本案例中, 依赖第一个请求的数据中的 id(跟节点数据),请求子节点的数据, 并将每轮子节点的数据里面加入 pid 字段,值就是父级的 id,此时当所有请求执行完成后,是一个二维数组的结构,我们利用 flat() (此方法之前篇章有做讲解) 将其扁平化,然后将子节点的数据和父节点的数据合并成扁平的数据,并传递给我写好的 convertToTreeSelectData 方法。

// 获取验收项信息
  getPileList = async () => {
    const pileList = await Service.getPile();
    Promise.all(pileList.map(item => {
      return get(`/measurement/standardItem/query?typeId=${item.id}&inspectionType=pile`).then(list => {
        return list.map(each => ({...each, pid: item.id}));
      });
    })).then(res => {
      const treeData = this.convertToTreeSelectData(res.flat().concat(pileList));
      this.setState({
        treeData
      });
    });
  };

此方法扁平化的数据(无论是多少层级的数据都传递)

convertToTreeSelectData(data) {
    // 首先,需要将数据按照父子关系进行分组
    data.map((item) => {
      if (item.pid == "-1") {
        item.id = item.id + "*";
      } else {
        item.pid = item.pid + "*";
      }
    });
    // 这一步目的就是将传递的数据以pid的值为键名,进行分组。
    const groupedData = {};
    data.forEach((item) => {
      const pid = item.pid || -1;
      if (!groupedData[pid]) {
        groupedData[pid] = [];
      }
      groupedData[pid].push(item);
    });
    // 上述的代码可以等同于loadsh中的 _.groupBy方法实现
    const groupedData = _.groupBy(data, 'pid' 或者 data => data.pid)
    
    

    // 然后,定义一个递归函数,将分组后的数据转换为树形结构
    function convert(data) {
      return data.map((item) => {
        const children = groupedData[item.id]
          ? convert(groupedData[item.id])
          : [];
        return {
          title: item.name,
          value: item.id,
          key: item.id,
          children,
        };
      });
    }
    // 最后,返回根节点的子节点作为 TreeSelect 的数据源
    return convert(groupedData[-1]);
  }