Element UI 组件el-table以高内聚的方式实现拖拽排序

最近碰到el-table做拖拽排序的需求,而且有好几个页面都用到相同的代码逻辑,就想着把这拖拽排序的代码给封装一下,多个地方都能复用。并且以高内聚的方式封装,其他项目要用的话,直接copy一个js文件过去就能用,方便快捷。

当然拖拽排序这个逻辑太复杂肯定无法手写,我们可以先安装sortablejs这个库,然后把初始化这个库的代码给封装一下:

/*
 * @Description  : el-table 拖拽排序
 */

import Sortable from 'sortablejs'

let oldIndex = null
let newIndex = null
const ROW_CLASS = 'sort-target-row'

export function initSortable(options = {}) {
  appendInlineStylesheet()

  const _this = this
  const {
    selector = '.el-table__body-wrapper tbody',
    handle = '.handle-move',
    draggable = '.el-table__row',
    list = 'list'
  } = options

  // 1. 获取拖拽元素的父元素
  // 因为使用的element的table 所以可直接获取tbody
  const el = document.querySelector(selector)

  // 2. 创建并配置相关参数
  var sortable = new Sortable(el, {
    // 只有按住手柄才能进行拖拽,故而设置此参数
    handle, // css选择器的字符串 若设置该值,则表示只有按住拖动手柄才能使列表单元进行拖动

    // 行拖拽,所以该值设置为行class
    draggable, // 允许拖拽的项目类名

    // 因为拖拽过程中不进行位置调整,所以就要记录拖拽行初始位置以及目标位置
    // 拖拽中 回调函数
    onMove(customEvent) {
      // 禁止在拖拽过程中交换位置
      // 可将初始位置及目标位置进行记录
      oldIndex = customEvent.dragged.rowIndex //  初始位置
      newIndex = customEvent.related.rowIndex // 目标位置

      cleanClass(el)
      // 给目标行添加高亮样式
      el.childNodes[newIndex].classList.add(ROW_CLASS)

      return false // 不进行交换
      // 返回值有三个
      // return false; — for cancel
      // return -1; — insert before target
      // return 1; — insert after target
    },

    // 拖拽结束,调整位置
    onEnd() {
      // 在拖拽结束后,获取初始及目标位置
      const sortList = getValueByPath(_this, list)
      const currRow = sortList.splice(oldIndex, 1)[0]
      sortList.splice(newIndex, 0, currRow)

      // 拖拽完成,初始位置及目标位置重置
      newIndex = null
      oldIndex = null

      cleanClass(el)
    }
  })

  console.log('sortable:', sortable)
}

// 清除非目标行的高亮样式
function cleanClass(el) {
  for (const i of el.childNodes) {
        i.classList?.remove(ROW_CLASS)
  }
}

// 全局添加高亮样式
let initialized = false
function appendInlineStylesheet() {
  if (initialized) return
  const style = document.createElement('style')
  style.type = 'text/css'
  const cssRules = `
    .${ROW_CLASS} {
        background: oldlace!important;
        box-shadow: inset 0px 0px 16px 0px rgba(210, 210, 210, 0.5);
    }
    .handle-move {
        cursor: move;
    }
    .handle-move > i {
        color: #555;
        font-size: 16px;
        transition: all .2s;
    }
    .handle-move:hover > i {
        transform: scale(1.3);
    }
    `
  style.appendChild(document.createTextNode(cssRules))

  // 将新的<style>元素添加到<head>中
  const head = document.head || document.getElementsByTagName('head')[0]
  head.appendChild(style)
  initialized = true
}

// 路径解析,例如传入'a.b.c'
function getValueByPath(obj, path) {
  const keys = path.split('.')
  let current = obj
  for (const key of keys) {
    if (current && Object.prototype.hasOwnProperty.call(current, key)) {
      current = current[key]
    } else {
      return undefined // 当前层级未找到对应属性,返回undefined
    }
  }
  return current
}

使用也很简单,只需要引入这个js,把需要改成拖拽的表格类名传入,把表格对应的数据传入,还要记得用call把当前vue组件实例传入,以便拖拽的时候,视图驱动修改表格数据:

// table-sort-demo.vue

import { initSortable } from './sort.js'



mounted() {
   initSortable.call(this, { list: 'list', selector: '.le-table .el-table__body-wrapper tbody' })
}

实现效果如下:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值