整合全局设置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

View File

@@ -317,4 +317,4 @@
<script type="module" src="/src/main.ts"></script>
</body>
</html>
</html>

View File

@@ -1,6 +1,6 @@
#loading-bg {
position: fixed;
z-index: 9999;
z-index: 99999;
display: block;
background: var(--initial-loader-bg, #fff);
block-size: 100vh;
@@ -94,4 +94,4 @@
opacity: 1;
transform: rotate(1turn);
}
}
}

View File

@@ -3,7 +3,7 @@ import { useTheme } from 'vuetify'
import { checkPrefersColorSchemeIsDark } from '@/@core/utils'
import { ensureRenderComplete, removeEl } from './@core/utils/dom'
import api from '@/api'
import { useAuthStore } from '@/stores/auth'
import { useAuthStore, useGlobalSettingsStore } from '@/stores'
import { getBrowserLocale, setI18nLanguage } from './plugins/i18n'
import { SupportedLocale } from '@/types/i18n'
import { checkAndEmitUnreadMessages } from '@/utils/badge'
@@ -19,13 +19,13 @@ globalTheme.name.value = themeValue === 'auto' ? autoTheme : themeValue
const localeValue = getBrowserLocale()
setI18nLanguage(localeValue as SupportedLocale)
// 显示状态
const show = ref(false)
// 检查是否登录
const authStore = useAuthStore()
const isLogin = computed(() => authStore.token)
// 全局设置store
const globalSettingsStore = useGlobalSettingsStore()
// 生成背景图片key
const loginStateKey = computed(() => (isLogin.value ? 'logged-in' : 'logged-out'))
@@ -130,9 +130,7 @@ function animateAndRemoveLoader() {
removeEl('#loading-bg')
// 将background属性从html的style中移除
document.documentElement.style.removeProperty('background')
// 显示页面
show.value = true
}, 500) // 与CSS动画持续时间匹配
}, 500)
}
}
@@ -155,6 +153,9 @@ async function loadBackgroundImages(retryCount = 0) {
}
onMounted(async () => {
// 初始化全局设置
await globalSettingsStore.initialize()
// 配置 ApexCharts
configureApexCharts()
@@ -172,11 +173,8 @@ onMounted(async () => {
},
)
// 默认隐藏页面
show.value = false
// 加载背景图片
loadBackgroundImages()
await loadBackgroundImages()
// 移除加载动画
ensureRenderComplete(() => {
@@ -213,7 +211,7 @@ onUnmounted(() => {
<div v-if="isLogin && isTransparentTheme" class="global-blur-layer"></div>
</div>
<!-- 页面内容 -->
<VApp v-show="show" :class="{ 'transparent-app': isTransparentTheme }">
<VApp :class="{ 'transparent-app': isTransparentTheme }">
<RouterView />
</VApp>
</div>

View File

@@ -5,6 +5,7 @@ import FileNavigator from './filebrowser/FileNavigator.vue'
import type { EndPoints, FileItem, StorageConf } from '@/api/types'
import { useDisplay } from 'vuetify'
import { storageIconDict } from '@/api/constants'
import { usePWA } from '@/composables/usePWA'
// 输入参数
const props = defineProps({
@@ -33,7 +34,8 @@ const emit = defineEmits(['pathchanged'])
const display = useDisplay()
// APP
const appMode = inject('pwaMode') && display.mdAndDown.value
// PWA模式检测
const { appMode } = usePWA()
const fileIcons = {
// 压缩包

View File

@@ -9,7 +9,7 @@ import { formatSeason, formatRating } from '@/@core/utils/formatters'
import { doneNProgress, startNProgress } from '@/api/nprogress'
import type { MediaInfo, Subscribe, MediaSeason, Site } from '@/api/types'
import router from '@/router'
import { useUserStore } from '@/stores'
import { useUserStore, useGlobalSettingsStore } from '@/stores'
import SubscribeEditDialog from '../dialog/SubscribeEditDialog.vue'
import SearchSiteDialog from '@/components/dialog/SearchSiteDialog.vue'
import SubscribeSeasonDialog from '../dialog/SubscribeSeasonDialog.vue'
@@ -28,7 +28,9 @@ const props = defineProps({
})
// 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings')
// 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 用户 Store
const userStore = useUserStore()

View File

@@ -2,6 +2,7 @@
import personIcon from '@images/misc/person-icon.png'
import type { Person } from '@/api/types'
import router from '@/router'
import { useGlobalSettingsStore } from '@/stores'
const personProps = defineProps({
person: Object as PropType<Person>,
@@ -10,7 +11,9 @@ const personProps = defineProps({
})
// 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings')
// 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 当前人物
const personInfo = ref(personProps.person)

View File

@@ -10,6 +10,7 @@ import type { Subscribe } from '@/api/types'
import router from '@/router'
import { useI18n } from 'vue-i18n'
import { useDisplay } from 'vuetify'
import { useGlobalSettingsStore } from '@/stores'
// 显示器宽度
const display = useDisplay()
@@ -23,7 +24,9 @@ const props = defineProps({
})
// 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings')
// 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 定义触发的自定义事件
const emit = defineEmits(['remove', 'save'])

View File

@@ -4,6 +4,7 @@ import type { SubscribeShare } from '@/api/types'
import router from '@/router'
import SubscribeEditDialog from '../dialog/SubscribeEditDialog.vue'
import ForkSubscribeDialog from '../dialog/ForkSubscribeDialog.vue'
import { useGlobalSettingsStore } from '@/stores'
// 输入参数
const props = defineProps({
@@ -14,7 +15,9 @@ const props = defineProps({
const emit = defineEmits(['delete'])
// 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings')
// 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 图片是否加载完成
const imageLoaded = ref(false)

View File

@@ -6,6 +6,7 @@ import router from '@/router'
import { useToast } from 'vue-toastification'
import { VBtn } from 'vuetify/lib/components/index.mjs'
import { useI18n } from 'vue-i18n'
import { useGlobalSettingsStore } from '@/stores'
// 国际化
const { t } = useI18n()
@@ -19,7 +20,9 @@ const props = defineProps({
const emit = defineEmits(['fork', 'delete', 'close'])
// 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings')
// 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 提示框
const $toast = useToast()

View File

@@ -4,6 +4,7 @@ import type { Plugin } from '@/api/types'
import PageRender from '@/components/render/PageRender.vue'
import api from '@/api'
import { loadRemoteComponent } from '@/utils/federationLoader'
import { usePWA } from '@/composables/usePWA'
// 输入参数
const props = defineProps({
@@ -22,7 +23,8 @@ const emit = defineEmits(['close', 'save', 'switch'])
// 显示器宽度
const display = useDisplay()
// APP
const appMode = inject('pwaMode') && display.mdAndDown.value
// PWA模式检测
const { appMode } = usePWA()
// 是否刷新
const isRefreshed = ref(false)

View File

@@ -8,6 +8,7 @@ import { useDisplay } from 'vuetify'
import ProgressDialog from './ProgressDialog.vue'
import { FileItem, StorageConf, TransferDirectoryConf, TransferForm } from '@/api/types'
import { useI18n } from 'vue-i18n'
import { useGlobalSettingsStore } from '@/stores'
// 国际化
const { t } = useI18n()
@@ -24,10 +25,12 @@ const props = defineProps({
})
// 从 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'])

View File

@@ -4,6 +4,7 @@ import { MediaInfo, MediaSeason, NotExistMediaInfo } from '@/api/types'
import { PropType } from 'vue'
import NoDataFound from '@/components/NoDataFound.vue'
import { useI18n } from 'vue-i18n'
import { useGlobalSettingsStore } from '@/stores'
// 国际化
const { t } = useI18n()
@@ -17,7 +18,9 @@ const props = defineProps({
})
// 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings')
// 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 季详情
const seasonInfos = ref<MediaSeason[]>([])

57
src/composables/usePWA.ts Normal file
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,
}
}

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 { useRoute } from 'vue-router'
import { usePWA } from './usePWA'
// 下拉手势配置类型
export interface PullDownConfig {
@@ -37,8 +38,7 @@ const DEFAULT_CONFIG: PullDownConfig = {
export function usePullDownGesture(options: PullDownOptions = {}) {
const display = useDisplay()
const route = useRoute()
const appMode = inject('pwaMode')
const { appMode } = usePWA()
// 合并配置
const config = { ...DEFAULT_CONFIG, ...options.config }
@@ -126,7 +126,7 @@ export function usePullDownGesture(options: PullDownOptions = {}) {
// 事件处理函数
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
@@ -149,7 +149,7 @@ export function usePullDownGesture(options: PullDownOptions = {}) {
}
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
@@ -192,7 +192,7 @@ export function usePullDownGesture(options: PullDownOptions = {}) {
}
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
@@ -217,20 +217,49 @@ export function usePullDownGesture(options: PullDownOptions = {}) {
}
// 生命周期管理
onMounted(() => {
if (appMode && display.mdAndDown.value) {
let eventsAdded = false
const addEventListeners = () => {
if (!eventsAdded && appMode.value && display.mdAndDown.value) {
document.addEventListener('touchstart', handleTouchStart, { passive: false })
document.addEventListener('touchmove', handleTouchMove, { passive: false })
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(() => {
if (appMode && display.mdAndDown.value) {
document.removeEventListener('touchstart', handleTouchStart)
document.removeEventListener('touchmove', handleTouchMove)
document.removeEventListener('touchend', handleTouchEnd)
}
removeEventListeners()
})
return {

View File

@@ -18,9 +18,11 @@ 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'
const display = useDisplay()
const appMode = inject('pwaMode')
// PWA模式检测
const { appMode } = usePWA()
const { t } = useI18n()
const route = useRoute()

View File

@@ -5,9 +5,11 @@ import { NavMenu } from '@/@layouts/types'
import { useI18n } from 'vue-i18n'
import { useUserStore } from '@/stores'
import { filterMenusByPermission } from '@/utils/permission'
import { usePWA } from '@/composables/usePWA'
const display = useDisplay()
const appMode = inject('pwaMode') && display.mdAndDown.value
// PWA模式检测
const { appMode } = usePWA()
const { t, locale } = useI18n()
// 判断当前是否为英文环境

View File

@@ -4,6 +4,7 @@ import useDragAndDrop from '@core/utils/workflow'
import { useDisplay } from 'vuetify'
import { useI18n } from 'vue-i18n'
import { actionStepDict } from '@/api/constants'
import { usePWA } from '@/composables/usePWA'
interface ActionItem {
name: string
@@ -13,7 +14,8 @@ interface ActionItem {
const display = useDisplay()
// APP
const appMode = inject('pwaMode') && display.mdAndDown.value
// PWA模式检测
const { appMode } = usePWA()
const { t } = useI18n()
const { onDragStart } = useDragAndDrop()

View File

@@ -18,9 +18,7 @@ import { PerfectScrollbarPlugin } from 'vue3-perfect-scrollbar'
import { CronVuetify } from '@vue-js-cron/vuetify'
// 4. 工具函数和其他辅助模块
import { isPWA } from './@core/utils/navigator'
import { loadRemoteComponents } from './utils/federationLoader'
import { fetchGlobalSettings } from './utils/globalSetting'
// 5. 其他插件和功能模块
import Toast from 'vue-toastification'
@@ -51,59 +49,43 @@ const app = createApp(App)
// 注册pinia
app.use(pinia)
// 初始化配置
async function initializeApp() {
try {
// 是否为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')
// 异步加载远程组件(不阻塞启动)
loadRemoteComponents().catch(error => {
console.error('Failed to load remote components', error)
})
// 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')

View File

@@ -9,13 +9,15 @@ import { useDisplay } from 'vuetify'
import { useDynamicButton } from '@/composables/useDynamicButton'
import { useI18n } from 'vue-i18n'
import { VCardActions } from 'vuetify/components'
import { usePWA } from '@/composables/usePWA'
// 国际化
const { t } = useI18n()
// APP
const display = useDisplay()
const appMode = inject('pwaMode') && display.mdAndDown.value
// PWA模式检测
const { appMode } = usePWA()
// 从用户 Store 中获取superuser信息
const superUser = useUserStore().superUser

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,
},
})

View File

@@ -12,5 +12,6 @@ export default pinia
// 所有的 store
import { useAuthStore } from './auth'
import { useUserStore } from './user'
import { useGlobalSettingsStore } from './globalSettings'
export { useAuthStore, useUserStore }
export { useAuthStore, useUserStore, useGlobalSettingsStore }

View File

@@ -21,3 +21,12 @@ export interface userState {
// 权限
permissions: { [key: string]: any }
}
export interface globalSettingsState {
// 全局设置数据
data: { [key: string]: any }
// 是否已初始化
initialized: boolean
// 是否正在加载
loading: boolean
}

View File

@@ -15,6 +15,7 @@ import SearchSiteDialog from '@/components/dialog/SearchSiteDialog.vue'
import { useTheme } from 'vuetify'
import { useI18n } from 'vue-i18n'
import { hasPermission } from '@/utils/permission'
import { useGlobalSettingsStore } from '@/stores'
// 国际化
const { t } = useI18n()
@@ -28,7 +29,9 @@ const mediaProps = defineProps({
})
// 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings')
// 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 用户 Store
const userStore = useUserStore()

View File

@@ -5,6 +5,7 @@ import personIcon from '@images/misc/person.png'
import type { Person } from '@/api/types'
import NoDataFound from '@/components/NoDataFound.vue'
import { useI18n } from 'vue-i18n'
import { useGlobalSettingsStore } from '@/stores'
// 国际化
const { t } = useI18n()
@@ -17,7 +18,9 @@ const personProps = defineProps({
})
// 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings')
// 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 媒体详情
const personDetail = ref<Person>({} as Person)

View File

@@ -13,6 +13,7 @@ import PluginMarketSettingDialog from '@/components/dialog/PluginMarketSettingDi
import { useDynamicButton } from '@/composables/useDynamicButton'
import { useI18n } from 'vue-i18n'
import PluginMixedSortCard from '@/components/cards/PluginMixedSortCard.vue'
import { usePWA } from '@/composables/usePWA'
// 国际化
const { t } = useI18n()
@@ -23,7 +24,8 @@ const route = useRoute()
const display = useDisplay()
// APP
const appMode = inject('pwaMode') && display.mdAndDown.value
// PWA模式检测
const { appMode } = usePWA()
// 当前标签
const activeTab = ref('installed')

View File

@@ -11,13 +11,15 @@ import router from '@/router'
import { useDisplay } from 'vuetify'
import { formatFileSize } from '@/@core/utils/formatters'
import { useI18n } from 'vue-i18n'
import { usePWA } from '@/composables/usePWA'
// i18n
const { t } = useI18n()
// APP
const display = useDisplay()
const appMode = inject('pwaMode') && display.mdAndDown.value
// PWA模式检测
const { appMode } = usePWA()
// 提示框
const $toast = useToast()

View File

@@ -3,18 +3,21 @@ import { useToast } from 'vue-toastification'
import api from '@/api'
import type { TorrentCacheData, TorrentCacheItem } from '@/api/types'
import { useI18n } from 'vue-i18n'
import { useDisplay } from 'vuetify'
import { formatFileSize, formatDateDifference } from '@core/utils/formatters'
import { useConfirm } from '@/composables/useConfirm'
import { useGlobalSettingsStore } from '@/stores'
import { usePWA } from '@/composables/usePWA'
// 国际化
const { t } = useI18n()
const display = useDisplay()
const appMode = inject('pwaMode') && display.mdAndDown.value
// 从 provide 中获取全局设置
const globalSettings: any = inject('globalSettings')
// PWA模式检测
const { appMode } = usePWA()
// 全局设置
const globalSettingsStore = useGlobalSettingsStore()
const globalSettings = globalSettingsStore.globalSettings
// 确认框
const createConfirm = useConfirm()

View File

@@ -8,13 +8,15 @@ import SiteAddEditDialog from '@/components/dialog/SiteAddEditDialog.vue'
import { useDisplay } from 'vuetify'
import { useDynamicButton } from '@/composables/useDynamicButton'
import { useI18n } from 'vue-i18n'
import { usePWA } from '@/composables/usePWA'
// 国际化
const { t } = useI18n()
// APP
const display = useDisplay()
const appMode = inject('pwaMode') && display.mdAndDown.value
// PWA模式检测
const { appMode } = usePWA()
// 站点列表
const siteList = ref<Site[]>([])

View File

@@ -9,13 +9,15 @@ import { useUserStore } from '@/stores'
import { useDisplay } from 'vuetify'
import { useDynamicButton } from '@/composables/useDynamicButton'
import { useI18n } from 'vue-i18n'
import { usePWA } from '@/composables/usePWA'
// 国际化
const { t } = useI18n()
// APP
const display = useDisplay()
const appMode = inject('pwaMode') && display.mdAndDown.value
// PWA模式检测
const { appMode } = usePWA()
// 用户 Store
const userStore = useUserStore()

View File

@@ -7,13 +7,15 @@ import UserAddEditDialog from '@/components/dialog/UserAddEditDialog.vue'
import { useDisplay } from 'vuetify'
import { useDynamicButton } from '@/composables/useDynamicButton'
import { useI18n } from 'vue-i18n'
import { usePWA } from '@/composables/usePWA'
// 国际化
const { t } = useI18n()
// APP
const display = useDisplay()
const appMode = inject('pwaMode') && display.mdAndDown.value
// PWA模式检测
const { appMode } = usePWA()
// 是否刷新过
const isRefreshed = ref(false)

View File

@@ -1,19 +1,19 @@
<script setup lang="ts">
import api from '@/api'
import { Workflow } from '@/api/types'
import { useDisplay } from 'vuetify'
import WorkflowAddEditDialog from '@/components/dialog/WorkflowAddEditDialog.vue'
import WorkflowTaskCard from '@/components/cards/WorkflowTaskCard.vue'
import NoDataFound from '@/components/NoDataFound.vue'
import { useDynamicButton } from '@/composables/useDynamicButton'
import { useI18n } from 'vue-i18n'
import { usePWA } from '@/composables/usePWA'
// 国际化
const { t } = useI18n()
// APP
const display = useDisplay()
const appMode = inject('pwaMode') && display.mdAndDown.value
// PWA模式检测
const { appMode } = usePWA()
// 是否刷新
const isRefreshed = ref(false)