環(huán)境:vue3+setup語法
首先放官方文檔的鏈接:
中文版本: vue.draggable.next 中文文檔 - itxst.com (民間翻譯)
英文版本:GitHub - SortableJS/vue.draggable.next: Vue 3 compatible drag-and-drop component based on Sortable.js
因?yàn)樽约簩懙倪^程中,官方文檔和網(wǎng)上的資料都非常不明,使用版本各不相同,極易踩坑,自己寫完后就總結(jié)一下,與諸位共勉。
(一)首先,明確需求:
做一個(gè)可重復(fù)拖拽生成的表格設(shè)計(jì)器,效果圖如下:

(二)搭一個(gè)基本的可互相拖拽的框架
(1)在終端使用npm命令下載插件
npm i -S vuedraggable@next
//導(dǎo)入
import draggable from 'vuedraggable'
(2)拖拽插件大致可分為兩種使用方式——分組拖拽與單組拖拽
單組拖拽為只有一組數(shù)據(jù),而拖拽是交換此組數(shù)據(jù)內(nèi)部的位置,如下圖所示:

而互相拖拽是有兩組數(shù)據(jù),兩組數(shù)據(jù)可以各自內(nèi)部換順序,可以相互拖拽到對(duì)方的數(shù)組中。
本文需求只用到了互相拖拽,互相拖拽的代碼形式如下:
//需要克隆的數(shù)據(jù),A組
<draggable :list="dragList" ghost-class="ghost" :force-fallback="true" :group="{ name: 'list', pull: 'clone' }"
:sort="false" itemKey="id">
<template #item="{ element }">
<div class="item move">
<label class="move">{{ element.name }}</label>
</div>
</template>
</draggable>
//拖拽的結(jié)果,B組
<draggable :list="widgetList" ghost-class="ghost" itemKey="id" :force-fallback="true" group="list" :fallback-class="true"
:fallback-on-body="true">
<template #item="{ element }">
<div class="item move">
<label class="move">{{ element.name }}</label>
</div>
</template>
</draggable>
<script lang="ts" setup>
import draggable from 'vuedraggable'
interface type {
name: string,
id: number
}
const dragList: type[] = reactive<type[]>([
{ name: "單行文本", id: 1 },
{ name: "多行文本", id: 2 },
{ name: "計(jì)數(shù)器", id: 3 },
{ name: "單選框組", id: 4 },
])
const widgetList = reactive<type[]>([
{ name: "多行文本", id: 2 },
])
draggable的一些常用的屬性我作了整理,方便根據(jù)不同的需求取用,可核對(duì)表格填寫:
屬性 |
說明 |
類型 |
是否必填 |
group |
如果是分組拖拽,可通過設(shè)置group的name來實(shí)現(xiàn)分類。 pull屬性代表拖出,put為拖入, :group="{ name :'list',pull : true, put: false }" 代表這個(gè)分組與其他name為list的分組可實(shí)現(xiàn)分組拖拽,此分組允許拖出,不允許拖入,pull為‘clone’則代表clone模式,pull與put可寫可不寫,默認(rèn)值都為true |
Object / string |
否 (分組拖拽時(shí)必填) |
list |
綁定的數(shù)據(jù) |
Array |
是 |
sort |
是否開啟排序功能,默認(rèn)為true,如果設(shè)置為false,它所在組無法排序 |
Boolean |
否 |
force-fallback |
默認(rèn)false,忽略HTML5的拖拽行為,因?yàn)閔5里有個(gè)屬性也是可以拖動(dòng),你要自定義ghostClass chosenClass dragClass樣式時(shí),建議forceFallback設(shè)置為true |
Boolean |
否 (設(shè)置ghost-class或drag-class時(shí)為必填) |
ghost-class |
:ghostClass=“ghostClass” 設(shè)置拖動(dòng)元素的占位符類名,可以將拖動(dòng)時(shí)的元素設(shè)置為不同的樣式。自定義樣式可能需要加!important才能生效,并把forceFallback屬性設(shè)置成true。 |
String |
否 |
disabled |
是否禁用,默認(rèn)false |
Boolean |
否 |
drag-class |
:drag-class="dragClass"拖動(dòng)元素的樣式,你的自定義樣式可能需要加!important才能生效,并把forceFallback屬性設(shè)置成true |
Boolean |
否 |
item-key |
每個(gè)元素唯一的標(biāo)識(shí),建議使用id itemKey='id',注意,此處無需寫成變量形式 |
String |
是 (不填也能運(yùn)行,但控制臺(tái)會(huì)報(bào)警告) |
到現(xiàn)在為止,這已經(jīng)是個(gè)互相拖拽的組件了。
(3)如果發(fā)現(xiàn)自己寫的組件 不能進(jìn)行拖拽,或者網(wǎng)頁上不顯示組件 ,或者出現(xiàn)紅色報(bào)錯(cuò)信息,則檢查以下注意點(diǎn) :
-
group中name是否一致,group為變量,需要 :group =‘{name:'' }’的形式
-
AB組的元素要實(shí)現(xiàn)互相拖拽,元素的數(shù)據(jù)結(jié)構(gòu)必須完全一致,如果不放心,可以使用interface來定義一個(gè)類型諸如 interface itemType { name : string , id:number }
-
AB組綁定的數(shù)組是否為響應(yīng)式,如非響應(yīng)式,會(huì)發(fā)生拖拽成功,但不能及時(shí)響應(yīng)的情況,可以用這個(gè)方法自檢-->先進(jìn)行拖拽,然后在代碼的html部分隨便加個(gè)東西,然后保存,觀察網(wǎng)頁上是否顯示剛才拖拽的內(nèi)容,如果出現(xiàn),那就是數(shù)組非響應(yīng)式的問題。
改正:const arr = reactive<itemType[]>([ { name : 'name ' , id : 1 } ])
-
在新版vue3中,item插槽是必寫的部分,不能使用v-for循環(huán)替代。
<draggable>
? ? <template #item="{ element }">
<div class="item move">
<label class="move">{{ element.name }}</label>
</div>
</template>
</draggable>
-
item插槽中只允許有一個(gè)子元素,此處有個(gè)坑,就是如果有兩個(gè)子元素,但注釋掉了一個(gè),也會(huì)報(bào)錯(cuò)。哪怕實(shí)際上這只有一個(gè)子元素。
//正確的
<template #item="{ element }">
<div class="item move">
<label class="move">{{ element.name }}</label>
</div>
</template>
//會(huì)報(bào)錯(cuò)的情況!
<template #item="{ element }">
? ? <!-- <div class="class"></div> -->
<div class="item move">
<label class="move">{{ element.name }}</label>
</div>
</template>
到此為止,已經(jīng)實(shí)現(xiàn)拖拽成功。有些人可能會(huì)碰到這個(gè)問題————如果B組為空,向B組內(nèi)拖第一個(gè)組件的時(shí)候,會(huì)出現(xiàn)拖拽困難,只能往拖拽區(qū)的最頂部拖才能成功 / 根本無法拖拽成功。
原因:因?yàn)锽組的draggable渲染時(shí)為一個(gè)高度為auto的div,當(dāng)前組如果為空,高度也為0,可拖拽區(qū)就會(huì)變得很小或不存在。
解決方法:為B組的draggable設(shè)計(jì)高度,具體代碼為添加類名,在css中設(shè)置
<draggable :list="widgetList" ghost-class="ghost" itemKey="id" :force-fallback="true" group="list" :fallback-class="true"
:fallback-on-body="true" class="drag-content">
<template #item="{ element }">
<div class="item move">
<label class="move">{{ element.name }}</label>
</div>
</template>
</draggable>
<style lang="less" scoped>
? ? .drag-content {
? ? ? ? height:500px; //建議是外層嵌套一層div,div固定高,此處再設(shè)為100%
? ? }
</style>
(三)將拖拽后的內(nèi)容替換為element組件/其它指定內(nèi)容
我們發(fā)現(xiàn),拖拽后顯示的內(nèi)容可自由定義,在B組的draggable中設(shè)置
此時(shí),我們需要完善dragList的數(shù)組結(jié)構(gòu),來映射拖拽后所形成的不同組件
(在組件少的情況下可以這么做)
interface itemType {
name: string,
id: number,
? element:string
}
const dragList: type[] = reactive<type[]>([
{ name: "單行文本", id: 1, element: 'Input' },
{ name: "多行文本", id: 2, element: 'Textarea' },
{ name: "計(jì)數(shù)器", id: 3, element: 'InputNumber' },
{ name: "單選框組", id: 4, element: 'Radio' },
])
const widgetList = reactive<type[]>([
])
// A組
<draggable :list="dragList" ghost-class="ghost" :force-fallback="true" :group="{ name: 'list', pull: 'clone' }"
:sort="false" itemKey="id">
<template #item="{ element }">
<div class="item move">
<label class="move">{{ element.name }}</label>
</div>
</template>
</draggable>
// B組
<draggable :list="widgetList" ghost-class="ghost" itemKey="id" :force-fallback="true" group="list"
:fallback-class="true" :fallback-on-body="true" class="drag-content">
<template #item="{ element }">
<div class="item move">
<label class="move title">{{ element.name }}</label>
<div> <el-input v-model="input" placeholder="Please input" v-if="element.element === 'input'" /></div>
<div><el-input v-model="textarea" :rows="2" type="textarea" placeholder="Please input"
v-if="element.element === 'textarea'" /> </div>
</div>
</template>
</draggable>
點(diǎn)擊控件拖過去,就可以實(shí)現(xiàn)如下的效果,如果不想要標(biāo)題,可以把label部分去掉,其他控件如法炮制即可:

