관리자 글 목록 고정 기능 추가

This commit is contained in:
2026-07-03 15:19:41 +09:00
parent 55efae810f
commit 288e73c0dc
11 changed files with 160 additions and 16 deletions

View File

@@ -5,6 +5,7 @@ definePageMeta({
const deletingId = ref('')
const togglingFeaturedId = ref('')
const togglingPinnedId = ref('')
const openPostMenuId = ref('')
const postMenuStyle = ref({})
const errorMessage = ref('')
@@ -51,7 +52,8 @@ const { data: postsResponse, refresh: refreshPosts } = await useFetch('/admin/ap
},
counts: {
total: 0,
featured: 0
featured: 0,
pinned: 0
}
})
})
@@ -325,7 +327,8 @@ const postPagination = computed(() => postsResponse.value?.pagination || {
/** 관리자 글 목록 통계 정보 */
const postCounts = computed(() => postsResponse.value?.counts || {
total: 0,
featured: 0
featured: 0,
pinned: 0
})
/** 게시물에 사용 중인 태그 옵션 */
@@ -370,6 +373,9 @@ const totalPostCount = computed(() => Number(postCounts.value.total || 0))
/** 추천(`isFeatured`) 게시물 수 */
const featuredPostCount = computed(() => Number(postCounts.value.featured || 0))
/** 관리자 목록 상단 고정 게시물 수 */
const pinnedPostCount = computed(() => Number(postCounts.value.pinned || 0))
/** 필터 적용 후 총 게시물 수 */
const filteredPostCount = computed(() => Number(postPagination.value.total || 0))
@@ -790,6 +796,31 @@ const togglePostFeatured = async (post) => {
}
}
/**
* 게시물 관리자 목록 고정 여부를 토글한다.
* @param {Object} post - 게시물
* @returns {Promise<void>}
*/
const togglePostPinned = async (post) => {
closePostMenu()
togglingPinnedId.value = post.id
errorMessage.value = ''
try {
await $fetch(`/admin/api/posts/${post.id}/pinned`, {
method: 'PUT',
body: {
adminPinned: !post.adminPinned
}
})
await refreshPosts()
} catch (error) {
errorMessage.value = error?.data?.message || '고정 설정을 변경하지 못했습니다.'
} finally {
togglingPinnedId.value = ''
}
}
/**
* 게시물 삭제
* @param {Object} post - 삭제할 게시물
@@ -871,6 +902,10 @@ watch(postPageCount, (pageCount) => {
<span class="admin-posts__count-separator text-[#c8ced3]"> · </span>
<span class="admin-posts__count-featured">추천 {{ featuredPostCount }}개</span>
</template>
<template v-if="pinnedPostCount > 0">
<span class="admin-posts__count-separator text-[#c8ced3]"> · </span>
<span class="admin-posts__count-pinned">고정 {{ pinnedPostCount }}개</span>
</template>
<template v-if="hasActiveListFilters && filteredPostCount !== totalPostCount">
<span class="admin-posts__count-separator text-[#c8ced3]"> · </span>
<span class="admin-posts__count-filtered">표시 {{ filteredPostCount }}개</span>
@@ -1062,6 +1097,12 @@ watch(postPageCount, (pageCount) => {
<span class="admin-posts__comment-count text-xs font-medium text-muted">
댓글 {{ post.commentCount || 0 }}
</span>
<span
v-if="post.adminPinned"
class="admin-posts__pinned-badge inline-flex h-5 items-center rounded bg-[#15171a] px-1.5 text-[10px] font-semibold text-white"
>
고정
</span>
</div>
<div class="admin-posts__slug-row mt-1 flex items-center gap-1 text-xs text-muted">
<span class="admin-posts__slug truncate">/post/{{ post.slug }}</span>
@@ -1133,14 +1174,14 @@ watch(postPageCount, (pageCount) => {
:ref="(element) => setPostMenuTriggerRef(post.id, element)"
class="admin-posts__row-menu-trigger inline-flex size-9 items-center justify-center rounded text-[#394047] transition-colors hover:bg-[#eceff2] focus-visible:outline focus-visible:ring-2 focus-visible:ring-[#8e9cac] focus-visible:ring-offset-1 disabled:pointer-events-none disabled:opacity-40"
type="button"
:disabled="deletingId === post.id || togglingFeaturedId === post.id"
:disabled="deletingId === post.id || togglingFeaturedId === post.id || togglingPinnedId === post.id"
:aria-expanded="openPostMenuId === post.id"
aria-haspopup="menu"
:aria-label="openPostMenuId === post.id ? '게시글 메뉴 닫기' : '게시글 메뉴'"
@click.stop="togglePostMenu(post.id)"
>
<span
v-if="deletingId === post.id || togglingFeaturedId === post.id"
v-if="deletingId === post.id || togglingFeaturedId === post.id || togglingPinnedId === post.id"
class="text-[10px] font-semibold text-muted"
aria-hidden="true"
>…</span>
@@ -1163,6 +1204,15 @@ watch(postPageCount, (pageCount) => {
role="menu"
data-admin-post-row-menu
>
<button
class="admin-posts__row-menu-item block w-full px-4 py-2.5 text-left hover:bg-[#f3f5f7] disabled:opacity-50"
type="button"
role="menuitem"
:disabled="togglingPinnedId === post.id"
@click="togglePostPinned(post)"
>
{{ post.adminPinned ? '고정 해제' : '상단 고정' }}
</button>
<button
class="admin-posts__row-menu-item block w-full px-4 py-2.5 text-left hover:bg-[#f3f5f7] disabled:opacity-50"
type="button"