sortableTable.html 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. {{define "component/sortableTableTrigger"}}
  2. <a-icon type="drag"
  3. style="cursor: move;"
  4. @mouseup="mouseUpHandler"
  5. @mousedown="mouseDownHandler"
  6. @click="clickHandler" />
  7. {{end}}
  8. {{define "component/sortableTable"}}
  9. <script>
  10. const DRAGGABLE_ROW_CLASS = 'draggable-row';
  11. const findParentRowElement = (el) => {
  12. if (!el || !el.tagName) {
  13. return null;
  14. } else if (el.classList.contains(DRAGGABLE_ROW_CLASS)) {
  15. return el;
  16. } else if (el.parentNode) {
  17. return findParentRowElement(el.parentNode);
  18. } else {
  19. return null;
  20. }
  21. }
  22. Vue.component('a-table-sortable', {
  23. data() {
  24. return {
  25. sortingElementIndex: null,
  26. newElementIndex: null,
  27. };
  28. },
  29. props: ['data-source', 'customRow'],
  30. inheritAttrs: false,
  31. provide() {
  32. const sortable = {}
  33. Object.defineProperty(sortable, "setSortableIndex", {
  34. enumerable: true,
  35. get: () => this.setCurrentSortableIndex,
  36. });
  37. Object.defineProperty(sortable, "resetSortableIndex", {
  38. enumerable: true,
  39. get: () => this.resetSortableIndex,
  40. });
  41. return {
  42. sortable,
  43. }
  44. },
  45. render: function (createElement) {
  46. return createElement(
  47. 'a-table',
  48. {
  49. class: {
  50. 'ant-table-is-sorting': this.isDragging(),
  51. },
  52. props: {
  53. ...this.$attrs,
  54. 'data-source': this.records,
  55. customRow: (record, index) => this.customRowRender(record, index),
  56. },
  57. on: this.$listeners,
  58. nativeOn: {
  59. drop: (e) => this.dropHandler(e),
  60. },
  61. scopedSlots: this.$scopedSlots,
  62. },
  63. this.$slots.default,
  64. )
  65. },
  66. created() {
  67. this.$memoSort = {};
  68. },
  69. methods: {
  70. isDragging() {
  71. const currentIndex = this.sortingElementIndex;
  72. return currentIndex !== null && currentIndex !== undefined;
  73. },
  74. resetSortableIndex(e, index) {
  75. this.sortingElementIndex = null;
  76. this.newElementIndex = null;
  77. this.$memoSort = {};
  78. },
  79. setCurrentSortableIndex(e, index) {
  80. this.sortingElementIndex = index;
  81. },
  82. dragStartHandler(e, index) {
  83. if (!this.isDragging()) {
  84. e.preventDefault();
  85. return;
  86. }
  87. },
  88. dragStopHandler(e, index) {
  89. this.resetSortableIndex(e, index);
  90. },
  91. dragOverHandler(e, index) {
  92. if (!this.isDragging()) {
  93. return;
  94. }
  95. e.preventDefault();
  96. const currentIndex = this.sortingElementIndex;
  97. if (index === currentIndex) {
  98. this.newElementIndex = null;
  99. return;
  100. }
  101. const row = findParentRowElement(e.target);
  102. if (!row) {
  103. return;
  104. }
  105. const rect = row.getBoundingClientRect();
  106. const offsetTop = e.pageY - rect.top;
  107. if (offsetTop < rect.height / 2) {
  108. this.newElementIndex = Math.max(index - 1, 0);
  109. } else {
  110. this.newElementIndex = index;
  111. }
  112. },
  113. dropHandler(e) {
  114. if (this.isDragging()) {
  115. this.$emit('onsort', this.sortingElementIndex, this.newElementIndex);
  116. }
  117. },
  118. customRowRender(record, index) {
  119. const parentMethodResult = this.customRow?.(record, index) || {};
  120. const newIndex = this.newElementIndex;
  121. const currentIndex = this.sortingElementIndex;
  122. return {
  123. ...parentMethodResult,
  124. attrs: {
  125. ...(parentMethodResult?.attrs || {}),
  126. draggable: true,
  127. },
  128. on: {
  129. ...(parentMethodResult?.on || {}),
  130. dragstart: (e) => this.dragStartHandler(e, index),
  131. dragend: (e) => this.dragStopHandler(e, index),
  132. dragover: (e) => this.dragOverHandler(e, index),
  133. },
  134. class: {
  135. ...(parentMethodResult?.class || {}),
  136. [DRAGGABLE_ROW_CLASS]: true,
  137. ['dragging']: this.isDragging()
  138. ? (newIndex === null ? index === currentIndex : index === newIndex)
  139. : false,
  140. },
  141. };
  142. }
  143. },
  144. computed: {
  145. records() {
  146. const newIndex = this.newElementIndex;
  147. const currentIndex = this.sortingElementIndex;
  148. if (!this.isDragging() || newIndex === null || currentIndex === newIndex) {
  149. return this.dataSource;
  150. }
  151. if (this.$memoSort.newIndex === newIndex) {
  152. return this.$memoSort.list;
  153. }
  154. let list = [...this.dataSource];
  155. list.splice(newIndex, 0, list.splice(currentIndex, 1)[0]);
  156. this.$memoSort = {
  157. newIndex,
  158. list,
  159. };
  160. return list;
  161. }
  162. }
  163. });
  164. Vue.component('table-sort-trigger', {
  165. template: `{{template "component/sortableTableTrigger"}}`,
  166. props: ['item-index'],
  167. inject: ['sortable'],
  168. methods: {
  169. mouseDownHandler(e) {
  170. if (this.sortable) {
  171. this.sortable.setSortableIndex(e, this.itemIndex);
  172. }
  173. },
  174. mouseUpHandler(e) {
  175. if (this.sortable) {
  176. this.sortable.resetSortableIndex(e, this.itemIndex);
  177. }
  178. },
  179. clickHandler(e) {
  180. e.preventDefault();
  181. },
  182. }
  183. })
  184. </script>
  185. <style>
  186. .ant-table-is-sorting .draggable-row td {
  187. background-color: white !important;
  188. }
  189. .dark .ant-table-is-sorting .draggable-row td {
  190. background-color: var(--dark-color-surface-100) !important;
  191. }
  192. .ant-table-is-sorting .dragging {
  193. opacity: 0.5;
  194. }
  195. .ant-table-is-sorting .dragging .ant-table-row-index {
  196. opacity: 0;
  197. }
  198. </style>
  199. {{end}}