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

Vue项目中使用vue-draggable拖拽时如何保留元素展开状态?

解决Vue嵌套拖拽时展开状态丢失的问题

核心问题是展开状态没有和数据源绑定,拖拽导致组件重新渲染后,局部维护的展开状态被重置。把每个元素的展开状态存在数据源自身的属性中,就能保持状态不丢失。

步骤1:修改数据源结构

给每个元素添加isExpanded属性,用于存储该元素的展开/折叠状态:

// 示例数据源(Vue3 ref示例)
const elements = ref([
  { id: 'A', children: [], isExpanded: true }, // 初始展开
  { id: 'B', children: [{ id: 'B1', children: [], isExpanded: false }], isExpanded: true },
  { id: 'C', children: [], isExpanded: false },
  { id: 'E', children: [], isExpanded: false }
]);

如果是从后端获取数据,需要遍历数组给每个元素补上isExpanded属性。

步骤2:修改NestedElement组件

把展开状态从组件局部data移到数据源的isExpanded属性,通过props传递并同步更新:

Vue3 版本

<template>
  <div class="nested-element">
    <div class="element-header" @click="toggleExpand">
      {{ element.id }}
      <span v-if="hasChildren">
        {{ element.isExpanded ? '▼' : '▶' }}
      </span>
    </div>
    <!-- 根据数据源的isExpanded控制显示 -->
    <div v-if="element.isExpanded && hasChildren" class="children-container">
      <draggable
        v-model="element.children"
        group="elements"
        handle=".element-header"
        item-key="id"
      >
        <template #item="{ element: child }">
          <NestedElement :element="child" @update:element="updateChild" />
        </template>
      </draggable>
    </div>
  </div>
</template>

<script setup>
import { computed, defineProps, defineEmits } from 'vue';
import draggable from 'vuedraggable';

const props = defineProps({
  element: {
    type: Object,
    required: true
  }
});
const emit = defineEmits(['update:element']);

const hasChildren = computed(() => props.element.children?.length > 0);

const toggleExpand = () => {
  // 更新数据源的isExpanded属性并同步到父组件
  emit('update:element', {
    ...props.element,
    isExpanded: !props.element.isExpanded
  });
};

const updateChild = (updatedChild) => {
  // 子元素状态更新时,同步更新当前元素的children数组
  const updatedChildren = props.element.children.map(child => 
    child.id === updatedChild.id ? updatedChild : child
  );
  emit('update:element', {
    ...props.element,
    children: updatedChildren
  });
};
</script>

Vue2 版本

<template>
  <div class="nested-element">
    <div class="element-header" @click="toggleExpand">
      {{ element.id }}
      <span v-if="hasChildren">
        {{ element.isExpanded ? '▼' : '▶' }}
      </span>
    </div>
    <div v-if="element.isExpanded && hasChildren" class="children-container">
      <draggable
        v-model="element.children"
        group="elements"
        handle=".element-header"
        item-key="id"
      >
        <template #item="{ element: child }">
          <NestedElement :element.sync="child" />
        </template>
      </draggable>
    </div>
  </div>
</template>

<script>
import draggable from 'vuedraggable';

export default {
  name: 'NestedElement',
  components: { draggable },
  props: {
    element: {
      type: Object,
      required: true
    }
  },
  computed: {
    hasChildren() {
      return this.element.children?.length > 0;
    }
  },
  methods: {
    toggleExpand() {
      this.$emit('update:element', {
        ...this.element,
        isExpanded: !this.element.isExpanded
      });
    }
  }
};
</script>

步骤3:在父组件中同步更新

父组件中使用NestedElement时,通过@update:element(Vue3)或.sync(Vue2)同步更新根数据源:

<!-- Vue3 父组件示例 -->
<template>
  <draggable
    v-model="elements"
    group="elements"
    handle=".element-header"
    item-key="id"
  >
    <template #item="{ element }">
      <NestedElement :element="element" @update:element="updateElement" />
    </template>
  </draggable>
</template>

<script setup>
import { ref } from 'vue';
import draggable from 'vuedraggable';
import NestedElement from './NestedElement.vue';

const elements = ref([/* 数据源 */]);

const updateElement = (updatedElement) => {
  const index = elements.value.findIndex(el => el.id === updatedElement.id);
  if (index !== -1) {
    elements.value[index] = updatedElement;
  }
};
</script>

原理说明

之前展开状态存在组件局部data中,拖拽时数据源结构变化会触发组件重新渲染,新的组件实例会使用默认的折叠状态。将展开状态绑定到数据源的元素属性后,状态会跟着数据走,拖拽操作只会改变元素的层级/顺序,不会丢失isExpanded属性,组件渲染时自然会保持原本的展开状态。

内容的提问来源于stack exchange,提问作者breking bed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 23:18:23