整合全局设置store,优化PWA模式检测

This commit is contained in:
jxxghp
2025-07-04 16:19:50 +08:00
parent e45919cac1
commit 85780917c2
31 changed files with 294 additions and 116 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
#loading-bg { #loading-bg {
position: fixed; position: fixed;
z-index: 9999; z-index: 99999;
display: block; display: block;
background: var(--initial-loader-bg, #fff); background: var(--initial-loader-bg, #fff);
block-size: 100vh; block-size: 100vh;
+10 -12
View File
@@ -3,7 +3,7 @@ import { useTheme } from 'vuetify'
import { checkPrefersColorSchemeIsDark } from '@/@core/utils' import { checkPrefersColorSchemeIsDark } from '@/@core/utils'
import { ensureRenderComplete, removeEl } from './@core/utils/dom' import { ensureRenderComplete, removeEl } from './@core/utils/dom'
import api from '@/api' import api from '@/api'
import { useAuthStore } from '@/stores/auth' import { useAuthStore, useGlobalSettingsStore } from '@/stores'
import { getBrowserLocale, setI18nLanguage } from './plugins/i18n' import { getBrowserLocale, setI18nLanguage } from './plugins/i18n'
import { SupportedLocale } from '@/types/i18n' import { SupportedLocale } from '@/types/i18n'
import { checkAndEmitUnreadMessages } from '@/utils/badge' import { checkAndEmitUnreadMessages } from '@/utils/badge'
@@ -19,13 +19,13 @@ globalTheme.name.value = themeValue === 'auto' ? autoTheme : themeValue
const localeValue = getBrowserLocale() const localeValue = getBrowserLocale()
setI18nLanguage(localeValue as SupportedLocale) setI18nLanguage(localeValue as SupportedLocale)
// 显示状态
const show = ref(false)
// 检查是否登录 // 检查是否登录
const authStore = useAuthStore() const authStore = useAuthStore()
const isLogin = computed(() => authStore.token) const isLogin = computed(() => authStore.token)
// 全局设置store
const globalSettingsStore = useGlobalSettingsStore()
// 生成背景图片key // 生成背景图片key
const loginStateKey = computed(() => (isLogin.value ? 'logged-in' : 'logged-out')) const loginStateKey = computed(() => (isLogin.value ? 'logged-in' : 'logged-out'))
@@ -130,9 +130,7 @@ function animateAndRemoveLoader() {
removeEl('#loading-bg') removeEl('#loading-bg')
// 将background属性从html的style中移除 // 将background属性从html的style中移除
document.documentElement.style.removeProperty('background') document.documentElement.style.removeProperty('background')
// 显示页面 }, 500)
show.value = true
}, 500) // 与CSS动画持续时间匹配
} }
} }
@@ -155,6 +153,9 @@ async function loadBackgroundImages(retryCount = 0) {
} }
onMounted(async () => { onMounted(async () => {
// 初始化全局设置
await globalSettingsStore.initialize()
// 配置 ApexCharts // 配置 ApexCharts
configureApexCharts() configureApexCharts()
@@ -172,11 +173,8 @@ onMounted(async () => {
}, },
) )
// 默认隐藏页面
show.value = false
// 加载背景图片 // 加载背景图片
loadBackgroundImages() await loadBackgroundImages()
// 移除加载动画 // 移除加载动画
ensureRenderComplete(() => { ensureRenderComplete(() => {
@@ -213,7 +211,7 @@ onUnmounted(() => {
<div v-if="isLogin && isTransparentTheme" class="global-blur-layer"></div> <div v-if="isLogin && isTransparentTheme" class="global-blur-layer"></div>
</div> </div>
<!-- 页面内容 --> <!-- 页面内容 -->
<VApp v-show="show" :class="{ 'transparent-app': isTransparentTheme }"> <VApp :class="{ 'transparent-app': isTransparentTheme }">
<RouterView /> <RouterView />
</VApp> </VApp>
</div> </div>
+3 -1
View File
@@ -5,6 +5,7 @@ import FileNavigator from './filebrowser/FileNavigator.vue'
import type { EndPoints, FileItem, StorageConf } from '@/api/types' import type { EndPoints, FileItem, StorageConf } from '@/api/types'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
import { storageIconDict } from '@/api/constants' import { storageIconDict } from '@/api/constants'
import { usePWA } from '@/composables/usePWA'
// 输入参数 // 输入参数
const props = defineProps({ const props = defineProps({
@@ -33,7 +34,8 @@ const emit = defineEmits(['pathchanged'])
const display = useDisplay() const display = useDisplay()
// APP // APP
const appMode = inject('pwaMode') && display.mdAndDown.value // PWA模式检测
const { appMode } = usePWA()
const fileIcons = { const fileIcons = {
// 压缩包 // 压缩包
+4 -2
View File
@@ -9,7 +9,7 @@ import { formatSeason, formatRating } from '@/@core/utils/formatters'
import { doneNProgress, startNProgress } from '@/api/nprogress' import { doneNProgress, startNProgress } from '@/api/nprogress'
import type { MediaInfo, Subscribe, MediaSeason, Site } from '@/api/types' import type { MediaInfo, Subscribe, MediaSeason, Site } from '@/api/types'
import router from '@/router' import router from '@/router'
import { useUserStore } from '@/stores' import { useUserStore, useGlobalSettingsStore } from '@/stores'
import SubscribeEditDialog from '../dialog/SubscribeEditDialog.vue' import SubscribeEditDialog from '../dialog/SubscribeEditDialog.vue'
import SearchSiteDialog from '@/components/dialog/SearchSiteDialog.vue' import SearchSiteDialog from '@/components/dialog/SearchSiteDialog.vue'
import SubscribeSeasonDialog from '../dialog/SubscribeSeasonDialog.vue' import SubscribeSeasonDialog from '../dialog/SubscribeSeasonDialog.vue'
@@ -28,7 +28,9 @@ const props = defineProps({
}) })
// 从 provide 中获取全局设置 // 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings') // 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 用户 Store // 用户 Store
const userStore = useUserStore() const userStore = useUserStore()
+4 -1
View File
@@ -2,6 +2,7 @@
import personIcon from '@images/misc/person-icon.png' import personIcon from '@images/misc/person-icon.png'
import type { Person } from '@/api/types' import type { Person } from '@/api/types'
import router from '@/router' import router from '@/router'
import { useGlobalSettingsStore } from '@/stores'
const personProps = defineProps({ const personProps = defineProps({
person: Object as PropType<Person>, person: Object as PropType<Person>,
@@ -10,7 +11,9 @@ const personProps = defineProps({
}) })
// 从 provide 中获取全局设置 // 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings') // 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 当前人物 // 当前人物
const personInfo = ref(personProps.person) const personInfo = ref(personProps.person)
+4 -1
View File
@@ -10,6 +10,7 @@ import type { Subscribe } from '@/api/types'
import router from '@/router' import router from '@/router'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
import { useGlobalSettingsStore } from '@/stores'
// 显示器宽度 // 显示器宽度
const display = useDisplay() const display = useDisplay()
@@ -23,7 +24,9 @@ const props = defineProps({
}) })
// 从 provide 中获取全局设置 // 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings') // 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 定义触发的自定义事件 // 定义触发的自定义事件
const emit = defineEmits(['remove', 'save']) const emit = defineEmits(['remove', 'save'])
+4 -1
View File
@@ -4,6 +4,7 @@ import type { SubscribeShare } from '@/api/types'
import router from '@/router' import router from '@/router'
import SubscribeEditDialog from '../dialog/SubscribeEditDialog.vue' import SubscribeEditDialog from '../dialog/SubscribeEditDialog.vue'
import ForkSubscribeDialog from '../dialog/ForkSubscribeDialog.vue' import ForkSubscribeDialog from '../dialog/ForkSubscribeDialog.vue'
import { useGlobalSettingsStore } from '@/stores'
// 输入参数 // 输入参数
const props = defineProps({ const props = defineProps({
@@ -14,7 +15,9 @@ const props = defineProps({
const emit = defineEmits(['delete']) const emit = defineEmits(['delete'])
// 从 provide 中获取全局设置 // 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings') // 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 图片是否加载完成 // 图片是否加载完成
const imageLoaded = ref(false) const imageLoaded = ref(false)
@@ -6,6 +6,7 @@ import router from '@/router'
import { useToast } from 'vue-toastification' import { useToast } from 'vue-toastification'
import { VBtn } from 'vuetify/lib/components/index.mjs' import { VBtn } from 'vuetify/lib/components/index.mjs'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useGlobalSettingsStore } from '@/stores'
// 国际化 // 国际化
const { t } = useI18n() const { t } = useI18n()
@@ -19,7 +20,9 @@ const props = defineProps({
const emit = defineEmits(['fork', 'delete', 'close']) const emit = defineEmits(['fork', 'delete', 'close'])
// 从 provide 中获取全局设置 // 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings') // 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 提示框 // 提示框
const $toast = useToast() const $toast = useToast()
+3 -1
View File
@@ -4,6 +4,7 @@ import type { Plugin } from '@/api/types'
import PageRender from '@/components/render/PageRender.vue' import PageRender from '@/components/render/PageRender.vue'
import api from '@/api' import api from '@/api'
import { loadRemoteComponent } from '@/utils/federationLoader' import { loadRemoteComponent } from '@/utils/federationLoader'
import { usePWA } from '@/composables/usePWA'
// 输入参数 // 输入参数
const props = defineProps({ const props = defineProps({
@@ -22,7 +23,8 @@ const emit = defineEmits(['close', 'save', 'switch'])
// 显示器宽度 // 显示器宽度
const display = useDisplay() const display = useDisplay()
// APP // APP
const appMode = inject('pwaMode') && display.mdAndDown.value // PWA模式检测
const { appMode } = usePWA()
// 是否刷新 // 是否刷新
const isRefreshed = ref(false) const isRefreshed = ref(false)
+5 -2
View File
@@ -8,6 +8,7 @@ import { useDisplay } from 'vuetify'
import ProgressDialog from './ProgressDialog.vue' import ProgressDialog from './ProgressDialog.vue'
import { FileItem, StorageConf, TransferDirectoryConf, TransferForm } from '@/api/types' import { FileItem, StorageConf, TransferDirectoryConf, TransferForm } from '@/api/types'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useGlobalSettingsStore } from '@/stores'
// 国际化 // 国际化
const { t } = useI18n() const { t } = useI18n()
@@ -24,10 +25,12 @@ const props = defineProps({
}) })
// 从 provide 中获取全局设置 // 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings') // 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 当前识别类型 // 当前识别类型
const mediaSource = ref(globalSettings.data?.RECOGNIZE_SOURCE || 'themoviedb') const mediaSource = ref(globalSettings.RECOGNIZE_SOURCE || 'themoviedb')
// 定义事件 // 定义事件
const emit = defineEmits(['done', 'close']) const emit = defineEmits(['done', 'close'])
@@ -4,6 +4,7 @@ import { MediaInfo, MediaSeason, NotExistMediaInfo } from '@/api/types'
import { PropType } from 'vue' import { PropType } from 'vue'
import NoDataFound from '@/components/NoDataFound.vue' import NoDataFound from '@/components/NoDataFound.vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useGlobalSettingsStore } from '@/stores'
// 国际化 // 国际化
const { t } = useI18n() const { t } = useI18n()
@@ -17,7 +18,9 @@ const props = defineProps({
}) })
// 从 provide 中获取全局设置 // 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings') // 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 季详情 // 季详情
const seasonInfos = ref<MediaSeason[]>([]) const seasonInfos = ref<MediaSeason[]>([])
+57
View File
@@ -0,0 +1,57 @@
import { ref, computed, onMounted } from 'vue'
import { useDisplay } from 'vuetify'
import { isPWA } from '@/@core/utils/navigator'
// 全局PWA状态,确保只初始化一次
const globalPwaMode = ref<boolean | null>(null)
const globalLoading = ref(false)
let initPromise: Promise<void> | null = null
// 全局初始化函数
async function initializePWAGlobally() {
if (initPromise) return initPromise
if (globalPwaMode.value !== null || globalLoading.value) return Promise.resolve()
initPromise = new Promise(async (resolve, reject) => {
globalLoading.value = true
try {
globalPwaMode.value = await isPWA()
resolve()
} catch (error) {
console.error('Failed to detect PWA mode', error)
globalPwaMode.value = false
reject(error)
} finally {
globalLoading.value = false
}
})
return initPromise
}
export function usePWA() {
const display = useDisplay()
const appMode = computed(() => {
return globalPwaMode.value && display.mdAndDown.value
})
// 自动初始化PWA检测
onMounted(() => {
initializePWAGlobally().catch(console.error)
})
// 如果是在服务端或首次调用,立即开始初始化
if (typeof window !== 'undefined' && globalPwaMode.value === null && !globalLoading.value) {
initializePWAGlobally().catch(console.error)
}
return {
pwaMode: globalPwaMode,
appMode,
loading: globalLoading,
// 保留手动初始化方法以防需要
initializePWA: initializePWAGlobally,
}
}
+42 -13
View File
@@ -1,6 +1,7 @@
import { ref, computed, onMounted, onBeforeUnmount, inject, readonly } from 'vue' import { ref, computed, onMounted, onBeforeUnmount, readonly, watch } from 'vue'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { usePWA } from './usePWA'
// 下拉手势配置类型 // 下拉手势配置类型
export interface PullDownConfig { export interface PullDownConfig {
@@ -37,8 +38,7 @@ const DEFAULT_CONFIG: PullDownConfig = {
export function usePullDownGesture(options: PullDownOptions = {}) { export function usePullDownGesture(options: PullDownOptions = {}) {
const display = useDisplay() const display = useDisplay()
const route = useRoute() const { appMode } = usePWA()
const appMode = inject('pwaMode')
// 合并配置 // 合并配置
const config = { ...DEFAULT_CONFIG, ...options.config } const config = { ...DEFAULT_CONFIG, ...options.config }
@@ -126,7 +126,7 @@ export function usePullDownGesture(options: PullDownOptions = {}) {
// 事件处理函数 // 事件处理函数
const handleTouchStart = (event: TouchEvent) => { const handleTouchStart = (event: TouchEvent) => {
if (!appMode || !display.mdAndDown.value || !options.enabled) return if (!appMode.value || !display.mdAndDown.value || !options.enabled) return
// 检查是否可以使用下拉手势 // 检查是否可以使用下拉手势
if (options.canUsePullGesture && !options.canUsePullGesture()) return if (options.canUsePullGesture && !options.canUsePullGesture()) return
@@ -149,7 +149,7 @@ export function usePullDownGesture(options: PullDownOptions = {}) {
} }
const handleTouchMove = (event: TouchEvent) => { const handleTouchMove = (event: TouchEvent) => {
if (!appMode || !display.mdAndDown.value || !options.enabled) return if (!appMode.value || !display.mdAndDown.value || !options.enabled) return
// 检查是否可以使用下拉手势 // 检查是否可以使用下拉手势
if (options.canUsePullGesture && !options.canUsePullGesture()) return if (options.canUsePullGesture && !options.canUsePullGesture()) return
@@ -192,7 +192,7 @@ export function usePullDownGesture(options: PullDownOptions = {}) {
} }
const handleTouchEnd = () => { const handleTouchEnd = () => {
if (!appMode || !display.mdAndDown.value || !options.enabled) return if (!appMode.value || !display.mdAndDown.value || !options.enabled) return
// 检查是否可以使用下拉手势 // 检查是否可以使用下拉手势
if (options.canUsePullGesture && !options.canUsePullGesture()) return if (options.canUsePullGesture && !options.canUsePullGesture()) return
@@ -217,20 +217,49 @@ export function usePullDownGesture(options: PullDownOptions = {}) {
} }
// 生命周期管理 // 生命周期管理
onMounted(() => { let eventsAdded = false
if (appMode && display.mdAndDown.value) {
const addEventListeners = () => {
if (!eventsAdded && appMode.value && display.mdAndDown.value) {
document.addEventListener('touchstart', handleTouchStart, { passive: false }) document.addEventListener('touchstart', handleTouchStart, { passive: false })
document.addEventListener('touchmove', handleTouchMove, { passive: false }) document.addEventListener('touchmove', handleTouchMove, { passive: false })
document.addEventListener('touchend', handleTouchEnd, { passive: true }) document.addEventListener('touchend', handleTouchEnd, { passive: true })
eventsAdded = true
}
}
const removeEventListeners = () => {
if (eventsAdded) {
document.removeEventListener('touchstart', handleTouchStart)
document.removeEventListener('touchmove', handleTouchMove)
document.removeEventListener('touchend', handleTouchEnd)
eventsAdded = false
}
}
// PWA状态确定后,一次性决定是否添加事件监听器
onMounted(() => {
// 如果PWA已经检测完成,直接添加事件监听器
if (appMode.value !== null) {
addEventListeners()
} else {
// 等待PWA检测完成(从null变为boolean
const stopWatcher = watch(
appMode,
newValue => {
if (newValue !== null) {
addEventListeners()
// PWA状态确定后停止监听
stopWatcher()
}
},
{ immediate: true },
)
} }
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
if (appMode && display.mdAndDown.value) { removeEventListeners()
document.removeEventListener('touchstart', handleTouchStart)
document.removeEventListener('touchmove', handleTouchMove)
document.removeEventListener('touchend', handleTouchEnd)
}
}) })
return { return {
+3 -1
View File
@@ -18,9 +18,11 @@ import { filterMenusByPermission } from '@/utils/permission'
import { onUnreadMessage } from '@/utils/badge' import { onUnreadMessage } from '@/utils/badge'
import { usePullDownGesture } from '@/composables/usePullDownGesture' import { usePullDownGesture } from '@/composables/usePullDownGesture'
import { useScrollLockWithWatch } from '@/composables/useScrollLock' import { useScrollLockWithWatch } from '@/composables/useScrollLock'
import { usePWA } from '@/composables/usePWA'
const display = useDisplay() const display = useDisplay()
const appMode = inject('pwaMode') // PWA模式检测
const { appMode } = usePWA()
const { t } = useI18n() const { t } = useI18n()
const route = useRoute() const route = useRoute()
+3 -1
View File
@@ -5,9 +5,11 @@ import { NavMenu } from '@/@layouts/types'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useUserStore } from '@/stores' import { useUserStore } from '@/stores'
import { filterMenusByPermission } from '@/utils/permission' import { filterMenusByPermission } from '@/utils/permission'
import { usePWA } from '@/composables/usePWA'
const display = useDisplay() const display = useDisplay()
const appMode = inject('pwaMode') && display.mdAndDown.value // PWA模式检测
const { appMode } = usePWA()
const { t, locale } = useI18n() const { t, locale } = useI18n()
// 判断当前是否为英文环境 // 判断当前是否为英文环境
+3 -1
View File
@@ -4,6 +4,7 @@ import useDragAndDrop from '@core/utils/workflow'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { actionStepDict } from '@/api/constants' import { actionStepDict } from '@/api/constants'
import { usePWA } from '@/composables/usePWA'
interface ActionItem { interface ActionItem {
name: string name: string
@@ -13,7 +14,8 @@ interface ActionItem {
const display = useDisplay() const display = useDisplay()
// APP // APP
const appMode = inject('pwaMode') && display.mdAndDown.value // PWA模式检测
const { appMode } = usePWA()
const { t } = useI18n() const { t } = useI18n()
const { onDragStart } = useDragAndDrop() const { onDragStart } = useDragAndDrop()
+5 -23
View File
@@ -18,9 +18,7 @@ import { PerfectScrollbarPlugin } from 'vue3-perfect-scrollbar'
import { CronVuetify } from '@vue-js-cron/vuetify' import { CronVuetify } from '@vue-js-cron/vuetify'
// 4. 工具函数和其他辅助模块 // 4. 工具函数和其他辅助模块
import { isPWA } from './@core/utils/navigator'
import { loadRemoteComponents } from './utils/federationLoader' import { loadRemoteComponents } from './utils/federationLoader'
import { fetchGlobalSettings } from './utils/globalSetting'
// 5. 其他插件和功能模块 // 5. 其他插件和功能模块
import Toast from 'vue-toastification' import Toast from 'vue-toastification'
@@ -51,26 +49,11 @@ const app = createApp(App)
// 注册pinia // 注册pinia
app.use(pinia) app.use(pinia)
// 初始化配置 // 异步加载远程组件(不阻塞启动)
async function initializeApp() { loadRemoteComponents().catch(error => {
try { console.error('Failed to load remote components', error)
// 是否为PWA })
const pwaMode = await isPWA()
app.provide('pwaMode', pwaMode)
// 全局设置
const globalSettings = await fetchGlobalSettings()
app.provide('globalSettings', globalSettings)
// 加载并注册远程联邦组件
await loadRemoteComponents()
} catch (error) {
console.error('Failed to initialize app', error)
}
}
// 注册全局组件
initializeApp().then(() => {
// 1. 注册 UI 框架 // 1. 注册 UI 框架
app.use(vuetify) app.use(vuetify)
@@ -96,7 +79,7 @@ initializeApp().then(() => {
.component('VHeaderTab', HeaderTab) .component('VHeaderTab', HeaderTab)
.component('VPageContentTitle', PageContentTitle) .component('VPageContentTitle', PageContentTitle)
// 5. 注册其他插件 // 4. 注册其他插件
app app
.use(PerfectScrollbarPlugin) .use(PerfectScrollbarPlugin)
.use(Toast, { .use(Toast, {
@@ -106,4 +89,3 @@ initializeApp().then(() => {
.use(ConfirmDialog) .use(ConfirmDialog)
.use(i18n) .use(i18n)
.mount('#app') .mount('#app')
})
+3 -1
View File
@@ -9,13 +9,15 @@ import { useDisplay } from 'vuetify'
import { useDynamicButton } from '@/composables/useDynamicButton' import { useDynamicButton } from '@/composables/useDynamicButton'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { VCardActions } from 'vuetify/components' import { VCardActions } from 'vuetify/components'
import { usePWA } from '@/composables/usePWA'
// 国际化 // 国际化
const { t } = useI18n() const { t } = useI18n()
// APP // APP
const display = useDisplay() const display = useDisplay()
const appMode = inject('pwaMode') && display.mdAndDown.value // PWA模式检测
const { appMode } = usePWA()
// 从用户 Store 中获取superuser信息 // 从用户 Store 中获取superuser信息
const superUser = useUserStore().superUser const superUser = useUserStore().superUser
+51
View File
@@ -0,0 +1,51 @@
import { defineStore } from 'pinia'
import type { globalSettingsState } from '@/stores/types'
import { fetchGlobalSettings } from '@/utils/globalSetting'
export const useGlobalSettingsStore = defineStore('globalSettings', {
state: (): globalSettingsState => ({
data: {},
initialized: false,
loading: false,
}),
actions: {
async initialize() {
if (this.initialized || this.loading) return
this.loading = true
try {
const result = await fetchGlobalSettings()
this.data = result || {}
this.initialized = true
} catch (error) {
console.error('Failed to initialize global settings', error)
} finally {
this.loading = false
}
},
setData(data: { [key: string]: any }) {
this.data = data
this.initialized = true
},
get(key: string) {
return this.data[key]
},
reset() {
this.data = {}
this.initialized = false
this.loading = false
},
},
getters: {
isInitialized: state => state.initialized,
isLoading: state => state.loading,
getData: state => state.data,
// 直接返回data对象,避免使用.value
globalSettings: state => state.data,
},
})
+2 -1
View File
@@ -12,5 +12,6 @@ export default pinia
// 所有的 store // 所有的 store
import { useAuthStore } from './auth' import { useAuthStore } from './auth'
import { useUserStore } from './user' import { useUserStore } from './user'
import { useGlobalSettingsStore } from './globalSettings'
export { useAuthStore, useUserStore } export { useAuthStore, useUserStore, useGlobalSettingsStore }
+9
View File
@@ -21,3 +21,12 @@ export interface userState {
// 权限 // 权限
permissions: { [key: string]: any } permissions: { [key: string]: any }
} }
export interface globalSettingsState {
// 全局设置数据
data: { [key: string]: any }
// 是否已初始化
initialized: boolean
// 是否正在加载
loading: boolean
}
+4 -1
View File
@@ -15,6 +15,7 @@ import SearchSiteDialog from '@/components/dialog/SearchSiteDialog.vue'
import { useTheme } from 'vuetify' import { useTheme } from 'vuetify'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { hasPermission } from '@/utils/permission' import { hasPermission } from '@/utils/permission'
import { useGlobalSettingsStore } from '@/stores'
// 国际化 // 国际化
const { t } = useI18n() const { t } = useI18n()
@@ -28,7 +29,9 @@ const mediaProps = defineProps({
}) })
// 从 provide 中获取全局设置 // 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings') // 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 用户 Store // 用户 Store
const userStore = useUserStore() const userStore = useUserStore()
+4 -1
View File
@@ -5,6 +5,7 @@ import personIcon from '@images/misc/person.png'
import type { Person } from '@/api/types' import type { Person } from '@/api/types'
import NoDataFound from '@/components/NoDataFound.vue' import NoDataFound from '@/components/NoDataFound.vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useGlobalSettingsStore } from '@/stores'
// 国际化 // 国际化
const { t } = useI18n() const { t } = useI18n()
@@ -17,7 +18,9 @@ const personProps = defineProps({
}) })
// 从 provide 中获取全局设置 // 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings') // 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 媒体详情 // 媒体详情
const personDetail = ref<Person>({} as Person) const personDetail = ref<Person>({} as Person)
+3 -1
View File
@@ -13,6 +13,7 @@ import PluginMarketSettingDialog from '@/components/dialog/PluginMarketSettingDi
import { useDynamicButton } from '@/composables/useDynamicButton' import { useDynamicButton } from '@/composables/useDynamicButton'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import PluginMixedSortCard from '@/components/cards/PluginMixedSortCard.vue' import PluginMixedSortCard from '@/components/cards/PluginMixedSortCard.vue'
import { usePWA } from '@/composables/usePWA'
// 国际化 // 国际化
const { t } = useI18n() const { t } = useI18n()
@@ -23,7 +24,8 @@ const route = useRoute()
const display = useDisplay() const display = useDisplay()
// APP // APP
const appMode = inject('pwaMode') && display.mdAndDown.value // PWA模式检测
const { appMode } = usePWA()
// 当前标签 // 当前标签
const activeTab = ref('installed') const activeTab = ref('installed')
+3 -1
View File
@@ -11,13 +11,15 @@ import router from '@/router'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
import { formatFileSize } from '@/@core/utils/formatters' import { formatFileSize } from '@/@core/utils/formatters'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { usePWA } from '@/composables/usePWA'
// i18n // i18n
const { t } = useI18n() const { t } = useI18n()
// APP // APP
const display = useDisplay() const display = useDisplay()
const appMode = inject('pwaMode') && display.mdAndDown.value // PWA模式检测
const { appMode } = usePWA()
// 提示框 // 提示框
const $toast = useToast() const $toast = useToast()
+8 -5
View File
@@ -3,18 +3,21 @@ import { useToast } from 'vue-toastification'
import api from '@/api' import api from '@/api'
import type { TorrentCacheData, TorrentCacheItem } from '@/api/types' import type { TorrentCacheData, TorrentCacheItem } from '@/api/types'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useDisplay } from 'vuetify'
import { formatFileSize, formatDateDifference } from '@core/utils/formatters' import { formatFileSize, formatDateDifference } from '@core/utils/formatters'
import { useConfirm } from '@/composables/useConfirm' import { useConfirm } from '@/composables/useConfirm'
import { useGlobalSettingsStore } from '@/stores'
import { usePWA } from '@/composables/usePWA'
// 国际化 // 国际化
const { t } = useI18n() const { t } = useI18n()
const display = useDisplay()
const appMode = inject('pwaMode') && display.mdAndDown.value
// 从 provide 中获取全局设置 // PWA模式检测
const globalSettings: any = inject('globalSettings') const { appMode } = usePWA()
// 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 确认框 // 确认框
const createConfirm = useConfirm() const createConfirm = useConfirm()
+3 -1
View File
@@ -8,13 +8,15 @@ import SiteAddEditDialog from '@/components/dialog/SiteAddEditDialog.vue'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
import { useDynamicButton } from '@/composables/useDynamicButton' import { useDynamicButton } from '@/composables/useDynamicButton'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { usePWA } from '@/composables/usePWA'
// 国际化 // 国际化
const { t } = useI18n() const { t } = useI18n()
// APP // APP
const display = useDisplay() const display = useDisplay()
const appMode = inject('pwaMode') && display.mdAndDown.value // PWA模式检测
const { appMode } = usePWA()
// 站点列表 // 站点列表
const siteList = ref<Site[]>([]) const siteList = ref<Site[]>([])
+3 -1
View File
@@ -9,13 +9,15 @@ import { useUserStore } from '@/stores'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
import { useDynamicButton } from '@/composables/useDynamicButton' import { useDynamicButton } from '@/composables/useDynamicButton'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { usePWA } from '@/composables/usePWA'
// 国际化 // 国际化
const { t } = useI18n() const { t } = useI18n()
// APP // APP
const display = useDisplay() const display = useDisplay()
const appMode = inject('pwaMode') && display.mdAndDown.value // PWA模式检测
const { appMode } = usePWA()
// 用户 Store // 用户 Store
const userStore = useUserStore() const userStore = useUserStore()
+3 -1
View File
@@ -7,13 +7,15 @@ import UserAddEditDialog from '@/components/dialog/UserAddEditDialog.vue'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
import { useDynamicButton } from '@/composables/useDynamicButton' import { useDynamicButton } from '@/composables/useDynamicButton'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { usePWA } from '@/composables/usePWA'
// 国际化 // 国际化
const { t } = useI18n() const { t } = useI18n()
// APP // APP
const display = useDisplay() const display = useDisplay()
const appMode = inject('pwaMode') && display.mdAndDown.value // PWA模式检测
const { appMode } = usePWA()
// 是否刷新过 // 是否刷新过
const isRefreshed = ref(false) const isRefreshed = ref(false)
+3 -3
View File
@@ -1,19 +1,19 @@
<script setup lang="ts"> <script setup lang="ts">
import api from '@/api' import api from '@/api'
import { Workflow } from '@/api/types' import { Workflow } from '@/api/types'
import { useDisplay } from 'vuetify'
import WorkflowAddEditDialog from '@/components/dialog/WorkflowAddEditDialog.vue' import WorkflowAddEditDialog from '@/components/dialog/WorkflowAddEditDialog.vue'
import WorkflowTaskCard from '@/components/cards/WorkflowTaskCard.vue' import WorkflowTaskCard from '@/components/cards/WorkflowTaskCard.vue'
import NoDataFound from '@/components/NoDataFound.vue' import NoDataFound from '@/components/NoDataFound.vue'
import { useDynamicButton } from '@/composables/useDynamicButton' import { useDynamicButton } from '@/composables/useDynamicButton'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { usePWA } from '@/composables/usePWA'
// 国际化 // 国际化
const { t } = useI18n() const { t } = useI18n()
// APP // APP
const display = useDisplay() // PWA模式检测
const appMode = inject('pwaMode') && display.mdAndDown.value const { appMode } = usePWA()
// 是否刷新 // 是否刷新
const isRefreshed = ref(false) const isRefreshed = ref(false)