0
点赞
收藏
分享

微信扫一扫

【Vue 项目】使用 vuedraggable 实现拖拽效果时遇到的问题及解决方案总结(允许 el-table 行拖拽、部分元素不允许拖拽、拖拽避免影响文字复制和输入框输入文字)

alanwhy 2022-04-13 阅读 335

文中内容是在 Vue 项目中使用 vuedraggable 插件实现简单的拖拽效果,并记录在使用过程中遇到的一些问题和解决方案。文中将会贴出很多参考文章,重叠的部分如果各位有需要,可以前往原文章查阅。


vuedraggable


介绍

在开发过程中,可能会遇到一些拖拽排序的功能需求。在这里向大家推荐使用👉 vuedraggable 👈插件。

在这里插入图片描述
在这里插入图片描述
该插件总结来说,有以下几个特点让人印象深刻:

1、对较早期使用的 Sortable.js 相关用法的兼容;
(由于笔者没使用过早期 Sortable.js,所以没什么概念。不过在文中会有使用到 Sortable 用法的功能)

2、对 Vue 相关功能的支持;

3、在拖拽的各个阶段都有对应的事件处理;

4、兼容很多常用的 UI 组件库,比如常用的 ElementUI。
(这个将在稍后的使用中有所体现)

话不多说。如有需要,可以参照👉 中文文档 👈学习使用。相关用法很简单,如没有特殊需求,参照文档即可完整实现拖拽效果。相关的例子、属性、事件在文档中都有所体现。后续一些简单的用法,将不再赘述。


简单使用

首先,先 npm 下载:

npm install vuedraggable --save

下载完成之后,对于该插件的简单使用,可以直接参考👉文档用例👈。例子、属性、事件在文档中有详细解答。在这里就仅把完整代码粘贴一下(不要忘记在组件中导入 draggable ):

<template>
  <div>
	  <div>{{drag?'拖拽中':'拖拽停止'}}</div>
	  <!--使用draggable组件-->
	  <draggable v-model="myArray" chosenClass="chosen" forceFallback="true" group="people" animation="1000" @start="onStart" @end="onEnd">
	    <transition-group>
	      <div class="item" v-for="element in myArray" :key="element.id">{{element.name}}</div>
	    </transition-group>
	  </draggable> 
  </div>
</template>
<style scoped>
   /*被拖拽对象的样式*/
   .item {
       padding: 6px;
       background-color: #fdfdfd;
       border: solid 1px #eee;
       margin-bottom: 10px;
       cursor: move;
   } 
   /*选中样式*/
   .chosen {
       border: solid 1px #3089dc !important;
   }
</style>
<script>
//导入draggable组件
import draggable from 'vuedraggable'
export default {
  //注册draggable组件
   components: {
   	 draggable
   },
   data() {
    return { 
      drag:false,
      //定义要被拖拽对象的数组
      myArray:[
        {people:'cn',id:10,name:'www.itxst.com'},
        {people:'cn',id:20,name:'www.baidu.com'},
        {people:'cn',id:30,name:'www.taobao.com'},
        {people:'us',id:40,name:'www.yahoo.com'}
      ] 
    };
  },
  methods: {
    //开始拖拽事件
    onStart(){
       this.drag=true;
    },
    //拖拽结束事件
    onEnd(e) {
       console.log(e); // 这里将会有调整前后的 index 及其他可能需要传递给接口的信息
       this.drag=false;
    }
  }
};
</script>

简单尝试了一下,果然好用!利用相关用法,也可以做一些样式上的改观。

【vuedraggable】实现部分元素不允许拖拽

vue.draggable filter 排除不允许拖动的元素

虽然从目前来看没什么难度,不过这也将导致以下一系列的问题。


拖拽避免影响文字复制和输入框输入文字

在使用的过程中将会发现,如果我们实现了拖拽效果,那么我们将不能复制其中的文字,只要鼠标一点击就会触发拖拽。同时,如果拖拽的元素涉及到了文本输入框或者选择框等需要改变信息的操作,那么也会受到拖拽的影响,同时不能复制文字。

从我在网上寻找解决办法的过程中发现,似乎没有能够有办法在进行拖拽的过程中,去解决这样的一个问题。如果大家有办法可以在评论区留言。不过,虽然在使用拖拽的过程中没有办法解决,但我们可以在特定的情况下,避免使用拖拽就好了。

比如:

1、我们可以不允许包含输入框的元素进行拖拽(使用上面的部分元素不允许拖拽的方法就可以解决):

在这里插入图片描述
2、又或者 拖拽效果只在点击按钮后触发或者在每个元素前面加一个拖动的标志,从而避免文字区域或者输入框区域无法复制的问题:
在这里插入图片描述
点击按钮后触发拖拽很简单,依然可以使用 filter 的用法,比如点击按钮后,将所有元素都包含在内,再次点击按钮将所有元素排除在外。代码简单粘贴一下:

