当前位置:   article > 正文

vue3使用拖拽组件draggable-next的使用教程【保姆级】_vue3拖拽组件

vue3拖拽组件

环境:vue3+setup语法

首先放官方文档的链接:

中文版本: vue.draggable.next 中文文档 - itxst.com (民间翻译)

英文版本:GitHub - SortableJS/vue.draggable.next: Vue 3 compatible drag-and-drop component based on Sortable.js

因为自己写的过程中,官方文档和网上的资料都非常不明,使用版本各不相同,极易踩坑,自己写完后就总结一下,与诸位共勉。

(一)首先,明确需求:

做一个可重复拖拽生成的表格设计器,效果图如下:

(二)搭一个基本的可互相拖拽的框架

(1)在终端使用npm命令下载插件

  1. npm i -S vuedraggable@next
  2. //导入
  3. import draggable from 'vuedraggable'

(2)拖拽插件大致可分为两种使用方式——分组拖拽与单组拖拽

单组拖拽为只有一组数据,而拖拽是交换此组数据内部的位置,如下图所示:

而互相拖拽是有两组数据,两组数据可以各自内部换顺序,可以相互拖拽到对方的数组中。

本文需求只用到了互相拖拽,互相拖拽的代码形式如下:

  1. //需要克隆的数据,A组
  2. <draggable :list="dragList" ghost-class="ghost" :force-fallback="true" :group="{ name: 'list', pull: 'clone' }"
  3. :sort="false" itemKey="id">
  4. <template #item="{ element }">
  5. <div class="item move">
  6. <label class="move">{{ element.name }}</label>
  7. </div>
  8. </template>
  9. </draggable>
  10. //拖拽的结果,B组
  11. <draggable :list="widgetList" ghost-class="ghost" itemKey="id" :force-fallback="true" group="list" :fallback-class="true"
  12. :fallback-on-body="true">
  13. <template #item="{ element }">
  14. <div class="item move">
  15. <label class="move">{{ element.name }}</label>
  16. </div>
  17. </template>
  18. </draggable>
  19. <script lang="ts" setup>
  20. import draggable from 'vuedraggable'
  21. interface type {
  22. name: string,
  23. id: number
  24. }
  25. const dragList: type[] = reactive<type[]>([
  26. { name: "单行文本", id: 1 },
  27. { name: "多行文本", id: 2 },
  28. { name: "计数器", id: 3 },
  29. { name: "单选框组", id: 4 },
  30. ])
  31. const widgetList = reactive<type[]>([
  32. { name: "多行文本", id: 2 },
  33. ])

draggable的一些常用的属性我作了整理,方便根据不同的需求取用,可核对表格填写:

属性

说明

类型

是否必填

group

如果是分组拖拽,可通过设置group的name来实现分类。

pull属性代表拖出,put为拖入,

:group="{ name :'list',pull : true, put: false }"

代表这个分组与其他name为list的分组可实现分组拖拽,此分组允许拖出,不允许拖入,pull为‘clone’则代表clone模式,pull与put可写可不写,默认值都为true

Object / string

(分组拖拽时必填)

list

绑定的数据

Array

sort

是否开启排序功能,默认为true,如果设置为false,它所在组无法排序

Boolean

force-fallback

默认false,忽略HTML5的拖拽行为,因为h5里有个属性也是可以拖动,你要自定义ghostClass chosenClass dragClass样式时,建议forceFallback设置为true

Boolean

(设置ghost-class或drag-class时为必填)

ghost-class

:ghostClass=“ghostClass” 设置拖动元素的占位符类名,可以将拖动时的元素设置为不同的样式。自定义样式可能需要加!important才能生效,并把forceFallback属性设置成true。

String

disabled

是否禁用,默认false

Boolean

drag-class

:drag-class="dragClass"拖动元素的样式,你的自定义样式可能需要加!important才能生效,并把forceFallback属性设置成true

Boolean

item-key

每个元素唯一的标识,建议使用id

itemKey='id',注意,此处无需写成变量形式

String

(不填也能运行,但控制台会报警告)

到现在为止,这已经是个互相拖拽的组件了。

(3)如果发现自己写的组件 不能进行拖拽,或者网页上不显示组件 ,或者出现红色报错信息,则检查以下注意点 :

  1. group中name是否一致,group为变量,需要 :group =‘{name:'' }’的形式

  2. AB组的元素要实现互相拖拽,元素的数据结构必须完全一致,如果不放心,可以使用interface来定义一个类型诸如 interface itemType { name : string , id:number }

  3. AB组绑定的数组是否为响应式,如非响应式,会发生拖拽成功,但不能及时响应的情况,可以用这个方法自检-->先进行拖拽,然后在代码的html部分随便加个东西,然后保存,观察网页上是否显示刚才拖拽的内容,如果出现,那就是数组非响应式的问题。

  4. 在新版vue3中,item插槽是必写的部分,不能使用v-for循环替代。
     

    1. <draggable>
    2. <template #item="{ element }">
    3. <div class="item move">
    4. <label class="move">{{ element.name }}</label>
    5. </div>
    6. </template>
    7. </draggable>

  5. item插槽中只允许有一个子元素,此处有个坑,就是如果有两个子元素,但注释掉了一个,也会报错。哪怕实际上这只有一个子元素。
     

    1. //正确的
    2. <template #item="{ element }">
    3. <div class="item move">
    4. <label class="move">{{ element.name }}</label>
    5. </div>
    6. </template>
    7. //会报错的情况!
    8. <template #item="{ element }">
    9. <!-- <div class="class"></div> -->
    10. <div class="item move">
    11. <label class="move">{{ element.name }}</label>
    12. </div>
    13. </template>

