Vue递归树形组件点击元素添加Active类后无法重置选中状态求助
Vue递归树形组件选中状态重置问题解决思路
核心问题分析
当前每个TreeMenu子组件独立维护selectedElement状态,resetSelection方法仅能处理当前组件的子节点,无法遍历整个树形结构的所有层级节点,导致旧选中节点的状态无法被全局重置。
具体解决方案
方案1:状态提升到父组件统一管理(推荐)
将选中状态从子组件剥离,由父组件维护当前选中节点的唯一标识(如节点label或id),子组件通过对比标识判断是否激活active类,天然实现全局状态同步。
父组件修改:
<script setup> import { ref } from 'vue' // 维护当前选中节点的唯一标识(假设label唯一) const selectedNodeKey = ref('') // 子组件点击时更新选中标识 const handleSelect = (node) => { selectedNodeKey.value = node.label } </script> <template> <div class="tree-menu-links"> <ul class="tree-menu-links"> <tree-menu :label="PP.tree.label" :nodes="PP.tree.nodes" :depth="0" :expand-all="expandAll" :selected-key="selectedNodeKey" @select="handleSelect" ></tree-menu> </ul> </div> </template>
子组件修改:
<template> <li> <ul class="tree-ul"> <div @click="toggleChildren" :class="{ 'tree-item': true, active: selectedKey === label }" > <v-icon class="tree-icon" v-if="nodes" :icon=" !showChildren && !expandAll ? 'mdi-plus-circle-outline' : 'mdi-minus-circle-outline' " ></v-icon> <div> <span @click="handleSelect" class="tree-span" :style="[{ color }, showChildren ? { fontWeight: 'bold' } : {}]" >{{ label }}</span > </div> </div> <tree-menu v-if="showChildren || expandAll" v-for="(node, index) in nodes" :index="index" :key="node.label" :nodes="node.nodes" :label="node.label" :color="node.color" :depth="depth + 1" :expand-all="expandAll" :selected-key="selectedKey" @select="handleSelect" ></tree-menu> </ul> </li> </template> <script setup> import { ref } from 'vue' const props = defineProps({ label: String, color: String, nodes: Array, index: Number, depth: { type: Number, default: 0 }, expandAll: Boolean, selectedKey: String // 接收父组件的选中标识 }) const emit = defineEmits(['select']) const showChildren = ref(false) // 点击节点时向父组件传递选中标识 const handleSelect = () => { emit('select', { label: props.label }) } function toggleChildren() { showChildren.value = !showChildren.value } </script>
方案2:递归遍历重置所有节点状态(适用于依赖节点自身selected字段的场景)
如果必须保留节点自身的selected字段,需要在父组件实现递归函数,遍历整个树形结构重置所有节点状态,再设置目标节点为选中状态。
父组件核心逻辑:
<script setup> import { ref } from 'vue' const PP = ref({ tree: { label: 'Root', nodes: [], selected: false } }) // 递归重置所有节点的选中状态 const resetAllSelection = (nodes) => { if (!nodes) return nodes.forEach(node => { node.selected = false resetAllSelection(node.nodes) // 递归处理子节点 }) } // 处理节点选中事件 const handleSelect = (targetLabel) => { // 先重置所有节点 resetAllSelection(PP.value.tree.nodes) // 再递归找到目标节点并设置选中 const setTargetSelected = (nodes) => { if (!nodes) return nodes.forEach(node => { if (node.label === targetLabel) { node.selected = true } setTargetSelected(node.nodes) }) } setTargetSelected(PP.value.tree.nodes) } </script>
子组件调整:
子组件直接使用props.selected控制active类,点击时向父组件传递当前节点的label即可。
内容的提问来源于stack exchange,提问作者Leo
相关产品推荐
相关产品推荐

