原文地址

前言

公司项目中原本使用vue+jQuryUI实现拖拽的效果。原本实现方式也只是要它的拖拽动态效果,阻止默认的改变dom。通过事件对象获取targetElement和toElement,就手动判断接下来的数据操作逻辑。但jQueryUI相对臃肿。就需求换个轻量级的拖拽插件。

插件选型:

先推荐个vue相关的插件库
image
都尝试了下,选择了第一个基于sortablejs的vuedraggable。(star最多哈哈哈,配置项也最丰富)
这个插件使用方式很简单。

使用方式

注意要安装两个依赖
npm install vuedraggable sortable --save
用draggable包裹被拖拽的元素,options是拖拽效果行为的相关配置,和sortable的配置基本完全一样,start end move是相关事件。

<draggable v-model="myArray" :options="{group:'people'}" @start="drag" @end="drop" :move="checkMove">
   <div v-for="element in myArray">{{element.name}}</div>
</draggable>

.vue or .js 文件

  import draggable from 'vuedraggable'
  ...
  export default {
        components: {
            draggable,
        },
        method:{
              drag : function(){
               ...
             }
              ...
        }       
}
  ...

遇到的问题

首先是拖拽的目标元素,因为被包装后的事件对象只有index,对于更广的使用场景来说就不太适用。例如目标元素的数据需要包装加工,或者不需要显示拖拽后的效果。vuedraggable介绍中与vuex一起使用的方式,并未直接修改原有数据,而进行代理。

computed: {
    myList: {
        get() {
            return this.$store.state.myList
        },
        set(value) {
            this.$store.commit('updateList', value)
        }
    }
}

虽然我司项目未使用vuex,但同理改造出如下代码

  computed: {
    xAxis: {
      get: function () {
        return this.dataConfig.xAxis;
         //return []  此时将不会没有拖拽对象添加到对应元素中的效果。但是可获取拖拽对象绑定的数据,
         //在目标元素中显示canvas,图表等等数据对应的组件
      },
      set: function (v) {
         //此处可对value进行修改,比如_.uniqBy,_.filter,或者限制长度Array.slice剪切
          ...
        this.$emit('updateDataColumn', {
          dataSourceCode: this.dataConfig.dataSourceCode,
          code: this.dataConfig.code,
          type: 'xAxis',
          columns: v
        });
      }
    }
}

PS:同时出现了个问题,使用的vuedraggable 2.13.1存在一个bug 拖入空元素中,当空元素的min-height大于第一个被拖拽进的元素时(必须对空元素设置min-height),会产生报错Cannot read property 'map' of undefined。但是在2.14.1版本中得到修复,相关issue可点击查看

第二个遇到的值得一提问题是关于HTML5原生拖拽和sortable自己实现的拖拽。默认为原生拖拽。在拖拽到空元素,容器中生成新的组件的时候,空元素的结构和位置就出现了一些问题。空元素原本放在容器中,但是设置了min-height必然会影响页面布局。于是考虑到vuedraggable拖拽添加的具体行为:拖拽经过目标空元素的时候,会先生成影子元素,除非在另一个可落入区域drop的时候,都会添加到有影子元素的目标空元素。

  <draggable v-model="willAddToDataColumn" class="dragArea" :options="{group:'people'}" class="list-group list-group-right">
    <template v-for="item in willAddDataColumn">
    </template>
  </draggable>
  <draggable v-model="willAddDataColumn" class="dragArea" :options="{group:'people'}" class="list-group list-group-bottom">
    <template v-for="item in willAddToDataColumn">
    </template>
  </draggable>

分别绝对定位于容器的下,右边,且右边宽1px,下边高1px。

  .list-group{
  .sortable-chosen{display: none}  //不显示影子元素。
  }
  .list-group-right{
    right: 0;
    position: absolute;
    height: 100%;
    width: 1px;
    top: 0;
  }
  .list-group-bottom{
    position: absolute;
    height: 1px;
    width: 100%;
    bottom: 0;
    margin-bottom: 0;
  }

实现倒是能实现了。但是在容器中却出现了鼠标变为不可落入状态的显示bug。于是查看sortable配置的文档。发现可通过设置forceFallback:true, fallbackClass:'draggingStyle'来禁止使用HTML5原生拖拽。转而使用可自定义样式的拖拽。实际操作过程用又发现之前的拖拽添加的具体行为发生了改变。没法添加进去了。囧。
最终还是找了HTML5原生拖拽来fix,在容器中添加
<div class='container' @dragover="allowDrop">...</div>

        allowDrop:function (ev) {
          ev.preventDefault();
        }

以上

Logo

前往低代码交流专区

更多推荐