路由
官网介绍 https://router.vuejs.org/
项目路由配置存放于 src/router/routes 下面。 src/router/routes/modules用于存放路由模块,在该目录下的文件会自动注册。
配置
#模块说明
在 src/router/routes/modules 内的 .ts
文件会被视为一个路由模块。
一个路由模块包含以下结构
import type { AppRouteModule } from '/@/router/types';
import { LAYOUT } from '/@/router/constant';
import { t } from '/@/hooks/web/useI18n';
const dashboard: AppRouteModule = {
path: '/dashboard',
name: 'Dashboard',
component: LAYOUT,
redirect: '/dashboard/analysis',
meta: {
icon: 'ion:grid-outline',
title: t('routes.dashboard.dashboard'),
},
children: [
{
path: 'analysis',
name: 'Analysis',
component: () => import('/@/views/dashboard/analysis/index.vue'),
meta: {
affix: true,
title: t('routes.dashboard.analysis'),
},
},
{
path: 'workbench',
name: 'Workbench',
component: () => import('/@/views/dashboard/workbench/index.vue'),
meta: {
title: t('routes.dashboard.workbench'),
},
},
],
};
export default dashboard;
#多级路由
注意事项
- 整个项目所有路由
name
不能重复 - 所有的多级路由最终都会转成二级路由,所以不能内嵌子路由
- 除了 layout 对应的 path 前面需要加
/
,其余子路由都不要以/
开头
示例
import type { AppRouteModule } from '/@/router/types';
import { getParentLayout, LAYOUT } from '/@/router/constant';
import { t } from '/@/hooks/web/useI18n';
const permission: AppRouteModule = {
path: '/level',
name: 'Level',
component: LAYOUT,
redirect: '/level/menu1/menu1-1/menu1-1-1',
meta: {
icon: 'ion:menu-outline',
title: t('routes.demo.level.level'),
},
children: [
{
path: 'tabs/:id',
name: 'TabsParams',
component: getParentLayout('TabsParams'),
meta: {
carryParam: true,
hidePathForChildren: true, // 本级path将会在子级菜单中合成完整path时会忽略这一层级
},
children: [
path: 'tabs/id1', // 其上级有标记hidePathForChildren,所以本级在生成菜单时最终的path为 /level/tabs/id1
name: 'TabsParams',
component: getParentLayout('TabsParams'),
meta: {
carryParam: true,
ignoreRoute: true, // 本路由仅用于菜单生成,不会在实际的路由表中出现
},
]
},
{
path: 'menu1',
name: 'Menu1Demo',
component: getParentLayout('Menu1Demo'),
meta: {
title: 'Menu1',
},
redirect: '/level/menu1/menu1-1/menu1-1-1',
children: [
{
path: 'menu1-1',
name: 'Menu11Demo',
component: getParentLayout('Menu11Demo'),
meta: {
title: 'Menu1-1',
},
redirect: '/level/menu1/menu1-1/menu1-1-1',
children: [
{
path: 'menu1-1-1',
name: 'Menu111Demo',
component: () => import('/@/views/demo/level/Menu111.vue'),
meta: {
title: 'Menu111',
},
},
],
},
],
},
],
};
export default permission;
#Meta 配置说明 (\types\vue-router.d.ts)
export interface RouteMeta {
// 路由title 一般必填
title: string;
// 动态路由可打开Tab页数
dynamicLevel?: number;
// 动态路由的实际Path, 即去除路由的动态部分;
realPath?: string;
// 是否忽略权限,只在权限模式为Role的时候有效
ignoreAuth?: boolean;
// 可以访问的角色,只在权限模式为Role的时候有效
roles?: RoleEnum[];
// 是否忽略KeepAlive缓存
ignoreKeepAlive?: boolean;
// 是否固定标签
affix?: boolean;
// 图标,也是菜单图标
icon?: string;
// 内嵌iframe的地址
frameSrc?: string;
// 指定该路由切换的动画名
transitionName?: string;
// 隐藏该路由在面包屑上面的显示
hideBreadcrumb?: boolean;
// 如果该路由会携带参数,且需要在tab页上面显示。则需要设置为true
carryParam?: boolean;
// 隐藏所有子菜单
hideChildrenInMenu?: boolean;
// 当前激活的菜单。用于配置详情页时左侧激活的菜单路径
currentActiveMenu?: string;
// 当前路由不再标签页显示
hideTab?: boolean;
// 当前路由不再菜单显示
hideMenu?: boolean;
// 菜单排序,只对第一级有效
orderNo?: number;
// 忽略路由。用于在ROUTE_MAPPING以及BACK权限模式下,生成对应的菜单而忽略路由。2.5.3以上版本有效
ignoreRoute?: boolean;
// 是否在子级菜单的完整path中忽略本级path。2.5.3以上版本有效
hidePathForChildren?: boolean;
}
#外部页面嵌套
只需要将 frameSrc
设置为需要跳转的地址即可
const IFrame = () => import('/@/views/sys/iframe/FrameBlank.vue');
{
path: 'doc',
name: 'Doc',
component: IFrame,
meta: {
frameSrc: 'https://vvbin.cn/doc-next/',
title: t('routes.demo.iframe.doc'),
},
},
#外链
只需要将 path
设置为需要跳转的HTTP 地址即可
{
path: 'https://vvbin.cn/doc-next/',
name: 'DocExternal',
component: IFrame,
meta: {
title: t('routes.demo.iframe.docExternal'),
},
}
#动态路由Tab自动关闭功能
若需要开启该功能,需要在动态路由的meta
中设置如下两个参数:
dynamicLevel
最大能打开的Tab标签页数realPath
动态路由实际路径(考虑到动态路由有时候可能存在N层的情况, 例:/:id/:subId/:...
), 为了减少计算开销, 使用配置方式事先规定好路由的实际路径(注意: 该参数若不设置,将无法使用该功能)
{
path: 'detail/:id',
name: 'TabDetail',
component: () => import('/@/views/demo/feat/tabs/TabDetail.vue'),
meta: {
currentActiveMenu: '/feat/tabs',
title: t('routes.demo.feat.tabDetail'),
hideMenu: true,
dynamicLevel: 3,
realPath: '/feat/tabs/detail',
},
}
#图标
这里的 icon
配置,会同步到 菜单(icon 的值可以查看此处)。
#新增路由
#如何新增一个路由模块
- 在 src/router/routes/modules 内新增一个模块文件。
示例,新增 test.ts 文件
import type { AppRouteModule } from '/@/router/types';
import { LAYOUT } from '/@/router/constant';
import { t } from '/@/hooks/web/useI18n';
const dashboard: AppRouteModule = {
path: '/about',
name: 'About',
component: LAYOUT,
redirect: '/about/index',
meta: {
icon: 'simple-icons:about-dot-me',
title: t('routes.dashboard.about'),
},
children: [
{
path: 'index',
name: 'AboutPage',
component: () => import('/@/views/sys/about/index.vue'),
meta: {
title: t('routes.dashboard.about'),
icon: 'simple-icons:about-dot-me',
},
},
],
};
export default dashboard;
此时路由已添加完成,不需要手动引入,放在src/router/routes/modules 内的文件会自动被加载。
#白名单 应用路由 配置路由 重置路由
路径 (jeecgboot-vue3-master\src\router\index.ts)
import type { RouteRecordRaw } from 'vue-router';
import type { App } from 'vue';
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router';
import { basicRoutes } from './routes';
// 白名单应该包含基本静态路由
const WHITE_NAME_LIST: string[] = [];
const getRouteNames = (array: any[]) =>
array.forEach((item) => {
WHITE_NAME_LIST.push(item.name);
getRouteNames(item.children || []);
});
getRouteNames(basicRoutes);
// 应用路由
export const router = createRouter({
// 使用 HTML5 模式的历史记录
history: createWebHistory(import.meta.env.VITE_PUBLIC_PATH),
// 路由配置
routes: basicRoutes as unknown as RouteRecordRaw[],
// 严格模式
strict: true,
// 滚动行为
scrollBehavior: () => ({ left: 0, top: 0 }),
});
// TODO 【QQYUN-4517】【表单设计器】记录分享路由守卫测试
router.beforeEach(async (to, from, next) => {
// 路由守卫,可以在这里添加一些路由跳转前的逻辑
//console.group('【QQYUN-4517】beforeEach');
//console.warn('from', from);
//console.warn('to', to);
//console.groupEnd();
next();
});
// 重置路由
export function resetRouter() {
router.getRoutes().forEach((route) => {
const { name } = route;
if (name && !WHITE_NAME_LIST.includes(name as string)) {
router.hasRoute(name) && router.removeRoute(name);
}
});
}
// 配置路由
export function setupRouter(app: App<Element>) {
app.use(router);
}