vue element-ui表格行拖拽

查找一下,现有的拖拽实现,是基于Sortable.js 实现,例如vuedraggable 就是基于此实现的 但vuedraggable这个实现有一个坑点就是要求,要拖动项必须是其标签的第一个子集。

 

但element-ui的表格,里面嵌套了很多div,如果用vuedraggable这个就无法达到其效果了。

 

基于此,我参考vuedraggable,写了一个专门针对element-ui表格的拖拽封装。 today-vue-plug-in

[另外也有针对vue-easytable 的表格拖拽封装]

git:   https://github.com/wfwfwf/vue-plug-in

代码如下:(如果有需要优化的地方,可以联系我)

 

var Sortable = require('sortablejs')
if (!Array.from) {
  Array.from = function (object) {
    return [].slice.call(object)
  }
}

function buildAttribute (object, propName, value) {
  if (value === undefined || value === 'undefined') {
    return object
  }
  object = (object === null) ? {} : object
  object[propName] = value
  return object
}

function removeNode (node) {
  node.parentElement.removeChild(node)
}

function insertNodeAt (fatherNode, node, position) {
  const refNode = (position === 0) ? fatherNode.children[0] : fatherNode.children[position - 1].nextSibling
  fatherNode.insertBefore(node, refNode)
}

function computeVmIndex (vnodes, element) {
  return vnodes.map(elt => elt.elm).indexOf(element)
}

function computeIndexes (slots, children, isTransition) {
  if (!slots) {
    return []
  }

  const elmFromNodes = slots.map(elt => elt.elm)
  const rawIndexes = [...children].map(elt => elmFromNodes.indexOf(elt))
  return isTransition ? rawIndexes.filter(ind => ind !== -1) : rawIndexes
}

function emit (evtName, evtData) {
  this.$nextTick(() => this.$emit(evtName.toLowerCase(), evtData))
}

function delegateAndEmit (evtName) {
  return (evtData) => {
    if (this.realList !== null) {
      this['onDrag' + evtName](evtData)
    }
    emit.call(this, evtName, evtData)
  }
}

const eventsListened = ['Start', 'Add', 'Remove', 'Update', 'End', 'RowClick']
const eventsToEmit = ['Choose', 'Sort', 'Filter', 'Clone', 'RowClick', 'selectionChange']
const readonlyProperties = ['Move', ...eventsListened, ...eventsToEmit].map(evt => 'on' + evt)

const props = {
  options: Object,
  list: {
    type: Array,
    required: false,
    default: null
  },
  value: {
    type: Array,
    required: false,
    default: null
  },
  noTransitionOnDrag: {
    type: Boolean,
    default: false
  },
  clone: {
    type: Function,
    default: (original) => { return original }
  },
  element: {
    type: String,
    default: 'div'
  },
  move: {
    type: Function,
    default: null
  },

  dragSelector: {
    type: String,
    default: null
  }
}

export default {
  name: 'td-draggable',
  props,
  data () {
    return {
      transitionMode: false,
      noneFunctionalComponentMode: false,
      init: false
    }
  },

  render (h, context) {
    const slots = this.$slots.default
    if (slots && slots.length === 1) {
      const child = slots[0]
      if (child.componentOptions && child.componentOptions.tag === 'transition-group') {
        this.transitionMode = true
      }
    }
    let children = slots
    const { footer } = this.$slots
    if (footer) {
      children = slots ? [...slots, ...footer] : [...footer]
    }
    var attributes = null

    const update = (name, value) => { attributes = buildAttribute(attributes, name, value) }

    // 对element tabel行点击事件进行兼容
    if (JSON.stringify(this.$attrs) !== '{}') {
      update('attrs', {data: this.list})
    } else {
      update('attrs', this.$attrs)
    }

    // 继承element table 的所有方法
    attributes.on = this.$listeners
    // {
    //   'selection-change': (selection) => {
    //     console.log('params: ', selection)
    //     console.log('this: ', this)
    //     this.$emit('selection-change', selection)
    //   },
    //   'row-click': (row, event, column) => {
    //     console.log('params: ', row)
    //     this.$emit('row-click', row)
    //   }
    // }
    return h(this.element, attributes, children)
  },

  mounted () {
    this.noneFunctionalComponentMode = this.element.toLowerCase() !== this.$el.nodeName.toLowerCase()

    if (this.noneFunctionalComponentMode && this.transitionMode) {
      throw new Error(`Transition-group inside component is not supported. Please alter element value or remove transition-group. Current element value: ${this.element}`)
    }
    var optionsAdded = {}
    eventsListened.forEach(elt => {
      optionsAdded['on' + elt] = delegateAndEmit.call(this, elt)
    })

    eventsToEmit.forEach(elt => {
      optionsAdded['on' + elt] = emit.bind(this, elt)
    })

    const options = Object.assign({}, this.options, optionsAdded, { onMove: (evt, originalEvent) => { return this.onDragMove(evt, originalEvent) } })
    !('draggable' in options) && (options.draggable = '>*')
    this._sortable = new Sortable(this.rootContainer, options)
    // this.computeIndexes()
  },
  beforeDestroy () {
    this._sortable.destroy()
  },

  computed: {
    rootContainer () {
      return this.transitionMode ? this.$el.children[0] : (this.dragSelector ? this.$el.querySelector(this.dragSelector) : this.$el)
    },
    realList () {
      return (this.list) ? this.list : this.value
    }
  },

  watch: {
    options: {
      handler (newOptionValue) {
        for (var property in newOptionValue) {
          if (readonlyProperties.indexOf(property) === -1) {
            this._sortable.option(property, newOptionValue[property])
          }
        }
      },
      deep: true
    }
  },

  methods: {
    getUnderlyingVm (evt) {
      if (!evt) {
        return null
      }
      let index = evt.oldIndex
      if (typeof (index) === 'undefined' || index === -1) {
        return null
      }
      const element = this.realList[index]
      return { index, element }
    },
    emitChanges (evt) {
      this.$nextTick(() => {
        this.$emit('change', evt)
      })
    },
    alterList (onList) {
      if (this.list) {
        onList(this.list)
      } else {
        const newList = [...this.value]
        onList(newList)
        this.$emit('input', newList)
      }
    },
    spliceList () {
      const spliceList = list => list.splice(...arguments)
      this.alterList(spliceList)
    },
    updatePosition (oldIndex, newIndex) {
      const updatePosition = list => list.splice(newIndex, 0, list.splice(oldIndex, 1)[0])
      this.alterList(updatePosition)
    },
    onDragStart (evt) {
      this.context = this.getUnderlyingVm(evt)
      const element = this.realList[evt.oldIndex]
      if (this.context) {
        evt.item._underlying_vm_ = this.clone(this.context.element)
      }
    },
    onDragMove (evt) {
      // console.log("onDragMove: ", evt)
    },
    onDragUpdate (evt) {
      removeNode(evt.item)
      insertNodeAt(evt.from, evt.item, evt.oldIndex)
      const oldIndex = this.context.index
      const newIndex = evt.newIndex
      this.updatePosition(evt.oldIndex, evt.newIndex)
      const moved = { element: this.context.element, oldIndex, newIndex }
      this.emitChanges({ moved })
    },
    onDragEnd (evt) {
    }
  }
}

 

你可能感兴趣的:(js)