0
点赞
收藏
分享

微信扫一扫

基于vue2,vuedraggable开发组图层2级拖拽组件

📒 背景

最近项目中需要制作一个图层拖拽到组的交互(如下图展示),今天分享一下这个组件功能。希望能抛砖引玉,给大家带来启发。

基于vue2,vuedraggable开发组图层2级拖拽组件_图层


🔍需求功能

  • 1.新建组,再新建图层,在组下显示;
  • 2.新建组没选中,则图层和组在同级;
  • 3.图层拖拽可以在一级也可在组下面;
  • 4.组拖拽只能在一级,不能叠加到组内;
  • 5.组删除,该组下图层全部删除;
  • 6.组点击眼睛,该组下图层全部显示,点击闭眼,该组下图层全部不显示。


👣设计开发

先说一下我的开发环境版本:

基于vue2,vuedraggable开发组图层2级拖拽组件_vue2_02

node: v11.3.0

npm: 6.4.1

vue:2.5.11

vuedraggable:2.24.0

如果不是以上版本也没关系,今日分享的思路,相信你可以自己造出来~

安装

npm i -S vuedraggable

引入

import draggable from 'vuedraggable'

注册

components:{
   draggable
},

使用帮助

group: "name",  // or { name: "...", pull: [true, false, clone], put: [true, false, array] } name相同的组可以互相拖动

 sort: true,  // 内部排序列表

 delay: 0, // 以毫秒为单位定义排序何时开始。

 touchStartThreshold: 0, // px,在取消延迟拖动事件之前,点应该移动多少像素?

 disabled: false, // 如果设置为真,则禁用sortable。

 store: null,  // @see Store

 animation: 150,  // ms, 动画速度运动项目排序时,' 0 ' -没有动画。

 handle: ".my-handle",  // 在列表项中拖动句柄选择器。

 filter: ".ignore-elements",  // 不导致拖拽的选择器(字符串或函数)

 preventOnFilter: true, // 调用“event.preventDefault()”时触发“filter”

 draggable: ".item",  // 指定元素中的哪些项应该是可拖动的。

 ghostClass: "sortable-ghost",  // 设置拖动元素的class的占位符的类名。

 chosenClass: "sortable-chosen",  // 设置被选中的元素的class

 dragClass: "sortable-drag",  //拖动元素的class。

 dataIdAttr: 'data-id',

 forceFallback: false,  // 忽略HTML5的DnD行为,并强制退出。

 fallbackClass: "sortable-fallback",  // 使用forceFallback时克隆的DOM元素的类名。

 fallbackOnBody: false,  // 将克隆的DOM元素添加到文档的主体中。(默认放在被拖动元素的同级)

 fallbackTolerance: 0, // 用像素指定鼠标在被视为拖拽之前应该移动的距离。

 scroll: true, // or HTMLElement

scrollFn: function(offsetX, offsetY, originalEvent, touchEvt, hoverTargetEl) { ... }, // if you have custom scrollbar scrollFn may be used for autoscrolling

scrollSensitivity: 30, // px, how near the mouse must be to an edge to start scrolling.

scrollSpeed: 10, // px

我的使用如下:封装成了组件,也可以嵌套循环的方式压缩一下哈。这边主要是组和图层的逻辑有点不一样,就懒得嵌套了。

<draggable group="A" handle=".mover" ghost-class="ghost" chosen-class="chosenClass" drag-class="dragClass" :list="realList" :value="value" :move="dragMove" @start="dragStart" @end="dragEnd" @change="dragChange" @input="saveSort">
				<!--// eslint-disable-next-line vue/valid-v-for, vue/no-parsing-error-->
				<div v-for="item,i in realList" :key="item.id" class="groupBig" :class="{'hover': curId === item.id}">
					<div :class="item.isGroup ? 'groupOne':'childOne'">
						<div class="groupLeft">
							<div class="groupLeftIcon">
								<i class="zhiconfont zhiconfont-xiangxia" :class="{'show':item.isClose}" v-show="item.isGroup && item.links.length>0" @click="changeGroupShow(item,i)"></i>
							</div>
							<div class="groupName mover" @click="selectOne(item)">
								<i v-if="item.isGroup===0" class="zknicon" :class="item.icon"></i>
								<b v-if="!item.isEdit" :title="item.name">{{ item.name || "组" }}</b>
								<input type="text" v-if="item.isEdit" v-model="item.name" size="mini" />
							</div>
						</div>
						<!--图层默认显示操作,组不显示(选中并展开才可操作组)-->
						<div class="groupOperation" v-show="((curId === item.id && !item.isClose) || !item.isGroup) && !item.isEdit ">
            <span v-if="item.isGroup === 1">
              <i class="zhiconfont zhiconfont-yanjing1" v-if="computedVisible(item) && item.links && item.links.length>0" @click="conVisible(item,i,false)"> </i>
              <i class="zhiconfont zhiconfont-yanjing" v-if="!computedVisible(item) && item.links && item.links.length>0" @click="conVisible(item,i,true)"> </i>
            </span>
							<span v-if="item.isGroup === 0">
              <i class="zhiconfont zhiconfont-yanjing1" v-if="!item.visible" @click="item.visible = !item.visible"> </i>
              <i class="zhiconfont zhiconfont-yanjing" v-if="item.visible" @click="item.visible = !item.visible"> </i>
            </span>
							<i @click="editGroupSite(item,i)" class="zhiconfont zhiconfont-bianji"></i>
							<i @click="deleteGroupSite(item,i)" class="zhiconfont zhiconfont-shanchu btngry"></i>
						</div>
						<div class="groupOperation smalbg" v-if="item.isEdit">
							<i class="zhiconfont zhiconfont-baocun1" @click="saveName(item,i)"> </i>
						</div>
					</div>
					<!--<el-collapse-transition>-->
						<!--图层不允许加叶子节点,折叠时也不允许拖拽-->
						<draggable group="A" handle=".mover" ghost-class="ghost" chosen-class="chosenClass" drag-class="dragClass" :list="item.links" :disabled="childrenDisabled" v-if="!item.isClose && item.links" :move="dragMove" @start="dragStart" @end="dragEnd" @change="dragChange" @input="saveSort">
							<div class="childBig" v-for="itm,n in item.links" :key="itm.id" :class="{'hover': curId === itm.id}">
								<div class="groupChildren">
									<div class="childName mover" @click="selectOne(itm)">
										<i v-if="itm.isGroup===0" class="zhiconfont" :class="itm.icon"></i>
										<b v-if="!itm.isEdit" :title="itm.name">{{ itm.name }}</b>
										<input type="text" v-if="itm.isEdit" v-model="itm.name" size="mini" />
									</div>
									<div class="childOperation morebg" v-show="!itm.isEdit">
										<i class="zhiconfont zhiconfont-yanjing1" v-if="!itm.visible" @click="itm.visible = !itm.visible"> </i>
										<i class="zhiconfont zhiconfont-yanjing" v-if="itm.visible" @click="itm.visible = !itm.visible"> </i>
										<i @click="editSonSite(itm)" class="zhiconfont zhiconfont-bianji"></i>
										<i @click="deleteSonSite(itm, n, i)" class="zhiconfont zhiconfont-shanchu btngry"></i>
									</div>
									<div class="childOperation" v-if="itm.isEdit">
										<i class="zhiconfont zhiconfont-baocun1" @click="saveName(itm,i)"> </i>
									</div>
								</div>
							</div>
						</draggable>
					<!--</el-collapse-transition>-->
				</div>
			</draggable>

