aTableSortable.html 6.6 KB

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