在組件數(shù)量少的時(shí)候,可以這么做,但數(shù)量多時(shí),建議使用component標(biāo)簽來映射
(1)先更改一下文件的結(jié)構(gòu)

(2)寫一個(gè)公共的函數(shù)去返回當(dāng)前widgets文件夾下的所有文件
// getWidget.ts
const gets = {} as any
const modules = import.meta.glob('./*.vue', {eager:true})
for (let each in modules) {
const name = (modules[each] as any).default.__name
gets[name] = (modules[each] as any).default
}
console.log(gets);
export default gets
也可使用globEager方法,編譯器會(huì)報(bào)錯(cuò):函數(shù)已經(jīng)棄用,不過不影響使用。
此處注意,getWidget.ts文件必須是第一個(gè)寫入文件夾的,之后再添加vue文件,不然會(huì)出現(xiàn)default對(duì)象內(nèi)沒有__name屬性的情況!?。?/span>文章來源:http://www.zghlxwxcb.cn/news/detail-428302.html
(3)分別把每個(gè)組件的部分寫好

// Input.vue
<template>
<div>
<el-input v-model="input" placeholder="Please input" />
</div>
</template>
<script lang="ts" setup>
const input=ref('')
</script>
<style lang="less" scoped>
</style>
(4)使用component標(biāo)簽映射
<draggable :list="widgetList" ghost-class="ghost" itemKey="id" :force-fallback="true" group="list"
:fallback-class="true" :fallback-on-body="true" class="drag-content">
<template #item="{ element }">
<div class="item move">
<label class="move title">{{ element.name }}</label>
<div>
<component :is="getWidget(element.element)"></component>
</div>
</div>
</template>
</draggable>
// 使用函數(shù)映射
<script lang="ts" setup>
import draggable from 'vuedraggable'
//要注意導(dǎo)入
import getName from './widgets/getWidget'
const getWidget = (name: string) => {
//寫的時(shí)候,組件的起名一定要與dragList中的element名字一模一樣,不然會(huì)映射不上
return getName[name]
}
(5)最終效果
成功實(shí)現(xiàn)!文章來源地址http://www.zghlxwxcb.cn/news/detail-428302.html

到了這里,關(guān)于vue3使用拖拽組件draggable.next的使用教程【保姆級(jí)】的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!