WebGIS 图层树 筛选/过滤 使适配 Cascader 级联选择器

需求

需要筛选出一个新的对象数组 treeLayers,该数据中无空节点,即没有图层数据的文件夹不存在 treeLayers 中。
使之能应用到 element - Cascader 级联选择器上。

数据

有一个共用的图层树,存储在 store.state.layerManager.treeLayers
数据来自于后台的“图层配置管理”,数据结构为对象数组

结构如下

- 三维模型数据【📁】
	- XX市白膜【🌏】
	- XX市DEM【🌏】
	- XXBIM【🌏】
- 时空基础数据【📁】
	- XX市电子地图【📁】
		- 电子地图1【🌏】
		- 电子地图2【🌏】
		- 电子地图3【🌏】
	- XX市遥感影像【📁】
		- 遥感影像1【🌏】
		- 遥感影像2【🌏】
	- XX市行政区划【📁】
		- 市行政区划图【🌏】
		- 2023 行政区划【📁】
		- 2020 行政区划【📁】
			- 图层1【🌏】
			- 图层2【🌏】
	- XX市公园数据【📁】
	- XX市地铁线路数据【📁】

不论文件夹📁还是图层🌏

  • 都是对象
  • 都含有属性 type 来标识是不是图层数据
    • type === 'dir' 为目录
    • type === 'layer' 为图层数据
  • 都含有属性 children (数组)来记录包含的内容
    • 🌏的 children 为空数组 []
    • 📁如果为空目录,children 为空数组 []

示例如下:

treeLayers: [
  {
    name: '三维模型数据',
    type: 'dir',
    children: [
      {
        name: 'XX市白膜',
        type: 'layer',
        children: []
      },
      {
        name: 'XX市DEM',
        // ...
      }
    ]
  },
  // ...
  // ...
  {
    name: 'XX市地铁线路数据',
    type: 'dir',
    children: []
  }
]

实现

推理步骤一

打印出图层树结构

/**
 * 递归遍历图层树数据数组,删除没有实际图层的节点
 * @param {Array} newTreeWithOutEmptyLayer 保存没有空图层的图层树
 * @param {Array} parentNode 图层树节点,数组,每个子元素是一个对象,包含children属性(同 parentNode)
 * @param {Number} level 图层树的层级
 */
pruneTree(newTreeWithOutEmptyLayer, parentNode, level = 0) {
  parentNode.forEach((node, idx, array) => {
    const preLevelSign = '  '.repeat(level)
    const dataType = node.type !== 'dir' ? '图层' : '目录'
    if (node.children.length > 0 || dataType === '图层') {
      // 打印要存入的数据
      console.log(`${preLevelSign}level-${level}  ---  name: ${node.name}  ---  ${dataType} --- ${idx}`)

      // 递归遍历
      dataType === '目录' && this.pruneTree(newTreeWithOutEmptyLayer, node.children, level + 1)
    }
  })
},

推理步骤二

将打印的数据存入新数组,按照同样的结构位置存入。
我们先用举例的办法来找规律。

通过上面的图层树打印,已知图层树最深处,level === 3

/**
 * 递归遍历图层树数据数组,删除没有实际图层的节点
 * @param {Array} newTreeWithOutEmptyLayer 保存没有空图层的图层树 >> 传入一个空数组即可
 * @param {Array} parentNode 图层树节点,数组,每个子元素是一个对象,包含children属性(同 parentNode)>> 传入需要处理的图层树的根节点
 * @param {Number} level 图层树的层级
 */
pruneTree(newTreeWithOutEmptyLayer, parentNode, level = 0) {
  parentNode.forEach((node, idx, array) => {
    const preLevelSign = '  '.repeat(level)
    const dataType = node.type !== 'dir' ? '图层' : '目录'
    if (node.children.length > 0 || dataType === '图层') {
      // 打印要存入的数据
      console.log(`${preLevelSign}level-${level}  ---  name: ${node.name}  ---  ${dataType} --- ${idx}`)

      // 存入新数组,按照同样的结构位置存入。【1】
      if (level === 0) {
        const length = newTreeWithOutEmptyLayer.length
        newTreeWithOutEmptyLayer[length] = JSON.parse(JSON.stringify(node))
        // 移除原有的 children
        newTreeWithOutEmptyLayer[length].children = []
      } else if (level === 1) {
        const length_0 = newTreeWithOutEmptyLayer.length - 1
        const length_1 = newTreeWithOutEmptyLayer[length_0].children.length // 因为是要在children数组的末尾添加,所以这里不需要减1
        newTreeWithOutEmptyLayer[length_0].children[length_1] = JSON.parse(JSON.stringify(node))
        // 移除原有的 children
        newTreeWithOutEmptyLayer[length_0].children[length_1].children = []
      } else if (level === 2) {
        const length_0 = newTreeWithOutEmptyLayer.length - 1
        const length_1 = newTreeWithOutEmptyLayer[length_0].children.length - 1
        const length_2 = newTreeWithOutEmptyLayer[length_0].children[length_1].children.length
        newTreeWithOutEmptyLayer[length_0].children[length_1].children[length_2] = JSON.parse(JSON.stringify(node))
        // 移除原有的 children
        newTreeWithOutEmptyLayer[length_0].children[length_1].children[length_2].children = []
      } else if (level === 3) {
        const length_0 = newTreeWithOutEmptyLayer.length - 1
        const length_1 = newTreeWithOutEmptyLayer[length_0].children.length - 1
        const length_2 = newTreeWithOutEmptyLayer[length_0].children[length_1].children.length - 1
        const length_3 = newTreeWithOutEmptyLayer[length_0].children[length_1].children[length_2].children.length
        newTreeWithOutEmptyLayer[length_0].children[length_1].children[length_2].children[length_3] = JSON.parse(JSON.stringify(node))
        // 移除原有的 children
        newTreeWithOutEmptyLayer[length_0].children[length_1].children[length_2].children[length_3].children = []
      }

      // 递归遍历
      dataType === '目录' && this.pruneTree(newTreeWithOutEmptyLayer, node.children, level + 1)
    }
  })
},

