글쓰기 입력 흐름과 저장 피드백 보정
This commit is contained in:
@@ -9,6 +9,8 @@ const saving = ref(false)
|
||||
const deleting = ref(false)
|
||||
const errorMessage = ref('')
|
||||
const postForm = ref(null)
|
||||
const toast = ref(null)
|
||||
let toastTimer = null
|
||||
|
||||
const { data: post } = await useFetch(() => `/admin/api/posts/${id.value}`)
|
||||
|
||||
@@ -19,6 +21,39 @@ if (!post.value) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 저장 상태 토스트 표시
|
||||
* @param {'success'|'error'|'info'} type - 토스트 타입
|
||||
* @param {string} message - 표시 메시지
|
||||
* @returns {void}
|
||||
*/
|
||||
const showToast = (type, message) => {
|
||||
window.clearTimeout(toastTimer)
|
||||
toast.value = { type, message }
|
||||
toastTimer = window.setTimeout(() => {
|
||||
toast.value = null
|
||||
}, 3200)
|
||||
}
|
||||
|
||||
/**
|
||||
* 이전 화면에서 전달한 토스트 표시
|
||||
* @returns {void}
|
||||
*/
|
||||
const showStoredToast = () => {
|
||||
const storedToast = sessionStorage.getItem('SORI_ADMIN_POST_TOAST')
|
||||
|
||||
if (!storedToast) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedToast = JSON.parse(storedToast)
|
||||
showToast(parsedToast.type || 'success', parsedToast.message || '저장되었습니다.')
|
||||
} finally {
|
||||
sessionStorage.removeItem('SORI_ADMIN_POST_TOAST')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 게시물 수정 저장
|
||||
* @param {Object} payload - 게시물 입력값
|
||||
@@ -27,6 +62,7 @@ if (!post.value) {
|
||||
const savePost = async (payload) => {
|
||||
saving.value = true
|
||||
errorMessage.value = ''
|
||||
showToast('info', '변경 내용을 저장하는 중입니다.')
|
||||
|
||||
try {
|
||||
const updatedPost = await $fetch(`/admin/api/posts/${id.value}`, {
|
||||
@@ -36,8 +72,10 @@ const savePost = async (payload) => {
|
||||
|
||||
post.value = updatedPost
|
||||
postForm.value?.clearAutosave()
|
||||
showToast('success', '변경 내용이 저장되었습니다.')
|
||||
} catch (error) {
|
||||
errorMessage.value = error?.data?.message || '글을 저장하지 못했습니다.'
|
||||
showToast('error', errorMessage.value)
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
@@ -54,6 +92,7 @@ const deletePost = async () => {
|
||||
|
||||
deleting.value = true
|
||||
errorMessage.value = ''
|
||||
showToast('info', '글을 삭제하는 중입니다.')
|
||||
|
||||
try {
|
||||
await $fetch(`/admin/api/posts/${id.value}`, {
|
||||
@@ -62,10 +101,17 @@ const deletePost = async () => {
|
||||
await navigateTo('/admin/posts')
|
||||
} catch (error) {
|
||||
errorMessage.value = error?.data?.message || '글을 삭제하지 못했습니다.'
|
||||
showToast('error', errorMessage.value)
|
||||
} finally {
|
||||
deleting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(showStoredToast)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.clearTimeout(toastTimer)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -102,5 +148,17 @@ const deletePost = async () => {
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
<AdminPostForm ref="postForm" :initial-post="post" submit-label="변경 저장" :saving="saving" @submit="savePost" />
|
||||
<div
|
||||
v-if="toast"
|
||||
class="admin-post-edit__toast fixed right-5 top-5 z-50 rounded border px-4 py-3 text-sm font-semibold shadow-lg"
|
||||
:class="{
|
||||
'border-green-200 bg-green-50 text-green-800': toast.type === 'success',
|
||||
'border-red-200 bg-red-50 text-red-800': toast.type === 'error',
|
||||
'border-line bg-white text-ink': toast.type === 'info'
|
||||
}"
|
||||
role="status"
|
||||
>
|
||||
{{ toast.message }}
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -6,6 +6,22 @@ definePageMeta({
|
||||
const saving = ref(false)
|
||||
const errorMessage = ref('')
|
||||
const postForm = ref(null)
|
||||
const toast = ref(null)
|
||||
let toastTimer = null
|
||||
|
||||
/**
|
||||
* 저장 상태 토스트 표시
|
||||
* @param {'success'|'error'|'info'} type - 토스트 타입
|
||||
* @param {string} message - 표시 메시지
|
||||
* @returns {void}
|
||||
*/
|
||||
const showToast = (type, message) => {
|
||||
window.clearTimeout(toastTimer)
|
||||
toast.value = { type, message }
|
||||
toastTimer = window.setTimeout(() => {
|
||||
toast.value = null
|
||||
}, 3200)
|
||||
}
|
||||
|
||||
/**
|
||||
* 새 게시물 저장
|
||||
@@ -15,6 +31,7 @@ const postForm = ref(null)
|
||||
const savePost = async (payload) => {
|
||||
saving.value = true
|
||||
errorMessage.value = ''
|
||||
showToast('info', '글을 저장하는 중입니다.')
|
||||
|
||||
try {
|
||||
const post = await $fetch('/admin/api/posts', {
|
||||
@@ -23,13 +40,22 @@ const savePost = async (payload) => {
|
||||
})
|
||||
|
||||
postForm.value?.clearAutosave()
|
||||
sessionStorage.setItem('SORI_ADMIN_POST_TOAST', JSON.stringify({
|
||||
type: 'success',
|
||||
message: '글이 저장되었습니다.'
|
||||
}))
|
||||
await navigateTo(`/admin/posts/${post.id}`)
|
||||
} catch (error) {
|
||||
errorMessage.value = error?.data?.message || '글을 저장하지 못했습니다.'
|
||||
showToast('error', errorMessage.value)
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.clearTimeout(toastTimer)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -46,5 +72,17 @@ const savePost = async (payload) => {
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
<AdminPostForm ref="postForm" submit-label="글 저장" :saving="saving" @submit="savePost" />
|
||||
<div
|
||||
v-if="toast"
|
||||
class="admin-post-editor__toast fixed right-5 top-5 z-50 rounded border px-4 py-3 text-sm font-semibold shadow-lg"
|
||||
:class="{
|
||||
'border-green-200 bg-green-50 text-green-800': toast.type === 'success',
|
||||
'border-red-200 bg-red-50 text-red-800': toast.type === 'error',
|
||||
'border-line bg-white text-ink': toast.type === 'info'
|
||||
}"
|
||||
role="status"
|
||||
>
|
||||
{{ toast.message }}
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user