Files
MoviePilot-Frontend/src/main.ts

135 lines
3.8 KiB
TypeScript

// 1. 配置与兼容性
import '@/@core/utils/compatibility'
import '@/plugins/webfontloader'
// 2. 核心插件和 UI 框架
import { createApp, defineAsyncComponent } from 'vue'
import vuetify from '@/plugins/vuetify'
import router from '@/router'
import pinia from '@/stores/index'
import i18n from '@/plugins/i18n'
// 3. 全局组件
import App from '@/App.vue'
import { PerfectScrollbarPlugin } from 'vue3-perfect-scrollbar'
// 4. 其他插件和功能模块
import Toast from 'vue-toastification'
import ConfirmDialog from '@/composables/useConfirm'
import { configureApexChartsTheme } from '@/utils/apexCharts'
// 5. 注册自定义组件
import DialogCloseBtn from '@/@core/components/DialogCloseBtn.vue'
import ScrollToTopBtn from '@/@core/components/ScrollToTopBtn.vue'
import PageContentTitle from './@core/components/PageContentTitle.vue'
// 6. 样式文件 - 合并为单一导入
import '@/styles/main.scss'
// 7. 状态恢复插件
import stateRestorePlugin from '@/plugins/stateRestore'
function runWhenBrowserIdle(callback: () => void, timeout = 1500) {
const requestIdle = globalThis.requestIdleCallback
if (requestIdle) {
requestIdle(callback, { timeout })
return
}
globalThis.setTimeout(callback, 0)
}
function loadIconBundle() {
import('@/@iconify/icons-bundle').catch(error => {
console.error('Failed to load icon bundle', error)
})
}
function loadRemoteComponentsAfterLogin() {
import('./utils/federationLoader')
.then(({ loadRemoteComponents }) => loadRemoteComponents())
.catch(error => {
console.error('Failed to load remote components', error)
})
}
let remoteComponentsInitialized = false
const AsyncAceEditor = defineAsyncComponent(async () => {
await import('./ace-config')
return (await import('vue3-ace-editor')).VAceEditor
})
const AsyncApexChart = defineAsyncComponent(async () => {
const component = (await import('vue3-apexcharts')).default
const themeName = document.documentElement.getAttribute('data-theme') || localStorage.getItem('theme') || 'light'
configureApexChartsTheme(themeName)
return component
})
const AsyncCronVuetify = defineAsyncComponent(async () => {
return (await import('@vue-js-cron/vuetify')).CronVuetify
})
const AsyncCronField = defineAsyncComponent(async () => {
return (await import('./components/field/CronField.vue')).default
})
const AsyncPathField = defineAsyncComponent(async () => {
return (await import('./components/field/PathField.vue')).default
})
// 创建Vue实例
const app = createApp(App)
// 1. 注册pinia
app.use(pinia)
// 2. 注册 UI 框架
app.use(vuetify)
// 3. 注册路由
app.use(router)
// 4. 注册状态恢复插件
app.use(stateRestorePlugin)
// 5. 注册全局组件
app
.component('VAceEditor', AsyncAceEditor)
.component('VApexChart', AsyncApexChart)
.component('VCronVuetify', AsyncCronVuetify)
.component('VDialogCloseBtn', DialogCloseBtn)
.component('VScrollToTopBtn', ScrollToTopBtn)
.component('VCronField', AsyncCronField)
.component('VPathField', AsyncPathField)
.component('VPageContentTitle', PageContentTitle)
// 6. 注册其他插件
app
.use(PerfectScrollbarPlugin)
.use(Toast, {
position: 'bottom-right',
hideProgressBar: true,
})
.use(ConfirmDialog)
.use(i18n)
app.mount('#app')
// 图标全集很大,延后到首屏挂载后的空闲时间加载,避免阻塞登录页首次渲染。
runWhenBrowserIdle(loadIconBundle)
// 插件远程入口只在登录后有用,延后初始化可以减少未登录首屏请求和解析成本。
router.isReady().then(() => {
const loadIfAuthenticated = () => {
if (!remoteComponentsInitialized && pinia.state.value.auth?.token) {
remoteComponentsInitialized = true
runWhenBrowserIdle(loadRemoteComponentsAfterLogin)
}
}
loadIfAuthenticated()
router.afterEach(loadIfAuthenticated)
})