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>
关键细节说明
group属性:必须给两个draggable组件设置相同的group名称,这样才允许跨列表拖拽。- 深拷贝原项:如果直接存原项的引用,后续修改目标数组里的副本会同步影响源数组,所以用
JSON.parse(JSON.stringify())做深拷贝;如果你的数据里包含函数、日期等特殊类型,可以用lodash的cloneDeep来处理。 - 生成新ID:Vue要求列表项的key唯一,所以必须给副本生成新的ID,避免和源项的key冲突导致渲染异常。
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
相关产品推荐
相关产品推荐

