|
@@ -0,0 +1,218 @@
|
|
|
+{{define "component/sortableTableTrigger"}}
|
|
|
+ <a-icon type="drag"
|
|
|
+ style="cursor: move;"
|
|
|
+ @mouseup="mouseUpHandler"
|
|
|
+ @mousedown="mouseDownHandler"
|
|
|
+ @click="clickHandler" />
|
|
|
+{{end}}
|
|
|
+
|
|
|
+{{define "component/sortableTable"}}
|
|
|
+<script>
|
|
|
+ const DRAGGABLE_ROW_CLASS = 'draggable-row';
|
|
|
+
|
|
|
+ const findParentRowElement = (el) => {
|
|
|
+ if (!el || !el.tagName) {
|
|
|
+ return null;
|
|
|
+ } else if (el.classList.contains(DRAGGABLE_ROW_CLASS)) {
|
|
|
+ return el;
|
|
|
+ } else if (el.parentNode) {
|
|
|
+ return findParentRowElement(el.parentNode);
|
|
|
+ } else {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Vue.component('a-table-sortable', {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ sortingElementIndex: null,
|
|
|
+ newElementIndex: null,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ props: ['data-source', 'customRow'],
|
|
|
+ inheritAttrs: false,
|
|
|
+ provide() {
|
|
|
+ const sortable = {}
|
|
|
+
|
|
|
+ Object.defineProperty(sortable, "setSortableIndex", {
|
|
|
+ enumerable: true,
|
|
|
+ get: () => this.setCurrentSortableIndex,
|
|
|
+ });
|
|
|
+
|
|
|
+ Object.defineProperty(sortable, "resetSortableIndex", {
|
|
|
+ enumerable: true,
|
|
|
+ get: () => this.resetSortableIndex,
|
|
|
+ });
|
|
|
+
|
|
|
+ return {
|
|
|
+ sortable,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ render: function (createElement) {
|
|
|
+ return createElement(
|
|
|
+ 'a-table',
|
|
|
+ {
|
|
|
+ class: {
|
|
|
+ 'ant-table-is-sorting': this.isDragging(),
|
|
|
+ },
|
|
|
+ props: {
|
|
|
+ ...this.$attrs,
|
|
|
+ 'data-source': this.records,
|
|
|
+ customRow: (record, index) => this.customRowRender(record, index),
|
|
|
+ },
|
|
|
+ on: this.$listeners,
|
|
|
+ nativeOn: {
|
|
|
+ drop: (e) => this.dropHandler(e),
|
|
|
+ },
|
|
|
+ scopedSlots: this.$scopedSlots,
|
|
|
+ },
|
|
|
+ this.$slots.default,
|
|
|
+ )
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.$memoSort = {};
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ isDragging() {
|
|
|
+ const currentIndex = this.sortingElementIndex;
|
|
|
+ return currentIndex !== null && currentIndex !== undefined;
|
|
|
+ },
|
|
|
+ resetSortableIndex(e, index) {
|
|
|
+ this.sortingElementIndex = null;
|
|
|
+ this.newElementIndex = null;
|
|
|
+ this.$memoSort = {};
|
|
|
+ },
|
|
|
+ setCurrentSortableIndex(e, index) {
|
|
|
+ this.sortingElementIndex = index;
|
|
|
+ },
|
|
|
+ dragStartHandler(e, index) {
|
|
|
+ if (!this.isDragging()) {
|
|
|
+ e.preventDefault();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ dragStopHandler(e, index) {
|
|
|
+ this.resetSortableIndex(e, index);
|
|
|
+ },
|
|
|
+ dragOverHandler(e, index) {
|
|
|
+ if (!this.isDragging()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ e.preventDefault();
|
|
|
+
|
|
|
+ const currentIndex = this.sortingElementIndex;
|
|
|
+ if (index === currentIndex) {
|
|
|
+ this.newElementIndex = null;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const row = findParentRowElement(e.target);
|
|
|
+ if (!row) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const rect = row.getBoundingClientRect();
|
|
|
+ const offsetTop = e.pageY - rect.top;
|
|
|
+
|
|
|
+ if (offsetTop < rect.height / 2) {
|
|
|
+ this.newElementIndex = Math.max(index - 1, 0);
|
|
|
+ } else {
|
|
|
+ this.newElementIndex = index;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ dropHandler(e) {
|
|
|
+ if (this.isDragging()) {
|
|
|
+ this.$emit('onsort', this.sortingElementIndex, this.newElementIndex);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ customRowRender(record, index) {
|
|
|
+ const parentMethodResult = this.customRow?.(record, index) || {};
|
|
|
+ const newIndex = this.newElementIndex;
|
|
|
+ const currentIndex = this.sortingElementIndex;
|
|
|
+
|
|
|
+ return {
|
|
|
+ ...parentMethodResult,
|
|
|
+ attrs: {
|
|
|
+ ...(parentMethodResult?.attrs || {}),
|
|
|
+ draggable: true,
|
|
|
+ },
|
|
|
+ on: {
|
|
|
+ ...(parentMethodResult?.on || {}),
|
|
|
+ dragstart: (e) => this.dragStartHandler(e, index),
|
|
|
+ dragend: (e) => this.dragStopHandler(e, index),
|
|
|
+ dragover: (e) => this.dragOverHandler(e, index),
|
|
|
+ },
|
|
|
+ class: {
|
|
|
+ ...(parentMethodResult?.class || {}),
|
|
|
+ [DRAGGABLE_ROW_CLASS]: true,
|
|
|
+ ['dragging']: this.isDragging()
|
|
|
+ ? (newIndex === null ? index === currentIndex : index === newIndex)
|
|
|
+ : false,
|
|
|
+ },
|
|
|
+ };
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ records() {
|
|
|
+ const newIndex = this.newElementIndex;
|
|
|
+ const currentIndex = this.sortingElementIndex;
|
|
|
+
|
|
|
+ if (!this.isDragging() || newIndex === null || currentIndex === newIndex) {
|
|
|
+ return this.dataSource;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.$memoSort.newIndex === newIndex) {
|
|
|
+ return this.$memoSort.list;
|
|
|
+ }
|
|
|
+
|
|
|
+ let list = [...this.dataSource];
|
|
|
+ list.splice(newIndex, 0, list.splice(currentIndex, 1)[0]);
|
|
|
+
|
|
|
+ this.$memoSort = {
|
|
|
+ newIndex,
|
|
|
+ list,
|
|
|
+ };
|
|
|
+
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ Vue.component('table-sort-trigger', {
|
|
|
+ template: `{{template "component/sortableTableTrigger"}}`,
|
|
|
+ props: ['item-index'],
|
|
|
+ inject: ['sortable'],
|
|
|
+ methods: {
|
|
|
+ mouseDownHandler(e) {
|
|
|
+ if (this.sortable) {
|
|
|
+ this.sortable.setSortableIndex(e, this.itemIndex);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mouseUpHandler(e) {
|
|
|
+ if (this.sortable) {
|
|
|
+ this.sortable.resetSortableIndex(e, this.itemIndex);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ clickHandler(e) {
|
|
|
+ e.preventDefault();
|
|
|
+ },
|
|
|
+ }
|
|
|
+ })
|
|
|
+</script>
|
|
|
+
|
|
|
+<style>
|
|
|
+ .ant-table-is-sorting .draggable-row td {
|
|
|
+ background-color: white !important;
|
|
|
+ }
|
|
|
+ .dark .ant-table-is-sorting .draggable-row td {
|
|
|
+ background-color: var(--dark-color-surface-100) !important;
|
|
|
+ }
|
|
|
+ .ant-table-is-sorting .dragging {
|
|
|
+ opacity: 0.5;
|
|
|
+ }
|
|
|
+ .ant-table-is-sorting .dragging .ant-table-row-index {
|
|
|
+ opacity: 0;
|
|
|
+ }
|
|
|
+</style>
|
|
|
+{{end}}
|