使用vuedraggable 对一个 `二维列表` 实现拖拽修改

使用vuedraggable 对一个 二维列表 实现拖拽修改

vuedraggable实现座位布局拖拽修改

方案一

修改 end,和move,在move中获取移动的元素值,和目的地元素值,在end中交换这两个元素

局限
  • 元素需要是唯一值,不能有重复的
    因为每次只能获取到值,而不是元素的位置,这就导致需要遍历整个列表查询位置,很麻烦
  • 在创建删除队列后,无法将元素拖到删除队列
实现
<vue-draggable
      :disabled="!draggable"
      v-model="arrange[row-1]"
      group="arrange"
      :move="dragMoveHandler"
      @end="dragEndHandler"
  >
  	<!--这里是每行前面的序号-->
    <div class="seat-arrange-col" slot="header" style="user-select: none;">
      <div class="col-index">
        {{ row }}
      </div>
    </div>
    <div class="seat-arrange-col" v-for="col in arrange[row - 1].length" :key="'col' + col">
      <seat-block :key="'row' + row + 'col'+ col" :is-select="selectStatus[row-1][col-1]"
                  :type="arrange[row-1][col-1]"
                  :row="row - 1"
                  :col="col - 1"
                  @seat-select="selectBlockHandler"
                  :isDrag="draggable"
      />
    </div>
    <!--这里是没行后面的添加按键-->
    <div class="seat-arrange-col" slot="footer">
      <div class="col-bt">
        <el-button v-if="draggable" icon="el-icon-plus" type="primary"
                   circle
                   size="mini"
                   :disabled="!canAdd(arrange[row-1])"
                   @click="addSeatHandler(row-1)"
        >
        </el-button>
      </div>
    </div>
</vue-draggable>
dragMoveHandler(evt, originalEvent) {
  this.moveStart = { element: evt.draggedContext.element, index: evt.draggedContext.index }
  this.moveEnd = { element: evt.relatedContext.element, index: evt.relatedContext.index }
  return false
},
dragEndHandler(evt) {
  let relatedRow = -1, draggedRow = -1
  /*start:查询元素*/
  for (let i = 0; i < this.arrange.length; i++) {
    if (draggedRow !== -1 && relatedRow !== -1) break
    if (draggedRow === -1 && this.arrange[i][this.moveStart.index] === this.moveStart.element) {
      draggedRow = i
    }
    if (relatedRow === -1 && this.arrange[i][this.moveEnd.index] === this.moveEnd.element) {
      relatedRow = i
    }
  }
  /*end*/
  /*start:交换对调的元素 更新矩阵*/
  this.$set(this.arrange[draggedRow], this.moveStart.index, this.moveEnd.element)
  this.$set(this.arrange[relatedRow], this.moveEnd.index, this.moveStart.element)
  /*end*/
},
效果(为了导出的gif比较小,就去色了)

演示一

方案二

突然发现 可以在 vue-draggable标签里添加自定义标签,可以从move的tofrom里面的$attrs拿到,这就好办了,以前那种方法就是因为不知道怎么获取移动开始元素和目的地元素的信息,才只能用全局搜索的方法定位,现在的话可以直接获取就好办多了

在这里插入图片描述

实现(代码写得很乱,能用就行,哈哈哈哈)

思路就是在vue-draggable 加属性用来标记属于哪个分组以及是哪一行,我用的divide表示分组,row表示是二维数组的哪一行

vue那里就是覆写move和end,move里面记录每次移动的参数,最后一次的参数就是目的地,end里面就是判断从那里移动到哪里,并执行移动

移动需要用this.$set或者是其他能出发更新的,比如数组的pop,push等方法,这里用的splice(arr,index,remove,...addItem),第一个参数的数组,第二个是操作位置,第三个是在这个位置往后要移除多少元素,后面的参数是移除后插入的元素

<vue-draggable
      :disabled="!draggable"
      v-model="arrange[row-1]"
      group="arrange"
      :row="row-1"
      divide="arrange"
      :move="dragMoveHandler"
      @end="dragEndHandler($event,'arrange')"
  >
    <div class="seat-arrange-col" slot="header" style="user-select: none;">
      <div class="col-index">
        {{ row }}
      </div>
    </div>
    <div class="seat-arrange-col" v-for="col in arrange[row - 1].length" :key="'col' + col">
      <seat-block :key="'row' + row + 'col'+ col" :is-select="selectStatus[row-1][col-1]"
                  :type="arrange[row-1][col-1]"
                  :row="row - 1"
                  :col="col - 1"
                  @seat-select="selectBlockHandler"
                  :isDrag="draggable"
      />
    </div>
    <div class="seat-arrange-col" slot="footer">
      <div class="col-bt">
        <el-button v-if="draggable" icon="el-icon-plus" type="primary"
                   circle
                   size="mini"
                   :disabled="!canAdd(arrange[row-1])"
                   @click="addSeatHandler(row-1)"
        >
        </el-button>
      </div>
    </div>
</vue-draggable>
dragMoveHandler(evt, originalEvent) {
  let from = evt.from.__vue__.$attrs
  let to = evt.to.__vue__.$attrs
  if(from == null || to == null) return;
  this.moveFrom = { row: from.row, index: evt.draggedContext.index, divide: from.divide }
  this.moveTo = { row: to.row, index: evt.relatedContext.index, divide: to.divide }
  return false // 阻止默认的拖拽
},
dragEndHandler(evt) {
   // 非法移动判断
  if (this.moveTo.divide === 'arrange' && this.moveTo.index == null) return

  // 禁止删除列交换拖动
  if (this.moveFrom.divide === this.moveTo.divide === 'deleted') return
  let elementTo = this.getElement(this.moveTo.divide, this.moveTo.row, this.moveTo.index)
  let elementFrom = this.getElement(this.moveFrom.divide, this.moveFrom.row, this.moveFrom.index)
  // 从删除列拖到正常列
  if (this.moveFrom.divide === 'deleted' && this.moveTo.divide === 'arrange') {
    // 只有拖到不为空的列才交换,不然是填充到这一列
    if (elementTo > 0) {
      this.$set(this.arrangeDeleted, this.moveFrom.index, elementTo)
    } else {
      this.arrangeDeleted.splice(this.moveFrom.index, 1)
    }
    this.$set(this.arrange[this.moveTo.row], this.moveTo.index, elementFrom)
  }
  // 正常列拖动都是交换
  if (this.moveFrom.divide === 'arrange' && this.moveTo.divide === 'arrange') {
    this.$set(this.arrange[this.moveFrom.row], this.moveFrom.index, elementTo)
    this.$set(this.arrange[this.moveTo.row], this.moveTo.index, elementFrom)
  }
  // 正常列拖到删除列 正常列填充-1,删除列添加元素
  if (this.moveFrom.divide === 'arrange' && this.moveTo.divide === 'deleted') {
    this.arrangeDeleted.push(elementFrom)
    this.$set(this.arrange[this.moveFrom.row], this.moveFrom.index, -1)
  }
},
getElement(divide, row, index) {
  if (divide === 'deleted') {
    return this.arrangeDeleted[index]
  } else if (divide === 'arrange') {
    return this.arrange[row][index]
  }
  return undefined
},
效果(完美,只需要处理下删除列,让它自动换行)

演示二

缺陷

因为是交换元素,所以在移动到删除列的时候需要有个格子在那才能拖过去

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值