페이지 섹션 관리와 홈 Pages 진열장 연동, 공개 목록 표시개수 설정 관련 누적 로컬 변경을 기능 단위로 정리해 반영한다. 문서 이력과 패키지 버전을 v1.9.23으로 맞춰 코드와 문서 상태를 동기화한다. Co-authored-by: Cursor <cursoragent@cursor.com>
150 lines
4.0 KiB
Vue
150 lines
4.0 KiB
Vue
<script setup>
|
|
import {
|
|
DEFAULT_POST_LIST_PAGE_SIZE,
|
|
normalizeDisplayCount,
|
|
normalizeListPage
|
|
} from '~/lib/display-count-settings.js'
|
|
|
|
const route = useRoute()
|
|
|
|
const { data: siteSettings } = await useFetch('/api/site-settings', {
|
|
default: () => ({})
|
|
})
|
|
|
|
const { data: tags } = await useFetch('/api/tags?tagType=all', {
|
|
default: () => []
|
|
})
|
|
|
|
const currentPage = computed(() => normalizeListPage(route.query.page))
|
|
const postListPageSize = computed(() => normalizeDisplayCount(siteSettings.value?.postListPageSize, DEFAULT_POST_LIST_PAGE_SIZE))
|
|
|
|
watch(currentPage, () => {
|
|
if (!import.meta.client) {
|
|
return
|
|
}
|
|
|
|
window.scrollTo({
|
|
top: 0,
|
|
behavior: 'smooth'
|
|
})
|
|
})
|
|
|
|
const { data: postsResponse } = await useFetch('/api/posts', {
|
|
query: computed(() => ({
|
|
paginated: '1',
|
|
page: currentPage.value,
|
|
limit: postListPageSize.value
|
|
})),
|
|
default: () => ({
|
|
items: [],
|
|
pagination: {
|
|
page: 1,
|
|
limit: DEFAULT_POST_LIST_PAGE_SIZE,
|
|
total: 0,
|
|
totalPages: 1
|
|
}
|
|
})
|
|
})
|
|
|
|
/**
|
|
* 태그 슬러그로 공개 태그 정보를 찾는다.
|
|
* @param {string | undefined} slug - 태그 슬러그
|
|
* @returns {{ name: string, slug: string }} 태그 정보
|
|
*/
|
|
const getTagMeta = (slug) => {
|
|
if (!slug) {
|
|
return {
|
|
name: '',
|
|
slug: ''
|
|
}
|
|
}
|
|
|
|
const matchedTag = tags.value.find((tag) => tag.slug === slug)
|
|
|
|
return {
|
|
name: matchedTag?.name || String(slug).toUpperCase(),
|
|
slug
|
|
}
|
|
}
|
|
|
|
const pagination = computed(() => postsResponse.value?.pagination || {
|
|
page: 1,
|
|
limit: postListPageSize.value,
|
|
total: 0,
|
|
totalPages: 1
|
|
})
|
|
|
|
const postCards = computed(() => (postsResponse.value?.items || []).map((post) => {
|
|
const primaryTag = getTagMeta(post.tags?.[0])
|
|
|
|
return {
|
|
title: post.title,
|
|
excerpt: createPostSummary(post.excerpt, post.content, {
|
|
maxLength: 320,
|
|
appendEllipsis: false
|
|
}),
|
|
featuredImage: post.featuredImage,
|
|
featuredImageThumbnail: post.featuredImageThumbnail || '',
|
|
tag: primaryTag.name,
|
|
tagSlug: primaryTag.slug,
|
|
publishedAt: formatPostDate(post.publishedAt),
|
|
to: `/post/${post.slug}`
|
|
}
|
|
}))
|
|
|
|
/**
|
|
* 게시물 목록 페이지 링크를 만든다.
|
|
* @param {number} page - 페이지 번호
|
|
* @returns {Object} Nuxt 링크 대상
|
|
*/
|
|
const getPostsPageTo = (page) => ({
|
|
path: '/posts',
|
|
query: page > 1 ? { page } : {}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<MainColumn>
|
|
<section class="posts-index site-section">
|
|
<div class="posts-index__header site-section-header">
|
|
<p class="posts-index__eyebrow text-xs font-semibold uppercase text-muted">
|
|
Posts
|
|
</p>
|
|
<h1 class="posts-index__title mt-3 text-4xl font-semibold leading-tight">
|
|
게시물
|
|
</h1>
|
|
</div>
|
|
</section>
|
|
|
|
<PostCard v-for="post in postCards" :key="post.to" :post="post" />
|
|
|
|
<nav
|
|
v-if="pagination.totalPages > 1"
|
|
class="posts-index__pagination site-section"
|
|
aria-label="게시물 페이지"
|
|
>
|
|
<div class="site-section-body mx-auto flex max-w-[720px] items-center justify-between gap-3 text-sm">
|
|
<NuxtLink
|
|
class="rounded border border-[var(--site-line)] px-3 py-2 font-semibold transition hover:bg-[var(--site-panel)]"
|
|
:class="pagination.page <= 1 ? 'pointer-events-none opacity-35' : ''"
|
|
:to="getPostsPageTo(Math.max(1, pagination.page - 1))"
|
|
:aria-disabled="pagination.page <= 1"
|
|
>
|
|
이전
|
|
</NuxtLink>
|
|
<span class="site-muted">
|
|
{{ pagination.page }} / {{ pagination.totalPages }}
|
|
</span>
|
|
<NuxtLink
|
|
class="rounded border border-[var(--site-line)] px-3 py-2 font-semibold transition hover:bg-[var(--site-panel)]"
|
|
:class="pagination.page >= pagination.totalPages ? 'pointer-events-none opacity-35' : ''"
|
|
:to="getPostsPageTo(Math.min(pagination.totalPages, pagination.page + 1))"
|
|
:aria-disabled="pagination.page >= pagination.totalPages"
|
|
>
|
|
다음
|
|
</NuxtLink>
|
|
</div>
|
|
</nav>
|
|
</MainColumn>
|
|
</template>
|