mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-05-17 05:57:36 +08:00
自实现 UseConfirm 组件
This commit is contained in:
86
src/@core/components/ConfirmDialog.vue
Normal file
86
src/@core/components/ConfirmDialog.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean
|
||||
type?: 'info' | 'warn' | 'error'
|
||||
title?: string
|
||||
content?: string
|
||||
confirmText?: string
|
||||
cancelText?: string
|
||||
width?: string | number
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
type: 'info',
|
||||
title: '',
|
||||
content: '',
|
||||
confirmText: '',
|
||||
cancelText: '',
|
||||
width: '30rem',
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
(e: 'confirm'): void
|
||||
(e: 'cancel'): void
|
||||
}>()
|
||||
|
||||
// 对话框类型对应的图标和颜色
|
||||
const typeConfig = {
|
||||
info: {
|
||||
icon: 'mdi-information',
|
||||
color: 'info',
|
||||
},
|
||||
warn: {
|
||||
icon: 'mdi-alert',
|
||||
color: 'warning',
|
||||
},
|
||||
error: {
|
||||
icon: 'mdi-alert-circle',
|
||||
color: 'error',
|
||||
},
|
||||
}
|
||||
|
||||
// 获取当前类型的配置
|
||||
const currentType = computed(() => typeConfig[props.type])
|
||||
|
||||
// 确认按钮点击
|
||||
function handleConfirm() {
|
||||
emit('confirm')
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
|
||||
// 取消按钮点击
|
||||
function handleCancel() {
|
||||
emit('cancel')
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VDialog :model-value="modelValue" @update:model-value="emit('update:modelValue', $event)" :max-width="width">
|
||||
<VCard>
|
||||
<VCardItem>
|
||||
<div class="d-flex align-center justify-center mt-3">
|
||||
<VAvatar :color="currentType.color" variant="text" size="x-large">
|
||||
<VIcon size="x-large" :icon="currentType.icon" />
|
||||
</VAvatar>
|
||||
<div class="mx-3">
|
||||
<p class="font-weight-bold text-xl text-high-emphasis">{{ title }}</p>
|
||||
<p>{{ content }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</VCardItem>
|
||||
<VCardActions class="mx-auto">
|
||||
<VBtn variant="tonal" color="secondary" class="px-5" @click="handleCancel">
|
||||
{{ cancelText }}
|
||||
</VBtn>
|
||||
<VBtn variant="elevated" :color="currentType.color" @click="handleConfirm" class="px-5">
|
||||
{{ confirmText }}
|
||||
</VBtn>
|
||||
</VCardActions>
|
||||
<VDialogCloseBtn @click="handleCancel" />
|
||||
</VCard>
|
||||
</VDialog>
|
||||
</template>
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { useToast } from 'vue-toast-notification'
|
||||
import { useConfirm } from 'vuetify-use-dialog'
|
||||
import { useConfirm } from '@/composables/useConfirm'
|
||||
import api from '@/api'
|
||||
import type { Plugin } from '@/api/types'
|
||||
import { isNullOrEmptyObject } from '@core/utils'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { useToast } from 'vue-toast-notification'
|
||||
import { useConfirm } from 'vuetify-use-dialog'
|
||||
import { useConfirm } from '@/composables/useConfirm'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useDisplay } from 'vuetify'
|
||||
|
||||
@@ -290,12 +290,7 @@ const dropdownItems = ref([
|
||||
<div class="plugin-folder-card__body" :class="{ 'plugin-folder-card__body--no-icon': !shouldShowIcon }">
|
||||
<!-- 文件夹图标 -->
|
||||
<div v-if="shouldShowIcon" class="plugin-folder-card__icon-container">
|
||||
<VIcon
|
||||
:icon="folderIcon"
|
||||
:size="display.mobile ? 56 : 72"
|
||||
class="plugin-folder-card__folder-icon"
|
||||
:color="iconColor"
|
||||
/>
|
||||
<VIcon :icon="folderIcon" :size="display.mobile ? 56 : 72" class="cursor-move" :color="iconColor" />
|
||||
</div>
|
||||
|
||||
<!-- 文件夹信息 -->
|
||||
@@ -534,14 +529,6 @@ const dropdownItems = ref([
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__folder-icon {
|
||||
transition: all 0.3s ease;
|
||||
|
||||
.plugin-folder-card--hover & {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
text-align: left;
|
||||
min-height: 0;
|
||||
|
||||
@@ -11,7 +11,7 @@ import api from '@/api'
|
||||
import type { Site, SiteStatistic, SiteUserData } from '@/api/types'
|
||||
import { isNullOrEmptyObject } from '@/@core/utils'
|
||||
import { formatFileSize } from '@/@core/utils/formatters'
|
||||
import { useConfirm } from 'vuetify-use-dialog'
|
||||
import { useConfirm } from '@/composables/useConfirm'
|
||||
|
||||
// 国际化
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { useToast } from 'vue-toast-notification'
|
||||
import { useConfirm } from 'vuetify-use-dialog'
|
||||
import { useConfirm } from '@/composables/useConfirm'
|
||||
import SubscribeEditDialog from '../dialog/SubscribeEditDialog.vue'
|
||||
import SubscribeFilesDialog from '../dialog/SubscribeFilesDialog.vue'
|
||||
import SubscribeShareDialog from '../dialog/SubscribeShareDialog.vue'
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Subscribe, User } from '@/api/types'
|
||||
import { useUserStore } from '@/stores'
|
||||
import avatar1 from '@images/avatars/avatar-1.png'
|
||||
import { useToast } from 'vue-toast-notification'
|
||||
import { useConfirm } from 'vuetify-use-dialog'
|
||||
import { useConfirm } from '@/composables/useConfirm'
|
||||
import UserAddEditDialog from '@/components/dialog/UserAddEditDialog.vue'
|
||||
import { useDisplay } from 'vuetify'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { Workflow } from '@/api/types'
|
||||
import { useToast } from 'vue-toast-notification'
|
||||
import { useConfirm } from 'vuetify-use-dialog'
|
||||
import { useConfirm } from '@/composables/useConfirm'
|
||||
import WorkflowAddEditDialog from '@/components/dialog/WorkflowAddEditDialog.vue'
|
||||
import WorkflowActionsDialog from '@/components/dialog/WorkflowActionsDialog.vue'
|
||||
import api from '@/api'
|
||||
|
||||
@@ -4,7 +4,7 @@ import { numberValidator } from '@/@validators'
|
||||
import api from '@/api'
|
||||
import type { DownloaderConf, FilterRuleGroup, Site, Subscribe, TransferDirectoryConf } from '@/api/types'
|
||||
import { useDisplay } from 'vuetify'
|
||||
import { useConfirm } from 'vuetify-use-dialog'
|
||||
import { useConfirm } from '@/composables/useConfirm'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { qualityOptions, resolutionOptions, effectOptions } from '@/api/constants'
|
||||
// i18n
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import type { AxiosRequestConfig } from 'axios'
|
||||
import type { PropType } from 'vue'
|
||||
import { useConfirm } from 'vuetify-use-dialog'
|
||||
import { useConfirm } from '@/composables/useConfirm'
|
||||
import { useToast } from 'vue-toast-notification'
|
||||
import ReorganizeDialog from '../dialog/ReorganizeDialog.vue'
|
||||
import { formatBytes } from '@core/utils/formatters'
|
||||
|
||||
88
src/composables/useConfirm.ts
Normal file
88
src/composables/useConfirm.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { ref } from 'vue'
|
||||
import { createApp } from 'vue'
|
||||
import i18n from '@/plugins/i18n'
|
||||
import vuetify from '@/plugins/vuetify'
|
||||
import ConfirmDialog from '@/@core/components/ConfirmDialog.vue'
|
||||
import DialogCloseBtn from '@/@core/components/DialogCloseBtn.vue'
|
||||
|
||||
interface ConfirmOptions {
|
||||
type?: 'info' | 'warn' | 'error'
|
||||
title?: string
|
||||
content?: string
|
||||
confirmText?: string
|
||||
cancelText?: string
|
||||
width?: string | number
|
||||
}
|
||||
|
||||
let resolvePromise: ((value: boolean) => void) | null = null
|
||||
|
||||
// 创建确认对话框实例
|
||||
async function createConfirmDialog(options: ConfirmOptions = {}) {
|
||||
return new Promise<boolean>(resolve => {
|
||||
resolvePromise = resolve
|
||||
|
||||
// 创建容器
|
||||
const container = document.createElement('div')
|
||||
document.body.appendChild(container)
|
||||
|
||||
// 处理国际化
|
||||
const i18nOptions = {
|
||||
...options,
|
||||
title: options.title || i18n.global.t('common.confirm'),
|
||||
confirmText: options.confirmText || i18n.global.t('common.confirm'),
|
||||
cancelText: options.cancelText || i18n.global.t('common.cancel'),
|
||||
}
|
||||
|
||||
// 创建应用实例
|
||||
const app = createApp(ConfirmDialog, {
|
||||
modelValue: true,
|
||||
...i18nOptions,
|
||||
'onUpdate:modelValue': (val: boolean) => {
|
||||
if (!val) {
|
||||
cleanup()
|
||||
}
|
||||
},
|
||||
onConfirm: () => {
|
||||
resolvePromise?.(true)
|
||||
cleanup()
|
||||
},
|
||||
onCancel: () => {
|
||||
resolvePromise?.(false)
|
||||
cleanup()
|
||||
},
|
||||
})
|
||||
|
||||
// 注册必要的组件
|
||||
app.component('VDialogCloseBtn', DialogCloseBtn)
|
||||
|
||||
// 使用插件
|
||||
app.use(vuetify)
|
||||
app.use(i18n)
|
||||
|
||||
// 挂载应用
|
||||
app.mount(container)
|
||||
|
||||
// 清理函数
|
||||
const cleanup = () => {
|
||||
app.unmount()
|
||||
document.body.removeChild(container)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 创建一个函数对象,同时支持直接调用和解构
|
||||
const confirmFunction = Object.assign(createConfirmDialog, {
|
||||
createConfirm: createConfirmDialog,
|
||||
})
|
||||
|
||||
// 导出 useConfirm 函数
|
||||
export function useConfirm() {
|
||||
return confirmFunction
|
||||
}
|
||||
|
||||
// 插件
|
||||
export default {
|
||||
install: (app: any) => {
|
||||
app.provide('confirm', { createConfirm: createConfirmDialog })
|
||||
},
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import { checkPrefersColorSchemeIsDark } from '@/@core/utils'
|
||||
import { getCurrentLocale, setI18nLanguage } from '@/plugins/i18n'
|
||||
import { saveLocalTheme } from '@/@core/utils/theme'
|
||||
import type { ThemeSwitcherTheme } from '@layouts/types'
|
||||
import { useConfirm } from '@/composables/useConfirm'
|
||||
|
||||
// 认证 Store
|
||||
const authStore = useAuthStore()
|
||||
@@ -32,9 +33,6 @@ const progressDialog = ref(false)
|
||||
// 站点认证对话框
|
||||
const siteAuthDialog = ref(false)
|
||||
|
||||
// 重启确认对话框
|
||||
const restartDialog = ref(false)
|
||||
|
||||
// 自定义CSS弹窗
|
||||
const cssDialog = ref(false)
|
||||
|
||||
@@ -47,6 +45,9 @@ const showLanguageMenu = ref(false)
|
||||
// 自定义CSS
|
||||
const customCSS = ref('')
|
||||
|
||||
// 确认框
|
||||
const { createConfirm } = useConfirm()
|
||||
|
||||
// 执行注销操作
|
||||
function logout() {
|
||||
// 清除登录状态信息
|
||||
@@ -57,7 +58,6 @@ function logout() {
|
||||
|
||||
// 执行重启操作
|
||||
async function restart() {
|
||||
restartDialog.value = false
|
||||
// 调用API重启
|
||||
try {
|
||||
// 显示等待框
|
||||
@@ -79,7 +79,15 @@ async function restart() {
|
||||
|
||||
// 显示重启确认对话框
|
||||
async function showRestartDialog() {
|
||||
restartDialog.value = true
|
||||
const isConfirmed = await createConfirm({
|
||||
type: 'warn',
|
||||
title: t('app.confirmRestart'),
|
||||
content: t('app.restartTip'),
|
||||
})
|
||||
|
||||
if (!isConfirmed) return
|
||||
|
||||
await restart()
|
||||
}
|
||||
|
||||
// 显示站点认证对话框
|
||||
@@ -417,32 +425,6 @@ onMounted(() => {
|
||||
<ProgressDialog v-if="progressDialog" v-model="progressDialog" :text="t('app.restarting')" />
|
||||
<!-- 用户认证对话框 -->
|
||||
<UserAuthDialog v-if="siteAuthDialog" v-model="siteAuthDialog" @done="siteAuthDone" @close="siteAuthDialog = false" />
|
||||
<!-- 重启确认对话框 -->
|
||||
<VDialog v-if="restartDialog" v-model="restartDialog" max-width="25rem">
|
||||
<VCard>
|
||||
<VCardItem>
|
||||
<div class="d-flex align-center justify-center mt-3">
|
||||
<VAvatar color="warning" variant="text" size="x-large">
|
||||
<VIcon size="x-large" icon="mdi-alert" />
|
||||
</VAvatar>
|
||||
<div class="ms-3">
|
||||
<p class="font-weight-bold text-xl text-high-emphasis">{{ t('app.confirmRestart') }}</p>
|
||||
<p>{{ t('app.restartTip') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</VCardItem>
|
||||
<VCardActions class="mx-auto">
|
||||
<VBtn variant="tonal" color="secondary" class="px-5" @click="restartDialog = false">{{
|
||||
t('common.cancel')
|
||||
}}</VBtn>
|
||||
<VBtn variant="elevated" color="error" @click="restart" prepend-icon="mdi-restart" class="px-5">{{
|
||||
t('common.confirm')
|
||||
}}</VBtn>
|
||||
</VCardActions>
|
||||
<VDialogCloseBtn @click="restartDialog = false" />
|
||||
</VCard>
|
||||
</VDialog>
|
||||
|
||||
<!-- 自定义 CSS -->
|
||||
<VDialog v-if="cssDialog" v-model="cssDialog" max-width="50rem" scrollable :fullscreen="!display.mdAndUp.value">
|
||||
<VCard>
|
||||
|
||||
21
src/main.ts
21
src/main.ts
@@ -24,7 +24,7 @@ import { fetchGlobalSettings } from './utils/globalSetting'
|
||||
|
||||
// 5. 其他插件和功能模块
|
||||
import ToastPlugin from 'vue-toast-notification'
|
||||
import VuetifyUseDialog from 'vuetify-use-dialog'
|
||||
import ConfirmDialog from '@/composables/useConfirm'
|
||||
import VueApexCharts from 'vue3-apexcharts'
|
||||
|
||||
// 6. 注册自定义组件
|
||||
@@ -102,24 +102,7 @@ initializeApp().then(() => {
|
||||
.use(ToastPlugin, {
|
||||
position: 'bottom-right',
|
||||
})
|
||||
.use(VuetifyUseDialog, {
|
||||
confirmDialog: {
|
||||
dialogProps: {
|
||||
maxWidth: '30rem',
|
||||
},
|
||||
confirmationButtonProps: {
|
||||
color: 'primary',
|
||||
class: 'me-3 px-5',
|
||||
'prepend-icon': 'mdi-check',
|
||||
},
|
||||
cancellationButtonProps: {
|
||||
color: 'secondary',
|
||||
class: 'me-3',
|
||||
},
|
||||
confirmationText: i18n.global.t('common.confirm'),
|
||||
cancellationText: i18n.global.t('common.cancel'),
|
||||
},
|
||||
})
|
||||
.use(ConfirmDialog)
|
||||
.use(i18n)
|
||||
.mount('#app')
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user