H5 拖动组件drop(基于Vue)

289 阅读1分钟

使用场景

获取数据循环生成列表,列表可以拖动设置列表排序。 如:

生成地图图层列表,拖动图层控制图层顺序,使用H5的drop来实现。

image.png

实现

判断层级关系通过sourceId和targetId判断的,sourceId是拖动Item的Id,targetId是你拖动放置位置下一个Item的Id。

image.png

如果拖动放置到最后位置的时候,由于没有Item了所以需要设置一个最后的Div当做foot。 image.png

实现代码思路

获取拖动Item的souce 和 索引,删除原始数组中的souce。查询targetId,获取targetId的索引, 在targetId前插入souce。

具体代码

Home.vue:

LayerItem:根据数据生成的Item div class="h60":最后设置为foot的Div

当被拖动Item放置在最后一位的时候触发

@drop.stop:拖动触动 @dragover:当被拖动元素在释放区内移动时触发 @dragleave:当被拖动元素没有放下就离开释放区时触发

      <LayerItem :layer="layer" 
        v-for="layer in revertLayers" 
        :key="layer.index" 
        @on-move="moveLayer"
      />
      <div class="h60" :class="{beforedrop: isDragOver}" @drop.stop.prevent="moveToBottom" @dragover="allowDrop" @dragleave="dragLeave"></div>

LayerItem.vue:

<div class="box" :class="{beforedrop: isDragOver}" @drop.stop.prevent="drop" @dragover="allowDrop" @dragleave="dragLeave">
    <label :title="title"  :draggable="true" @dragstart="dragStart">{{title}}</label>
</div>

全部代码: Home.vue

<template>
  <div class="warp">
      <LayerItem :layer="layer" 
        v-for="layer in revertLayers" 
        :key="layer.index" 
        @on-move="moveLayer"
      />
      <div class="h60" :class="{beforedrop: isDragOver}" @drop.stop.prevent="moveToBottom" @dragover="allowDrop" @dragleave="dragLeave"></div>
  </div>
</template>

<script>
import LayerItem from './LayerItem'
export default {
  data() {
    return {
      isDragOver: false,
      revertLayers:[{
        id:"1",
        value:"A"
      },
      {
        id:"2",
        value:"B"
      },
      {
        id:"3",
        value:"C"
      },
      {
        id:"4",
        value:"D"
      },
      {
        id:"5",
        value:"E"
      }]
    }
  },
  components: {
    LayerItem
  },
  methods: {
    allowDrop(e) {
      this.isDragOver = true
      e.preventDefault()
    },
    dragLeave() {
      this.isDragOver = false
    },
    moveToBottom(e) {
      
      this.isDragOver = false
      let sourceId = e.dataTransfer.getData('layerId')
      this.moveLayer({sourceId})
    },
    moveLayer(params) {
      let {sourceId, targetId} = params
      let sourcesIndex = this.revertLayers.findIndex(lay => lay.id === sourceId)
      let sourcesLayer = this.revertLayers[sourcesIndex]
      this.revertLayers.splice(sourcesIndex,1)
      let targetIndex = this.revertLayers.findIndex(lyr => lyr.id === targetId)
      if(targetId){
        this.revertLayers.splice(targetIndex, 0, sourcesLayer)
      } else {
        this.revertLayers.push(sourcesLayer)
      }
    }
  }
}   
</script>

<style lang="less" scoped>
  .h60{
    height: 60px;
    border-top: 2px solid transparent;
    &.beforedrop{
      border-top: 2px solid #8534b3;
    }
  }
  .warp{
    box-shadow:0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
    padding-top: 80px;
  }
</style>

LayerItem.vue

<template>
  <div class="box" :class="{beforedrop: isDragOver}" @drop.stop.prevent="drop" @dragover="allowDrop" @dragleave="dragLeave">
    <label :title="layer.value" class="layer" :draggable="true" @dragstart="dragStart">{{layer.value}}</label>
  </div>
</template>

<script>
export default {
  props:['layer'],
  data () {
    return {
      isDragOver:false
    }
  },
  methods: {
    dragStart(e){
      e.dataTransfer.setData('layerId', this.layer.id)
    },
    allowDrop(e) {
      this.isDragOver = true
      e.preventDefault()
    },
    dragLeave() {
      this.isDragOver = false
    },
    drop(e) {
      this.isDragOver = false
      let layerId = e.dataTransfer.getData('layerId')
      this.$emit('on-move', {sourceId: layerId, targetId: this.layer.id})
    }
  }
}
</script>

<style lang="less" scoped>
.box{
  border: 1px solid #b0b5b8;
  margin-bottom: 4px;
  padding:4px 12px;
  width: 230px;
  margin-left: 10px;
  border-radius: 3px;
  cursor: pointer;
  &.beforedrop{
    border-top: 1px solid #0099ff;
  }
  label{
    display: inline-block;
    vertical-align: middle;
    width: 100%;
    word-break: keep-all;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    cursor: pointer;
  }
}
</style>