如何在JavaScript中构建一级项ID非空的树结构及Vue3下拉框适配
Vue3 实现排除选中节点及其后代的下拉选项方案
核心思路
无需预先构建完整树结构,直接通过找出需排除的节点集合(选中节点+所有多层子节点),再从原数组过滤掉这些节点,即可生成符合要求的下拉选项。这种方式比先构建树再处理更高效简洁。
实现步骤
1. 编写获取排除节点UUID的函数
通过迭代方式(避免递归栈溢出)找出选中节点的所有后代,将这些节点的UUID存入集合:
// 获取需排除的所有节点UUID(选中节点+所有多层后代) function getExcludedUuids(selectedUuid, items) { const excluded = new Set([selectedUuid]) // 初始化队列,放入选中节点的直接子节点 const queue = [...items.filter(item => item.parentUUID === selectedUuid)] while (queue.length) { const currentNode = queue.shift() excluded.add(currentNode.uuid) // 将当前节点的子节点加入队列,继续遍历 queue.push(...items.filter(item => item.parentUUID === currentNode.uuid)) } return excluded }
2. 在Vue3组件中集成逻辑
结合计算属性,实时根据选中节点过滤下拉选项:
<template> <div> <!-- 树视图组件,选中节点时触发节点更新 --> <TreeView @node-select="handleNodeSelect" :items="rawTreeItems" /> <!-- 下拉选择框 --> <select v-model="selectedDropdownValue"> <option v-for="item in filteredDropdownItems" :key="item.uuid" :value="item.uuid" > {{ item.name }} </option> </select> </div> </template> <script setup> import { ref, computed } from 'vue' // 原始树结构数据,格式为 { uuid: string, parentUUID: string|null, name: string } const rawTreeItems = ref([ { uuid: 'root-1', parentUUID: null, name: '根节点1' }, { uuid: 'child-1-1', parentUUID: 'root-1', name: '子节点1-1' }, { uuid: 'child-1-1-1', parentUUID: 'child-1-1', name: '孙节点1-1-1' }, { uuid: 'root-2', parentUUID: null, name: '根节点2' } ]) // 存储选中的树节点UUID const selectedNodeUuid = ref(null) // 计算属性:过滤出符合条件的下拉选项 const filteredDropdownItems = computed(() => { if (!selectedNodeUuid.value) return rawTreeItems.value const excludedUuids = getExcludedUuids(selectedNodeUuid.value, rawTreeItems.value) return rawTreeItems.value.filter(item => !excludedUuids.has(item.uuid)) }) // 树节点选中事件回调 function handleNodeSelect(node) { selectedNodeUuid.value = node.uuid } // 复用之前定义的排除节点函数 function getExcludedUuids(selectedUuid, items) { const excluded = new Set([selectedUuid]) const queue = [...items.filter(item => item.parentUUID === selectedUuid)] while (queue.length) { const currentNode = queue.shift() excluded.add(currentNode.uuid) queue.push(...items.filter(item => item.parentUUID === currentNode.uuid)) } return excluded } </script>
3. 大数据量优化
如果原始数据量较大,重复遍历数组找子节点会影响性能,可以预先构建父UUID到子节点的映射表,优化查找效率:
// 构建父UUID到子节点的映射表 function buildParentChildMap(items) { const map = new Map() items.forEach(item => { if (!map.has(item.parentUUID)) { map.set(item.parentUUID, []) } map.get(item.parentUUID).push(item) }) return map } // 优化版的排除节点函数 function getExcludedUuidsOptimized(selectedUuid, parentMap) { const excluded = new Set([selectedUuid]) const queue = parentMap.get(selectedUuid) || [] while (queue.length) { const currentNode = queue.shift() excluded.add(currentNode.uuid) queue.push(...(parentMap.get(currentNode.uuid) || [])) } return excluded }
在组件中使用优化版:
// 计算属性生成映射表 const parentChildMap = computed(() => buildParentChildMap(rawTreeItems.value)) // 更新下拉选项的计算属性 const filteredDropdownItems = computed(() => { if (!selectedNodeUuid.value) return rawTreeItems.value const excludedUuids = getExcludedUuidsOptimized(selectedNodeUuid.value, parentChildMap.value) return rawTreeItems.value.filter(item => !excludedUuids.has(item.uuid)) })
内容的提问来源于stack exchange,提问作者tesicg
相关产品推荐
相关产品推荐