<template>
  <div>
      <el-button @click="changeForbid">点击拖拽 / 取消</el-button>
	  <!--使用draggable组件-->
	  <draggable v-model="myArray" filter=".forbid" chosenClass="chosen" forceFallback="true" group="people" animation="1000" @start="onStart" @end="onEnd">
	    <transition-group>
	      <div v-for="element in myArray" :key="element.id">
             <span :class="{ forbid: !canDrag }">{{element.name}}</span>
	      </div>
	    </transition-group>
	  </draggable> 
  </div>
</template>
<style scoped>
   /*被拖拽对象的样式*/
   .item {
       padding: 6px;
       background-color: #fdfdfd;
       border: solid 1px #eee;
       margin-bottom: 10px;
       cursor: move;
   } 
   /*选中样式*/
   .chosen {
       border: solid 1px #3089dc !important;
   }
</style>
<script>
//导入draggable组件
import draggable from 'vuedraggable'
export default {
  //注册draggable组件
   components: {
   	 draggable
   },
   data() {
    return {
      canDrag: false,
      drag:false,
      //定义要被拖拽对象的数组
      myArray:[
        {people:'cn',id:10,name:'www.itxst.com'},
        {people:'cn',id:20,name:'www.baidu.com'},
        {people:'cn',id:30,name:'www.taobao.com'},
        {people:'us',id:40,name:'www.yahoo.com'}
      ] 
    };
  },
  methods: {
    //开始拖拽事件
    onStart(){
       this.drag=true;
    },
    //拖拽结束事件
    onEnd(e) {
       console.log(e); // 这里将会有调整前后的 index 及其他可能需要传递给接口的信息
       this.drag=false;
    },
    changeForbid() {
   	   this.canDrag = !this.canDrag;
    }
  }
};
</script>

同理地,加一个拖拽的标志,从而让拖拽只在拖动这个标志的时候,才进行拖拽,也可以使用 filter 来实现。代码简单粘贴一下:

 <draggable v-model="myArray" filter=".forbid" chosen-class="chosen" force-fallback="true" group="people" animation="1000" @start="onStart" @end="onEnd">
     <transition-group>
         <div 
          	v-for="element in myArray" 
          	:key="element.id"
         >
           <span>拖拽按钮</span>
           <span class="forbid">{{element.name}}</span>
       </div>
     </transition-group>
 </draggable>

如果您有更好的办法,也可以在评论区提出。


ElementUI 的 el-table 进行行拖拽

之前有提到 vuedraggable 也将会支持相关组件库,即通常的 ElementUI 组件也可以使用 vuedraggable (一些普通的组件:比如 el-tag、el-card 等等。当然也会有一些自带排序功能的组件,比如 el-tree)。但是,一多半的 ElementUI 组件都是内部再次封装,如果想要对它们内部的元素进行排序,用之前的用法就无法实现了。比如接下来要说的 el-table,如果用 draggable 标签包裹,那么排序的只是表格本身。如果我想排序 el-table 里面的 行 / 列 该怎么办?这时候就可以使用 Sortable.js 。

看了一些文章,它们提到如果 npm vuedraggable 就也可以使用 sortablejs 。因为 vuedraggable 内部包含了 sortablejs。但是由于自己的项目里下载过 sortablejs,所以无法证实。如果各位无法直接使用 sortablejs 那就再 npm 一下。

npm install sortablejs --save

在这里插入图片描述
相关代码(由于考虑到对行直接进行拖拽,会导致表格文字无法复制以及输入框问题,所以采用的是通过按住拖拽按钮才进行拖拽的方式。如果不想用这个方式,做一下调整就行 => handle 去掉):

<el-table
  :data="data"
  row-key="id"
>
  <el-table-column
    label="拖拽排序"
    width="80"
    align="center"
  >
    <template slot-scope="{row}">
      <i class="el-icon-rank allowDrag" style="cursor:pointer" />
    </template>
  </el-table-column>
  <el-table-column
    label="标题"
    align="center"
  >
    <template slot-scope="{row}">
      <el-input v-model="row.title" />
    </template>
  </el-table-column>
</el-table>
created() {
  // 业务场景描述行拖拽
  const tbody = document.querySelector('.el-table__body-wrapper tbody');
  const _this = this;
  Sortable.create(tbody, {
    handle: '.allowDrag',
    onEnd({ newIndex, oldIndex }) {
      console.log(newIndex, oldIndex);
    }
  })
}

参考文章如下:

Sortablejs ElementUI

实现表格列拖拽的方法 以el-table为例

如果想要了解更多用法,就需要参考相关文档了:SortableJS 中文网


以上就可以满足目前开发中,我遇到的相关需求。希望能够给您提供帮助。

举报

相关推荐

0 条评论