book-web/src/router/index.ts

330 lines
6.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { useAppStore } from '@/store/modules/app'
import { useUserStore } from '@/store/modules/user'
import { useRouterStore } from '@/store/modules/router'
import { isExternalLink, pathToCamel } from '@/utils/tool'
import constant from '@/utils/constant'
NProgress.configure({ showSpinner: false })
const constantRoutes: RouteRecordRaw[] = [
{
path: '/redirect',
component: () => import('../layout/index.vue'),
children: [
{
path: '/redirect/:path(.*)',
component: () => import('../layout/components/Router/Redirect.vue')
}
]
},
{
path: '/iframe/:query?',
component: () => import('../layout/components/Router/Iframe.vue')
},
{
path: '/login',
component: () => import('../views/login/index.vue')
},
{
path: '/404',
component: () => import('../views/404.vue')
}
]
const asyncRoutes: RouteRecordRaw = {
path: '/',
component: () => import('../layout/index.vue'),
redirect: '/dashboard/index',
children: [
{
path: '/profile',
name: 'ProfileIndex',
component: () => import('../views/profile/index.vue'),
meta: {
title: '个人中心',
cache: true
}
}
]
}
// 配置控制台菜单
export const dashboardMenu = [
{
id: 100,
name: 'Dashboard',
url: null,
openStyle: 0,
icon: 'icon-appstore',
children: [
{
id: 101,
name: '首页',
url: 'dashboard/index',
openStyle: 0,
icon: 'icon-home',
affix: true
},
{
id: 102,
name: '工作台',
url: 'dashboard/workbench',
openStyle: 0,
icon: 'icon-appstore'
}
]
}
]
// 配置常量菜单
export const constantMenu = [
{
id: 1000,
name: 'Demo',
url: null,
openStyle: 0,
icon: 'icon-windows',
children: [
{
id: 1001,
name: 'Icon 图标',
url: 'demo/icons/index',
openStyle: 0,
icon: 'icon-unorderedlist'
},
{
id: 1002,
name: '表单设计器',
url: 'demo/formDesign/form',
openStyle: 0,
icon: 'icon-unorderedlist'
},
{
id: 1003,
name: '表单生成器',
url: 'demo/formDesign/generate',
openStyle: 0,
icon: 'icon-unorderedlist'
},
{
id: 1004,
name: '二维码生成',
url: 'demo/qrcode/index',
openStyle: 0,
icon: 'icon-unorderedlist'
},
{
id: 1005,
name: '页面打印',
url: 'demo/printJs/index',
openStyle: 0,
icon: 'icon-unorderedlist'
},
{
id: 1006,
name: '图片裁剪',
url: 'demo/cropper/index',
openStyle: 0,
icon: 'icon-unorderedlist'
},
{
id: 1007,
name: '富文本编辑器',
url: 'demo/editor/index',
openStyle: 0,
icon: 'icon-unorderedlist'
},
{
id: 1008,
name: 'Markdown',
url: 'demo/markdown/index',
openStyle: 0,
icon: 'icon-unorderedlist'
},
{
id: 1009,
name: 'ECharts图表',
url: 'demo/echarts/index',
openStyle: 0,
icon: 'icon-unorderedlist'
}
]
}
]
export const errorRoute: RouteRecordRaw = {
path: '/:pathMatch(.*)',
redirect: '/404'
}
export const router = createRouter({
history: createWebHashHistory(),
routes: constantRoutes
})
// 白名单列表
const whiteList = ['/login']
// 路由跳转前
router.beforeEach(async (to, from, next) => {
NProgress.start()
const appStore = useAppStore()
const userStore = useUserStore()
const routerStore = useRouterStore()
// token存在的情况
if (userStore.token) {
if (to.path === '/login') {
next(constant.loginPage)
} else {
// 用户信息不存在,则重新拉取
if (!userStore.user.id) {
try {
await userStore.getUserInfoAction()
await userStore.getAuthorityListAction()
await appStore.getDictListAction()
} catch (error) {
// 请求异常,则跳转到登录页
userStore?.setToken('')
next('/login')
return Promise.reject(error)
}
// 动态菜单+常量菜单
const menuRoutes = await routerStore.getMenuRoutes()
// 获取扁平化路由,将多级路由转换成一级路由
const keepAliveRoutes = getKeepAliveRoutes(menuRoutes, [])
// 添加菜单路由
asyncRoutes.children?.push(...keepAliveRoutes)
router.addRoute(asyncRoutes)
// 错误路由
router.addRoute(errorRoute)
// 保存路由数据
routerStore.setRoutes(constantRoutes.concat(asyncRoutes))
// 搜索菜单需要使用
routerStore.setSearchMenu(keepAliveRoutes)
next({ ...to, replace: true })
} else {
next()
}
}
} else {
// 没有token的情况下可以进入白名单
if (whiteList.indexOf(to.path) > -1) {
next()
} else {
next('/login')
}
}
})
// 路由加载后
router.afterEach(() => {
NProgress.done()
})
// 获取扁平化路由,将多级路由转换成一级路由
export const getKeepAliveRoutes = (rs: RouteRecordRaw[], breadcrumb: string[]): RouteRecordRaw[] => {
const routerList: RouteRecordRaw[] = []
rs.forEach((item: any) => {
if (item.meta.title) {
breadcrumb.push(item.meta.title)
}
if (item.children && item.children.length > 0) {
routerList.push(...getKeepAliveRoutes(item.children, breadcrumb))
} else {
item.meta.breadcrumb.push(...breadcrumb)
routerList.push(item)
}
breadcrumb.pop()
})
return routerList
}
// 加载vue组件
export interface ModuleMap {
[key: string]: any
}
export const layoutModules = () => {
return import.meta.glob('/src/views/**/*.vue')
}
// 根据路径动态获取vue组件
const getDynamicComponent = (path: string): any => {
const modules: ModuleMap = layoutModules()
return modules[`/src/views/${path}.vue`]
}
// 根据菜单列表,生成路由数据
export const generateRoutes = (menuList: any): RouteRecordRaw[] => {
const routerList: RouteRecordRaw[] = []
menuList.forEach((menu: any) => {
let component
let path
if (menu.children && menu.children.length > 0) {
component = () => import('@/layout/index.vue')
path = '/p/' + menu.id
} else {
// 判断是否iframe
if (isIframeUrl(menu)) {
component = () => import('@/layout/components/Router/Iframe.vue')
path = '/iframe/' + menu.id
} else {
component = getDynamicComponent(menu.url)
path = '/' + menu.url
}
}
const route: RouteRecordRaw = {
path: path,
name: pathToCamel(path),
component: component,
children: [],
meta: {
title: menu.name,
icon: menu.icon,
id: '' + menu.id,
url: menu.url,
cache: true,
newOpen: menu.openStyle === 1,
affix: menu.affix,
breadcrumb: []
}
}
// 有子菜单的情况
if (menu.children && menu.children.length > 0) {
route.children?.push(...generateRoutes(menu.children))
}
routerList.push(route)
})
return routerList
}
// 判断是否iframe
const isIframeUrl = (menu: any): boolean => {
// 如果是新页面打开则不用iframe
if (menu.openStyle === 1) {
return false
}
// 是否外部链接
return isExternalLink(menu.url)
}