mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-04 23:18:44 +08:00
整合全局设置store,优化PWA模式检测
This commit is contained in:
+1
-1
@@ -317,4 +317,4 @@
|
|||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
+2
-2
@@ -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;
|
||||||
@@ -94,4 +94,4 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: rotate(1turn);
|
transform: rotate(1turn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+10
-12
@@ -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>
|
||||||
|
|||||||
@@ -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 = {
|
||||||
// 压缩包
|
// 压缩包
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,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()
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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[]>([])
|
||||||
|
|||||||
@@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
// 判断当前是否为英文环境
|
// 判断当前是否为英文环境
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
+39
-57
@@ -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,59 +49,43 @@ 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 框架
|
|
||||||
app.use(vuetify)
|
|
||||||
|
|
||||||
// 2. 注册路由
|
|
||||||
app.use(router)
|
|
||||||
|
|
||||||
// 3. 注册全局组件
|
|
||||||
app
|
|
||||||
.component('VAceEditor', VAceEditor)
|
|
||||||
.component('VApexChart', VueApexCharts)
|
|
||||||
.component('VCronVuetify', CronVuetify)
|
|
||||||
.component('VDialogCloseBtn', DialogCloseBtn)
|
|
||||||
.component('VScrollToTopBtn', ScrollToTopBtn)
|
|
||||||
.component('VMediaCard', MediaCard)
|
|
||||||
.component('VPosterCard', PosterCard)
|
|
||||||
.component('VBackdropCard', BackdropCard)
|
|
||||||
.component('VPersonCard', PersonCard)
|
|
||||||
.component('VMediaInfoCard', MediaInfoCard)
|
|
||||||
.component('VTorrentCard', TorrentCard)
|
|
||||||
.component('VMediaIdSelector', MediaIdSelector)
|
|
||||||
.component('VCronField', CronField)
|
|
||||||
.component('VPathField', PathField)
|
|
||||||
.component('VHeaderTab', HeaderTab)
|
|
||||||
.component('VPageContentTitle', PageContentTitle)
|
|
||||||
|
|
||||||
// 5. 注册其他插件
|
|
||||||
app
|
|
||||||
.use(PerfectScrollbarPlugin)
|
|
||||||
.use(Toast, {
|
|
||||||
position: 'bottom-right',
|
|
||||||
hideProgressBar: true,
|
|
||||||
})
|
|
||||||
.use(ConfirmDialog)
|
|
||||||
.use(i18n)
|
|
||||||
.mount('#app')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 1. 注册 UI 框架
|
||||||
|
app.use(vuetify)
|
||||||
|
|
||||||
|
// 2. 注册路由
|
||||||
|
app.use(router)
|
||||||
|
|
||||||
|
// 3. 注册全局组件
|
||||||
|
app
|
||||||
|
.component('VAceEditor', VAceEditor)
|
||||||
|
.component('VApexChart', VueApexCharts)
|
||||||
|
.component('VCronVuetify', CronVuetify)
|
||||||
|
.component('VDialogCloseBtn', DialogCloseBtn)
|
||||||
|
.component('VScrollToTopBtn', ScrollToTopBtn)
|
||||||
|
.component('VMediaCard', MediaCard)
|
||||||
|
.component('VPosterCard', PosterCard)
|
||||||
|
.component('VBackdropCard', BackdropCard)
|
||||||
|
.component('VPersonCard', PersonCard)
|
||||||
|
.component('VMediaInfoCard', MediaInfoCard)
|
||||||
|
.component('VTorrentCard', TorrentCard)
|
||||||
|
.component('VMediaIdSelector', MediaIdSelector)
|
||||||
|
.component('VCronField', CronField)
|
||||||
|
.component('VPathField', PathField)
|
||||||
|
.component('VHeaderTab', HeaderTab)
|
||||||
|
.component('VPageContentTitle', PageContentTitle)
|
||||||
|
|
||||||
|
// 4. 注册其他插件
|
||||||
|
app
|
||||||
|
.use(PerfectScrollbarPlugin)
|
||||||
|
.use(Toast, {
|
||||||
|
position: 'bottom-right',
|
||||||
|
hideProgressBar: true,
|
||||||
|
})
|
||||||
|
.use(ConfirmDialog)
|
||||||
|
.use(i18n)
|
||||||
|
.mount('#app')
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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 }
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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[]>([])
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user