最近幾天在研究有關vue實現拖拽的功能,不過跟通常的拖拽排序有點不一樣,這個需求可能出現多行多列嵌套的表單元素,數據也是遞歸形式的出現。我也是在vuedraggable的基礎上擴展實現的,如何想了解更多的拖拽排序功能能夠參考https://sortablejs.github.io/Vue.Draggable/#/simplejavascript
須要實現的功能
- 表單元素可能出現嵌套,數據出現樹形結構
- 實現拖拽功能,表單元素能夠移動到空的列裏面,可是表單元素內容的不能來回拖拽排序
- 行與行之間能夠拖動排序,列與列直接不能移動排序,能移動的只是字段數據也就是表單元素
- 右邊列表裏的字段能夠拖拽添加到左邊的空白沒內容的列裏面
用的技術點
- vue組件遞歸實現
- vuedraggable拖拽排序
- vuedraggable的例子Functional third party,主要是元素移動
- vuedraggable實現拖拽複製功能
- vuetify :vue ui組件,這裏面主要用了它的刪格系統和vcard卡片
實現功能的部分代碼
Drag組件也是要遞歸的組件代碼css
<template> <draggable v-model="datas" tag="v-layout" class="row wrap fill-height align-center sortable-list" style="background: grey;" > <v-flex v-for="row in datas" :key="row.index" class="sorttable" xs12 my-2 style="background: red" > <div class="row wrap justify-space-around"> <v-flex v-for="item in row.items" :key="item.id" xs4 pa-3 class="row-v" > <!-- 加判斷若是item存在rows數組,則遞歸繼續執行這個組件--> <template v-if="item.rows && Array.isArray(item.rows)"> <drag :data="item.rows" /> </template> <draggable v-else :list="item.data" tag="div" :group="{ name: 'row'}" :move="getData" :animation="100" :empty-insert-threshold="60" @change="log" > <v-card v-for="item2 in item.data" :key="item2.title" style="height: 100px;" > { { item2.title }} </v-card> </draggable> </v-flex> </div> </v-flex> </draggable> </template> <script> import draggable from 'vuedraggable' import Vue from 'vue' import Vuetify from 'vuetify' import 'vuetify/dist/vuetify.min.css' Vue.use(Vuetify) export default { name: 'Drag', order: 17, components: { draggable }, props: { data: { type: Array, default () { return [] } } }, data () { return { datas: this.data, controlOnStart: true } }, methods: { // 限制移動的方法 getData (e, d) { if (e.relatedContext.list.length > 0) { return false } }, log: function (evt) { // window.console.log(evt) // console.log(this.data) if (Object.keys(evt)[0] === 'added') { this.arrLoop(this.data, evt.added.element) } }, addHandler (e, d) { // console.log(e) }, endHandler (e, b) { console.log(b) }, // 遞歸實現遍歷數據 arrLoop (arr, ele) { arr.forEach(item => { const itemArr = item.data if (itemArr && itemArr.length > 1) { for (let i = 0; i < itemArr.length; i++) { if (itemArr[i].title === ele.title) { itemArr.splice(i, 1) } } } if (item.items && item.items.length) { this.arrLoop(item.items, ele) } }) } } } </script> <style> .buttons { margin-top: 35px; } .row-v { /* height: 150px; width: 200px; */ width: 33%; height: 100px; display: inline-block; background: blue; border: 1px solid #ebebeb; } .row { margin-left: 0; margin-right: 0; } .ghost { opacity: 0.5; background: #c8ebfb; } </style>
注意:實現遞歸必定定義Drag組件的name值,要不就容易報錯vue
emptyInsertThreshold:拖動時,鼠標必須與空的可排序對象之間的距離(以像素爲單位),以便將拖動元素插入到該可排序對象中。默認爲5。設置爲0禁用此功能。這個參數要適當的設置,若是是默認值,當列爲空的時候,很難把元素拖進去,這個也是一個比較難解決的點,由於須要把右邊字段元素拖動到左邊空列中,或者左邊的元素移動到空的列裏。
move對應方法getData的方法主要實現若是relatedContext.list.length 大於0,則取消移動功能。
Drag的數據:java
rows: [ { index: 1, items: [ { id: 1, data: [{ title: 'item 1' }] }, { id: 11, data: [{ title: 'item 11' }] }, { id: 12, data: [ ] } ] }, { index: 2, items: [ { id: 0, rows: [ { index: 1, items: [ { id: 2, data: [{ title: 'item 211' }] }, { id: 3, data: [{ title: 'item 212' }] } ] }, { index: 2, items: [ { id: 4, data: [ { title: 'item 222' } ] } ] } ] }, { id: 5, data: [{ title: 'item 3' }] } ] }, { index: 3, items: [ { id: 6, data: [{ title: 'item 4' }] }, { id: 7, data: [{ title: 'item 5' }] }, { id: 8, data: [] } ] } ]
右邊列表的組件代碼:git
<template> <div> <div v-for="item in datas" :key="item.id" class="item-box" > <h2>{ { item.title }}</h2> <div class="item-con"> <draggable class="dragArea list-group" :list="item.items" :group="{ name: 'row', pull: 'clone', put: false }" :clone="cloneDog" > <span v-for="item2 in item.items" :key="item2.id" > { { item2.title }} </span> </draggable> </div> </div> </div> </template> <script> import draggable from 'vuedraggable' export default { name: 'Drag', components: { draggable }, props: { data: { type: Array, default () { return [] } } }, data () { return { datas: [ { id: 1, title: '標題1', items: [ { id: 11, title: 'item 11' }, { id: 12, title: 'item 12' } ] }, { id: 2, title: '標題2', items: [ { id: 21, title: 'item 21' }, { id: 22, title: 'item 22' } ] } ] } }, methods: { cloneDog (ele) { // console.log(ele) let b = this.arrLoop(this.rows, ele) if (!b) { return ele } }, arrLoop (arr, ele) { for (let i = 0; i < arr.length; i++) { if (arr[i].id === ele.id) { return true } if (arr[i].items && arr[i].items.length) { return this.arrLoop(arr[i].items, ele) } } } } } </script> <style lang="scss" scoped> .list-group{ span { display: inline-block; padding: 0 12px; border-radius: 4px; border: 1px solid #ebebeb; line-height: 32px; height: 32px; background: #f5f5f5; margin-right: 15px; } } </style>
clone的cloneDog方法實現複製功能,首先遞歸循環數據判斷是須要複製的元素在左邊的列表中是否存在,如果存在,則取消複製,不存在,則複製。github
效果以下圖:
數組
總結
這篇文章分享的主要技術點就是vuedraggable拖拽排序和複製、嵌套拖拽排序功能、vue組件遞歸功能。oop