本组件设计了2个入参:一个组件返回值或初始化值,一个是图层和组的id

props: {
      value: {
        required: false,
        type: Array,
        default: null
      },
	 initId: {
		required: false,
		type: Number,
        default: 1
	}
},

具体业务分析(本质就是二叉树)

基于vue2,vuedraggable开发组图层2级拖拽组件_vue2_03

新增组

// 新增组站点
addGroupSite () {
  this.realList.unshift({
    name: `G_${this.groupId}`,
    id: this.groupId,
    visible: true,
    isGroup: 1,
    isClose: 0,
    isEdit: 0,
    links: []
  })
  this.curId = this.groupId
  this.curIsGroup = 1
  this.groupId++
},

新增叶子

addSite () {
  let newItem = {
    name: `T_${this.groupId}`,
    id: this.groupId,
    icon: 'zhiconfont-iconfontwenzi',
    isEdit: 0,
    visible: true,
    isGroup: 0
  }
  if (this.curIsGroup === 0) {
    // 如果当前选择是图层
    let cur = this.realList.find(group => {
      if (group.isGroup === 1 && group.links.length > 0) {
        let res = group.links.find(one => one.id === this.curId)
        return res
      }
    })
    if (cur) {
      // 如果当前选择图层在组里
      cur.isClose = 0 // 添加到组组先展开
      cur.links.unshift(newItem)
    } else {
      // 如果当前选择图层在根目录
      this.realList.unshift(newItem)
    }
  } else if (this.curIsGroup === 1) {
    // 如果当前选择是组
    let curIndex = this.realList.findIndex(one => one.id === this.curId)
    this.$set(this.realList[curIndex], 'isClose', 0) // 添加到组组先展开
    this.realList[curIndex].links.unshift(newItem)
  } else if (this.curIsGroup === undefined || this.curIsGroup === null) {
    // 当前没有选择,则加在根目录
    this.realList.unshift(newItem)
  }
  this.curId = this.groupId
  this.curIsGroup = 0
  this.groupId++
},

拖拽开始和结束需要特别排除一下组,组不允许拖拽进入组,需求不允许深层嵌套。

// 拖拽开始
dragStart (evt) {
  this.curId = null // 拖拽开始选中取消
  this.curIsGroup = null // 拖拽开始选中取消
  if (evt.item._underlying_vm_.isGroup === 1) {
    // 如果isGroup是1则是组,不能拖入组里
    this.childrenDisabled = true
  }
},
 // 拖拽结束
  dragEnd (evt) {
    // console.log('拖拽结束', evt)
    this.curId = evt.item._underlying_vm_.id
    this.curIsGroup = evt.item._underlying_vm_.isGroup
    // 防止是组禁止了拖拽
    this.childrenDisabled = false
    this.saveSort() // 提交一下排列
  }, 

更多逻辑代码欢迎体验组件--mycomponentsvue

npm install mycomponentsvue@0.0.13

import mycomponents from 'mycomponentsvue'

Vue.use(mycomponents)

<dragGroup v-model="data"></dragGroup> // data空数组初始化

本组件只用于学习交流哈!所以名字起的比较随意!~


⛳参考学习

如果你还不会vuedraggable的使用,推荐先学习它的配置。

参考vuedraggable官方例子 https://sortablejs.github.io/Vue.Draggable/#/nested-with-vmodel

基于vue2,vuedraggable开发组图层2级拖拽组件_拖拽_04

🚀写在最后

如果本文中有bug、逻辑错误,或者您有更好的优化方案欢迎评论联系我哦!~关注我持续分享日常工作中的组件设计和学习分享,一起进步加油!


举报

相关推荐

0 条评论