Vue3中列表拖拽排序的實現(xiàn)示例
本文主要內(nèi)容分三個部分。先寫了在 Vue3 中利用 HTML5
的 draggable
屬性手寫實現(xiàn)列表拖拽排序的功能。接下來記錄了在 Element Plus
組件庫中結(jié)合 sortable.js
使用,對表格組件 el-table
進(jìn)行拖拽排序的。最后一個部分是 vuedraggable
拖拽組件的使用。
本文基于
Vite
、Vue3
、Element Plus
技術(shù)棧
draggable 實現(xiàn)拖拽排序
屬性和事件
draggable
屬性是 HTML5 新增的可拖拽屬性。
在 HTML 中,除了圖像、鏈接和選擇的文本默認(rèn)可拖拽外,其他元素默認(rèn)是不可拖拽的。如果想讓其他元素變成可拖拽的,首先需要把 draggable
屬性設(shè)置為 true
。
<p draggable="true"> 可拖拽</p>
設(shè)置完成之后,還需要結(jié)合一些事件才能完成拖拽:
拖拽元素的事件事件
- 拖拽元素的事件
事件 | 觸發(fā)時機(jī) |
---|---|
dragstart | 開始拖拽時執(zhí)行 1 次 |
drag | 拖拽開始后多次觸發(fā) |
dragend | 拖動結(jié)束后觸發(fā) 1 次 |
- 可釋放目標(biāo)的事件
事件 | 觸發(fā)時機(jī) |
---|---|
dragenter | 拖拽元素進(jìn)入可釋放目標(biāo)時執(zhí)行 1 次 |
dragover | 拖拽元素進(jìn)入可釋放目標(biāo)時觸發(fā)多次(100毫秒觸發(fā)一次) |
drop | 拖拽元素進(jìn)入可釋放目標(biāo)內(nèi)釋放時(設(shè)置了dragover此事件才會生效) |
可放置目標(biāo)
dragenter
或 dragover
事件可用于表示有效的放置目標(biāo),也就是被拖拽元素可能放置的地方。
設(shè)置允許被被放置還需要阻止 dragenter
和 dragover
事件的默認(rèn)處理。
<div ondragenter="event.preventDefault()">
做一個例子來理解 可放置目標(biāo)
:
<div id="div" ondragover="dragover(event)" ondragenter="dragenter(event)"" > 我是可放置目標(biāo) </div> <p id="drag" draggable="true" ondragstart="dragstart(event)" ondragend="dragend(event)" > 我是拖拽元素,把我拖拽到上面去 </p>
#div { width:300px; height:80px; padding:10px; border:1px solid gray; }
let targetDom function dragstart(e) { } function dragenter(e) { e.preventDefault() targetDom = e.target } function dragover(e) { e.preventDefault() e.dataTransfer.dropEffect = 'move' } function dragend(e) { e.preventDefault() targetDom.appendChild(e.target) }
DataTransfer 對象
DataTransfer
對象用于保存拖拽過程中的數(shù)據(jù)、設(shè)置拖動類型等。在所有拖動事件 event
的 dataTransfer
屬性上都能獲取到 DataTransfer
對象。
dataTransfer.dropEffect
設(shè)置拖拽的操作類型。值必須是none
, copy
, link
或 move
。
function dragover(e) { e.preventDefault() e.dataTransfer.dropEffect = 'move' }
傳遞數(shù)據(jù) (不建議使用這種方法,可能有兼容問題)只能在drop
事件中接收(測試的時候在其他拖拽事件中獲取不到)
function dragstart(e, index) { e.stopPropagation() e.dataTransfer.dropEffect = 'move' // 傳數(shù)據(jù) e.dataTransfer.setData('text/plain', '111111111') } function drop(e) { // 接收數(shù)據(jù) console.log(e.dataTransfer.getData('text/plain')); }
Vue3 中拖拽排序
根據(jù)上面描述的屬性和事件在 Vue3 編寫一個拖拽排序的列表大概分下面幾個步驟:
- 創(chuàng)建一個列表,遍歷渲染到頁面
- 列表項添加
draggable="true"
- 列表項添加事件
dragstart
dragenter
dragend
dragover
- 在
dragenter
事件中,需要傳入列表項的下標(biāo),實時進(jìn)行元素的排序。排序的核心邏輯也是在dragenter
中 - 代碼執(zhí)行的邏輯是:列表項拖拽到可放置目標(biāo)時,將該拖拽的元素從原位置刪除,再將拖拽的元素插入到當(dāng)前可放置目標(biāo)的位置
代碼如下:
<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() // 拖拽到原位置時不觸發(fā) if (dragIndex !== index) { const source = drag.list[dragIndex]; drag.list.splice(dragIndex, 1); drag.list.splice(index, 0, source); // 更新節(jié)點位置 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 中實現(xiàn)了拖拽排序。還可以利用 Vue 的<TransitionGroup>
內(nèi)置組件,添加動畫效果,讓元素的過渡不會很生硬。
添加代碼:
<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, /* 對移動中的元素應(yīng)用的過渡 */ .list-enter-active, .list-leave-active { transition: all 0.2s ease; } </style>
效果如下:
sortable.js
接下來在 Element Plus
組件庫中使用 sortable.js
進(jìn)行表格排序。
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>
現(xiàn)在就有了一個表格:
然后導(dǎo)入 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()
,傳入要進(jìn)行拖拽排序的節(jié)點 el
和其它一些配置參數(shù)?,F(xiàn)在可以進(jìn)行拖拽了。
有些時候,可能需要按住拖動圖標(biāo)才可以進(jìn)行拖動,需要添加 handle
配置,并指定對應(yīng)的樣式名。
<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>
上面代碼將表格添加了一個操作列,并將操作列的圖標(biāo)設(shè)置一個樣式類。下面的配置表示只有包含.handle-drag
樣式的元素才可以被拖動。其他位置不能被拖動。
new Sortable(el, { // ... handle: '.handle-drag', // ... })
vuedraggable
vue.draggable.next
是 Vue3 的拖拽組件,是基于 Sortable.js 實現(xiàn)的??梢杂糜谕献Я斜怼⒉藛?、工作臺、選項卡等常見的場景。
安裝:
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>
先導(dǎo)入 draggable
并定義一些基礎(chǔ)數(shù)據(jù)。
<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
為拖拽開始和結(jié)束時的事件。chosen-class
為拖拽時的樣式。
為組件設(shè)置相同的 group
屬性,可以實現(xiàn)在不同的塊之間拖拽。
<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>
相關(guān)地址
到此這篇關(guān)于Vue3中列表拖拽排序的實現(xiàn)示例的文章就介紹到這了,更多相關(guān)Vue3 列表拖拽排序內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue3自定義指令自動獲取節(jié)點的width和height代碼示例
這篇文章主要介紹了如何使用ResizeObserver監(jiān)聽組件的寬度和高度,并將其封裝成一個指令以便全局或局部使用,ResizeObserver可以監(jiān)聽元素的多個尺寸屬性,如top、bottom、left等,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-11-11