Files
sori.studio/pages/posts/index.vue

125 lines
2.9 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, color: string }} 태그 정보
*/
const getTagMeta = (slug) => {
if (!slug) {
return {
name: '',
slug: '',
color: '#4d4d4d'
}
}
const matchedTag = tags.value.find((tag) => tag.slug === slug)
return {
name: matchedTag?.name || String(slug).toUpperCase(),
slug,
color: matchedTag?.color || '#4d4d4d'
}
}
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,
tagColor: primaryTag.color,
publishedAt: formatPostDate(post.publishedAt),
to: `/post/${post.slug}`
}
}))
</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" />
<SitePagination
v-if="pagination.totalPages > 1"
class="posts-index__pagination"
:page="pagination.page"
:total-pages="pagination.totalPages"
label="게시물 페이지"
base-path="/posts"
/>
</MainColumn>
</template>