You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 06:36:12