mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-03-01 01:21:04 +08:00
@@ -21,6 +21,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<GlobalPopups />
|
||||
<ConfirmDialog />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -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()
|
||||
|
||||
53
frontend_nuxt/components/ConfirmDialog.vue
Normal file
53
frontend_nuxt/components/ConfirmDialog.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<BasePopup :visible="state.visible" @close="onCancel">
|
||||
<div class="confirm-dialog">
|
||||
<h3 class="confirm-title">{{ state.title }}</h3>
|
||||
<p class="confirm-message">{{ state.message }}</p>
|
||||
<div class="confirm-actions">
|
||||
<button class="cancel-button" @click="onCancel">取消</button>
|
||||
<button class="confirm-button" @click="onConfirm">确认</button>
|
||||
</div>
|
||||
</div>
|
||||
</BasePopup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import BasePopup from '~/components/BasePopup.vue'
|
||||
import { useConfirm } from '~/composables/useConfirm'
|
||||
|
||||
const { state, onConfirm, onCancel } = useConfirm()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.confirm-dialog {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.confirm-title {
|
||||
margin-top: 0;
|
||||
}
|
||||
.confirm-message {
|
||||
margin: 20px 0;
|
||||
}
|
||||
.confirm-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.confirm-button {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.cancel-button {
|
||||
background-color: #ccc;
|
||||
color: black;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
42
frontend_nuxt/composables/useConfirm.js
Normal file
42
frontend_nuxt/composables/useConfirm.js
Normal file
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -185,6 +185,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
|
||||
@@ -212,6 +213,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
|
||||
@@ -283,7 +285,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) {
|
||||
@@ -621,19 +623,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('操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user