sortableTable.html 7.0 KB

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