From 2552ef76d99a2b8fc1a42b4b7cfe9057f4e54db1 Mon Sep 17 00:00:00 2001 From: Ruslan Semak Date: Fri, 18 Apr 2025 17:54:25 +0300 Subject: [PATCH] stage drag n drop --- client/components/admin/admin-pages.vue | 134 +++++++++++++++++- .../admin/pages/update-page-priority.gql | 8 +- server/graph/schemas/page.graphql | 9 +- 3 files changed, 137 insertions(+), 14 deletions(-) diff --git a/client/components/admin/admin-pages.vue b/client/components/admin/admin-pages.vue index 27aa6a4d..e4502d59 100644 --- a/client/components/admin/admin-pages.vue +++ b/client/components/admin/admin-pages.vue @@ -27,7 +27,7 @@ hide-details dense style='max-width: 400px;' - ) + ) v-select.ml-2( solo flat @@ -59,6 +59,13 @@ v-model='selectedState' style='max-width: 250px;' ) + v-btn.ml-2( + color='primary' + depressed + @click='saveNewOrder' + ) + v-icon(left) mdi-content-save + span Save Order v-divider v-data-table( :items='filteredPages' @@ -74,7 +81,17 @@ @page-count="pageTotal = $event" ) template(slot='item', slot-scope='props') - tr.is-clickable(:active='props.selected', @click='$router.push(`/pages/` + props.item.id)') + tr.is-clickable( + :active='props.selected', + draggable="true" + @dragstart="dragStart($event, props.item, props.index)" + @dragover.prevent="dragOver($event, props.item, props.index)" + @dragenter.prevent="dragEnter($event, props.index)" + @dragleave="dragLeave($event)" + @drop="drop($event, props.item, props.index)" + @click='$router.push(`/pages/` + props.item.id)' + :class="{'drag-over': dragOverIndex === props.index}" + ) td.text-xs-right {{ props.item.id }} td v-edit-dialog( @@ -146,7 +163,10 @@ export default { editPriorityValue: null, editingItem: null, originalPriorities: new Map(), - loading: false + loading: false, + draggedItem: null, + draggedIndex: null, + dragOverIndex: null } }, computed: { @@ -162,7 +182,7 @@ export default { return false } return true - }) + }).sort((a, b) => a.orderPriority - b.orderPriority) }, langs () { return _.concat({ @@ -235,6 +255,34 @@ export default { } }, + async saveNewOrder() { + try { + const pagesToUpdate = this.filteredPages.map((page, index) => ({ + id: page.id, + orderPriority: index + 1 + })) + + await this.$apollo.mutate({ + mutation: updatePagePriorityMutation, + variables: { pages: pagesToUpdate } + }) + + this.$store.commit('showNotification', { + message: 'Order updated successfully', + style: 'success', + icon: 'check' + }) + + await this.refresh() + } catch (error) { + this.$store.commit('showNotification', { + message: 'Failed to update order', + style: 'error', + icon: 'error' + }) + } + }, + async refresh() { await this.$apollo.queries.pages.refetch() this.$store.commit('showNotification', { @@ -243,6 +291,68 @@ export default { icon: 'cached' }) }, + + dragStart(event, item, index) { + this.draggedItem = item + this.draggedIndex = index + event.dataTransfer.effectAllowed = 'move' + event.dataTransfer.setData('text/html', event.target.parentNode) + }, + + dragOver(event, item, index) { + event.preventDefault() + if (this.draggedItem && this.draggedItem.id !== item.id) { + this.dragOverIndex = index + event.dataTransfer.dropEffect = 'move' + } + }, + + dragEnter(event, index) { + event.preventDefault() + this.dragOverIndex = index + }, + + dragLeave(event) { + this.dragOverIndex = null + }, + + drop(event, item, index) { + event.preventDefault() + this.dragOverIndex = null + + if (!this.draggedItem || this.draggedItem.id === item.id) { + return + } + + // Создаем копию массива страниц + const pagesCopy = [...this.pages] + + // Находим индекс перетаскиваемого элемента в основном массиве + const draggedPageIndex = pagesCopy.findIndex(p => p.id === this.draggedItem.id) + if (draggedPageIndex === -1) return + + // Удаляем перетаскиваемый элемент из массива + const [draggedPage] = pagesCopy.splice(draggedPageIndex, 1) + + // Находим индекс целевого элемента в основном массиве + const targetPageIndex = pagesCopy.findIndex(p => p.id === item.id) + if (targetPageIndex === -1) return + + // Вставляем перетаскиваемый элемент перед целевым + pagesCopy.splice(targetPageIndex, 0, draggedPage) + + // Обновляем orderPriority для всех элементов в группе + let currentPriority = 1 + pagesCopy.forEach(page => { + if (page.group === this.selectedGroup) { + page.orderPriority = currentPriority++ + } + }) + + // Обновляем основной массив + this.pages = pagesCopy + }, + updateGroupSelector(pages) { const groups = Array.from(new Set(pages.filter(p => p.group).map(p => p.group))) @@ -256,6 +366,10 @@ export default { }, recyclebin () { } }, + mounted() {}, + watch: { + selectedGroup() {} + }, apollo: { pages: { query: pagesQuery, @@ -263,7 +377,6 @@ export default { update: function (data) { const pages = data.pages.list.map(p => { p.group = p.path.includes('/') ? p.path.split('/')[0] : null - return p }) @@ -298,4 +411,15 @@ export default { padding: 16px; } } + +tr.is-clickable[draggable="true"] { + cursor: move; + + &.drag-over { + td { + border-top: 2px solid #1976D2; + border-bottom: 2px solid #1976D2; + } + } +} diff --git a/client/graph/admin/pages/update-page-priority.gql b/client/graph/admin/pages/update-page-priority.gql index 7a3c7502..818ade91 100644 --- a/client/graph/admin/pages/update-page-priority.gql +++ b/client/graph/admin/pages/update-page-priority.gql @@ -1,13 +1,9 @@ mutation ( - $id: Int! - $orderPriority: Int! - $group: String! + $pages: [PageOrderInput!]! ) { pages { updatePriority( - id: $id - orderPriority: $orderPriority - group: $group + pages: $pages ) { responseResult { succeeded diff --git a/server/graph/schemas/page.graphql b/server/graph/schemas/page.graphql index 506443aa..5a1db422 100644 --- a/server/graph/schemas/page.graphql +++ b/server/graph/schemas/page.graphql @@ -118,9 +118,7 @@ type PageMutation { ): PageResponse @auth(requires: ["write:pages", "manage:pages", "manage:system"]) updatePriority( - id: Int! - orderPriority: Int! - group: String! + pages: [PageOrderInput!]! ): PageResponse @auth(requires: ["write:pages", "manage:pages", "manage:system"]) convert( @@ -185,6 +183,11 @@ type PageMigrationResponse { count: Int } +input PageOrderInput { + id: Int! + orderPriority: Int! +} + type Page { id: Int! path: String!