import type { Router, RouteLocationNormalized } from 'vue-router' import { useAppStoreWithOut } from '@/store/modules/app' import { useUserStoreWithOut } from '@/store/modules/user' import { useTransitionSetting } from '@/hooks/setting/useTransitionSetting' import { AxiosCanceler } from '@/utils/http/axios/axiosCancel' import { Modal, notification } from 'ant-design-vue' import { warn } from '@/utils/log' import { unref } from 'vue' import { setRouteChange } from '@/logics/mitt/routeChange' import { createPermissionGuard } from './permissionGuard' import { createStateGuard } from './stateGuard' import nProgress from 'nprogress' import projectSetting from '@/settings/projectSetting' import { createParamMenuGuard } from './paramMenuGuard' // Don't change the order of creation export function setupRouterGuard(router: Router) { createPageGuard(router) createPageLoadingGuard(router) createHttpGuard(router) createScrollGuard(router) createMessageGuard(router) createProgressGuard(router) createPermissionGuard(router) createParamMenuGuard(router) // must after createPermissionGuard (menu has been built.) createStateGuard(router) } /** * Hooks for handling page state */ function createPageGuard(router: Router) { const loadedPageMap = new Map() router.beforeEach(async (to) => { // The page has already been loaded, it will be faster to open it again, you don’t need to do loading and other processing to.meta.loaded = !!loadedPageMap.get(to.path) // Notify routing changes setRouteChange(to) return true }) router.afterEach((to) => { loadedPageMap.set(to.path, true) }) } // Used to handle page loading status function createPageLoadingGuard(router: Router) { const userStore = useUserStoreWithOut() const appStore = useAppStoreWithOut() const { getOpenPageLoading } = useTransitionSetting() router.beforeEach(async (to) => { if (!userStore.getAccessToken) { return true } if (to.meta.loaded) { return true } if (unref(getOpenPageLoading)) { appStore.setPageLoadingAction(true) return true } return true }) router.afterEach(async () => { if (unref(getOpenPageLoading)) { // TODO Looking for a better way // The timer simulates the loading time to prevent flashing too fast, setTimeout(() => { appStore.setPageLoading(false) }, 220) } return true }) } /** * The interface used to close the current page to complete the request when the route is switched * @param router */ function createHttpGuard(router: Router) { const { removeAllHttpPending } = projectSetting let axiosCanceler: Nullable if (removeAllHttpPending) { axiosCanceler = new AxiosCanceler() } router.beforeEach(async () => { // Switching the route will delete the previous request axiosCanceler?.removeAllPending() return true }) } // Routing switch back to the top function createScrollGuard(router: Router) { const isHash = (href: string) => { return /^#/.test(href) } const body = document.body router.afterEach(async (to) => { // scroll top isHash((to as RouteLocationNormalized & { href: string })?.href) && body.scrollTo(0, 0) return true }) } /** * Used to close the message instance when the route is switched * @param router */ export function createMessageGuard(router: Router) { const { closeMessageOnSwitch } = projectSetting router.beforeEach(async () => { try { if (closeMessageOnSwitch) { Modal.destroyAll() notification.destroy() } } catch (error) { warn('message guard error:' + error) } return true }) } export function createProgressGuard(router: Router) { const { getOpenNProgress } = useTransitionSetting() router.beforeEach(async (to) => { if (to.meta.loaded) { return true } unref(getOpenNProgress) && nProgress.start() return true }) router.afterEach(async () => { unref(getOpenNProgress) && nProgress.done() return true }) }