태그 관리 자동 저장 정리

This commit is contained in:
2026-05-15 11:21:57 +09:00
parent b4e4e37f5a
commit 59a50a0c97
9 changed files with 67 additions and 35 deletions

View File

@@ -54,7 +54,7 @@ const filteredGeneralTags = computed(() => {
)
})
/** 서버 기준 메인 태그 id 순서(정렬 저장 버튼 활성 비교용) */
/** 서버 기준 메인 태그 id 순서(자동 저장 필요 여부 비교용) */
const baselineManagedTagIds = ref([])
/**
@@ -77,7 +77,7 @@ const refreshTagsFromServer = async () => {
resetManagedOrderBaseline()
/**
* 메인 태그 드래그 순서가 기준선과 다른지 여부
* 메인 태그 드래그 순서가 서버 기준선과 다른지 여부
* @returns {boolean} 변경 여부
*/
const isManagedOrderDirty = computed(() => {
@@ -116,6 +116,11 @@ const showToast = (type, message) => {
* @returns {void}
*/
const handleDragStart = (event, tagId) => {
if (savingOrder.value) {
event.preventDefault()
return
}
if (!event.dataTransfer) {
return
}
@@ -130,6 +135,10 @@ const handleDragStart = (event, tagId) => {
* @returns {void}
*/
const handleDragOver = (event, tagId) => {
if (savingOrder.value) {
return
}
event.preventDefault()
dragOverTagId.value = tagId
}
@@ -172,15 +181,17 @@ const moveManagedTag = (sourceId, targetId) => {
* 관리용 태그 드롭 처리
* @param {DragEvent} event - 드래그 이벤트
* @param {string} targetId - 대상 태그 ID
* @returns {void}
* @returns {Promise<void>}
*/
const handleDrop = (event, targetId) => {
const handleDrop = async (event, targetId) => {
event.preventDefault()
if (!draggingTagId.value) {
if (!draggingTagId.value || savingOrder.value) {
return
}
moveManagedTag(draggingTagId.value, targetId)
handleDragEnd()
await nextTick()
await saveManagedOrder()
}
/**
@@ -204,9 +215,10 @@ const saveManagedOrder = async () => {
tags.value = [...reordered]
await refreshTagsFromServer()
showToast('success', '메인 태그 순서가 저장되었습니다.')
showToast('success', '메인 태그 순서가 자동 저장되었습니다.')
} catch (error) {
showToast('error', error?.data?.message || '정렬 순서를 저장하지 못했습니다.')
await refreshTagsFromServer()
} finally {
savingOrder.value = false
}
@@ -328,7 +340,7 @@ onBeforeUnmount(() => {
<template>
<section class="admin-tags bg-paper p-6">
<div class="admin-tags__header flex items-center justify-between gap-4">
<div class="admin-tags__header">
<div>
<p class="admin-tags__eyebrow text-xs font-semibold uppercase text-muted">
Tags
@@ -337,9 +349,6 @@ onBeforeUnmount(() => {
태그 관리
</h1>
</div>
<NuxtLink class="admin-tags__new rounded bg-[#15171a] px-4 py-2 text-sm font-semibold text-white" to="/admin/tags/new">
태그 추가
</NuxtLink>
</div>
<p class="mt-3 text-xs text-muted">
메인 태그는 홈페이지 카테고리 영역에서 사용되며 드래그 정렬이 가능합니다. 일반 태그는 아래 목록에서 확인하고 필요할 메인 태그로 전환할 있습니다.
@@ -348,14 +357,10 @@ onBeforeUnmount(() => {
<div class="admin-tags__table mt-6 overflow-hidden border border-line">
<div class="flex items-center justify-between border-b border-line bg-[#f7f7f5] px-4 py-2.5">
<p class="text-xs font-semibold uppercase text-muted">메인 태그</p>
<button
class="rounded border border-line bg-white px-3 py-1.5 text-xs font-semibold disabled:opacity-50"
type="button"
:disabled="savingOrder || managedTags.length === 0 || !isManagedOrderDirty"
@click="saveManagedOrder"
>
{{ savingOrder ? '저장 중' : '정렬 저장' }}
</button>
<span v-if="savingOrder" class="inline-flex items-center gap-2 text-xs font-semibold text-muted">
<span class="size-3 animate-spin rounded-full border-2 border-line border-t-[#15171a]" />
저장
</span>
</div>
<table class="admin-tags__table-inner w-full border-collapse text-left text-sm">
<thead class="admin-tags__table-head bg-[#f5f5f2] text-xs uppercase text-muted">
@@ -372,12 +377,13 @@ onBeforeUnmount(() => {
<tr
v-for="(tag, index) in managedTags"
:key="tag.id"
class="admin-tags__row cursor-move"
class="admin-tags__row"
:class="[
dragOverTagId === tag.id ? 'bg-[#f9f9f7]' : '',
draggingTagId === tag.id ? 'opacity-50' : ''
draggingTagId === tag.id ? 'opacity-50' : '',
savingOrder ? 'cursor-not-allowed opacity-60' : 'cursor-move'
]"
draggable="true"
:draggable="!savingOrder"
@dragstart="handleDragStart($event, tag.id)"
@dragover="handleDragOver($event, tag.id)"
@drop="handleDrop($event, tag.id)"
@@ -422,8 +428,11 @@ onBeforeUnmount(() => {
</div>
<div class="admin-tags__table mt-8 overflow-hidden border border-line">
<div class="border-b border-line bg-[#f7f7f5] px-4 py-2.5">
<div class="flex items-center justify-between gap-3 border-b border-line bg-[#f7f7f5] px-4 py-2.5">
<p class="text-xs font-semibold uppercase text-muted">일반 태그</p>
<NuxtLink class="admin-tags__new rounded bg-[#15171a] px-3 py-1.5 text-xs font-semibold text-white" to="/admin/tags/new">
태그 추가
</NuxtLink>
</div>
<div class="space-y-3 bg-white p-4">
<div class="flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between">