diff --git a/frontend_nuxt/app.vue b/frontend_nuxt/app.vue index 8148fe4c9..8eab548cf 100644 --- a/frontend_nuxt/app.vue +++ b/frontend_nuxt/app.vue @@ -21,6 +21,7 @@ + @@ -28,6 +29,7 @@ import HeaderComponent from '~/components/HeaderComponent.vue' import MenuComponent from '~/components/MenuComponent.vue' import GlobalPopups from '~/components/GlobalPopups.vue' +import ConfirmDialog from '~/components/ConfirmDialog.vue' import { useIsMobile } from '~/utils/screen' const isMobile = useIsMobile() diff --git a/frontend_nuxt/components/ConfirmDialog.vue b/frontend_nuxt/components/ConfirmDialog.vue new file mode 100644 index 000000000..922dd402a --- /dev/null +++ b/frontend_nuxt/components/ConfirmDialog.vue @@ -0,0 +1,53 @@ + + + + + \ No newline at end of file diff --git a/frontend_nuxt/composables/useConfirm.js b/frontend_nuxt/composables/useConfirm.js new file mode 100644 index 000000000..feffa0712 --- /dev/null +++ b/frontend_nuxt/composables/useConfirm.js @@ -0,0 +1,42 @@ +import { ref } from 'vue' + +const state = ref({ + visible: false, + title: '', + message: '', + resolve: null, + reject: null, +}) + +export const useConfirm = () => { + const confirm = (title, message) => { + state.value.title = title + state.value.message = message + state.value.visible = true + return new Promise((resolve, reject) => { + state.value.resolve = resolve + state.value.reject = reject + }) + } + + const onConfirm = () => { + if (state.value.resolve) { + state.value.resolve(true) + } + state.value.visible = false + } + + const onCancel = () => { + if (state.value.reject) { + state.value.reject(false) + } + state.value.visible = false + } + + return { + confirm, + onConfirm, + onCancel, + state, + } +} \ No newline at end of file diff --git a/frontend_nuxt/pages/posts/[id]/index.vue b/frontend_nuxt/pages/posts/[id]/index.vue index e5826b8d2..bbb6ab8bb 100644 --- a/frontend_nuxt/pages/posts/[id]/index.vue +++ b/frontend_nuxt/pages/posts/[id]/index.vue @@ -251,6 +251,7 @@ import { useRouter } from 'vue-router' import { useIsMobile } from '~/utils/screen' import Dropdown from '~/components/Dropdown.vue' import { ClientOnly } from '#components' +import { useConfirm } from '~/composables/useConfirm' const config = useRuntimeConfig() const API_BASE_URL = config.public.apiBaseUrl @@ -278,6 +279,7 @@ const subscribed = ref(false) const commentSort = ref('NEWEST') const isFetchingComments = ref(false) const isMobile = useIsMobile() +const { confirm } = useConfirm() const headerHeight = process.client ? parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--header-height')) || 0 @@ -349,7 +351,7 @@ const articleMenuItems = computed(() => { const items = [] if (isAuthor.value || isAdmin.value) { items.push({ text: '编辑文章', onClick: () => editPost() }) - items.push({ text: '删除文章', color: 'red', onClick: () => deletePost() }) + items.push({ text: '删除文章', color: 'red', onClick: deletePost }) } if (isAdmin.value) { if (pinnedAt.value) { @@ -687,19 +689,24 @@ const editPost = () => { } const deletePost = async () => { - const token = getToken() - if (!token) { - toast.error('请先登录') - return - } - const res = await fetch(`${API_BASE_URL}/api/posts/${postId}`, { - method: 'DELETE', - headers: { Authorization: `Bearer ${token}` }, - }) - if (res.ok) { - toast.success('已删除') - navigateTo('/', { replace: true }) - } else { + try { + await confirm('确认删除', '确定要删除这篇文章吗?此操作不可撤销。') + const token = getToken() + if (!token) { + toast.error('请先登录') + return + } + const res = await fetch(`${API_BASE_URL}/api/posts/${postId}`, { + method: 'DELETE', + headers: { Authorization: `Bearer ${token}` }, + }) + if (res.ok) { + toast.success('已删除') + navigateTo('/', { replace: true }) + } else { + toast.error('操作失败') + } + } catch (e) { toast.error('操作失败') } }