如何玩轉sortablejs-vuedraggable實現表單嵌套拖拽功能

最近幾天在研究有關vue實現拖拽的功能,不過跟通常的拖拽排序有點不一樣,這個需求可能出現多行多列嵌套的表單元素,數據也是遞歸形式的出現。我也是在vuedraggable的基礎上擴展實現的,如何想了解更多的拖拽排序功能能夠參考sortablejs.github.io/Vue.Draggab…javascript

須要實現的功能

  1. 表單元素可能出現嵌套,數據出現樹形結構
  2. 實現拖拽功能,表單元素能夠移動到空的列裏面,可是表單元素內容的不能來回拖拽排序
  3. 行與行之間能夠拖動排序,列與列直接不能移動排序,能移動的只是字段數據也就是表單元素
  4. 右邊列表裏的字段能夠拖拽添加到左邊的空白沒內容的列裏面

用的技術點

  1. vue組件遞歸實現
  2. vuedraggable拖拽排序
  3. vuedraggable的例子Functional third party,主要是元素移動
  4. vuedraggable實現拖拽複製功能
  5. 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的數據:

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: []
            }
            
          ]
        }
      ]
複製代碼

右邊列表的組件代碼:java

<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方法實現複製功能,首先遞歸循環數據判斷是須要複製的元素在左邊的列表中是否存在,如果存在,則取消複製,不存在,則複製。git

效果以下圖: github

在這裏插入圖片描述

總結

這篇文章分享的主要技術點就是vuedraggable拖拽排序和複製、嵌套拖拽排序功能、vue組件遞歸功能,這僅表明我的觀點,想了解更多請掃描二維碼: 數組

在這裏插入圖片描述
相關文章
相關標籤/搜索