到此为止,已经实现拖拽成功。有些人可能会碰到这个问题————如果B组为空,向B组内拖第一个组件的时候,会出现拖拽困难,只能往拖拽区的最顶部拖才能成功 / 根本无法拖拽成功。

原因:因为B组的draggable渲染时为一个高度为auto的div,当前组如果为空,高度也为0,可拖拽区就会变得很小或不存在。

解决方法:为B组的draggable设计高度,具体代码为添加类名,在css中设置

  1. <draggable :list="widgetList" ghost-class="ghost" itemKey="id" :force-fallback="true" group="list" :fallback-class="true"
  2. :fallback-on-body="true" class="drag-content">
  3. <template #item="{ element }">
  4. <div class="item move">
  5. <label class="move">{{ element.name }}</label>
  6. </div>
  7. </template>
  8. </draggable>
  9. <style lang="less" scoped>
  10. .drag-content {
  11. height:500px; //建议是外层嵌套一层divdiv固定高,此处再设为100%
  12. }
  13. </style>

(三)将拖拽后的内容替换为element组件/其它指定内容

我们发现,拖拽后显示的内容可自由定义,在B组的draggable中设置

此时,我们需要完善dragList的数组结构,来映射拖拽后所形成的不同组件

(在组件少的情况下可以这么做)

  1. interface itemType {
  2. name: string,
  3. id: number,
  4. element:string
  5. }
  6. const dragList: type[] = reactive<type[]>([
  7. { name: "单行文本", id: 1, element: 'Input' },
  8. { name: "多行文本", id: 2, element: 'Textarea' },
  9. { name: "计数器", id: 3, element: 'InputNumber' },
  10. { name: "单选框组", id: 4, element: 'Radio' },
  11. ])
  12. const widgetList = reactive<type[]>([
  13. ])
  1. // A组
  2. <draggable :list="dragList" ghost-class="ghost" :force-fallback="true" :group="{ name: 'list', pull: 'clone' }"
  3. :sort="false" itemKey="id">
  4. <template #item="{ element }">
  5. <div class="item move">
  6. <label class="move">{{ element.name }}</label>
  7. </div>
  8. </template>
  9. </draggable>
  10. // B组
  11. <draggable :list="widgetList" ghost-class="ghost" itemKey="id" :force-fallback="true" group="list"
  12. :fallback-class="true" :fallback-on-body="true" class="drag-content">
  13. <template #item="{ element }">
  14. <div class="item move">
  15. <label class="move title">{{ element.name }}</label>
  16. <div> <el-input v-model="input" placeholder="Please input" v-if="element.element === 'input'" /></div>
  17. <div><el-input v-model="textarea" :rows="2" type="textarea" placeholder="Please input"
  18. v-if="element.element === 'textarea'" /> </div>
  19. </div>
  20. </template>
  21. </draggable>

点击控件拖过去,就可以实现如下的效果,如果不想要标题,可以把label部分去掉,其他控件如法炮制即可:

在组件数量少的时候,可以这么做,但数量多时,建议使用component标签来映射

(1)先更改一下文件的结构

(2)写一个公共的函数去返回当前widgets文件夹下的所有文件

  1. // getWidget.ts
  2. const gets = {} as any
  3. const modules = import.meta.glob('./*.vue', {eager:true})
  4. for (let each in modules) {
  5. const name = (modules[each] as any).default.__name
  6. gets[name] = (modules[each] as any).default
  7. }
  8. console.log(gets);
  9. export default gets

也可使用globEager方法,编译器会报错:函数已经弃用,不过不影响使用。

(3)分别把每个组件的部分写好

  1. // Input.vue
  2. <template>
  3. <div>
  4. <el-input v-model="input" placeholder="Please input" />
  5. </div>
  6. </template>
  7. <script lang="ts" setup>
  8. const input=ref('')
  9. </script>
  10. <style lang="less" scoped>
  11. </style>

(4)使用component标签映射

  1. <draggable :list="widgetList" ghost-class="ghost" itemKey="id" :force-fallback="true" group="list"
  2. :fallback-class="true" :fallback-on-body="true" class="drag-content">
  3. <template #item="{ element }">
  4. <div class="item move">
  5. <label class="move title">{{ element.name }}</label>
  6. <div>
  7. <component :is="getWidget(element.element)"></component>
  8. </div>
  9. </div>
  10. </template>
  11. </draggable>
  12. // 使用函数映射
  13. <script lang="ts" setup>
  14. import draggable from 'vuedraggable'
  15. //要注意导入
  16. import getName from './widgets/getWidget'
  17. const getWidget = (name: string) => {
  18. //写的时候,组件的起名一定要与dragList中的element名字一模一样,不然会映射不上
  19. return getName[name]
  20. }

(5)最终效果

成功实现!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/421759
推荐阅读
相关标签
  

闽ICP备14008679号