index.vue 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. <template>
  2. <div :key="dragKey">
  3. <slot />
  4. </div>
  5. </template>
  6. <script>
  7. /*
  8. * Note by hht 2020-03-03
  9. * This component is used to make el-table draggable.
  10. * Because vue-draggable can not work fine with el-table.
  11. * So I use sortablejs to make el-table draggable instead.
  12. * The problem is :key refreshes the whole table is not a good idea,
  13. * But I can not find a better way to resolve sortable & el-table's cache problem.
  14. * Other scene we can use `vue-draggable` component in this project.
  15. * */
  16. /*
  17. * Design by hht 2020-03-03
  18. * 1 Use key on the root element to force table re-render.
  19. * 2 Use dragOptions to override default drag options. So all sortablejs options can be used.
  20. * 3 Use onEnd event to update table data and emit drag-change event.
  21. * 4 Use sortable.destroy() to destroy sortable instance after each drag.
  22. * 5 Use raw data of el-table as drag list, so drag-change modify and emit the same data as el-table.
  23. * */
  24. import Sortable from 'sortablejs'
  25. export default {
  26. name: 'TableDraggable',
  27. props: {
  28. dragOptions: {
  29. type: Object,
  30. default: () => ({})
  31. }
  32. },
  33. data() {
  34. return {
  35. dragVersion: 0,
  36. sortable: null,
  37. defaultSortOptions: {
  38. animation: 150,
  39. ghostClass: 'table-draggable-ghost',
  40. onStart: (evt) => {
  41. // close all expanded rows in el-table
  42. const tableRef = this.findElTable(this)
  43. tableRef.store.states.expandRows = []
  44. },
  45. onEnd: (evt) => {
  46. const tableRef = this.findElTable(this)
  47. // get reference of table data
  48. const list = tableRef.data
  49. // update list if drag index changed, and emit drag-change event
  50. if (evt.oldIndex !== evt.newIndex) {
  51. // exchange list item from old index to new index
  52. const item = list.splice(evt.oldIndex, 1)[0]
  53. list.splice(evt.newIndex, 0, item)
  54. // update version to force table re-render
  55. this.dragVersion++
  56. this.destroySortable()
  57. // emit drag-change event
  58. this.$emit('drag-change', list, evt)
  59. setTimeout(() => {
  60. this.initSortable()
  61. }, 0)
  62. }
  63. }
  64. }
  65. }
  66. },
  67. computed: {
  68. dragKey() {
  69. // NOTE: drag key is very important, it will force the table to re-render
  70. // $forceUpdate() is not enough, so does modify table data or call other methods
  71. return 'table-draggable-' + this.dragVersion
  72. },
  73. sortOptions() {
  74. return {
  75. ...this.defaultSortOptions,
  76. ...this.dragOptions
  77. }
  78. }
  79. },
  80. mounted() {
  81. this.initSortable()
  82. },
  83. methods: {
  84. initSortable() {
  85. this.destroySortable()
  86. const tableBody = this.$el.querySelector('tbody')
  87. this.sortable = Sortable.create(tableBody, this.sortOptions)
  88. },
  89. destroySortable() {
  90. this.sortable?.destroy()
  91. this.sortable = null
  92. },
  93. findElTable(node) {
  94. if (node.$options.name === 'ElTable') {
  95. return node
  96. }
  97. if (node.$children && node.$children.length > 0) {
  98. for (let i = 0; i < node.$children.length; i++) {
  99. const child = node.$children[i]
  100. const table = this.findElTable(child)
  101. if (table) {
  102. return table
  103. }
  104. }
  105. }
  106. },
  107. refresh() {
  108. this.destroySortable()
  109. this.initSortable()
  110. this.dragVersion++
  111. }
  112. }
  113. }
  114. </script>
  115. <style lang="scss">
  116. @import "@/assets/styles/variables.scss";
  117. .table-draggable-ghost {
  118. opacity: 0.8 !important;
  119. background-color: mix($--color-primary, #fff, 20%) !important;
  120. }
  121. </style>