123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- <template>
- <div :key="dragKey">
- <slot />
- </div>
- </template>
- <script>
- /*
- * Note by hht 2020-03-03
- * This component is used to make el-table draggable.
- * Because vue-draggable can not work fine with el-table.
- * So I use sortablejs to make el-table draggable instead.
- * The problem is :key refreshes the whole table is not a good idea,
- * But I can not find a better way to resolve sortable & el-table's cache problem.
- * Other scene we can use `vue-draggable` component in this project.
- * */
- /*
- * Design by hht 2020-03-03
- * 1 Use key on the root element to force table re-render.
- * 2 Use dragOptions to override default drag options. So all sortablejs options can be used.
- * 3 Use onEnd event to update table data and emit drag-change event.
- * 4 Use sortable.destroy() to destroy sortable instance after each drag.
- * 5 Use raw data of el-table as drag list, so drag-change modify and emit the same data as el-table.
- * */
- import Sortable from 'sortablejs'
- export default {
- name: 'TableDraggable',
- props: {
- dragOptions: {
- type: Object,
- default: () => ({})
- }
- },
- data() {
- return {
- dragVersion: 0,
- sortable: null,
- defaultSortOptions: {
- animation: 150,
- ghostClass: 'table-draggable-ghost',
- onStart: (evt) => {
- // close all expanded rows in el-table
- const tableRef = this.findElTable(this)
- tableRef.store.states.expandRows = []
- },
- onEnd: (evt) => {
- const tableRef = this.findElTable(this)
- // get reference of table data
- const list = tableRef.data
- // update list if drag index changed, and emit drag-change event
- if (evt.oldIndex !== evt.newIndex) {
- // exchange list item from old index to new index
- const item = list.splice(evt.oldIndex, 1)[0]
- list.splice(evt.newIndex, 0, item)
- // update version to force table re-render
- this.dragVersion++
- this.destroySortable()
- // emit drag-change event
- this.$emit('drag-change', list, evt)
- setTimeout(() => {
- this.initSortable()
- }, 0)
- }
- }
- }
- }
- },
- computed: {
- dragKey() {
- // NOTE: drag key is very important, it will force the table to re-render
- // $forceUpdate() is not enough, so does modify table data or call other methods
- return 'table-draggable-' + this.dragVersion
- },
- sortOptions() {
- return {
- ...this.defaultSortOptions,
- ...this.dragOptions
- }
- }
- },
- mounted() {
- this.initSortable()
- },
- methods: {
- initSortable() {
- this.destroySortable()
- const tableBody = this.$el.querySelector('tbody')
- this.sortable = Sortable.create(tableBody, this.sortOptions)
- },
- destroySortable() {
- this.sortable?.destroy()
- this.sortable = null
- },
- findElTable(node) {
- if (node.$options.name === 'ElTable') {
- return node
- }
- if (node.$children && node.$children.length > 0) {
- for (let i = 0; i < node.$children.length; i++) {
- const child = node.$children[i]
- const table = this.findElTable(child)
- if (table) {
- return table
- }
- }
- }
- },
- refresh() {
- this.destroySortable()
- this.initSortable()
- this.dragVersion++
- }
- }
- }
- </script>
- <style lang="scss">
- @import "@/assets/styles/variables.scss";
- .table-draggable-ghost {
- opacity: 0.8 !important;
- background-color: mix($--color-primary, #fff, 20%) !important;
- }
- </style>
|