使用vue-draggable-plus實(shí)現(xiàn)拖拽排序
最近實(shí)習(xí)期間遇到了一個需求:在 Vue3 的一個 H5 頁面當(dāng)中點(diǎn)擊拖拽圖標(biāo)上下拖動 tab 子項,然后點(diǎn)擊保存可以保存最新的 tab 項順序,當(dāng)時王哥和我說可以用 vue-draggable-plus 這個庫來實(shí)現(xiàn)拖拽,因?yàn)楣镜捻椖拷M件用的都是 TSX 寫法,是我之前沒接觸過的,所以寫起來比較慢,整個需求整整花了兩天才完成。下面的代碼展示還是使用傳統(tǒng)的 SFC 寫法。
vue-draggable-plus
首先在項目安裝好 vue-draggable-plus:
npm install vue-draggable-plus
具體使用可以參考中文文檔:vue-draggable-plus | vue-draggable-plus (gitee.io)
基本布局
vue-draggable-plus 的使用有三種方式,這里我采用的是組件方式引入:
<template>
...
<VueDraggable
ref="el"
v-model="sortList"
:disabled="disabled"
:animation="150"
ghostClass="ghost"
class="flex flex-col gap-2 p-4 w-300px h-300px m-auto bg-gray-500/5 rounded"
@start="onStart"
@update="onUpdate"
filter=".undraggable"
handle=".handle"
>
<div
class="sort-item"
v-for="item in sortList"
:key="item.id"
:class="item.id === 1 ? 'undraggable' : ''"
:style="item.id === 1 ? 'opacity:0.3' : ''"
>
<img
class="delete"
src="./assets/ic_delete.svg"
@click="removeItem(item.id)"
/>
<span>{{ item.name }}</span>
<img class="handle" src="./assets/ic_items.svg" />
</div>
</VueDraggable>
...
</template>
<script setup lang="ts">
import { ref } from "vue";
import { type UseDraggableReturn, VueDraggable } from "vue-draggable-plus";
...
const sortShow = ref(false);
const el = ref<UseDraggableReturn>();
const disabled = ref(false);
...
</script>
其中@start 和 @update 兩個自定義事件分別在拖拽開始和拖拽完成后調(diào)用,可以在這個期間進(jìn)行一些邏輯操作,像監(jiān)聽數(shù)組的前后變換等。
禁止部分元素拖動
因?yàn)楫?dāng)時項目還有一些其他的特殊需求:有些默認(rèn)的子項保持不變,不允許拖拽,這邊也可以用 vue-draggable-plus 實(shí)現(xiàn),如上述代碼所示:使用 filter=".undraggable" 過濾類名為 undraggable 的元素,只要是帶有這個類名的元素都無法被拖拽。
指定元素觸發(fā)拖拽
當(dāng)時 UI 還有一個需求,只能點(diǎn)擊側(cè)邊的拖拽按鈕才能實(shí)現(xiàn)拖動,可以通過 handle 屬性傳遞一個選擇器,來指定拖拽的句柄,這里我指定的是 handle類名,只有點(diǎn)擊攜帶這個類名的拖拽圖標(biāo)才能實(shí)現(xiàn)拖拽。
實(shí)現(xiàn)需求基本邏輯:
首先定義兩個數(shù)組 sortList 和 constList,其中 sortList 用來接收后端傳過來的數(shù)據(jù),后面實(shí)現(xiàn)排序完的數(shù)據(jù)也是存在這個數(shù)組當(dāng)中,而 constList 則是作為一個參照數(shù)組與 sortList 進(jìn)行比較實(shí)現(xiàn)元素的動態(tài)增添。最后點(diǎn)擊保存的時候可將 sortList 作為參數(shù)請求保存當(dāng)前順序的接口,因?yàn)楫?dāng)時后端需要通過 sortOrder字段來判斷排序,所以最后在保存的時候給 sortList 的每個子項都按順序添加一個 sortOrder字段再請求接口,完整代碼如下:
完整代碼
<template>
<van-button type="success" @click="sortClick">排列菜單</van-button>
<div v-for="item in sortList" :key="item.id">{{ item }}</div>
<van-action-sheet v-model:show="sortShow" title="排序管理">
<div class="content">
<!-- 當(dāng)前排序 -->
<div class="current-sort">
<span class="label">當(dāng)前排序</span>
<VueDraggable
ref="el"
v-model="sortList"
:disabled="disabled"
:animation="150"
ghostClass="ghost"
class="flex flex-col gap-2 p-4 w-300px h-300px m-auto bg-gray-500/5 rounded"
@start="onStart"
@update="onUpdate"
filter=".undraggable"
handle=".handle"
>
<div
class="sort-item"
v-for="item in sortList"
:key="item.id"
:class="item.id === 1 ? 'undraggable' : ''"
:style="item.id === 1 ? 'opacity:0.3' : ''"
>
<img
class="delete"
src="./assets/ic_delete.svg"
@click="removeItem(item.id)"
/>
<span>{{ item.name }}</span>
<img class="handle" src="./assets/ic_items.svg" />
</div>
</VueDraggable>
</div>
<!-- 默認(rèn)子項 -->
<div class="change-sort">
<span class="label" style="margin-top: 24px">默認(rèn)子項</span>
<div
class="sort-item"
v-for="item in constList"
:key="item.id"
:style="sortList.find((el) => el.id === item.id) ? 'opacity:0.3' : ''"
>
<img
class="add"
src="./assets/ic_add.svg"
@click="addItem(item.id)"
/>
<span>{{ item.name }}</span>
</div>
</div>
</div>
<div class="sheet-footer">
<van-button>取消</van-button>
<van-button type="primary" @click="onConfirm">保存</van-button>
</div>
</van-action-sheet>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { type UseDraggableReturn, VueDraggable } from "vue-draggable-plus";
interface SortItemType {
name: string;
id: number;
}
// 當(dāng)前排列項
let sortList = ref<SortItemType[]>([
{ id: 1, name: "Vue" },
{ id: 2, name: "React" },
{ id: 3, name: "TypeScript" },
{ id: 4, name: "Uniapp" },
{ id: 5, name: "Vite" },
]);
// 對比項(內(nèi)容不變,作為參照與sortList進(jìn)行對比)
let constList = ref<SortItemType[]>([
{ id: 2, name: "React" },
{ id: 3, name: "TypeScript" },
{ id: 4, name: "Uniapp" },
{ id: 5, name: "Vite" },
]);
const sortShow = ref(false);
const el = ref<UseDraggableReturn>();
const disabled = ref(false);
const onStart = () => {
console.log("拖拽前-sortList:", sortList.value);
};
?
const onUpdate = () => {
console.log("拖拽后-sortList:", sortList.value);
};
?
const sortClick = () => {
sortShow.value = true;
};
/**
* 移除當(dāng)前排列項
*/
const removeItem = (id: number) => {
if (id === 1) return; // 默認(rèn)不能刪除
let idx = sortList.value.findIndex((item: SortItemType) => item.id === id);
if (idx !== -1) {
sortList.value.splice(idx, 1);
}
};
/**
* 新增排列項
*/
const addItem = (id: number) => {
let idx = sortList.value.findIndex((item: SortItemType) => item.id === id);
if (idx === -1) {
sortList.value.push(
constList.value.find((item: SortItemType) => item.id === id),
);
}
};
/**
* 保存當(dāng)前排序
*/
const onConfirm = () => {
sortList.value.map((item: any, index: number) => {
item.sortOrder = index + 1;
});
sortShow.value = false;
};
</script>
?
<style lang="less" scoped>
.content {
.label {
display: block;
font-weight: 600;
font-size: 13px;
color: #969799;
height: 18px;
padding-left: 16px;
margin-bottom: 16px;
}
.sort-item {
margin-bottom: 16px;
font-weight: 600;
font-size: 15px;
color: #323233;
position: relative;
.delete,
.add {
vertical-align: middle;
margin: 0 12px 0 19px;
}
.handle {
vertical-align: middle;
position: absolute;
right: 15px;
}
}
}
.sheet-footer {
display: flex;
justify-content: center;
padding: 10px;
.van-button {
width: 165px;
height: 40px;
margin: 0 6px;
}
}
</style>
?
最終實(shí)現(xiàn)效果圖

