The main content of this paper is divided into three parts. First, I wrote the function of dragging and sorting the list by handwriting in Vue3 using HTML5
the attribute. draggable
Next, it records how to drag and sort table components in combination with Element Plus
the component library . The last part is the use of drag and drop components.sortable.js
el-table
vuedraggable
This article is based on
Vite
,Vue3
,Element Plus
technology stack
draggable implements drag and drop sorting
properties and events
draggable
Attributes are new draggable attributes in HTML5.
In HTML, except for images, links and selected text which are draggable by default, other elements are not draggable by default. If you want other elements to be draggable, you first need to set draggable
the property to true
.
<p draggable="true"> 可拖拽</p>
复制代码
After the setting is complete, some events need to be combined to complete the drag and drop:
- Events for dragging elements
event | trigger timing |
---|---|
dragstart |
Executed once when dragging starts |
drag |
Triggered multiple times after dragging starts |
dragend |
Triggered 1 time after dragging |
- Events that can drop targets
event | trigger timing |
---|---|
dragenter |
Executed once when dragging an element into a droppable target |
dragover |
Triggered multiple times when dragging an element into a droppable target (once every 100 milliseconds) |
drop |
When the dragged element is released into the dropable target (this event will only take effect if dragover is set) |
placeable target
dragenter
or dragover
event can be used to indicate valid drop targets, where the dragged element might be dropped.
Setting allow to be placed also requires blocking dragenter
and dragover
default handling of events.
<div ondragenter="event.preventDefault()">
复制代码
Make an example to understand 可放置目标
:
DataTransfer object
DataTransfer
The object is used to save the data during the dragging process, set the dragging type, etc. The object can be obtained on the properties event
of all drag events .dataTransfer
DataTransfer
dataTransfer.dropEffect
设置拖拽的操作类型。值必须是none
,copy
,link
或move
。
function dragover(e) {
e.preventDefault()
e.dataTransfer.dropEffect = 'move'
}
复制代码
- 传递数据 (不建议使用这种方法,可能有兼容问题)只能在
drop
事件中接收(测试的时候在其他拖拽事件中获取不到)
function dragstart(e, index) {
e.stopPropagation()
e.dataTransfer.dropEffect = 'move'
// 传数据
e.dataTransfer.setData('text/plain', '111111111')
}
function drop(e) {
// 接收数据
console.log(e.dataTransfer.getData('text/plain'));
}
复制代码
Vue3 中拖拽排序
根据上面描述的属性和事件在 Vue3 编写一个拖拽排序的列表大概分下面几个步骤:
- 创建一个列表,遍历渲染到页面
- 列表项添加
draggable="true"
- 列表项添加事件
dragstart
dragenter
dragend
dragover
- 在
dragenter
事件中,需要传入列表项的下标,实时进行元素的排序。排序的核心逻辑也是在dragenter
中 - 代码执行的逻辑是:列表项拖拽到可放置目标时,将该拖拽的元素从原位置删除,再将拖拽的元素插入到当前可放置目标的位置
代码如下:
<template>
<div>
<div
class="item"
v-for="(item, i) in drag.list"
:key="item.id"
draggable="true"
@dragstart="dragstart($event, i)"
@dragenter="dragenter($event, i)"
@dragend="dragend"
@dragover="dragover"
>
{{ item.name }}
</div>
</div>
</template>
<script setup>
import { reactive } from 'vue'
const drag = reactive({
list: [
{ name: 'a', id: 1 },
{ name: 'b', id: 2 },
{ name: 'c', id: 3 },
{ name: 'd', id: 4 },
{ name: 'e', id: 5 },
]
})
let dragIndex = 0
function dragstart(e, index) {
e.stopPropagation()
dragIndex = index
setTimeout(() => {
e.target.classList.add('moveing')
},0)
}
function dragenter(e, index) {
e.preventDefault()
// 拖拽到原位置时不触发
if (dragIndex !== index) {
const source = drag.list[dragIndex];
drag.list.splice(dragIndex, 1);
drag.list.splice(index, 0, source);
// 更新节点位置
dragIndex = index
}
}
function dragover(e) {
e.preventDefault()
e.dataTransfer.dropEffect = 'move'
}
function dragend(e) {
e.target.classList.remove('moveing')
}
</script>
<style lang="scss" scoped>
.item {
width: 200px;
height: 40px;
line-height: 40px;
// background-color: #f5f6f8;
background-color: skyblue;
text-align: center;
margin: 10px;
color: #fff;
font-size: 18px;
}
.container {
position: relative;
padding: 0;
}
.moveing {
opacity: 0;
}
</style>
复制代码
此时是这样的效果:
这样就在 Vue3 中实现了拖拽排序。还可以利用 Vue 的<TransitionGroup>
内置组件,添加动画效果,让元素的过渡不会很生硬。
添加代码:
<template>
<div>
<TransitionGroup name="list" tag="div" class="container">
<div
class="item"
v-for="(item, i) in drag.list"
:key="item.id"
draggable="true"
@dragstart="dragstart($event, i)"
@dragenter="dragenter($event, i)"
@dragend="dragend"
@dragover="dragover"
>
{{ item.name }}
</div>
</TransitionGroup>
</div>
</template>
<style lang="scss" scoped>
.list-move, /* 对移动中的元素应用的过渡 */
.list-enter-active,
.list-leave-active {
transition: all 0.2s ease;
}
</style>
复制代码
效果如下:
sortable.js
接下来在 Element Plus
组件库中使用 sortable.js
进行表格排序。
Element Plus 是基于 Vue3 的常用的开源组件库
安装 sortable.js
npm i sortablejs -S
复制代码
使用 el-table
组件,写一个表格:
<template>
<div>
<el-table :data="tableData" id="dragTable" border style="width: 800px;">
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>
</div>
</template>
<script setup>
const tableData = [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Cilly',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Linda',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'John',
address: 'No. 189, Grove St, Los Angeles',
},
]
</script>
复制代码
现在就有了一个表格:
然后导入 sortable.js
:
<script setup>
import Sortable from 'sortablejs'
import { onMounted } from 'vue'
function setSort() {
const el = document.querySelector('#dragTable table tbody')
new Sortable(el, {
sort: true,
ghostClass: 'sortable-ghost',
onEnd: (e) => {
const targetRow = tableData.splice(e.oldIndex, 1)[0]
tableData.splice(e.newIndex, 0, targetRow)
console.log(tableData)
},
})
}
onMounted(() => {
setSort()
})
const tableData = [
// ...
]
</script>
复制代码
在 onMounted
中,也就是组件挂载完成之后,实例化 Sortable()
,传入要进行拖拽排序的节点 el
和其它一些配置参数。现在可以进行拖拽了。
有些时候,可能需要按住拖动图标才可以进行拖动,需要添加 handle
配置,并指定对应的样式名。
<el-table :data="tableData" id="dragTable" border style="width: 600px; margin: 20px">
<!-- ...... 省略代码 -->
<el-table-column label="操作" width="100">
<template #default>
<div class="handle-drag">
<el-icon>
<Sort />
</el-icon>
</div>
</template>
</el-table-column>
</el-table>
复制代码
上面代码将表格添加了一个操作列,并将操作列的图标设置一个样式类。下面的配置表示只有包含.handle-drag
样式的元素才可以被拖动。其他位置不能被拖动。
new Sortable(el, {
// ...
handle: '.handle-drag',
// ...
})
复制代码
vuedraggable
vue.draggable.next
是 Vue3 的拖拽组件,是基于 Sortable.js 实现的。可以用于拖拽列表、菜单、工作台、选项卡等常见的场景。
安装:
npm i -S vuedraggable@next
复制代码
使用:
<script setup>
import draggable from 'vuedraggable'
import { reactive } from 'vue'
const state = reactive({
list1: [1, 2, 3, 4],
list2: ['a', 'b', 'c', 'd'],
})
function onStart() {}
function onEnd() {
console.log(state)
}
</script>
复制代码
先导入 draggable
并定义一些基础数据。
<template>
<div style="margin-left: 30px;">
<draggable
:list="state.list1"
:force-fallback="true"
chosen-class="chosen"
animation="300"
@start="onStart"
@end="onEnd"
>
<template #item="{ element }">
<div class="item">
{{ element }}
</div>
</template>
</draggable>
</div>
</template>
复制代码
其中 @start
和 @end
为拖拽开始和结束时的事件。chosen-class
为拖拽时的样式。
为组件设置相同的 group
属性,可以实现在不同的块之间拖拽。
<draggable group="group" :list="state.list1" >
<template #item="{ element }">
<div class="item bck1">
{{ element }}
</div>
</template>
</draggable>
<draggable group="group" :list="state.list2" >
<template #item="{ element }">
<div class="item bck2">
{{ element }}
</div>
</template>
</draggable>
复制代码
相关地址
-
sortable.js github.com/SortableJS/…
-
vue.draggable.next https://github.com/SortableJS/vue.draggable.next
-
我的 Vue3 juejin.cn/column/7001…