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 @@
+
+
+
+
{{ state.title }}
+
{{ state.message }}
+
+
+
+
+
+
+
+
+
+
+
\ 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('操作失败')
}
}