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

Vue Draggable是否支持拖拽时保留源数组中的项?

Vue Draggable实现拖拽复制(原项保留在源数组)

好问题!Vue Draggable(底层基于SortableJS)本身并没有提供直接开启「拖拽复制」的原生配置,但借助它的事件钩子,我们可以非常轻松地实现这个需求,思路也很清晰。

核心实现思路

我们要做的就是:

  • 在拖拽开始时,记录下被拖拽的原始项(注意要深拷贝,避免后续修改副本影响源项)
  • 在拖拽项被添加到目标数组时,阻止默认的「移除源数组项」行为,手动将副本插入目标数组

完整代码示例(Vue2版本)

<template>
  <div class="drag-container">
    <div class="source-column">
      <h3>源列表(拖拽后保留原项)</h3>
      <draggable
        v-model="sourceItems"
        @start="onDragStart"
        group="copy-group"
        item-key="id"
      >
        <div v-for="item in sourceItems" :key="item.id" class="drag-item">
          {{ item.label }}
        </div>
      </draggable>
    </div>

    <div class="target-column">
      <h3>目标列表(仅添加副本)</h3>
      <draggable
        v-model="targetItems"
        @add="onDragAdd"
        group="copy-group"
        item-key="id"
      >
        <div v-for="item in targetItems" :key="item.id" class="drag-item">
          {{ item.label }}
        </div>
      </draggable>
    </div>
  </div>
</template>

<script>
import draggable from 'vuedraggable';

export default {
  components: { draggable },
  data() {
    return {
      sourceItems: [
        { id: 1, label: '待拖拽项1' },
        { id: 2, label: '待拖拽项2' },
        { id: 3, label: '待拖拽项3' },
      ],
      targetItems: [],
      currentDraggedItem: null,
    };
  },
  methods: {
    onDragStart(evt) {
      // 深拷贝被拖拽的原项,避免引用类型的副作用
      this.currentDraggedItem = JSON.parse(JSON.stringify(evt.item.__vue__.$props.item));
    },
    onDragAdd(evt) {
      // 阻止默认行为:默认会把源数组里的项移除并添加到目标数组
      evt.preventDefault();
      
      // 给副本生成新的唯一ID(避免Vue渲染时的key冲突,根据你的业务规则调整)
      this.currentDraggedItem.id = Date.now() + Math.floor(Math.random() * 1000);
      
      // 将副本推入目标数组
      this.targetItems.push(this.currentDraggedItem);
    },
  },
};
</script>

<style scoped>
.drag-container {
  display: flex;
  gap: 2rem;
  padding: 1rem;
}
.source-column, .target-column {
  flex: 1;
  padding: 1rem;
  border: 1px solid #eee;
  border-radius: 4px;
}
.drag-item {
  padding: 0.8rem;
  margin: 0.5rem 0;
  background: #f5f5f5;
  border-radius: 4px;
  cursor: move;
}
</style>

关键细节说明

  1. group属性:必须给两个draggable组件设置相同的group名称,这样才允许跨列表拖拽。
  2. 深拷贝原项:如果直接存原项的引用,后续修改目标数组里的副本会同步影响源数组,所以用JSON.parse(JSON.stringify())做深拷贝;如果你的数据里包含函数、日期等特殊类型,可以用lodash的cloneDeep来处理。
  3. 生成新ID:Vue要求列表项的key唯一,所以必须给副本生成新的ID,避免和源项的key冲突导致渲染异常。
  4. evt.preventDefault():这是核心操作,它会阻止SortableJS默认的「移除源项+添加到目标」的流程,让我们可以手动控制复制逻辑。

Vue3 Composition API版本思路

如果是Vue3项目,写法逻辑完全一致,只是用ref来定义响应式数据:

import { ref } from 'vue';
import draggable from 'vuedraggable';

export default {
  components: { draggable },
  setup() {
    const sourceItems = ref([/* 源数据 */]);
    const targetItems = ref([]);
    const currentDraggedItem = ref(null);

    const onDragStart = (evt) => {
      currentDraggedItem.value = JSON.parse(JSON.stringify(evt.item.__vue_exposed__.item));
    };

    const onDragAdd = (evt) => {
      evt.preventDefault();
      currentDraggedItem.value.id = Date.now() + Math.floor(Math.random() * 1000);
      targetItems.value.push(currentDraggedItem.value);
    };

    return { sourceItems, targetItems, onDragStart, onDragAdd };
  },
};

这样就能完美实现你要的「拖拽时原项保留,仅在目标数组创建副本」的效果啦。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:11:43