この文書の主な内容は 3 つの部分に分かれています。HTML5
まず、属性を使ってdraggable
リストをドラッグして並べ替える機能をVue3で手書きで書きました。次に、Element Plus
コンポーネント ライブラリsortable.js
と組み合わせて、テーブル コンポーネントをel-table
ドラッグして並べ替える方法を記録します。最後の部分は、vuedraggable
ドラッグ アンド ドロップ コンポーネントの使用です。
この記事は
Vite
、 、Vue3
、Element Plus
テクノロジー スタックに基づいています
ドラッグ可能はドラッグアンドドロップソートを実装します
プロパティとイベント
draggable
属性は、HTML5 の新しいドラッグ可能な属性です。
HTML では、デフォルトでドラッグ可能な画像、リンク、および選択したテキストを除き、他の要素はデフォルトではドラッグできません。draggable
他の要素をドラッグ可能にしたい場合は、まずプロパティを に設定する必要がありますtrue
。
<p draggable="true"> 可拖拽</p>
复制代码
設定が完了したら、ドラッグ アンド ドロップを完了するためにいくつかのイベントを組み合わせる必要があります。
- 要素をドラッグするためのイベント
イベント | トリガータイミング |
---|---|
dragstart |
ドラッグ開始時に1回実行 |
drag |
ドラッグ開始後に複数回トリガーされる |
dragend |
ドラッグ後に 1 回トリガーされる |
- ターゲットをドロップできるイベント
イベント | トリガータイミング |
---|---|
dragenter |
要素をドロップ可能なターゲットにドラッグするときに 1 回実行されます。 |
dragover |
要素をドロップ可能なターゲットにドラッグするときに複数回トリガーされます (100 ミリ秒ごとに 1 回) |
drop |
ドラッグされた要素がドロップ可能なターゲットに解放されたとき (このイベントは、ドラッグオーバーが設定されている場合にのみ有効になります) |
配置可能なターゲット
dragenter
または dragover
イベントを使用して、ドラッグされた要素がドロップされる有効なドロップ ターゲットを示すことができます。
配置を許可する設定では、 イベントのブロックdragenter
と デフォルトの処理も必要になります。dragover
<div ondragenter="event.preventDefault()">
复制代码
理解するために例を作成します可放置目标
。
データ転送オブジェクト
DataTransfer
このオブジェクトは、ドラッグ プロセス中のデータの保存、ドラッグ タイプの設定などに使用されます。オブジェクトは、すべてのドラッグ イベント event
の 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…