/** * Copyright [2022] [https://www.xiaonuo.vip] * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: * 1.请不要删除和修改根目录下的LICENSE文件。 * 2.请不要删除和修改Snowy源码头部的版权声明。 * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip */ import { createRouter, createWebHistory } from 'vue-router' import { notification } from 'ant-design-vue' import NProgress from 'nprogress' import 'nprogress/nprogress.css' import systemRouter from './systemRouter' import { afterEach, beforeEach } from './scrollBehavior' import whiteListRouters from './whiteList' import userRoutes from '@/config/route' import tool from '@/utils/tool' import { cloneDeep } from 'lodash-es' const modules = import.meta.glob('/src/views/**/**.vue') import { globalStore, searchStore } from '@/store' // 进度条配置 NProgress.configure({ showSpinner: false, speed: 500 }) // 系统特殊路由 const routes_404 = [ { path: '/:pathMatch(.*)*', hidden: true, component: () => import('@/layout/other/404.vue') } ] // 系统路由 const routes = [...systemRouter, ...whiteListRouters, ...routes_404] const router = createRouter({ history: createWebHistory(), routes }) // 设置标题 // document.title = sysBaseConfig.SNOWY_SYS_NAME // 判断是否已加载过动态/静态路由 const isGetRouter = ref(false) // 白名单校验 const exportWhiteListFromRouter = (router) => { const res = [] for (const item of router) res.push(item.path) return res } const whiteList = exportWhiteListFromRouter(whiteListRouters) router.beforeEach(async (to, from, next) => { NProgress.start() const store = globalStore() const sysBaseConfig = tool.data.get('SNOWY_SYS_BASE_CONFIG') || store.sysBaseConfig // 动态标题 document.title = to.meta.title ? `${to.meta.title} - ${sysBaseConfig.SNOWY_SYS_NAME}` : `${sysBaseConfig.SNOWY_SYS_NAME}` // 过滤白名单 if (whiteList.includes(to.path)) { next() // NProgress.done() return false } const token = tool.data.get('TOKEN') if (to.path === '/login') { // 当用户输入了login路由,将其跳转首页即可 if (token) { next({ path: '/' }) return false } // 删除路由(替换当前layout路由) router.addRoute(routes[0]) isGetRouter.value = false next() return false } else { if (token) { // 有token的时候才保存登录之前要访问的页面 tool.data.set('LAST_VIEWS_PATH', to.fullPath) } } if (!token) { next({ path: '/login' }) return false } // 整页路由处理 if (to.meta.fullpage) { to.matched = [to.matched[to.matched.length - 1]] } // 加载动态/静态路由 if (!isGetRouter.value) { const apiMenu = tool.data.get('MENU') || [] if (apiMenu.length === 0) { // 创建默认模块,显示默认菜单 apiMenu[0] = cloneDeep(userRoutes.module[0]) } const childrenApiMenu = apiMenu[0].children apiMenu[0].children = [...childrenApiMenu ? childrenApiMenu : [], ...userRoutes.menu] let menuRouter = filterAsyncRouter(apiMenu) menuRouter = flatAsyncRoutes(menuRouter) menuRouter.forEach((item) => { router.addRoute('layout', item) }) const search_store = searchStore() search_store.init(menuRouter) isGetRouter.value = true next({ ...to, replace: true }) return false } beforeEach(to, from) next() }) router.afterEach((to, from) => { afterEach(to, from) NProgress.done() }) router.onError((error) => { NProgress.done() notification.error({ message: '路由错误', description: error.message }) }) // 入侵追加自定义方法、对象 router.getMenu = () => { let apiMenu = tool.data.get('MENU') || [] // 增加固定路由 if (apiMenu.length === 0) { // 创建默认模块,显示默认菜单 apiMenu[0] = cloneDeep(userRoutes.module[0]) } const childrenApiMenu = apiMenu[0].children apiMenu[0].children = [...childrenApiMenu ? childrenApiMenu : [], ...userRoutes.menu] return filterUrl(apiMenu) } const filterUrl = (map) => { const newMap = [] const traverse = (maps) => { maps && maps.forEach((item) => { item.meta = item.meta ? item.meta : {} // 处理iframe if (item.meta.type === 'iframe') { item.path = `/${item.name}` } // 递归循环 if (item.children && item.children.length > 0) { item.children = filterUrl(item.children) } newMap.push(item) }) } traverse(map) return newMap } // 转换 const filterAsyncRouter = (routerMap) => { const accessedRouters = [] routerMap.forEach((item) => { item.meta = item.meta ? item.meta : {} // 处理外部链接特殊路由 if (item.meta.type === 'iframe') { item.meta.url = item.path item.path = `/${item.name}` } // MAP转路由对象 const route = { path: item.path, name: item.name, meta: item.meta, redirect: item.redirect, children: item.children ? filterAsyncRouter(item.children) : null, component: loadComponent(item.component) } accessedRouters.push(route) }) return accessedRouters } const loadComponent = (component) => { if (component) { if (component.includes('/')) { return modules[`/src/views/${component}.vue`] } return modules[`/src/views/${component}/index.vue`] } else { return () => import(/* @vite-ignore */ `/src/layout/other/empty.vue`) } } // 路由扁平化 const flatAsyncRoutes = (routes, breadcrumb = []) => { const res = [] routes.forEach((route) => { const tmp = { ...route } if (tmp.children) { const childrenBreadcrumb = [...breadcrumb] childrenBreadcrumb.push(route) const tmpRoute = { ...route } tmpRoute.meta.breadcrumb = childrenBreadcrumb delete tmpRoute.children res.push(tmpRoute) const childrenRoutes = flatAsyncRoutes(tmp.children, childrenBreadcrumb) childrenRoutes.map((item) => { res.push(item) }) } else { const tmpBreadcrumb = [...breadcrumb] tmpBreadcrumb.push(tmp) tmp.meta.breadcrumb = tmpBreadcrumb res.push(tmp) } }) return res } export default router