글쓰기 에디터 문단 처리와 설정 패널 액션 보정

This commit is contained in:
2026-05-07 15:02:41 +09:00
parent 398877fd92
commit 5bda4d5472
9 changed files with 199 additions and 57 deletions

View File

@@ -11,10 +11,26 @@ const props = defineProps({
saving: {
type: Boolean,
default: false
},
canViewPost: {
type: Boolean,
default: false
},
publicUrl: {
type: String,
default: ''
},
deleting: {
type: Boolean,
default: false
},
showDelete: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['submit', 'preview'])
const emit = defineEmits(['submit', 'preview', 'delete'])
const autosaveStoragePrefix = 'SORI_ADMIN_POST_AUTOSAVE'
const slugTouched = ref(Boolean(props.initialPost.slug))
@@ -88,6 +104,8 @@ const form = reactive({
})
const autosaveKey = computed(() => `${autosaveStoragePrefix}:${props.initialPost.id || 'new'}`)
const postUrlLabel = computed(() => form.slug || toSlug(form.title) || '')
const postUrlHint = computed(() => props.publicUrl || (postUrlLabel.value ? `/post/${postUrlLabel.value}/` : '/post/'))
/**
* 문자열을 URL 슬러그로 변환
@@ -439,6 +457,14 @@ const previewPost = () => {
emit('preview', createPostPayload())
}
/**
* 게시물 삭제 요청
* @returns {void}
*/
const deletePost = () => {
emit('delete')
}
/**
* 설정 패널 표시 상태 전환
* @returns {void}
@@ -597,7 +623,36 @@ defineExpose({
</button>
</div>
<div class="admin-post-form__settings-body grid content-start gap-4 overflow-y-auto px-6 pb-8 pt-8">
<div class="admin-post-form__settings-body grid flex-1 content-start gap-4 overflow-y-auto px-6 pb-8 pt-8">
<div class="admin-post-form__field grid gap-1 text-sm">
<div class="admin-post-form__post-url-header flex h-[22px] items-center justify-between">
<span class="admin-post-form__label font-bold text-[#15171a]">Post URL</span>
<NuxtLink
v-if="canViewPost"
class="admin-post-form__view-post inline-flex items-center gap-1 rounded px-3 py-1.5 text-xs text-[#8e9cac] transition-colors hover:bg-[#eff1f2] hover:text-[#394047]"
:to="publicUrl"
target="_blank"
>
<span>View Post</span>
<span aria-hidden="true"></span>
</NuxtLink>
</div>
<label class="admin-post-form__post-url-input flex h-[38px] items-center gap-2 rounded border border-[#e3e6e8] bg-[#eff1f2] px-3 py-1 transition-colors hover:border-[#c8ced3] focus-within:border-[#8e9cac]">
<span class="admin-post-form__post-url-icon text-sm text-[#394047]" aria-hidden="true"></span>
<input
v-model="form.slug"
class="admin-post-form__input min-w-0 flex-1 border-0 bg-transparent p-0 text-sm text-black outline-none"
type="text"
pattern="[a-z0-9가-힣]+(-[a-z0-9가-힣]+)*"
required
@input="touchSlug"
>
</label>
<p class="admin-post-form__post-url-hint text-xs text-[#7c8b9a]">
{{ postUrlHint }}
</p>
</div>
<label class="admin-post-form__field grid gap-2 text-sm">
<span class="admin-post-form__label font-medium">상태</span>
<select v-model="form.status" class="admin-post-form__select h-[38px] rounded border border-[#e3e6e8] bg-[#eff1f2] px-3 py-2 transition-colors hover:border-[#c8ced3] focus:border-[#8e9cac] focus:outline-none">
@@ -621,18 +676,6 @@ defineExpose({
</span>
</label>
<label class="admin-post-form__field grid gap-2 text-sm">
<span class="admin-post-form__label font-medium">슬러그</span>
<input
v-model="form.slug"
class="admin-post-form__input h-[38px] rounded border border-[#e3e6e8] bg-[#eff1f2] px-3 py-2 transition-colors hover:border-[#c8ced3] focus:border-[#8e9cac] focus:outline-none"
type="text"
pattern="[a-z0-9가-힣]+(-[a-z0-9가-힣]+)*"
required
@input="touchSlug"
>
</label>
<label class="admin-post-form__field grid gap-2 text-sm">
<span class="admin-post-form__label font-medium">요약</span>
<textarea
@@ -743,6 +786,17 @@ defineExpose({
</div>
</div>
</div>
<div v-if="showDelete" class="admin-post-form__settings-bottom shrink-0 border-t border-[#e3e6e8] px-8 py-6">
<button
class="admin-post-form__delete-post flex h-10 w-full items-center justify-center gap-2 rounded border border-[#d21a26] text-sm font-bold text-[#d21a26] transition-colors hover:bg-red-50 disabled:opacity-50"
type="button"
:disabled="deleting"
@click="deletePost"
>
<span aria-hidden="true"></span>
<span>{{ deleting ? '삭제 중' : 'Delete post' }}</span>
</button>
</div>
</div>
</aside>