import '@/styles/adminDensity.css' import { computed, createApp } from 'vue' import 'dayjs/locale/zh-cn' import { createPinia } from 'pinia' import { ElMessage, ElMessageBox } from 'element-plus' import 'element-plus/es/components/message/style/css' import 'element-plus/es/components/message-box/style/css' import App from './App.vue' import router from '@/app/router' import { sanitizeInternalPath } from '@/core/routing/accessDecision' import { createNavigationRecoveryHandler, installAssetRecoveryListeners, isStaleRouteAssetError, reloadNavigationTarget } from '@/core/routing/navigationRecovery' import { useAccessPreviewStore } from '@/core/access/accessPreview' import { useUnsavedWorkStore } from '@/core/unsaved-work/unsavedWork' import { useWorkspaceTabsStore } from '@/core/workspace-tabs/workspaceTabs' import { useAuthStore } from '@/core/auth/auth' import { registerAuthSessionExpiredHandler } from '@/shared/api/http' import { UI_PREFERENCE_SCOPE } from '@/shared/utils/uiLayoutPreference' import './styles/index.css' const pinia = createPinia() const auth = useAuthStore(pinia) const unsaved = useUnsavedWorkStore(pinia) const workspace = useWorkspaceTabsStore(pinia) registerAuthSessionExpiredHandler(async () => { const currentRoute = router.currentRoute.value if (currentRoute.name === 'login') return useAccessPreviewStore(pinia).reset() unsaved.clear() workspace.reset(true) const redirect = sanitizeInternalPath(currentRoute.fullPath) ElMessage.warning('登录状态已过期,请重新登录') await router.replace({ name: 'login', query: redirect === '/' ? undefined : { redirect }, }) }) const recoverAssetError = createNavigationRecoveryHandler({ storage: { getItem: (key) => window.sessionStorage.getItem(key), setItem: (key, value) => window.sessionStorage.setItem(key, value), }, now: Date.now, hasUnsavedWork: () => unsaved.entries.some((entry) => entry.dirty), confirmDiscard: async () => { try { await ElMessageBox.confirm( '页面资源加载失败,可能已发布新版本,需要重新加载页面。继续后将放弃当前未保存内容。', '重新加载页面?', { confirmButtonText: '重新加载', cancelButtonText: '继续编辑', type: 'warning', customClass: 'admin-density-confirm' }, ) return true } catch { return false } }, discardUnsavedWork: () => unsaved.clear(), navigate: (target) => reloadNavigationTarget(window, target), showError: (message) => ElMessage.error(message), }) let pendingRoute: string | undefined const recoveryTarget = () => pendingRoute ?? `${window.location.pathname}${window.location.search}${window.location.hash}` router.beforeEach((to) => { pendingRoute = to.fullPath }) router.onError((error, to) => { void recoverAssetError(error, to.fullPath) if (pendingRoute === to.fullPath) pendingRoute = undefined }) router.afterEach((to) => { if (pendingRoute === to.fullPath) pendingRoute = undefined }) installAssetRecoveryListeners(window, recoveryTarget, recoverAssetError) window.addEventListener('beforeunload', (event) => { if (!unsaved.entries.some((entry) => entry.dirty)) return event.preventDefault() event.returnValue = '' }) const app = createApp(App) app.config.errorHandler = (error) => { if (isStaleRouteAssetError(error)) void recoverAssetError(error, recoveryTarget()) else console.error(error) } app.provide(UI_PREFERENCE_SCOPE, computed(() => auth.user?.id || 'anonymous')) await auth.restoreSession() app.use(pinia).use(router).mount('#app')