推理步骤三

上面的【1】处,我们可以看到,从 level_0 到要插入数据的上一级 level 都是相似的行为,即往下一级探索

因此,我们可以将其抽象出来。

/**
 * 递归遍历图层树数据数组,删除没有实际图层的节点
 * @param {Array} newTreeWithOutEmptyLayer 保存没有空图层的图层树 >> 传入一个空数组即可
 * @param {Array} parentNode 图层树节点,数组,每个子元素是一个对象,包含children属性(同 parentNode)>> 传入需要处理的图层树的根
 * @param {Number} level 图层树的层级
 */
pruneTree(newTreeWithOutEmptyLayer, parentNode, level = 0) {
  parentNode.forEach((node, idx, array) => {
    const preLevelSign = '  '.repeat(level)
    const dataType = node.type !== 'dir' ? '图层' : '目录'
    if (node.children.length > 0 || dataType === '图层') {
      // 打印要存入的数据
      console.log(`${preLevelSign}level-${level}  ---  name: ${node.name}  ---  ${dataType} --- ${idx}`)

      // 存入新数组,按照同样的结构位置存入。
      let tempNode = newTreeWithOutEmptyLayer // 用于记录当前准备存入的节点的,从根节点开始遍历到的各级父节点。
      // level === 0 时,不执行下述循环。直接插入数据。
      for (let i = 0; i < level; i++) {
        const idx = tempNode.length - 1
        tempNode = tempNode[idx].children
      }
      // 上面不设置 i <= level,是因为最后一层时直接在数组长度处插入,无需 length - 1。
      // 插入数据
      const insertIdx = tempNode.length
      tempNode[insertIdx] = JSON.parse(JSON.stringify(node))
      // 移除原有的 children
      tempNode[insertIdx].children = []

      // 递归遍历
      dataType === '目录' && this.pruneTree(newTreeWithOutEmptyLayer, node.children, level + 1)
    }
  })
},

最后

为了便于将新的图层树(不存在没有图层的节点的图层树)应用到 element - Cascader 级联选择器上,可以将 dataType === '图层' 的节点的 children 值赋值为 null

const newTreeWithOutEmptyLayer = []
const treeLayers = this.$store.state.layerManager.treeLayers
this.pruneTree(newTreeWithOutEmptyLayer, treeLayers)

/**
 * 递归遍历图层树数据数组,删除没有实际图层的节点
 * @param {Array} newTreeWithOutEmptyLayer 保存没有空图层的图层树 >> 传入一个空数组即可
 * @param {Array} parentNode 图层树节点,数组,每个子元素是一个对象,包含children属性(同 parentNode)>> 传入需要处理的图层树的根
 * @param {Number} level 图层树的层级
 */
pruneTree(newTreeWithOutEmptyLayer, parentNode, level = 0) {
  parentNode.forEach((node, idx, array) => {
    const preLevelSign = '  '.repeat(level)
    const dataType = node.type !== 'dir' ? '图层' : '目录'
    if (node.children.length > 0 || dataType === '图层') {
      // 打印要存入的数据
      // console.log(`${preLevelSign}level-${level}  ---  name: ${node.name}  ---  ${dataType} --- ${idx}`)

      // 存入新数组,按照同样的结构位置存入。
      let tempNode = newTreeWithOutEmptyLayer // 用于记录当前准备存入的节点的,从根节点开始遍历到的各级父节点。
      // level === 0 时,不执行下述循环。直接插入数据。
      for (let i = 0; i < level; i++) {
        const idx = tempNode.length - 1
        tempNode = tempNode[idx].children
      }
      // 上面不设置 i <= level,是因为最后一层时直接在数组长度处插入,无需 length - 1。
      // 插入数据
      const insertIdx = tempNode.length
      tempNode[insertIdx] = JSON.parse(JSON.stringify(node))
      // 移除原有的 children
      tempNode[insertIdx].children = []

      // 递归遍历,
      // 如果是目录,继续遍历。
      // 如果是图层,将 children 设置为 null。
      dataType === '目录' && this.pruneTree(newTreeWithOutEmptyLayer, node.children, level + 1)
      dataType === '图层' && (tempNode[insertIdx].children = null)
    }
  })
  // 数据全部遍历完毕,返回新数组
  return newTreeWithOutEmptyLayer
},

EOF

posted @ 2024-04-19 10:52  zheyi420  阅读(15)  评论(0编辑  收藏  举报