DragTable.vue 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. <template>
  2. <table class="table drag-table">
  3. <colgroup>
  4. <col v-if="dragEnable" width="20" />
  5. <col v-for="column in columns" :key="column.prop" :width="column.width" />
  6. </colgroup>
  7. <thead>
  8. <tr>
  9. <th v-if="dragEnable"></th>
  10. <th v-for="column in columns" :key="column.prop">
  11. {{ column.label }}
  12. </th>
  13. </tr>
  14. </thead>
  15. <tbody
  16. class="drag-table-tbody"
  17. @drop.prevent
  18. @dragover.prevent="dragOver($event)"
  19. @dragleave.prevent
  20. >
  21. <tr
  22. v-for="row in tableData"
  23. :key="row.$key"
  24. :id="row.$key"
  25. :class="[
  26. 'drag-table-row',
  27. {
  28. 'after-drop': row.$key === curDropRow.$key && isDragDown,
  29. 'before-drop': row.$key === curDropRow.$key && !isDragDown,
  30. },
  31. ]"
  32. draggable="false"
  33. @dragstart="($event) => dragStart($event, row)"
  34. @dragend.prevent="dragEnd"
  35. >
  36. <td
  37. v-if="dragEnable"
  38. class="drag-handle"
  39. @mousedown.stop="dragHandleMousedown"
  40. @mouseup.stop="dragHandleMouseup"
  41. >
  42. <i class="el-icon-more"></i>
  43. </td>
  44. <td
  45. v-for="column in columns"
  46. :key="column.prop"
  47. :class="column.className"
  48. >
  49. <slot :name="column.prop" v-bind:row="row">
  50. {{ row[column.prop] }}
  51. </slot>
  52. </td>
  53. </tr>
  54. </tbody>
  55. </table>
  56. </template>
  57. <script>
  58. import { randomCode } from "@/plugins/utils";
  59. export default {
  60. name: "drag-table",
  61. props: {
  62. data: {
  63. type: Array,
  64. default() {
  65. return [];
  66. },
  67. },
  68. columns: {
  69. type: Array,
  70. default() {
  71. // {label,prop,width?}[]
  72. return [];
  73. },
  74. },
  75. sortField: {
  76. type: String,
  77. default: "sortNum",
  78. },
  79. dragEnable: {
  80. type: Boolean,
  81. default: true,
  82. },
  83. },
  84. data() {
  85. return {
  86. tableData: [],
  87. curDragRow: {},
  88. curDropRow: {},
  89. dragStartPageY: null,
  90. isDragDown: false,
  91. };
  92. },
  93. methods: {
  94. buildData(val) {
  95. if (!val) {
  96. this.tableData = [];
  97. return;
  98. }
  99. this.tableData = val.map((item, index) => {
  100. return {
  101. ...item,
  102. $index: index + 1,
  103. $key: randomCode(),
  104. };
  105. });
  106. },
  107. getRelateElement(dom) {
  108. let element = null;
  109. let parentNode = dom;
  110. while (!element && !parentNode.className.includes("drag-table-tbody")) {
  111. if (!element && parentNode["id"]) {
  112. element = parentNode;
  113. } else {
  114. parentNode = parentNode.parentNode;
  115. }
  116. }
  117. return element;
  118. },
  119. getSiblingRow(targetKey, offset) {
  120. const pos = this.tableData.findIndex((row) => row.$key === targetKey);
  121. return this.tableData[pos + offset] || null;
  122. },
  123. dragStart(e, row) {
  124. if (!this.dragEnable) return;
  125. this.dragStartPageY = e.pageY;
  126. this.curDragRow = row;
  127. },
  128. dragOver(e) {
  129. if (!this.dragEnable) return;
  130. // console.log(e.target);
  131. this.isDragDown = e.pageY > this.dragStartPageY;
  132. if (e.target.className.includes("drag-table-tbody")) {
  133. this.curDropRow = this.isDragDown
  134. ? this.tableData.slice(-1)[0]
  135. : this.tableData[0];
  136. return;
  137. }
  138. const elementDom = this.getRelateElement(e.target);
  139. if (!elementDom) return;
  140. const targetKey = elementDom.id;
  141. this.curDropRow = this.getSiblingRow(targetKey, 0);
  142. },
  143. dragEnd(e) {
  144. if (!this.dragEnable) return;
  145. if (this.curDragRow.id === this.curDropRow.id || !this.curDropRow) return;
  146. // 往下:target上一个位置
  147. // 往上:target下一个位置
  148. this.moveElementToElement({
  149. curRow: this.curDragRow,
  150. toRow: this.curDropRow,
  151. isDragDown: this.isDragDown,
  152. });
  153. e.target.setAttribute("draggable", false);
  154. this.curDragRow = {};
  155. this.curDropRow = {};
  156. this.dragStartPageY = null;
  157. this.isDragDown = false;
  158. },
  159. moveElementToElement({ curRow, toRow, isDragDown }) {
  160. const curPos = this.tableData.findIndex(
  161. (row) => row.$key === curRow.$key
  162. );
  163. this.tableData.splice(curPos, 1);
  164. const toPos = this.tableData.findIndex((row) => row.$key === toRow.$key);
  165. const offset = isDragDown ? 1 : 0;
  166. this.tableData.splice(toPos + offset, 0, curRow);
  167. this.tableData.forEach((row, index) => {
  168. row[this.sortField] = index + 1;
  169. row.$index = index + 1;
  170. });
  171. this.$emit("sort-change", this.tableData);
  172. },
  173. dragHandleMousedown(e) {
  174. const elementDom = this.getRelateElement(e.target);
  175. elementDom.setAttribute("draggable", true);
  176. },
  177. dragHandleMouseup(e) {
  178. const elementDom = this.getRelateElement(e.target);
  179. elementDom.setAttribute("draggable", false);
  180. },
  181. },
  182. watch: {
  183. data: {
  184. handler(val) {
  185. this.buildData(val);
  186. },
  187. immediate: true,
  188. },
  189. },
  190. };
  191. </script>