vue 实现拖动排序

552 阅读1分钟

1647416186(1).png 目的是在已有组件基础 或 自己手写列表 实现可拖动排序

思路

1.vue 是数据驱动视图 所以 改变数据就改变视图,所以拖动排序是操作数据就可实现

2.通过 原生js 获取所有即将拖动的元素,然后添加拖动事件 draggable是否能拖动、dragstart被拖动元素触发、drop放下到目标元素触发、dragover拖动到目标元素上触发

3.给遍历每个参与拖动的元素 用 setAttribute 加上 索引,这个索引对应 原来渲染用的数组索引,通过触发 拖拽事件 操作数组

image.png

image.png

image.png

/**
 * 拖动列表排序 获取所有参与拖动的最外层元素,并通过js添加 拖动事件。 添加事件前先清下之前添加的事件,每次添加监听事件都会占用内存避免泄漏
*/

addEvent(){
  // domList
  let domList = document.getElementsByClassName('ant-upload-list-picture-card-container')
  for(let i = 0; i < domList.length; i++){
    let domItem = domList[i]

    domItem.draggable = false;
    domItem.style.cursor = 'auto';

    domItem.removeEventListener('dragstart',this.drag) 
    domItem.removeEventListener('drop',this.drop) 
    domItem.removeEventListener('dragover',this.allowDrop) 
  }

  for(let i = 0; i < domList.length; i++){
    let domItem = domList[i]

    domItem.draggable = true;
    domItem.setAttribute('data-index',i);
    domItem.style.cursor = 'all-scroll';

    domItem.addEventListener('dragstart',this.drag, false) // 开始拖元素触发,作用于拖拽元素
    domItem.addEventListener('drop',this.drop, false) // 当元素放下到drop元素触发,作用于目标元素
    domItem.addEventListener('dragover',this.allowDrop, false) // 当元素拖动到drop元素上时触发,作用于目标元素
  }
},
// 开始拖元素触发,作用于拖拽元素
drag(event) {
  event.dataTransfer.setData('index', event.target.dataset.index ); 
},
// 当元素放下到drop元素触发,作用于目标元素
drop(event) {
  console.log(event,event.srcElement.getAttribute('data-index'))

  event.preventDefault();
  event.stopPropagation();
  
  let startIndex = parseInt(event.dataTransfer.getData('index')),
      trNode = this.handleGetTrNode(event.target),
      trIndex = trNode.getAttribute('data-index'),
      currentIndex = parseInt(trIndex);
      // console.log("start", startIndex);
      // console.log("drop", currentIndex);

  if (startIndex - currentIndex > 0) {
    // console.log("要拖拽的元素的索引 大于 当前位置的元素的索引");
    this.fileList.splice(currentIndex, 0, this.fileList[startIndex]);
    // console.log("删除" + startIndex + 1);
    this.fileList.splice(startIndex + 1, 1)
  } else if (startIndex - currentIndex < 0) {

    // console.log("要拖拽的元素的索引  小于 当前位置的元素的索引");
    this.fileList.splice(currentIndex + 1, 0, this.fileList[startIndex]);
    this.fileList.splice(startIndex, 1)

  } else {
    // console.log("什么也不用做");
  }
},
// 当元素拖动到drop元素上时触发,作用于目标元素
allowDrop(event) {
  event.preventDefault();
  event.stopPropagation();
},
// 防止拖拽松开时 落在子元素上,递归找到目标元素。ob.className 只是一种获取目标元素的形式 也可也是其它方式
handleGetTrNode(ob) {
  console.log(ob.className)
  if(ob.className == 'ant-upload-list-picture-card-container'){
    return ob
  } else{
    return this.handleGetTrNode(ob.parentNode)
  }
},