import { createRouter, createWebHashHistory, RouteRecordRaw, RouteLocationNormalized } from 'vue-router' import { useStorage, useStorageType } from '@/utils' import { TOKEN, ROUTE_WHITE_LIST, LOGIN_PATH } from '@/constants' // import { useUserStore } from '@/store/user' import { useLayoutStore } from '@/store/layout' import NProgress from '@/plugins/NProgress' // 引入顶部进度条插件 // 公开路由表 const routes: RouteRecordRaw[] = [ { name: 'Login', path: LOGIN_PATH, component: () => import(/* webpackChunkName: "login" */ '@/views/login/index.vue') }, { name: 'Layout', path: '/', redirect: '/main', component: () => import(/* webpackChunkName: "layout" */ '@/layout/index.vue'), children: [ /* { name: 'Main', path: '/main', meta: { title: '首页', icon: 'location' }, component: () => import('@/views/main/index.vue') } */ ] } /* { name: '404', path: '/404', meta: { title: '页面未找到' }, component: () => import(/!* webpackChunkName: "404" *!/ '@/views/error/404.vue') }, { path: '/:pathMatch(.*)*', redirect: '/404' // 路径不对跳转该页面 } */ ] const router = createRouter({ history: createWebHashHistory(), routes, /** * 滚动行为 * @param to: 即将要进入的目标 用一种标准化的方式 * @param from: 当前导航正要离开的路由 用一种标准化的方式 * @param savedPosition: popstate 导航时才可用(由浏览器的后退/前进按钮触发) */ scrollBehavior(to, from, savedPosition) { if (savedPosition) { // 返回 savedPosition,在按下 后退/前进 按钮时,就会像浏览器的原生表现那样 return savedPosition } else { return { // 当切换到新路由时,页面滚到顶部 top: 0, // el: to.hash, // 锚点 behavior: 'smooth' // 滚动平滑(动画) } } } }) /** * 动态加载路由 */ // 处理菜单组件路径 const routeParentName = 'Layout' // 递归所有路由挂载在Layout组件下 function handleMenuComponent(list: any) { if (!list || !list.length) return list.forEach((item: any) => { if (!item.children || !item.children.length) { const componentPath = '@/views' + item.path + '/index.vue' console.log(componentPath) item.component = () => import('@/views' + item.path + '/index.vue') /** * 添加路由 * @param routeParentName: 需要挂载的父级组件名 * @param item: 路由规则 */ router.addRoute(routeParentName, item) } else { // router.addRoute(routeParentName, item) // routeParentName = item.name handleMenuComponent(item.children) } }) } /** * 全局前置守卫 * @param to: 即将要进入的目标 用一种标准化的方式 * @param from: 当前导航正要离开的路由 用一种标准化的方式 * @param next: 路由放行与跳转 * 场景:登录校验等 */ router.beforeEach(async (to: RouteLocationNormalized) => { // 开启进度条 NProgress.start() const token = useStorage.getItem(TOKEN, useStorageType.Cookies) /* 1.用户未登录操作 */ if (!token) { // 1.1 避免无限重定向 if (!ROUTE_WHITE_LIST.includes(to.path)) return LOGIN_PATH // if (!ROUTE_WHITE_LIST.includes(to.path)) return { path: LOGIN_PATH, query: { redirect: to.fullPath } } } else { /* 2.用户已登录操作 */ /* const userStore = useUserStore() // 2.1 是否有用户信息 if (!userStore.userInfo) { console.log('没有用户信息,重新获取') await userStore.getUserInfoAction() } */ // 2.2 是否动态添加过路由 const layoutStore = useLayoutStore() if (!layoutStore.isAddRoute) { console.log('页面刷新,重新获取菜单与处理动态添加路由') let menuList: any = [] /* 菜单处理 */ if (!layoutStore.menuList.length) { // 从服务器获取 menuList = [ { name: 'Main', path: '/main', meta: { menuName: '首页', menuCode: 'Main', icon: 'location' }, component: null }, { name: 'Components', path: '/components', meta: { menuName: '组件', menuCode: 'Components', icon: 'location' }, children: [ { name: 'ComponentsChart', path: '/components/chart', component: null, meta: { menuName: '图表组件', menuCode: 'ComponentsChart', icon: 'location' } }, { name: 'ComponentsTrend', path: '/components/trend', component: null, meta: { menuName: '趋势展示组件', menuCode: 'ComponentsTrend', icon: 'location' } }, { name: 'ComponentsProgress', path: '/components/progress', component: null, meta: { menuName: '进度条组件', menuCode: 'ComponentsProgress', icon: 'location' } }, { name: 'ComponentsForm', path: '/components/form', component: null, meta: { menuName: '表单组件', menuCode: 'ComponentsForm', icon: 'location' } }, { name: 'ComponentsDialogForm', path: '/components/dialogForm', component: null, meta: { menuName: '对话框表单组件', menuCode: 'ComponentsDialogForm', icon: 'location' } }, { name: 'ComponentsTable', path: '/components/table', component: null, meta: { menuName: '表格组件', menuCode: 'ComponentsTable', icon: 'location' } }, { name: 'ComponentsPagination', path: '/components/pagination', component: null, meta: { menuName: '分页组件', menuCode: 'ComponentsPagination', icon: 'location' } } ] }, { name: 'Rbac', path: '/rbac', meta: { menuName: '后台管理', menuCode: 'Rbac', icon: 'location' }, component: null, children: [ { name: 'RbacUser', path: '/rbac/user', component: null, meta: { menuName: '用户管理', menuCode: 'RbacUser', icon: 'location' } }, { name: 'RbacRole', path: '/rbac/role', component: null, meta: { menuName: '角色管理', menuCode: 'RbacRole', icon: 'location' } }, { name: 'RbacPermission', path: '/rbac/permission', component: null, meta: { menuName: '权限管理', menuCode: 'RbacPermission', icon: 'location' } } ] } ] } else { // 从缓存获取 menuList = layoutStore.menuList } /* 动态添加路由 */ handleMenuComponent(menuList) /* 动态添加报错页面(一定要最后添加,不然先进错误页面了) */ const errorList = [ { name: '404', path: '/404', meta: { title: '页面未找到' }, component: () => import(/* webpackChunkName: "404" */ '@/views/error/404.vue') }, { path: '/:pathMatch(.*)*', redirect: '/404' // 路径不对跳转该页面 } ] errorList.forEach((item: RouteRecordRaw) => { router.addRoute(item) }) // console.log('查看现有路由', router.getRoutes()) /* 缓存数据 */ layoutStore.$patch({ isAddRoute: true, // 缓存是否动态添加过菜单避免递归 menuList // 缓存菜单 }) return { ...to, replace: true } // return to.path } // 2.3 如果不在白名单 就同步选中菜单与选中tag if (!ROUTE_WHITE_LIST.includes(to.path)) { const item: any = router.getRoutes().find((item: any) => item.path === to.path) if (!item || JSON.stringify(item) === '{}') return const tag = { name: item.name, path: item.path, meta: item.meta } /* 同步选中tags - 设置并存储tags列表 */ layoutStore.setTagsListAction(tag) /* 同步选中菜单 - 设置并存储当前菜单 */ layoutStore.setMenuActivePathAction(tag.path) } // 2.4 如果是登录页就跳转当前页或首页 if (to.path === LOGIN_PATH) return layoutStore.menuActivePath || '/' } }) /** * 全局后置守卫 * @param to: 即将要进入的目标 用一种标准化的方式 * @param from: 当前导航正要离开的路由 用一种标准化的方式 * @param failure: 检测导航故障 * 场景:分析、更改页面标题、声明页面等辅助功能 */ router.afterEach((to, from, failure) => { if (failure) return false // 关闭进度条 NProgress.done() // 设置网页标题 // document.title = (to.meta.title) as string }) export default router