以上就是使用vue-draggable-plus實(shí)現(xiàn)拖拽排序的詳細(xì)內(nèi)容,更多關(guān)于vue-draggable-plus拖拽排序的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue結(jié)合vue-electron創(chuàng)建應(yīng)用程序小結(jié)
這篇文章主要介紹了vue結(jié)合vue-electron創(chuàng)建應(yīng)用程序,本文給大家介紹了安裝electron有兩種方式,兩種方式創(chuàng)建的項目結(jié)構(gòu)大不相同,需要的朋友可以參考下2024-03-03
vue的無縫滾動組件vue-seamless-scroll實(shí)例
本篇文章主要給大家講解了vue的無縫滾動組件vue-seamless-scroll的用法,需要的朋友參考學(xué)習(xí)下吧。2017-12-12
vue3實(shí)現(xiàn)alert自定義的plugins方式
這篇文章主要介紹了vue3實(shí)現(xiàn)alert自定義的plugins方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08
Vue實(shí)現(xiàn)預(yù)覽文件(Word/Excel/PDF)功能的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何通過Vue實(shí)現(xiàn)預(yù)覽文件(Word/Excel/PDF)的功能,文中的實(shí)現(xiàn)步驟講解詳細(xì),需要的小伙伴可以參考一下2023-03-03
詳解Element-ui NavMenu子菜單使用遞歸生成時使用報錯
這篇文章主要介紹了詳解Element-ui NavMenu子菜單使用遞歸生成時使用報錯,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
vue.js學(xué)習(xí)筆記之綁定style樣式和class列表
數(shù)據(jù)綁定一個常見需求是操作元素的 class 列表和它的內(nèi)聯(lián)樣式。這篇文章主要介紹了vue.js綁定style和class的相關(guān)資料,需要的朋友可以參考下2016-10-10

