重构对话框组件,将所有 DialogWrapper 替换为 VDialog,并更新缓存版本至 v1.1.0

This commit is contained in:
jxxghp
2025-08-23 18:54:00 +08:00
parent 6e50acf106
commit 472d1960d9
66 changed files with 163 additions and 637 deletions

2
components.d.ts vendored
View File

@@ -10,7 +10,6 @@ declare module 'vue' {
export interface GlobalComponents {
ConfirmDialog: typeof import('./src/@core/components/ConfirmDialog.vue')['default']
DialogCloseBtn: typeof import('./src/@core/components/DialogCloseBtn.vue')['default']
DialogWrapper: typeof import('./src/@core/components/DialogWrapper.vue')['default']
ErrorHeader: typeof import('./src/@core/components/ErrorHeader.vue')['default']
ExistIcon: typeof import('./src/@core/components/ExistIcon.vue')['default']
LoadingBanner: typeof import('./src/@core/components/LoadingBanner.vue')['default']
@@ -20,5 +19,6 @@ declare module 'vue' {
RouterView: typeof import('vue-router')['RouterView']
ScrollToTopBtn: typeof import('./src/@core/components/ScrollToTopBtn.vue')['default']
StatIcon: typeof import('./src/@core/components/StatIcon.vue')['default']
VDialog: typeof import('./src/@core/components/VDialog.vue')['default']
}
}

View File

@@ -59,7 +59,7 @@ function handleCancel() {
</script>
<template>
<DialogWrapper :model-value="modelValue" @update:model-value="emit('update:modelValue', $event)" :max-width="width">
<VDialog :model-value="modelValue" @update:model-value="emit('update:modelValue', $event)" :max-width="width">
<VCard>
<VCardItem>
<div class="d-flex align-center justify-start mt-3">
@@ -82,5 +82,5 @@ function handleCancel() {
</VCardActions>
<VDialogCloseBtn @click="handleCancel" />
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -1,70 +0,0 @@
<template>
<VDialog v-model="dialogModel" v-bind="$attrs" @update:model-value="handleDialogChange">
<slot />
</VDialog>
</template>
<script setup lang="ts">
import { computed, watch, onBeforeUnmount } from 'vue'
import { useScrollLockWithWatch } from '@/composables/useScrollLock'
// Props
interface Props {
modelValue?: boolean
// 滚动锁定配置
scrollLock?: boolean
preserveScrollPosition?: boolean
preventTouchScroll?: boolean
}
const props = withDefaults(defineProps<Props>(), {
scrollLock: true,
preserveScrollPosition: true,
preventTouchScroll: true,
})
// Emits
const emit = defineEmits<{
'update:modelValue': [value: boolean]
}>()
// 计算属性
const dialogModel = computed({
get: () => props.modelValue || false,
set: (value: boolean) => emit('update:modelValue', value),
})
// 使用滚动锁定
const { isLocked, lockScroll, restoreScroll } = useScrollLockWithWatch(dialogModel, {
autoRestore: true,
preserveScrollPosition: props.preserveScrollPosition,
preventTouchScroll: props.preventTouchScroll,
})
// 处理弹窗状态变化
const handleDialogChange = (value: boolean) => {
emit('update:modelValue', value)
}
// 监听弹窗状态变化
watch(
dialogModel,
newValue => {
if (props.scrollLock) {
if (newValue) {
lockScroll()
} else {
restoreScroll()
}
}
},
{ immediate: true },
)
// 组件卸载时确保恢复滚动
onBeforeUnmount(() => {
if (isLocked.value) {
restoreScroll()
}
})
</script>

View File

@@ -133,7 +133,7 @@ const instructions = computed(() => {
</Teleport>
<!-- 手动安装说明对话框 -->
<DialogWrapper v-model="showInstructions" max-width="500">
<VDialog v-model="showInstructions" max-width="500">
<VCard>
<VCardItem>
<VCardTitle class="d-flex align-center">
@@ -170,7 +170,7 @@ const instructions = computed(() => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>
<style scoped>

View File

@@ -116,7 +116,7 @@ function onClose() {
<VImg :src="filter_svg" cover class="mt-7" max-width="3rem" />
</VCardText>
</VCard>
<DialogWrapper
<VDialog
v-if="ruleInfoDialog"
v-model="ruleInfoDialog"
scrollable
@@ -222,6 +222,6 @@ function onClose() {
}}</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</div>
</template>

View File

@@ -147,7 +147,7 @@ const { stop: stopRefresh } = useConditionalDataRefresh(
loadDownloaderInfo,
shouldRefresh, // 响应式条件只有当allowRefresh为true且downloader启用时才运行
3000, // 3秒间隔
true // 立即执行一次
true, // 立即执行一次
)
onUnmounted(() => {
@@ -196,7 +196,7 @@ onUnmounted(() => {
</VCard>
</VHover>
<DialogWrapper
<VDialog
v-if="downloaderInfoDialog"
v-model="downloaderInfoDialog"
scrollable
@@ -383,6 +383,6 @@ onUnmounted(() => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</div>
</template>

View File

@@ -223,7 +223,7 @@ function onClose() {
<VImg :src="filter_group_svg" cover class="mt-10" max-width="3rem" />
</VCardText>
</VCard>
<DialogWrapper
<VDialog
v-if="groupInfoDialog"
v-model="groupInfoDialog"
scrollable
@@ -308,7 +308,7 @@ function onClose() {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
<ImportCodeDialog
v-if="importCodeDialog"
v-model="importCodeDialog"

View File

@@ -204,7 +204,7 @@ onMounted(() => {
</VCardText>
</VCard>
<DialogWrapper
<VDialog
v-if="mediaServerInfoDialog"
v-model="mediaServerInfoDialog"
scrollable
@@ -506,6 +506,6 @@ onMounted(() => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</div>
</template>

View File

@@ -141,7 +141,7 @@ function onClose() {
</VCardText>
</VCard>
<DialogWrapper
<VDialog
v-if="notificationInfoDialog"
v-model="notificationInfoDialog"
scrollable
@@ -476,6 +476,6 @@ function onClose() {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</div>
</template>

View File

@@ -106,7 +106,9 @@ const iconPath: Ref<string> = computed(() => {
if (imageLoadError.value) return noImage
// 如果是网络图片则使用代理后返回
if (props.plugin?.plugin_icon?.startsWith('http'))
return `${import.meta.env.VITE_API_BASE_URL}system/img/1?imgurl=${encodeURIComponent(props.plugin?.plugin_icon)}&cache=true`
return `${import.meta.env.VITE_API_BASE_URL}system/img/1?imgurl=${encodeURIComponent(
props.plugin?.plugin_icon,
)}&cache=true`
return `./plugin_icon/${props.plugin?.plugin_icon}`
})
@@ -267,15 +269,15 @@ const dropdownItems = ref([
<!-- 安装插件进度框 -->
<ProgressDialog v-if="progressDialog" v-model="progressDialog" :text="progressText" />
<!-- 更新日志 -->
<DialogWrapper v-if="releaseDialog" v-model="releaseDialog" width="600" scrollable>
<VDialog v-if="releaseDialog" v-model="releaseDialog" width="600" scrollable>
<VCard :title="t('plugin.updateHistoryTitle', { name: props.plugin?.plugin_name })">
<VDialogCloseBtn @click="releaseDialog = false" />
<VDivider />
<VersionHistory :history="props.plugin?.history" />
</VCard>
</DialogWrapper>
</VDialog>
<!-- 插件详情-->
<DialogWrapper v-if="detailDialog" v-model="detailDialog" max-width="30rem">
<VDialog v-if="detailDialog" v-model="detailDialog" max-width="30rem">
<VCard>
<VDialogCloseBtn @click="detailDialog = false" />
<VCardText>
@@ -335,6 +337,6 @@ const dropdownItems = ref([
</VCol>
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
</div>
</template>

View File

@@ -547,7 +547,7 @@ watch(
<ProgressDialog v-if="progressDialog" v-model="progressDialog" :text="progressText" />
<!-- 更新日志 -->
<DialogWrapper v-if="releaseDialog" v-model="releaseDialog" width="600" scrollable :fullscreen="!display.mdAndUp.value">
<VDialog v-if="releaseDialog" v-model="releaseDialog" width="600" scrollable :fullscreen="!display.mdAndUp.value">
<VCard :title="t('plugin.updateHistoryTitle', { name: props.plugin?.plugin_name })">
<VDialogCloseBtn @click="releaseDialog = false" />
<VDivider />
@@ -562,10 +562,10 @@ watch(
</VBtn>
</VCardItem>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 实时日志弹窗 -->
<DialogWrapper
<VDialog
v-if="loggingDialog"
v-model="loggingDialog"
scrollable
@@ -591,10 +591,10 @@ watch(
<LoggingView :logfile="`plugins/${props.plugin?.id?.toLowerCase()}.log`" />
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 插件分身对话框 -->
<DialogWrapper
<VDialog
v-if="pluginCloneDialog"
v-model="pluginCloneDialog"
width="600"
@@ -700,7 +700,7 @@ watch(
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</div>
</template>

View File

@@ -350,7 +350,7 @@ const dropdownItems = ref([
</VHover>
<!-- 重命名对话框 -->
<DialogWrapper v-if="renameDialog" v-model="renameDialog" max-width="400">
<VDialog v-if="renameDialog" v-model="renameDialog" max-width="400">
<VCard>
<VCardItem>
<template #prepend>
@@ -374,10 +374,10 @@ const dropdownItems = ref([
<VBtn color="primary" prepend-icon="mdi-check" class="px-5" @click="confirmRename">确认</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 设置对话框 -->
<DialogWrapper
<VDialog
v-if="settingDialog"
v-model="settingDialog"
max-width="600"
@@ -480,7 +480,7 @@ const dropdownItems = ref([
<VBtn color="primary" prepend-icon="mdi-content-save" class="px-5" @click="saveSettings">保存</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</div>
</template>

View File

@@ -220,7 +220,7 @@ function onClose() {
@close="smbConfigDialog = false"
@done="handleDone"
/>
<DialogWrapper
<VDialog
v-if="customConfigDialog"
v-model="customConfigDialog"
scrollable
@@ -263,6 +263,6 @@ function onClose() {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</div>
</template>

View File

@@ -278,7 +278,7 @@ onMounted(() => {
</VCard>
<!-- 更多来源对话框 -->
<DialogWrapper v-model="showMoreTorrents" max-width="25rem" location="center">
<VDialog v-model="showMoreTorrents" max-width="25rem" location="center">
<VCard>
<VCardTitle class="py-3 d-flex align-center">
<span>其他来源</span>
@@ -361,7 +361,7 @@ onMounted(() => {
</VList>
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
<AddDownloadDialog
v-if="addDownloadDialog"
@@ -418,7 +418,7 @@ onMounted(() => {
}
.chip-web-source {
background-color: #8000FF;
background-color: #8000ff;
color: white;
}

View File

@@ -132,7 +132,7 @@ onMounted(() => {
})
</script>
<template>
<DialogWrapper max-width="35rem" scrollable>
<VDialog max-width="35rem" scrollable>
<VCard>
<VCardItem class="py-2">
<template #prepend>
@@ -209,5 +209,5 @@ onMounted(() => {
</VBtn>
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -70,7 +70,7 @@ async function savaAlistConfig() {
</script>
<template>
<DialogWrapper width="50rem" scrollable :fullscreen="!display.mdAndUp.value">
<VDialog width="50rem" scrollable :fullscreen="!display.mdAndUp.value">
<VCard>
<VDialogCloseBtn @click="emit('close')" />
<VCardItem>
@@ -143,5 +143,5 @@ async function savaAlistConfig() {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -110,7 +110,7 @@ onUnmounted(() => {
</script>
<template>
<DialogWrapper width="40rem" scrollable :fullscreen="!display.mdAndUp.value">
<VDialog width="40rem" scrollable :fullscreen="!display.mdAndUp.value">
<VCard>
<VDialogCloseBtn @click="emit('close')" />
<VCardItem>
@@ -148,5 +148,5 @@ onUnmounted(() => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -170,7 +170,7 @@ onMounted(() => {
})
</script>
<template>
<DialogWrapper max-width="40rem" scrollable>
<VDialog max-width="40rem" scrollable>
<VCard>
<VCardText>
<VCol>
@@ -286,5 +286,5 @@ onMounted(() => {
</VCardText>
<VDialogCloseBtn @click="emit('close')" />
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -156,7 +156,7 @@ async function doDelete() {
}
</script>
<template>
<DialogWrapper max-width="40rem" scrollable>
<VDialog max-width="40rem" scrollable>
<VCard>
<VCardText>
<VCol>
@@ -266,7 +266,7 @@ async function doDelete() {
</VCardText>
<VDialogCloseBtn @click="emit('close')" />
</VCard>
</DialogWrapper>
</VDialog>
</template>
<style lang="scss">

View File

@@ -24,7 +24,7 @@ function handleImport() {
</script>
<template>
<DialogWrapper width="40rem" scrollable max-height="85vh">
<VDialog width="40rem" scrollable max-height="85vh">
<VCard>
<VCardItem>
<template #prepend>
@@ -43,5 +43,5 @@ function handleImport() {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -15,12 +15,12 @@ defineProps({
const emit = defineEmits(['close'])
</script>
<template>
<DialogWrapper max-width="50rem">
<VDialog max-width="50rem">
<VCard>
<VDialogCloseBtn @click="emit('close')" />
<VCardItem>
<MediaInfoCard :context="context" />
</VCardItem>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -148,7 +148,7 @@ onBeforeMount(async () => {
})
</script>
<template>
<DialogWrapper scrollable max-width="60rem" :fullscreen="!display.mdAndUp.value">
<VDialog scrollable max-width="60rem" :fullscreen="!display.mdAndUp.value">
<!-- Vuetify 渲染模式 -->
<VCard v-if="renderMode === 'vuetify'" :title="`${props.plugin?.plugin_name} - ${t('dialog.pluginConfig.title')}`">
<VDialogCloseBtn @click="emit('close')" />
@@ -187,5 +187,5 @@ onBeforeMount(async () => {
<!-- 进度框 -->
<ProgressDialog v-if="progressDialog" v-model="progressDialog" :text="progressText" />
</DialogWrapper>
</VDialog>
</template>

View File

@@ -124,7 +124,7 @@ onMounted(() => {
})
</script>
<template>
<DialogWrapper scrollable max-width="80rem" :fullscreen="!display.mdAndUp.value">
<VDialog scrollable max-width="80rem" :fullscreen="!display.mdAndUp.value">
<!-- Vuetify 渲染模式 -->
<VCard v-if="renderMode === 'vuetify'" :title="`${props.plugin?.plugin_name}`">
<VDialogCloseBtn @click="emit('close')" />
@@ -160,5 +160,5 @@ onMounted(() => {
/>
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -63,7 +63,7 @@ onMounted(() => {
</script>
<template>
<DialogWrapper width="50rem" scrollable :fullscreen="!display.mdAndUp.value">
<VDialog width="50rem" scrollable :fullscreen="!display.mdAndUp.value">
<VCard>
<VCardItem>
<VCardTitle>
@@ -89,5 +89,5 @@ onMounted(() => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -10,12 +10,12 @@ const props = defineProps({
</script>
<template>
<!-- Progress Dialog -->
<DialogWrapper :scrim="false" width="25rem">
<VDialog :scrim="false" width="25rem">
<VCard elevation="3" color="primary">
<VCardText class="text-center">
{{ props.text || t('dialog.progress.processing') }}
<VProgressLinear color="white" class="mb-0 mt-1" :model-value="props.value" indeterminate />
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -57,7 +57,7 @@ async function handleReset() {
</script>
<template>
<DialogWrapper width="50rem" scrollable :fullscreen="!display.mdAndUp.value">
<VDialog width="50rem" scrollable :fullscreen="!display.mdAndUp.value">
<VCard>
<VDialogCloseBtn @click="emit('close')" />
<VCardItem>
@@ -99,5 +99,5 @@ async function handleReset() {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -205,7 +205,7 @@ const progressSSE = useProgressSSE(
`${import.meta.env.VITE_API_BASE_URL}system/progress/filetransfer`,
handleProgressMessage,
'reorganize-progress',
progressActive
progressActive,
)
// 使用SSE监听加载进度
@@ -269,7 +269,7 @@ onUnmounted(() => {
</script>
<template>
<DialogWrapper scrollable max-width="45rem" :fullscreen="!display.mdAndUp.value">
<VDialog scrollable max-width="45rem" :fullscreen="!display.mdAndUp.value">
<VCard>
<VCardItem class="py-2">
<template #prepend> <VIcon icon="mdi-folder-move" class="me-2" /> </template>
@@ -487,7 +487,7 @@ onUnmounted(() => {
<!-- 手动整理进度框 -->
<ProgressDialog v-if="progressDialog" v-model="progressDialog" :text="progressText" :value="progressValue" />
<!-- TMDB ID搜索框 -->
<DialogWrapper v-model="mediaSelectorDialog" width="40rem" scrollable max-height="85vh">
<VDialog v-model="mediaSelectorDialog" width="40rem" scrollable max-height="85vh">
<MediaIdSelector
v-if="mediaSource === 'themoviedb'"
v-model="transferForm.tmdbid"
@@ -500,6 +500,6 @@ onUnmounted(() => {
@close="mediaSelectorDialog = false"
:type="mediaSource"
/>
</DialogWrapper>
</DialogWrapper>
</VDialog>
</VDialog>
</template>

View File

@@ -370,7 +370,7 @@ onMounted(() => {
})
</script>
<template>
<DialogWrapper v-model="dialog" max-width="42rem" scrollable :fullscreen="!display.mdAndUp.value">
<VDialog v-model="dialog" max-width="42rem" scrollable :fullscreen="!display.mdAndUp.value">
<VCard class="search-dialog">
<!-- 搜索输入框 -->
<VCardItem class="pa-4 pa-sm-5 search-box-container">
@@ -785,7 +785,7 @@ onMounted(() => {
</div>
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 站点选择对话框 -->
<SearchSiteDialog

View File

@@ -56,7 +56,7 @@ const filteredSites = computed(() => {
</script>
<template>
<!-- Site Selection Dialog -->
<DialogWrapper max-width="40rem" fullscreen-mobile>
<VDialog max-width="40rem" fullscreen-mobile>
<VCard class="site-dialog">
<VCardItem>
<template #prepend>
@@ -169,7 +169,7 @@ const filteredSites = computed(() => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>
<style scoped>
.site-checkbox-wrapper {

View File

@@ -147,7 +147,7 @@ onMounted(async () => {
</script>
<template>
<DialogWrapper scrollable :close-on-back="false" eager max-width="45rem" :fullscreen="!display.mdAndUp.value">
<VDialog scrollable :close-on-back="false" eager max-width="45rem" :fullscreen="!display.mdAndUp.value">
<VCard>
<VCardItem :class="props.oper === 'add' ? 'py-3' : 'py-2'">
<template #prepend>
@@ -350,5 +350,5 @@ onMounted(async () => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -71,7 +71,7 @@ async function updateSiteCookie() {
}
</script>
<template>
<DialogWrapper max-width="30rem" scrollable>
<VDialog max-width="30rem" scrollable>
<!-- Dialog Content -->
<VCard :title="t('dialog.siteCookieUpdate.title')">
<VDialogCloseBtn @click="emit('close')" />
@@ -114,5 +114,5 @@ async function updateSiteCookie() {
</VCard>
<!-- 进度框 -->
<ProgressDialog v-if="progressDialog" v-model="progressDialog" :text="progressText" />
</DialogWrapper>
</VDialog>
</template>

View File

@@ -222,7 +222,7 @@ watch(selectedFile, async newFile => {
</script>
<template>
<DialogWrapper scrollable max-width="50rem" :fullscreen="!display.mdAndUp.value">
<VDialog scrollable max-width="50rem" :fullscreen="!display.mdAndUp.value">
<VCard>
<VCardItem class="py-2">
<template #prepend>
@@ -383,7 +383,7 @@ watch(selectedFile, async newFile => {
</div>
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
</template>
<style scoped>

View File

@@ -130,7 +130,7 @@ onMounted(() => {
})
</script>
<template>
<DialogWrapper scrollable fullscreen :scrim="false" transition="dialog-bottom-transition">
<VDialog scrollable fullscreen :scrim="false" transition="dialog-bottom-transition">
<VCard>
<!-- Toolbar -->
<div>
@@ -281,7 +281,7 @@ onMounted(() => {
@error="addDownloadError"
@close="addDownloadDialog = false"
/>
</DialogWrapper>
</VDialog>
</template>
<style lang="scss" scoped>

View File

@@ -205,7 +205,7 @@ onMounted(() => {
</script>
<template>
<DialogWrapper max-width="50rem" :fullscreen="display.smAndDown.value" scrollable>
<VDialog max-width="50rem" :fullscreen="display.smAndDown.value" scrollable>
<VCard>
<!-- 标题栏 -->
<VCardItem>
@@ -302,7 +302,7 @@ onMounted(() => {
</VCard>
<!-- 详情弹窗 -->
<DialogWrapper v-model="detailDialog" :max-width="display.mdAndUp.value ? 600 : '95%'" scrollable>
<VDialog v-model="detailDialog" :max-width="display.mdAndUp.value ? 600 : '95%'" scrollable>
<VCard v-if="selectedSite">
<VCardItem class="py-3">
<template #prepend>
@@ -379,8 +379,8 @@ onMounted(() => {
</div>
</VCardText>
</VCard>
</DialogWrapper>
</DialogWrapper>
</VDialog>
</VDialog>
</template>
<style scoped>

View File

@@ -287,7 +287,7 @@ onBeforeMount(() => {
</script>
<template>
<DialogWrapper scrollable eager max-width="80rem" :fullscreen="!display.mdAndUp.value">
<VDialog scrollable eager max-width="80rem" :fullscreen="!display.mdAndUp.value">
<VCard>
<VCardItem>
<VCardTitle>
@@ -484,5 +484,5 @@ onBeforeMount(() => {
</VCard>
<!-- 进度框 -->
<ProgressDialog v-if="progressDialog" v-model="progressDialog" :text="t('dialog.siteUserData.refreshing')" />
</DialogWrapper>
</VDialog>
</template>

View File

@@ -50,7 +50,7 @@ async function saveSmbConfig() {
</script>
<template>
<DialogWrapper width="50rem" scrollable :fullscreen="!display.mdAndUp.value">
<VDialog width="50rem" scrollable :fullscreen="!display.mdAndUp.value">
<VCard>
<VDialogCloseBtn @click="emit('close')" />
<VCardItem>
@@ -127,5 +127,5 @@ async function saveSmbConfig() {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -284,7 +284,7 @@ onMounted(() => {
</script>
<template>
<DialogWrapper scrollable max-width="45rem" :fullscreen="!display.mdAndUp.value">
<VDialog scrollable max-width="45rem" :fullscreen="!display.mdAndUp.value">
<VCard>
<VCardItem class="py-2">
<VDialogCloseBtn @click="emit('close')" />
@@ -543,5 +543,5 @@ onMounted(() => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -85,7 +85,7 @@ onBeforeMount(() => {
})
</script>
<template>
<DialogWrapper scrollable max-width="80rem" :fullscreen="!display.mdAndUp.value">
<VDialog scrollable max-width="80rem" :fullscreen="!display.mdAndUp.value">
<VCard>
<VCardItem class="my-2">
<VDialogCloseBtn @click="emit('close')" />
@@ -206,7 +206,7 @@ onBeforeMount(() => {
</div>
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
</template>
<style lang="scss" scoped>

View File

@@ -146,7 +146,7 @@ function getMediaTypeText(type: string | undefined) {
</script>
<template>
<DialogWrapper scrollable max-width="50rem" :fullscreen="!display.mdAndUp.value">
<VDialog scrollable max-width="50rem" :fullscreen="!display.mdAndUp.value">
<VCard class="mx-auto" width="100%">
<VCardItem>
<VCardTitle>{{ t('dialog.subscribeHistory.title', { type: getMediaTypeText(props.type) }) }}</VCardTitle>
@@ -220,5 +220,5 @@ function getMediaTypeText(type: string | undefined) {
</VCard>
<!-- 进度框 -->
<ProgressDialog v-if="progressDialog" v-model="progressDialog" :text="progressText" />
</DialogWrapper>
</VDialog>
</template>

View File

@@ -55,7 +55,7 @@ const $toast = useToast()
</script>
<template>
<DialogWrapper scrollable max-width="30rem" :fullscreen="!display.mdAndUp.value">
<VDialog scrollable max-width="30rem" :fullscreen="!display.mdAndUp.value">
<VCard>
<VCardItem class="py-2">
<template #prepend>
@@ -112,5 +112,5 @@ const $toast = useToast()
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -118,7 +118,7 @@ onMounted(() => {
</script>
<template>
<DialogWrapper scrollable max-width="40rem" :fullscreen="!display.mdAndUp.value">
<VDialog scrollable max-width="40rem" :fullscreen="!display.mdAndUp.value">
<VCard>
<VCardItem>
<template #prepend>
@@ -331,7 +331,7 @@ onMounted(() => {
</div>
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
</template>
<style scoped>

View File

@@ -154,7 +154,7 @@ onUnmounted(() => {
</script>
<template>
<DialogWrapper scrollable max-width="50rem" :fullscreen="!display.mdAndUp.value">
<VDialog scrollable max-width="50rem" :fullscreen="!display.mdAndUp.value">
<VCard class="mx-auto" width="100%">
<VCardItem>
<VCardTitle>{{ t('dialog.transferQueue.title') }}</VCardTitle>
@@ -203,5 +203,5 @@ onUnmounted(() => {
</VWindow>
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -115,7 +115,7 @@ onUnmounted(() => {
</script>
<template>
<DialogWrapper width="40rem" scrollable :fullscreen="!display.mdAndUp.value">
<VDialog width="40rem" scrollable :fullscreen="!display.mdAndUp.value">
<VCard>
<VDialogCloseBtn @click="emit('close')" />
<VCardItem>
@@ -147,5 +147,5 @@ onUnmounted(() => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -366,7 +366,7 @@ onMounted(() => {
</script>
<template>
<DialogWrapper scrollable max-width="40rem" :fullscreen="!display.mdAndUp.value">
<VDialog scrollable max-width="40rem" :fullscreen="!display.mdAndUp.value">
<VCard>
<VCardItem :class="props.oper === 'add' ? 'py-3' : 'py-2'">
<template #prepend>
@@ -619,5 +619,5 @@ onMounted(() => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -4,7 +4,6 @@ import api from '@/api'
import { useToast } from 'vue-toastification'
import { useI18n } from 'vue-i18n'
// 多语言支持
const { t } = useI18n()
@@ -134,7 +133,7 @@ onMounted(async () => {
</script>
<template>
<DialogWrapper width="40rem" scrollable>
<VDialog width="40rem" scrollable>
<VCard>
<VCardItem>
<VCardTitle>
@@ -179,5 +178,5 @@ onMounted(async () => {
</VBtn>
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -197,7 +197,7 @@ const isMacOS = computed(() => {
</script>
<template>
<DialogWrapper scrollable fullscreen :scrim="false" transition="dialog-bottom-transition">
<VDialog scrollable fullscreen :scrim="false" transition="dialog-bottom-transition">
<VCard class="workflow-dialog">
<!-- Toolbar -->
<VToolbar color="primary" density="comfortable">
@@ -256,7 +256,7 @@ const isMacOS = computed(() => {
@close="importCodeDialog = false"
@save="saveCodeString"
/>
</DialogWrapper>
</VDialog>
</template>
<style lang="scss">

View File

@@ -182,7 +182,7 @@ onMounted(() => {
</script>
<template>
<DialogWrapper scrollable :close-on-back="false" eager max-width="30rem" :fullscreen="!display.mdAndUp.value">
<VDialog scrollable :close-on-back="false" eager max-width="30rem" :fullscreen="!display.mdAndUp.value">
<VCard>
<VCardItem>
<template #prepend>
@@ -269,5 +269,5 @@ onMounted(() => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -68,7 +68,7 @@ const $toast = useToast()
</script>
<template>
<DialogWrapper scrollable max-width="30rem" :fullscreen="!display.mdAndUp.value">
<VDialog scrollable max-width="30rem" :fullscreen="!display.mdAndUp.value">
<VCard>
<VCardItem class="py-2">
<template #prepend>
@@ -132,5 +132,5 @@ const $toast = useToast()
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -749,7 +749,7 @@ onMounted(() => {
</VCardText>
</VCard>
<!-- 重命名弹窗 -->
<DialogWrapper v-if="renamePopper" v-model="renamePopper" max-width="35rem">
<VDialog v-if="renamePopper" v-model="renamePopper" max-width="35rem">
<VCard>
<VCardItem>
<template #prepend>
@@ -783,7 +783,7 @@ onMounted(() => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 文件整理弹窗 -->
<ReorganizeDialog
v-if="transferPopper"

View File

@@ -166,7 +166,7 @@ const sortIcon = computed(() => {
<VIcon icon="mdi-arrow-up-bold-outline" />
</IconBtn>
<!-- 新建文件夹 -->
<DialogWrapper v-model="newFolderPopper" max-width="35rem">
<VDialog v-model="newFolderPopper" max-width="35rem">
<template #activator="{ props }">
<IconBtn>
<VIcon v-bind="props" icon="mdi-folder-plus-outline" />
@@ -191,6 +191,6 @@ const sortIcon = computed(() => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</VToolbar>
</template>

View File

@@ -1,383 +0,0 @@
import { ref, watch, onBeforeUnmount, readonly } from 'vue'
/**
* 滚动锁定 Composable
*
* 使用示例:
*
* // 基本用法
* const { isLocked, lockScroll, restoreScroll } = useScrollLock()
*
* // 带配置的用法
* const { isLocked, lockScroll, restoreScroll } = useScrollLock({
* preventTouchScroll: true,
* preserveScrollPosition: true,
* allowScrollSelectors: ['.my-modal', '.scrollable-content'],
* allowScrollContainerSelectors: ['.modal-content'],
* customScrollCheck: (element) => {
* // 自定义逻辑
* return element.classList.contains('allow-scroll')
* }
* })
*
* // 自动监听版本
* const { isLocked, lockScroll, restoreScroll } = useScrollLockWithWatch(
* showModal, // 响应式布尔值
* {
* allowScrollSelectors: ['.modal-content'],
* allowScrollContainerSelectors: ['.scrollable-area']
* }
* )
*/
// 滚动锁定配置
export interface ScrollLockOptions {
// 是否在组件卸载时自动恢复滚动
autoRestore?: boolean
// 是否保存和恢复滚动位置
preserveScrollPosition?: boolean
// 是否阻止触摸事件穿透
preventTouchScroll?: boolean
// 自定义锁定时的样式
lockStyles?: {
overflow?: string
position?: string
width?: string
}
// 允许滚动的选择器列表CSS选择器
// 例如:['.my-modal', '.scrollable-content']
allowScrollSelectors?: string[]
// 允许滚动的容器选择器列表CSS选择器
// 这些容器内的可滚动元素将被允许滚动
// 例如:['.modal-content', '.scroll-container']
allowScrollContainerSelectors?: string[]
// 自定义滚动检查函数
// 返回 true 表示允许滚动false 表示阻止滚动
customScrollCheck?: (element: Element) => boolean
}
// 默认配置
const DEFAULT_OPTIONS: Required<
Omit<ScrollLockOptions, 'allowScrollSelectors' | 'allowScrollContainerSelectors' | 'customScrollCheck'>
> = {
autoRestore: true,
preserveScrollPosition: true,
preventTouchScroll: true,
lockStyles: {
overflow: 'hidden',
position: 'fixed',
width: '100%',
},
}
// 全局状态管理
const globalLockCount = ref(0)
const globalOriginalStyles = ref<{
body: { [key: string]: string }
documentElement: { [key: string]: string }
html: { [key: string]: string }
} | null>(null)
const globalSavedScrollPosition = ref(0)
const globalTouchEventListeners = new Set<(event: TouchEvent) => void>()
// 保存全局原始样式(只在第一次锁定时保存)
const saveGlobalOriginalStyles = () => {
if (globalOriginalStyles.value === null) {
globalOriginalStyles.value = {
body: {
overflow: document.body.style.overflow,
},
documentElement: {
overflow: document.documentElement.style.overflow,
},
html: {
overflow: document.documentElement.style.overflow,
},
}
}
}
// 保存全局滚动位置(只在第一次锁定时保存)
const saveGlobalScrollPosition = () => {
if (globalLockCount.value === 0) {
globalSavedScrollPosition.value =
window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
}
}
// 应用全局锁定样式
const applyGlobalLockStyles = (config: any) => {
if (globalLockCount.value === 1) {
// 第一次锁定时应用样式
document.body.style.overflow = config.lockStyles.overflow || 'hidden'
document.documentElement.style.overflow = config.lockStyles.overflow || 'hidden'
document.documentElement.classList.add('v-overlay-scroll-blocked')
}
}
// 恢复全局样式(只在最后一个锁定时恢复)
const restoreGlobalStyles = (config: any) => {
if (globalLockCount.value === 0 && globalOriginalStyles.value) {
// 最后一个锁定时恢复样式
document.body.style.overflow = globalOriginalStyles.value.body.overflow || ''
document.documentElement.style.overflow = globalOriginalStyles.value.documentElement.overflow || ''
// 移除 CSS 类名
document.documentElement.classList.remove('v-overlay-scroll-blocked')
// 重置全局状态
globalOriginalStyles.value = null
globalSavedScrollPosition.value = 0
}
}
// 添加全局触摸事件监听器
const addGlobalTouchEventListener = (listener: (event: TouchEvent) => void) => {
globalTouchEventListeners.add(listener)
if (globalTouchEventListeners.size === 1) {
// 第一次添加监听器时绑定到document
document.addEventListener('touchmove', listener, { passive: false })
}
}
// 移除全局触摸事件监听器
const removeGlobalTouchEventListener = (listener: (event: TouchEvent) => void) => {
globalTouchEventListeners.delete(listener)
if (globalTouchEventListeners.size === 0) {
// 最后一个监听器被移除时解绑
document.removeEventListener('touchmove', listener)
}
}
export function useScrollLock(options: ScrollLockOptions = {}) {
const config = {
...DEFAULT_OPTIONS,
allowScrollSelectors: options.allowScrollSelectors || [],
allowScrollContainerSelectors: options.allowScrollContainerSelectors || [],
customScrollCheck: options.customScrollCheck,
...options,
}
// 状态管理
const isLocked = ref(false)
const savedScrollPosition = ref(0)
// 保存当前滚动位置
const saveScrollPosition = () => {
if (config.preserveScrollPosition) {
savedScrollPosition.value =
window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
}
}
// 检查元素是否应该允许滚动
const shouldAllowScroll = (element: Element): boolean => {
// 1. 检查是否匹配允许滚动的选择器
for (const selector of config.allowScrollSelectors) {
if (element.matches(selector) || element.closest(selector)) {
return true
}
}
// 2. 检查是否在允许滚动的容器内
for (const selector of config.allowScrollContainerSelectors) {
const container = element.closest(selector)
if (container) {
// 检查容器是否可滚动
const style = getComputedStyle(container)
const isScrollable =
container.scrollHeight > container.clientHeight &&
style.overflow !== 'hidden' &&
(style.overflow === 'auto' ||
style.overflow === 'scroll' ||
style.overflowY === 'auto' ||
style.overflowY === 'scroll')
if (isScrollable) {
return true
}
}
}
// 3. 检查是否在弹窗、菜单或其他覆盖层内
const isInDialog = element.closest(
'.v-dialog, .v-menu, .v-bottom-sheet, .v-snackbar, [role="dialog"], .v-overlay__content',
)
// 4. 检查是否是可滚动的内容区域
const isScrollableContent = element.closest(
'.v-card-text, .v-list, .v-table__wrapper, .v-data-table__wrapper, .v-sheet, .v-card__content, .v-data-table, .v-table',
)
// 5. 检查是否在可滚动的容器内
const scrollableContainer = element.closest('[style*="overflow"], [class*="overflow"]')
const isInScrollableContainer =
scrollableContainer &&
(scrollableContainer.scrollHeight > scrollableContainer.clientHeight ||
getComputedStyle(scrollableContainer).overflow !== 'hidden')
// 6. 使用自定义检查函数
if (config.customScrollCheck && config.customScrollCheck(element)) {
return true
}
// 如果不在弹窗内且不是可滚动内容且不在可滚动容器内,则不允许滚动
return !!(isInDialog || isScrollableContent || isInScrollableContainer)
}
// 阻止触摸滚动事件
const preventTouchScroll = (event: TouchEvent) => {
if (isLocked.value && config.preventTouchScroll) {
// 检查触摸事件的目标元素
const target = event.target as Element
if (target) {
// 如果元素应该允许滚动,则不阻止事件
if (shouldAllowScroll(target)) {
return
}
}
// 否则阻止滚动
event.preventDefault()
event.stopPropagation()
}
}
// 锁定滚动
const lockScroll = () => {
if (isLocked.value) return
// 增加全局锁定计数
globalLockCount.value++
// 保存当前状态(只在第一次锁定时)
if (globalLockCount.value === 1) {
saveGlobalOriginalStyles()
saveGlobalScrollPosition()
}
// 应用锁定样式
applyGlobalLockStyles(config)
// 添加触摸事件监听器
if (config.preventTouchScroll) {
addGlobalTouchEventListener(preventTouchScroll)
}
isLocked.value = true
}
// 恢复滚动
const restoreScroll = () => {
if (!isLocked.value) return
// 减少全局锁定计数
globalLockCount.value--
// 移除触摸事件监听器
if (config.preventTouchScroll) {
removeGlobalTouchEventListener(preventTouchScroll)
}
// 恢复样式(只在最后一个锁定时)
restoreGlobalStyles(config)
isLocked.value = false
}
// 切换滚动锁定状态
const toggleScrollLock = (lock?: boolean) => {
const shouldLock = lock !== undefined ? lock : !isLocked.value
if (shouldLock) {
lockScroll()
} else {
restoreScroll()
}
}
// 监听响应式值的变化
const watchTarget = (target: any) => {
return watch(
target,
newValue => {
toggleScrollLock(!!newValue)
},
{ immediate: false },
)
}
// 生命周期清理
onBeforeUnmount(() => {
if (config.autoRestore && isLocked.value) {
restoreScroll()
}
})
return {
// 状态
isLocked: readonly(isLocked),
savedScrollPosition: readonly(savedScrollPosition),
// 方法
lockScroll,
restoreScroll,
toggleScrollLock,
watchTarget,
// 工具方法
saveScrollPosition,
}
}
// 便捷的自动监听版本
export function useScrollLockWithWatch(target: any, options: ScrollLockOptions = {}) {
const scrollLock = useScrollLock(options)
// 自动监听目标值的变化
const stopWatcher = scrollLock.watchTarget(target)
// 返回所有功能 + 停止监听的方法
return {
...scrollLock,
stopWatcher,
}
}
// 全局弹窗检测和管理
export function useGlobalDialogScrollLock() {
const activeDialogs = ref<Set<string>>(new Set())
const registerDialog = (dialogId: string) => {
activeDialogs.value.add(dialogId)
if (activeDialogs.value.size === 1) {
// 第一个弹窗时锁定滚动
lockGlobalScroll()
}
}
const unregisterDialog = (dialogId: string) => {
activeDialogs.value.delete(dialogId)
if (activeDialogs.value.size === 0) {
// 没有弹窗时恢复滚动
unlockGlobalScroll()
}
}
const lockGlobalScroll = () => {
document.body.style.overflow = 'hidden'
document.documentElement.classList.add('v-overlay-scroll-blocked')
}
const unlockGlobalScroll = () => {
document.body.style.overflow = ''
document.documentElement.classList.remove('v-overlay-scroll-blocked')
}
return {
activeDialogs: readonly(activeDialogs),
registerDialog,
unregisterDialog,
lockGlobalScroll,
unlockGlobalScroll,
}
}

View File

@@ -18,7 +18,6 @@ import { useRoute } from 'vue-router'
import { filterMenusByPermission } from '@/utils/permission'
import { onUnreadMessage } from '@/utils/badge'
import { usePullDownGesture } from '@/composables/usePullDownGesture'
import { useScrollLockWithWatch } from '@/composables/useScrollLock'
import { usePWA } from '@/composables/usePWA'
import OfflinePage from '@/layouts/components/OfflinePage.vue'
import { useGlobalOfflineStatus } from '@/composables/useOfflineStatus'
@@ -163,17 +162,6 @@ const handleServiceWorkerMessage = (event: MessageEvent) => {
}
}
// 使用滚动锁定 composable自动监听showPluginQuickAccess的变化
useScrollLockWithWatch(showPluginQuickAccess, {
preventTouchScroll: true,
preserveScrollPosition: true,
autoRestore: true,
// 允许快速访问面板内的滚动
allowScrollSelectors: ['.plugin-quick-access'],
// 允许快速访问面板内的可滚动容器
allowScrollContainerSelectors: ['.plugin-grid'],
})
// 检查是否可以使用下拉手势
const canUsePullGesture = () => {
// 检查是否在dashboard页面

View File

@@ -265,7 +265,7 @@ onMounted(() => {
</VCard>
</VMenu>
<!-- 名称测试弹窗 -->
<DialogWrapper
<VDialog
v-if="nameTestDialog"
v-model="nameTestDialog"
max-width="45rem"
@@ -285,9 +285,9 @@ onMounted(() => {
<NameTestView />
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 网络测试弹窗 -->
<DialogWrapper
<VDialog
v-if="netTestDialog"
v-model="netTestDialog"
max-width="35rem"
@@ -307,9 +307,9 @@ onMounted(() => {
<NetTestView />
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 实时日志弹窗 -->
<DialogWrapper
<VDialog
v-if="loggingDialog"
v-model="loggingDialog"
scrollable
@@ -335,9 +335,9 @@ onMounted(() => {
<LoggingView logfile="moviepilot.log" />
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 过滤规则弹窗 -->
<DialogWrapper
<VDialog
v-if="ruleTestDialog"
v-model="ruleTestDialog"
max-width="35rem"
@@ -357,9 +357,9 @@ onMounted(() => {
<RuleTestView />
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 系统健康检查弹窗 -->
<DialogWrapper
<VDialog
v-if="systemTestDialog"
v-model="systemTestDialog"
max-width="35rem"
@@ -379,9 +379,9 @@ onMounted(() => {
<ModuleTestView />
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 消息中心弹窗 -->
<DialogWrapper
<VDialog
v-if="messageDialog"
v-model="messageDialog"
max-width="50rem"
@@ -424,5 +424,5 @@ onMounted(() => {
</div>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -650,7 +650,7 @@ onUnmounted(() => {
<!-- 用户认证对话框 -->
<UserAuthDialog v-if="siteAuthDialog" v-model="siteAuthDialog" @done="siteAuthDone" @close="siteAuthDialog = false" />
<!-- 自定义 CSS -->
<DialogWrapper v-if="cssDialog" v-model="cssDialog" max-width="50rem" scrollable :fullscreen="!display.mdAndUp.value">
<VDialog v-if="cssDialog" v-model="cssDialog" max-width="50rem" scrollable :fullscreen="!display.mdAndUp.value">
<VCard>
<VCardItem>
<VCardTitle>
@@ -671,10 +671,10 @@ onUnmounted(() => {
</VBtn>
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 透明度调整对话框 -->
<DialogWrapper v-if="showTransparencyDialog" v-model="showTransparencyDialog" max-width="30rem">
<VDialog v-if="showTransparencyDialog" v-model="showTransparencyDialog" max-width="30rem">
<VCard>
<VCardItem>
<VCardTitle>
@@ -763,7 +763,7 @@ onUnmounted(() => {
</VBtn>
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
</template>
<style lang="scss" scoped>

View File

@@ -389,7 +389,7 @@ onDeactivated(() => {
</Teleport>
<!-- 弹窗根据配置生成选项 -->
<DialogWrapper v-if="dialog" v-model="dialog" max-width="35rem" :fullscreen="!display.mdAndUp.value" scrollable>
<VDialog v-if="dialog" v-model="dialog" max-width="35rem" :fullscreen="!display.mdAndUp.value" scrollable>
<VCard>
<VCardItem>
<VCardTitle>
@@ -443,7 +443,7 @@ onDeactivated(() => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>
<style lang="scss" scoped>
.settings-card-header {

View File

@@ -216,7 +216,7 @@ onActivated(async () => {
</VWindowItem>
</VWindow>
<!-- 弹窗根据配置生成选项 -->
<DialogWrapper
<VDialog
v-if="orderConfigDialog"
v-model="orderConfigDialog"
max-width="35rem"
@@ -265,7 +265,7 @@ onActivated(async () => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 快速滚动到顶部按钮 -->
<Teleport to="body" v-if="route.path === '/discover'">
<VScrollToTopBtn />

View File

@@ -269,13 +269,7 @@ onActivated(async () => {
</div>
<!-- 设置面板 -->
<DialogWrapper
v-model="dialog"
width="35rem"
class="settings-dialog"
scrollable
:fullscreen="!display.mdAndUp.value"
>
<VDialog v-model="dialog" width="35rem" class="settings-dialog" scrollable :fullscreen="!display.mdAndUp.value">
<VCard class="settings-card">
<VCardItem class="settings-card-header">
<VCardTitle>
@@ -327,7 +321,7 @@ onActivated(async () => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 快速滚动到顶部按钮 -->
<Teleport to="body" v-if="route.path === '/recommend'">

View File

@@ -6,7 +6,7 @@ declare let self: ServiceWorkerGlobalScope & {
}
// 缓存版本控制
const CACHE_VERSION = 'v1.0.7'
const CACHE_VERSION = 'v1.0.8'
const CACHE_NAMES = {
appShell: `app-shell-${CACHE_VERSION}`,
static: `static-resources-${CACHE_VERSION}`,

View File

@@ -215,10 +215,7 @@ const defaultColor = '#2196F3'
// 计算过滤表单是否全部为空
const isFilterFormEmpty = computed(() => {
return (
!filterForm.name &&
filterForm.author.length === 0 &&
filterForm.label.length === 0 &&
filterForm.repo.length === 0
!filterForm.name && filterForm.author.length === 0 && filterForm.label.length === 0 && filterForm.repo.length === 0
)
})
@@ -1552,7 +1549,7 @@ function onDragStartPlugin(evt: any) {
/>
<!-- 插件搜索窗口 -->
<DialogWrapper
<VDialog
v-if="SearchDialog"
v-model="SearchDialog"
scrollable
@@ -1611,20 +1608,20 @@ function onDragStartPlugin(evt: any) {
</VVirtualScroll>
</VList>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 安装插件进度框 -->
<DialogWrapper v-if="progressDialog" v-model="progressDialog" :scrim="false" width="25rem">
<VDialog v-if="progressDialog" v-model="progressDialog" :scrim="false" width="25rem">
<VCard color="primary">
<VCardText class="text-center">
{{ progressText }}
<VProgressLinear indeterminate color="white" class="mb-0 mt-1" />
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 新建文件夹对话框 -->
<DialogWrapper v-if="newFolderDialog" v-model="newFolderDialog" max-width="400">
<VDialog v-if="newFolderDialog" v-model="newFolderDialog" max-width="400">
<VCard>
<VDialogCloseBtn @click="newFolderDialog = false" />
<VCardItem>
@@ -1646,5 +1643,5 @@ function onDragStartPlugin(evt: any) {
}}</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -338,7 +338,7 @@ onMounted(() => {
</div>
</div>
</div>
<DialogWrapper v-if="releaseDialog" v-model="releaseDialog" width="600" scrollable>
<VDialog v-if="releaseDialog" v-model="releaseDialog" width="600" scrollable>
<VCard>
<VCardItem>
<VDialogCloseBtn @click="releaseDialog = false" />
@@ -346,7 +346,7 @@ onMounted(() => {
</VCardItem>
<VCardText v-html="releaseDialogBody" />
</VCard>
</DialogWrapper>
</VDialog>
</template>
<style type="scss" scoped>

View File

@@ -11,7 +11,6 @@ import { usePWA } from '@/composables/usePWA'
// 国际化
const { t } = useI18n()
// PWA模式检测
const { appMode } = usePWA()
@@ -423,7 +422,7 @@ onMounted(() => {
</VCard>
<!-- 重新识别对话框 -->
<DialogWrapper v-model="reidentifyDialog" scrollable max-width="35rem">
<VDialog v-model="reidentifyDialog" scrollable max-width="35rem">
<VCard>
<VCardItem class="py-2">
<template #prepend>
@@ -469,5 +468,5 @@ onMounted(() => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -444,7 +444,7 @@ onMounted(() => {
:indeterminate="true"
/>
<!-- 模板编辑器对话框 -->
<DialogWrapper v-model="editorVisible" v-if="editorVisible" max-width="50rem" :fullscreen="!display.mdAndUp.value">
<VDialog v-model="editorVisible" v-if="editorVisible" max-width="50rem" :fullscreen="!display.mdAndUp.value">
<VCard>
<VCardItem class="py-2">
<template #prepend>
@@ -472,7 +472,7 @@ onMounted(() => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>
<style scoped>
/* Monaco编辑器容器样式 */

View File

@@ -733,7 +733,7 @@ onDeactivated(() => {
</VRow>
<!-- 高级系统设置 -->
<DialogWrapper
<VDialog
v-if="advancedDialog"
v-model="advancedDialog"
scrollable
@@ -1329,5 +1329,5 @@ onDeactivated(() => {
</VForm>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
</template>

View File

@@ -617,7 +617,7 @@ const handleSortIconClick = () => {
</VCard>
<!-- 全部筛选弹窗 -->
<DialogWrapper
<VDialog
v-model="allFilterMenuOpen"
max-width="50rem"
location="center"
@@ -690,10 +690,10 @@ const handleSortIconClick = () => {
</div>
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 筛选弹窗 -->
<DialogWrapper v-model="filterMenuOpen" max-width="25rem" location="center" max-height="85vh" scrollable>
<VDialog v-model="filterMenuOpen" max-width="25rem" location="center" max-height="85vh" scrollable>
<VCard>
<VCardTitle class="py-3 d-flex align-center">
<VIcon :icon="getFilterIcon(currentFilter)" class="me-2"></VIcon>
@@ -735,7 +735,7 @@ const handleSortIconClick = () => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 资源列表 -->
<VInfiniteScroll mode="intersect" side="end" :items="displayDataList" class="overflow-visible" @load="loadMore">

View File

@@ -597,7 +597,7 @@ onMounted(() => {
</VCard>
<!-- 全部筛选弹窗 -->
<DialogWrapper
<VDialog
v-model="allFilterMenuOpen"
max-width="50rem"
location="center"
@@ -670,10 +670,10 @@ onMounted(() => {
</div>
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 筛选弹窗 -->
<DialogWrapper v-model="filterMenuOpen" max-width="25rem" max-height="85vh" location="center" scrollable>
<VDialog v-model="filterMenuOpen" max-width="25rem" max-height="85vh" location="center" scrollable>
<VCard>
<VCardTitle class="py-3 d-flex align-center">
<VIcon :icon="getFilterIcon(currentFilter)" class="me-2"></VIcon>
@@ -715,7 +715,7 @@ onMounted(() => {
</VBtn>
</VCardActions>
</VCard>
</DialogWrapper>
</VDialog>
<!-- 资源列表容器 -->
<VCard class="resource-list-container">

View File

@@ -454,7 +454,7 @@ watch(
</VRow>
<!-- 双重验证弹窗 -->
<DialogWrapper v-if="otpDialog" v-model="otpDialog" max-width="45rem" scrollable>
<VDialog v-if="otpDialog" v-model="otpDialog" max-width="45rem" scrollable>
<!-- 开启双重验证弹窗内容 -->
<VCard>
<VDialogCloseBtn @click="otpDialog = false" />
@@ -492,6 +492,6 @@ watch(
</VForm>
</VCardText>
</VCard>
</DialogWrapper>
</VDialog>
</div>
</template>