글쓰기 태그 제한과 표 기능 추가

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-09 17:10:16 +09:00
parent ed30926250
commit 95d234a625
24 changed files with 560 additions and 54 deletions

View File

@@ -17,6 +17,12 @@ import {
getSocialIconPreset,
normalizeSocialLinks
} from '~/lib/social-links.js'
import {
DEFAULT_POST_TAG_LIMIT,
MAX_POST_TAG_LIMIT,
MIN_POST_TAG_LIMIT,
normalizePostTagLimit
} from '~/lib/post-tag-limit.js'
definePageMeta({
layout: 'admin'
@@ -103,7 +109,8 @@ const socialSnapshot = reactive({
})
/** 편집 시작 시점의 POST 설정(취소 시 복원용) */
const postSnapshot = reactive({
showPostUpdatedAt: false
showPostUpdatedAt: false,
postTagLimit: DEFAULT_POST_TAG_LIMIT
})
/** 편집 시작 시점의 메인 화면 커버(취소 시 복원용) */
const homeCoverSnapshot = reactive({
@@ -166,6 +173,7 @@ const form = reactive({
copyrightText: settings.value?.copyrightText || '©2026 sori.studio',
socialLinks: normalizeSocialLinks(settings.value?.socialLinks || []),
showPostUpdatedAt: Boolean(settings.value?.showPostUpdatedAt),
postTagLimit: normalizePostTagLimit(settings.value?.postTagLimit),
homeCoverImageUrl: settings.value?.homeCoverImageUrl || '',
homeCoverDarkImageUrl: settings.value?.homeCoverDarkImageUrl || '',
homeCoverTitle: settings.value?.homeCoverTitle || '',
@@ -222,7 +230,10 @@ const hasSocialChanges = computed(() => editSocial.value
* @returns {boolean} 변경 여부
*/
const hasPostChanges = computed(() => editPost.value
&& form.showPostUpdatedAt !== postSnapshot.showPostUpdatedAt)
&& (
form.showPostUpdatedAt !== postSnapshot.showPostUpdatedAt
|| normalizePostTagLimit(form.postTagLimit) !== normalizePostTagLimit(postSnapshot.postTagLimit)
))
/**
* 메인 화면 커버 변경 여부
@@ -1122,6 +1133,7 @@ const buildSiteSettingsPayload = () => ({
copyrightText: form.copyrightText,
socialLinks: normalizeSocialLinks(form.socialLinks),
showPostUpdatedAt: Boolean(form.showPostUpdatedAt),
postTagLimit: normalizePostTagLimit(form.postTagLimit),
homeCoverImageUrl: form.homeCoverImageUrl || '',
homeCoverDarkImageUrl: form.homeCoverDarkImageUrl || '',
homeCoverTitle: form.homeCoverTitle || '',
@@ -1351,6 +1363,8 @@ const saveSocialSection = async () => {
*/
const beginEditPost = () => {
postSnapshot.showPostUpdatedAt = form.showPostUpdatedAt
postSnapshot.postTagLimit = normalizePostTagLimit(form.postTagLimit)
form.postTagLimit = normalizePostTagLimit(form.postTagLimit)
editPost.value = true
}
@@ -1360,6 +1374,7 @@ const beginEditPost = () => {
*/
const cancelEditPost = () => {
form.showPostUpdatedAt = postSnapshot.showPostUpdatedAt
form.postTagLimit = normalizePostTagLimit(postSnapshot.postTagLimit)
editPost.value = false
}
@@ -1379,6 +1394,8 @@ const savePostSection = async () => {
if (ok) {
postSnapshot.showPostUpdatedAt = form.showPostUpdatedAt
postSnapshot.postTagLimit = normalizePostTagLimit(form.postTagLimit)
form.postTagLimit = postSnapshot.postTagLimit
editPost.value = false
}
}
@@ -2382,6 +2399,7 @@ onBeforeUnmount(() => {
class="mr-5 mt-1 text-pretty text-sm leading-relaxed text-[#657080]"
>
공개 상세·관리자 목록에서 발행 수정이 있었을 수정일을 함께 표시합니다.
글쓰기에서 선택할 있는 태그 최대 개수도 함께 관리합니다.
</p>
</div>
<div class="-mr-1 mt-[-5px] flex shrink-0 items-center justify-start gap-2">
@@ -2433,24 +2451,47 @@ onBeforeUnmount(() => {
<span class="relative ml-1 size-5 rounded-full bg-[#f4f6f8] shadow transition-transform peer-checked:translate-x-5" aria-hidden="true" />
</span>
</div>
<div class="mt-5 flex items-center justify-between gap-4 border-t border-[#eceff2] pt-5">
<span class="font-bold text-[#15171a]">태그 최대 개수</span>
<span class="font-mono text-sm font-semibold text-[#657080]">
{{ normalizePostTagLimit(form.postTagLimit) }}
</span>
</div>
</div>
<label
<div
v-else
class="admin-settings-screen__post-toggle flex items-center justify-between gap-4 border-t border-[#eceff2] pt-5 text-sm"
class="grid gap-5 border-t border-[#eceff2] pt-5 text-sm"
>
<span class="font-bold text-[#15171a]">수정일 표시</span>
<span class="admin-settings-screen__post-toggle-control relative inline-flex h-7 w-12 shrink-0 items-center">
<label class="admin-settings-screen__post-toggle flex items-center justify-between gap-4">
<span class="font-bold text-[#15171a]">수정일 표시</span>
<span class="admin-settings-screen__post-toggle-control relative inline-flex h-7 w-12 shrink-0 items-center">
<input
v-model="form.showPostUpdatedAt"
class="peer sr-only"
type="checkbox"
aria-label="수정일 표시"
>
<span class="absolute inset-0 rounded-full bg-[#c8ced3] transition-colors peer-checked:bg-[#15171a]" aria-hidden="true" />
<span class="relative ml-1 size-5 rounded-full bg-white shadow transition-transform peer-checked:translate-x-5" aria-hidden="true" />
</span>
</label>
<label class="admin-settings-screen__field grid gap-2">
<span class="font-bold text-[#15171a]">태그 최대 개수</span>
<input
v-model="form.showPostUpdatedAt"
class="peer sr-only"
type="checkbox"
aria-label="수정일 표시"
v-model.number="form.postTagLimit"
class="h-10 rounded-md border border-[#dce0e5] bg-white px-3 text-[#15171a] outline-none focus:border-[#15171a] focus:ring-1 focus:ring-[#15171a]"
type="number"
:min="MIN_POST_TAG_LIMIT"
:max="MAX_POST_TAG_LIMIT"
step="1"
@blur="form.postTagLimit = normalizePostTagLimit(form.postTagLimit)"
>
<span class="absolute inset-0 rounded-full bg-[#c8ced3] transition-colors peer-checked:bg-[#15171a]" aria-hidden="true" />
<span class="relative ml-1 size-5 rounded-full bg-white shadow transition-transform peer-checked:translate-x-5" aria-hidden="true" />
</span>
</label>
<span class="text-xs leading-relaxed text-[#657080]">
최소 {{ MIN_POST_TAG_LIMIT }}, 최대 {{ MAX_POST_TAG_LIMIT }}개까지 설정할 있습니다. 기본값은 {{ DEFAULT_POST_TAG_LIMIT }}개입니다.
</span>
</label>
</div>
</section>
<h2 class="admin-settings-screen__section-heading z-20 mb-px pt-10 text-2xl font-bold tracking-tight text-[#15171a]">