移动端树选择组件封装~你值得拥有

786 阅读2分钟

前言

针对移动端的部门人员选择树组件、网上一直没有找到好用的顺手的于是就萌生了自己封装一个的想法。效果如下:

GIF.gif 并没有很完善系统的去测试,有可能存在bug

下面分享几个开发过程中用到的针对处理树形数据分方法,如果想要源代码的可以关注**IMyself**后续会在此发布。

树形数据示例

const treeData = [
  {
    id: 1,
    name: '部门A',
    children: [
      {
        id: 2,
        name: '子部门A1',
        children: [
          {id: 4, name: '子部门A1-1'}, 
          {id: 5, name: '子部门A1-2'}
        ]
      },
      {
        id: 3, name: '子部门A2'
      }
    ]
  }
];

根据id查找出节点

const filterNode=(data, id) {
  const result = [];
  data.forEach(item => {
    if (item.id === id) {
      result.push(item); 
    }
    if (item.children) {
      result.push(...filterNode(item.children, id));
    }
  });
  return result;
}
// 使用示例
const filtered = filterNode(treeData, 5); 
//[
{id: 5, name: '子部门A1-2'}]

根据id查找出节点并向上获取所有父节点

  1. 方法一
const filterNode=(tree, nodeId)=> {
    let result;
    function search(nodes) {
        for (let node of nodes) {
            if (node.id === nodeId) {
                result = node;
                return true;
            }
            if (node.children) {
                if (search(node.children)) {
                    node.children = [result];
                    result = node;
                    return true;
                }
            }
        }
        return false;
    }
    search(tree);
    return [result];
}
// 使用示例
const filtered = filterNode(treeData, 5); 

//result:
[
    {
        "id": 1,
        "name": "部门A",
        "children": [
            {
                "id": 2,
                "name": "子部门A1",
                "children": [
                    {
                        "id": 5,
                        "name": "子部门A1-2"
                    }
                ]
            }
        ]
    }
]
  1. 方法二
const filterNode = (tree, nodeId) =>{
    for (const node of tree) {
        if (node.id === nodeId) {
            return node;
        }
        if (node.children) {
            const found = filterNode(node.children, nodeId);
            if (found) {
                return {
                    ...node,
                    children: [found]
                };
            }
        }
    }
    return null;
}
//返回数组
const filterNode=(tree, nodeId)=> {
      let result = [];
      for (const node of tree) {
        if (node.name === nodeId) {
          result.push(node);
        }
        if (node.children) {
          const found = filterNode(node.children, nodeId);
          if (found.length) {
            result.push({
              ...node,
              children: found,
            });
          }
        }
      }
      return result;
    }

获取所有选中的值

let treeValue=[]
getSelectData(list = treeData, isRoot = true) {
  list.forEach((item) => {
    if (!item.isParent && item.selected) {
      treeValue.push({
        name: item.name,
        id: item.id,
      });
    }
    if (item.children && item.children.length) {
      this.getSelectData(item.children, false);
    }
  });
  if (isRoot) {
    return treeValue;
  }
},

获取选中的最底层节点的数量

const getSelectedLeafNodesCount=(treeData)=> {
      let count = 0;
      function traverse(node) {
        if (node.children && node.children.length > 0) {
          node.children.forEach(child => traverse(child));
        } else if (node.selected) {
          count++;
        }
      }
      treeData.forEach(node => traverse(node));
      return count;
    }

判断父节点下所有子节点是否全部选中

const isAllChildrenChecked=(node)=> {
  if (!node.children) {
    return node.selected;
  }
  return node.children.every(this.isAllChildrenChecked);
}

设置节点选中(用于数据回显)

const setTreeData=(treeData, selectedIds)=> {
      treeData.forEach((node) => {
        if (node.children) {
          this.setTreeData(node.children, selectedIds);
        }
        if (selectedIds.find((i) => i.id == node.id)) {
          node.selected = true;
        }
        if (node.children && node.children.every((child) => child.selectedDept)) {
          node.selected = true;
        }
      });
    }

判断节点是否是最外层根节点

const isRoot=(node, treeData)=> {
      // 判断是否在 treeData 数组中
      if (!treeData.includes(node)) {
        return false;
      }
      // 遍历 treeData 数组,判断是否有父节点
      for (let n of treeData) {
        if (n.children && n.children.includes(node)) {
          return false;
        }
      }

      return true;
    },

给树形结构数据加上层级变量

const treeData=[
    {
      "name": "公司",
      "children": [
        {
          "name": "CEO",
          "children": [
            {
              "name": "财务部",
              "children": [
                {"name": "会计"},
                {"name": "出纳"}
              ]
            },
            {
              "name": "人事部",
              "children": [
                {"name": "招聘"},
                {"name": "培训"}
              ]
            }
          ]
        },
        {
          "name": "COO",
          "children": [
            {
              "name": "技术部",
              "children": [
                {"name": "开发"},
                {"name": "测试"}
              ]
            },
            {
              "name": "市场部",
              "children": [
                {"name": "营销"},
                {"name": "策划"}
              ]
            }
          ]
        }
      ]
    }
  ];

const arrayTreeAddLevel = (array, levelName = 'level', childrenName = 'children') => {
    if (!Array.isArray(array)) return []
      const recursive = (array, level = 0) => {
      level++
      return array.map(v => {
        v[levelName] = level
        const child = v[childrenName]
        if (child && child.length) recursive(child, level)
          return v
        })
      }
    return recursive(array)
  }
  
  const newData = arrayTreeAddLevel(treeData)
  console.log(newData);
  // newData
  [
    {
        "name": "公司",
        "children": [
            {
                "name": "CEO",
                "children": [
                    {
                        "name": "财务部",
                        "children": [
                            {
                                "name": "会计",
                                "level": 4
                            },
                            {
                                "name": "出纳",
                                "level": 4
                            }
                        ],
                        "level": 3
                    },
                    {
                        "name": "人事部",
                        "children": [
                            {
                                "name": "招聘",
                                "level": 4
                            },
                            {
                                "name": "培训",
                                "level": 4
                            }
                        ],
                        "level": 3
                    }
                ],
                "level": 2
            },
            {
                "name": "COO",
                "children": [
                    {
                        "name": "技术部",
                        "children": [
                            {
                                "name": "开发",
                                "level": 4
                            },
                            {
                                "name": "测试",
                                "level": 4
                            }
                        ],
                        "level": 3
                    },
                    {
                        "name": "市场部",
                        "children": [
                            {
                                "name": "营销",
                                "level": 4
                            },
                            {
                                "name": "策划",
                                "level": 4
                            }
                        ],
                        "level": 3
                    }
                ],
                "level": 2
            }
        ],
        "level": 1
    }
]