最近几天在研究有关vue实现拖拽的功能,不过跟一般的拖拽排序有点不同,这个需求可能出现多行多列嵌套的表单元素,数据也是递归形式的出现。我也是在vuedraggable的基础上扩展实现的,如何想了解更多的拖拽排序功能可以参考​​https://sortablejs.github.io/Vue.Draggable/#/simple​

需要实现的功能

  1. 表单元素可能出现嵌套,数据出现树形结构
  2. 实现拖拽功能,表单元素可以移动到空的列里面,但是表单元素内容的不能来回拖拽排序
  3. 行与行之间可以拖动排序,列与列直接不能移动排序,能移动的只是字段数据也就是表单元素
  4. 右边列表里的字段可以拖拽添加到左边的空白没内容的列里面

用的技术点

  1. vue组件递归实现
  2. vuedraggable拖拽排序
  3. vuedraggable的例子Functional third party,主要是元素移动
  4. vuedraggable实现拖拽复制功能
  5. vuetify :vue ui组件,这里面主要用了它的删格系统和vcard卡片

实现功能的部分代码

Drag组件也是要递归的组件代码

<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值,要不就容易报错

如何玩转sortablejs-vuedraggable实现表单嵌套拖拽功能_vuetity


emptyInsertThreshold:拖动时,鼠标必须与空的可排序对象之间的距离(以像素为单位),以便将拖动元素插入到该可排序对象中。默认为5。设置为0禁用此功能。这个参数要适当的设置,如果是默认值,当列为空的时候,很难把元素拖进去,这个也是一个比较难解决的点,因为需要把右边字段元素拖动到左边空列中,或者左边的元素移动到空的列里。

move对应方法getData的方法主要实现如果relatedContext.list.length 大于0,则取消移动功能。

Drag的数据:

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: []
}

]
}
]

右边列表的组件代码:

<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方法实现复制功能,首先递归循环数据判断是需要复制的元素在左边的列表中是否存在,若是存在,则取消复制,不存在,则复制。

效果如下图:

如何玩转sortablejs-vuedraggable实现表单嵌套拖拽功能_vuetity_02

总结

这篇文章分享的主要技术点就是vuedraggable拖拽排序和复制、嵌套拖拽排序功能、vue组件递归功能。