From 6333c4254f0ef9f32e7b4037bd7b9058b4245ead Mon Sep 17 00:00:00 2001 From: zenn Date: Tue, 26 May 2026 16:07:10 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B2=8C=EC=8B=9C=EB=AC=BC=EA=B3=BC=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B3=B5=EA=B0=9C=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=ED=99=95=EC=9E=A5=20v1.5.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/admin/AdminPageForm.vue | 24 ++++++++ components/admin/AdminPostForm.vue | 58 ++++++++++++++++--- .../036_content_visibility_statuses.sql | 15 +++++ docs/changelog.md | 5 ++ docs/history.md | 6 ++ docs/map.md | 17 +++--- docs/spec.md | 12 ++-- docs/todo.md | 1 + docs/update.md | 11 ++++ package-lock.json | 4 +- package.json | 2 +- pages/admin/pages/[id].vue | 2 +- pages/admin/pages/index.vue | 40 +++++++++++++ pages/admin/posts/index.vue | 24 +++++++- server/api/posts.get.js | 6 +- server/api/posts/[slug].get.js | 5 +- server/repositories/content-repository.js | 50 ++++++++++++---- server/utils/admin-page-input.js | 2 + server/utils/admin-post-input.js | 2 +- server/utils/content-schema.js | 4 +- 20 files changed, 252 insertions(+), 38 deletions(-) create mode 100644 db/migrations/036_content_visibility_statuses.sql diff --git a/components/admin/AdminPageForm.vue b/components/admin/AdminPageForm.vue index a81b07d..e999417 100644 --- a/components/admin/AdminPageForm.vue +++ b/components/admin/AdminPageForm.vue @@ -43,6 +43,7 @@ const htmlCursorRange = reactive({ const form = reactive({ title: props.initialPage.title || '', slug: props.initialPage.slug || '', + status: props.initialPage.status || 'published', renderMode: props.initialPage.renderMode || 'html_document', content: props.initialPage.content || '' }) @@ -101,6 +102,7 @@ const pageUrlHint = computed(() => `/pages/${pageSlug.value || 'page-slug'}/`) const serializePageForm = () => JSON.stringify({ title: form.title.trim(), slug: pageSlug.value, + status: form.status, renderMode: form.renderMode, content: form.content }) @@ -242,6 +244,7 @@ const uploadPageAsset = async (event) => { const createPayload = () => ({ title: form.title.trim(), slug: pageSlug.value, + status: form.status, renderMode: form.renderMode, content: form.content, featuredImage: null @@ -419,6 +422,27 @@ defineExpose({

+ +
페이지 형식
diff --git a/components/admin/AdminPostForm.vue b/components/admin/AdminPostForm.vue index dc78edc..6ea4c1b 100644 --- a/components/admin/AdminPostForm.vue +++ b/components/admin/AdminPostForm.vue @@ -47,8 +47,8 @@ const slugTouched = ref((() => { if (!post?.id) { return false } - const status = post.status === 'private' ? 'draft' : post.status - if (status === 'published') { + const status = post.status || 'draft' + if (status !== 'draft') { return true } return Boolean(post.slug) && !isAdminPostDraftPlaceholderSlug(post.slug) @@ -160,7 +160,7 @@ const form = reactive({ featuredImage: props.initialPost.featuredImage || '', isFeatured: Boolean(props.initialPost.isFeatured), noindex: props.initialPost.noindex === true, - status: props.initialPost.status === 'private' ? 'draft' : (props.initialPost.status || 'draft'), + status: props.initialPost.status || 'draft', publishedAt: toDateTimeLocalValue(props.initialPost.publishedAt), tagsText: props.initialPost.tags?.join(', ') || '' }) @@ -168,13 +168,16 @@ const form = reactive({ /** * 서버에 반영된 게시 형태(툴바·자동 저장·슬러그 자동 연동 분기) * @param {Object} post - 게시물 - * @returns {'draft' | 'publishedLive' | 'scheduled'} + * @returns {'draft' | 'publishedLive' | 'scheduled' | 'members' | 'private'} */ const getPersistedPublishKind = (post) => { if (!post?.id) { return 'draft' } - const st = post.status === 'private' ? 'draft' : post.status + const st = post.status || 'draft' + if (st === 'members' || st === 'private') { + return st + } if (st !== 'published') { return 'draft' } @@ -425,6 +428,16 @@ const displayScheduled = computed(() => persistedPublishKind.value === 'scheduled' || (persistedPublishKind.value === 'draft' && isScheduledPost())) +/** 툴바·상태줄에 멤버십 글로 표시할지 */ +const displayMembersOnly = computed(() => + persistedPublishKind.value === 'members' + || (persistedPublishKind.value === 'draft' && form.status === 'members')) + +/** 툴바·상태줄에 비공개 글로 표시할지 */ +const displayPrivatePost = computed(() => + persistedPublishKind.value === 'private' + || (persistedPublishKind.value === 'draft' && form.status === 'private')) + /** * 발행 모달에 표시할 게시 상태 요약 문구 * @returns {string} 요약 문구 @@ -556,7 +569,7 @@ const isRouteLeaveDirty = computed(() => { if (!hasUnsavedPostChanges.value) { return false } - if (persistedPublishKind.value === 'publishedLive' || persistedPublishKind.value === 'scheduled') { + if (['publishedLive', 'scheduled', 'members', 'private'].includes(persistedPublishKind.value)) { return true } return false @@ -570,7 +583,7 @@ const headerDraftStatusText = computed(() => { if (persistedPublishKind.value !== 'draft') { return null } - if (displayPublishedLive.value || displayScheduled.value) { + if (displayPublishedLive.value || displayScheduled.value || displayMembersOnly.value || displayPrivatePost.value) { return null } const persisting = props.saving || props.autoSaving @@ -1416,6 +1429,18 @@ defineExpose({ > Scheduled + + Members + + + Private + + +