路由和菜单
路由和菜单是极为重要的,配置正确很关键,平台尽最大可能详细描述
# 路由和菜单配置
点击查看完整路由和菜单配置
interface RouteConfigsTable {
// 路由路径
path: string;
// 路由名称(必须保持唯一)
name: string;
// 路由重定向(默认跳转地址)
redirect: string;
// 路由元信息,通俗来说就是路由上的额外信息 https://router.vuejs.org/zh/guide/advanced/meta.html#%E8%B7%AF%E7%94%B1%E5%85%83%E4%BF%A1%E6%81%AF
meta: {
// 菜单名称(兼容国际化、非国际化,如果用国际化的写法就必须在根目录的locales文件夹下对应添加)
title: string;
// 菜单图标
icon: string | FunctionalComponent | IconifyIcon;
// 是否在菜单中显示
showLink: boolean;
// 菜单排序,值越高排的越后(只针对顶级路由)
rank: number;
};
// 子路由配置项
children: [
{
// 路由路径
path: string;
// 路由名称(必须唯一并且和当前路由component字段对应的页面里用defineOptions包起来的name保持一致)
name: string;
// 路由重定向
redirect: string;
// 按需加载需要展示的页面
component: RouteComponent;
// 路由元信息
meta: {
// 菜单名称(兼容国际化、非国际化,如果用国际化的写法就必须在根目录的locales文件夹下对应添加)
title: string;
// 菜单图标
icon: string | FunctionalComponent | IconifyIcon;
// 菜单名称右侧的额外图标
extraIcon: string | FunctionalComponent | IconifyIcon;
// 是否显示该菜单
showLink: boolean;
// 是否显示父级菜单
showParent: boolean;
// 页面级别权限设置
roles: Array<string>;
// 按钮级别权限设置
auths: Array<string>;
// 是否缓存该路由页面(开启后,会保存该页面的整体状态,刷新后会清空状态)
keepAlive: boolean;
// 需要内嵌的iframe链接地址
frameSrc: string;
// 内嵌的iframe页面是否开启首次加载动画
frameLoading: boolean;
// 页面加载动画(两种模式,第二种权重更高,第一种直接采用vue内置的transitions动画,第二种是使用animate.css编写进、离场动画,平台更推荐使用第二种模式,已经内置了animate.css,直接写对应的动画名即可)
transition: {
// 当前页面动画,这里是第一种模式,比如 name: "fade" 更具体看后面链接 https://cn.vuejs.org/api/built-in-components.html#transition
name: string;
// 当前页面进场动画,这里是第二种模式,比如 enterTransition: "animate__fadeInLeft"
enterTransition: string;
// 当前页面离场动画,这里是第二种模式,比如 leaveTransition: "animate__fadeOutRight"
leaveTransition: string;
};
// 当前菜单名称或自定义信息禁止添加到标签页
hiddenTag: boolean;
// 显示在标签页的最大数量,需满足后面的条件:不显示在菜单中的路由并且是通过query或params传参模式打开的页面。在完整版全局搜dynamicLevel即可查看代码演示
dynamicLevel: number;
// 将某个菜单激活(主要用于通过query或params传参的路由,当它们通过配置showLink: false后不在菜单中显示,就不会有任何菜单高亮,而通过设置activePath指定激活菜单即可获得高亮,activePath为指定激活菜单的path)
activePath: string;
};
},
],
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# 静态路由
在前端代码里写上固定的路由即静态路由。平台支持任意层级菜单格式,下面列举最常用的菜单层级模式,更深层级按照下面格式即可
阅读前须知
一般来说一级菜单代表无任何子菜单的菜单,二级菜单代表一级菜单下面有子菜单,三级菜单代表二级菜单下面有子菜单 以此类推
# 如何生成一级菜单
点击查看
比如我们要生成一个名为 加油
的菜单
- 新建
src/router/modules/fighting.ts
文件,加入如下代码
fighting.ts
// 最简代码,也就是这些字段必须有
export default {
path: "/fighting",
meta: {
title: "加油"
},
children: [
{
path: "/fighting/index",
name: "Fighting",
component: () => import("@/views/fighting/index.vue"),
meta: {
title: "加油"
}
}
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 新建
src/views/fighting/index.vue
文件,加入如下代码
index.vue
<script setup lang="ts">
defineOptions({
// name 作为一种规范最好必须写上并且和路由的name保持一致
name: "Fighting"
});
</script>
<template>
<h1>加油</h1>
</template>
2
3
4
5
6
7
8
9
10
上面所写代码如下图效果
# 如何生成二级菜单(两种模式)
# 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta
属性中加上 showParent: true
即可)
点击查看
// 最简代码,也就是这些字段必须有
export default {
path: "/fighting",
meta: {
title: "励志"
},
children: [
{
path: "/fighting/index",
name: "Fighting",
component: () => import("@/views/fighting/index.vue"),
meta: {
title: "加油",
// 通过设置showParent为true,显示父级
showParent: true
}
}
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
如下图效果
# 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)
点击查看
// 最简代码,也就是这些字段必须有
export default {
path: "/fighting",
meta: {
title: "励志"
},
children: [
{
path: "/fighting/index",
name: "Fighting",
component: () => import("@/views/fighting/index.vue"),
meta: {
title: "加油"
}
},
{
path: "/fighting/effort",
name: "Effort",
component: () => import("@/views/fighting/effort.vue"),
meta: {
title: "努力"
}
}
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
如下图效果
# 如何生成三级菜单(两种模式)
# 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta
属性中加上 showParent: true
即可)
点击查看
// 最简代码,也就是这些字段必须有
export default {
path: "/nested",
meta: {
title: "多级菜单"
},
children: [
{
path: "/nested/menu1",
meta: {
title: "菜单1"
},
children: [
{
path: "/nested/menu1/menu1-1",
component: () => import("@/views/nested/menu1/menu1-1/index.vue"),
name: "Menu1-1",
meta: {
title: "菜单1-1",
// 通过设置showParent为true,显示父级
showParent: true
}
}
]
}
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
如下图效果
# 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)
点击查看
// 最简代码,也就是这些字段必须有
export default {
path: "/nested",
meta: {
title: "多级菜单"
},
children: [
{
path: "/nested/menu1",
meta: {
title: "菜单1"
},
children: [
{
path: "/nested/menu1/menu1-1",
component: () => import("@/views/nested/menu1/menu1-1/index.vue"),
name: "Menu1-1",
meta: {
title: "菜单1-1"
}
},
{
path: "/nested/menu1/menu1-2",
component: () => import("@/views/nested/menu1/menu1-2/index.vue"),
name: "Menu1-2",
meta: {
title: "菜单1-2"
}
}
]
}
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
如下图效果
# 如何生成四级菜单(两种模式)
# 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta
属性中加上 showParent: true
即可)
点击查看
// 最简代码,也就是这些字段必须有
export default {
path: "/nested",
meta: {
title: "多级菜单"
},
children: [
{
path: "/nested/menu1",
meta: {
title: "菜单1"
},
children: [
{
path: "/nested/menu1/menu1-1",
meta: {
title: "菜单1-1"
},
children: [
{
path: "/nested/menu1/menu1-1/menu1-1-1",
component: () =>
import("@/views/nested/menu1/menu1-1/menu1-1-1/index.vue"),
name: "Menu1-1-1",
meta: {
title: "菜单1-1-1",
// 通过设置showParent为true,显示父级
showParent: true
}
}
]
}
]
}
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
如下图效果
# 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)
点击查看
// 最简代码,也就是这些字段必须有
export default {
path: "/nested",
meta: {
title: "多级菜单"
},
children: [
{
path: "/nested/menu1",
meta: {
title: "菜单1"
},
children: [
{
path: "/nested/menu1/menu1-1",
meta: {
title: "菜单1-1"
},
children: [
{
path: "/nested/menu1/menu1-1/menu1-1-1",
component: () =>
import("@/views/nested/menu1/menu1-1/menu1-1-1/index.vue"),
name: "Menu1-1-1",
meta: {
title: "菜单1-1-1"
}
},
{
path: "/nested/menu1/menu1-1/menu1-1-2",
component: () =>
import("@/views/nested/menu1/menu1-1/menu1-1-2/index.vue"),
name: "Menu1-1-2",
meta: {
title: "菜单1-1-2"
}
}
]
}
]
}
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
如下图效果
# 注意点
# 国际化
只需要在路由 meta
的 title
字段中使用国际化写法即可,比如 title: "menus.hsabnormal"
这个 menus.hsabnormal
必须在 locales
文件夹下进行配置哦,如下图
# 重定向
可以看到上面生成菜单的路由配置项我并没有加上 redirect
属性,当使用 fighting
路径跳转时,就会跑到 404
页面
所以我们最好在写静态路由配置时将父级加上 redirect
属性,redirect
可以填写您想要重定向的 path
即可,具体可以参考 src/router/modules/nested.ts (opens new window)
# 类型提示
分两种
export default
后跟{}
,这也是最常用的一种,在路由的配置项最后加上as RouteConfigsTable
即可获得类型提示,如下图
export default
后跟[]
,在路由的配置项最后加上as Array<RouteConfigsTable>
即可获得类型提示,如下图
您肯定想问 RouteConfigsTable 是哪里来的呢?
# 添加无 layout
的页面
使用场景:需要外嵌平台某个页面,不需要展示菜单导航以及额外模块,只需要展示业务内容模块
无 layout
页面也就相当于一个全屏空白页面,没有菜单导航以及额外模块
如何添加?
在
src/router/modules/remaining.ts
文件添加对应路由 具体参考 (opens new window)添加对应业务代码页面 具体参考 (opens new window)
温馨提示
必须在 src/router/modules/remaining.ts
文件添加哦
# 禁止修改或删除的路由
src/router/modules
文件夹里的 home.ts
、remaining.ts
、error.ts
这三个文件不可修改或删除,因为平台的某些核心逻辑是根据这三个文件作处理
如果不想在页面显示,在顶级路由的 meta
属性加上 showLink: false
即可,如下图
# 如何只要静态路由
平台在登录时默认会走 getAsyncRoutes (opens new window) 接口请求后端返回的动态路由,默认用 mock (opens new window) 模拟返回,如果只要静态路由,按照下面两个步骤修改即可
- 在
src/views/login/index.vue
文件作如下改动
<script setup lang="ts">
import { setToken } from "@/utils/auth";
import { addPathMatch, getTopMenu } from "@/router/utils";
import { usePermissionStoreHook } from "@/store/modules/permission";
const onLogin = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await formEl.validate(valid => {
if (valid) {
loading.value = true;
setToken({
username: "admin",
roles: ["admin"],
accessToken: "eyJhbGciOiJIUzUxMiJ9.admin"
} as any);
// 全部采取静态路由模式
usePermissionStoreHook().handleWholeMenus([]);
addPathMatch();
router.push(getTopMenu(true).path);
message("登录成功", { type: "success" });
loading.value = false;
}
});
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- 在
src/router/index.ts
文件作如下改动
import { addPathMatch } from "./utils";
// 使用下面方法替换initRouter
usePermissionStoreHook().handleWholeMenus([]);
addPathMatch();
if (!useMultiTagsStoreHook().getMultiTagsCache) {
const { path } = to;
const route = findRouteByPath(path, router.options.routes[0].children);
getTopMenu(true);
// query、params模式路由传参数的标签页不在此处处理
if (route && route.meta?.title) {
if (isAllEmpty(route.parentId) && route.meta?.backstage) {
// 此处为动态顶级路由(目录)
const { path, name, meta } = route.children[0];
useMultiTagsStoreHook().handleTags("push", {
path,
name,
meta,
});
} else {
const { path, name, meta } = route;
useMultiTagsStoreHook().handleTags("push", {
path,
name,
meta,
});
}
}
}
// 确保动态路由完全加入路由列表并且不影响静态路由(注意:动态路由刷新时router.beforeEach可能会触发两次,第一次触发动态路由还未完全添加,第二次动态路由才完全添加到路由列表,如果需要在router.beforeEach做一些判断可以在to.name存在的条件下去判断,这样就只会触发一次)
if (isAllEmpty(to.name)) router.push(to.fullPath);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 动态路由
后端返回的路由即动态路由。平台支持任意层级菜单格式,下面列举最常用的菜单层级模式,更深层级按照下面格式即可
平台用 mock
模拟后端,下面举例也是用 mock
举例哦,当然和后端返回的路由格式是一模一样的
阅读前须知
后端传 path
或传 component
,平台对这两种模式都做了兼容 具体实现代码 1 (opens new window) 具体实现代码 2 (opens new window)
第一种
:下面所有生成菜单例子中的 path
都有个规则:如果这个 path
对应的是实际业务 .vue
或 .tsx
代码,就必须跟实际业务 .vue
或 .tsx
代码路径保持一致
第二种
:菜单的 component
对应实际业务 .vue
或 .tsx
代码路径,这时 path
可以随便写啦,但是前面必须有个 /
,比如 path: '/anything'
(这种情况我只在 如何生成一级菜单 里举例,多级菜单写法类似)
点击查看具体实现代码逻辑解析
// 具体实现代码1高亮处解析:
// 将/src/views/文件里所有的.vue和.tsx文件塞进modulesRoutes变量
// 当然这里只匹配了`.vue`和`.tsx`,如果您想匹配别的在下面 {} 加上即可,记得用 , 分开
const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");
// 具体实现代码2高亮处解析:
// addAsyncRoutes函数作用为过滤后端传来的动态路由,重新生成规范路由,
// 先读取后端返回的所有路由,从中过滤出component和path字段,然后在modulesRoutesKeys中对应匹配,
// 如果对应路由同时匹配到component和path,优先以component为主,这样您的path路径就可以随便写啦,
// 如果匹配到path未匹配到component,path必须按上面提到的跟实际业务`.vue`或`.tsx`代码路径保持一致,
// 如果匹配到component未匹配到path或者两个都未匹配到,我想问问您咱能好好玩不😭
function addAsyncRoutes(arrRoutes: Array<RouteRecordRaw>) {
if (!arrRoutes || !arrRoutes.length) return;
const modulesRoutesKeys = Object.keys(modulesRoutes);
arrRoutes.forEach((v: RouteRecordRaw) => {
// 将backstage属性加入meta,标识此路由为后端返回路由
v.meta.backstage = true;
// 父级的redirect属性取值:如果子级存在且父级的redirect属性不存在,默认取第一个子级的path;如果子级存在且父级的redirect属性存在,取存在的redirect属性,会覆盖默认值
if (v?.children && v.children.length && !v.redirect)
v.redirect = v.children[0].path;
// 父级的name属性取值:如果子级存在且父级的name属性不存在,默认取第一个子级的name;如果子级存在且父级的name属性存在,取存在的name属性,会覆盖默认值(注意:测试中发现父级的name不能和子级name重复,如果重复会造成重定向无效(跳转404),所以这里给父级的name起名的时候后面会自动加上`Parent`,避免重复)
if (v?.children && v.children.length && !v.name)
v.name = (v.children[0].name as string) + "Parent";
if (v.meta?.frameSrc) {
v.component = IFrame;
} else {
// 对后端传component组件路径和不传做兼容(如果后端传component组件路径,那么path可以随便写,如果不传,component组件路径会跟path保持一致)
const index = v?.component
? modulesRoutesKeys.findIndex(ev => ev.includes(v.component as any))
: modulesRoutesKeys.findIndex(ev => ev.includes(v.path));
v.component = modulesRoutes[modulesRoutesKeys[index]];
}
if (v?.children && v.children.length) {
addAsyncRoutes(v.children);
}
});
return arrRoutes;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 如何生成一级菜单
# 只传 path
模式
点击查看
比如我们要生成一个名为 加油
的菜单
- 来到
mock/asyncRoutes.ts
文件,加入如下代码
asyncRoutes.ts
// 最简代码,也就是这些字段必须有
const fightingRouter = {
path: "/fighting",
meta: {
title: "加油"
},
children: [
{
path: "/fighting/index",
name: "Fighting",
meta: {
title: "加油"
}
}
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 新建
src/views/fighting/index.vue
文件,加入如下代码
index.vue
<script setup lang="ts">
defineOptions({
// name 作为一种规范最好必须写上并且和路由的name保持一致
name: "Fighting"
});
</script>
<template>
<h1>加油</h1>
</template>
2
3
4
5
6
7
8
9
10
上面所写代码如下图效果
# 传 path
和 component
模式
点击查看
比如我们要生成一个名为 加油
的菜单
- 来到
mock/asyncRoutes.ts
文件,加入如下代码
asyncRoutes.ts
// 最简代码,也就是这些字段必须有
const fightingRouter = {
path: "/fighting",
meta: {
title: "加油"
},
children: [
{
// path随便写,但前面必须有个 `/`
path: "/anything",
// component对应的值前不需要加 / 值对应的是实际业务 `.vue` 或 `.tsx` 代码路径
component: "fighting/index",
name: "Fighting",
meta: {
title: "加油"
}
}
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- 新建
src/views/fighting/index.vue
文件,加入如下代码
index.vue
<script setup lang="ts">
defineOptions({
// name 作为一种规范最好必须写上并且和路由的name保持一致
name: "Fighting"
});
</script>
<template>
<h1>加油</h1>
</template>
2
3
4
5
6
7
8
9
10
上面所写代码如下图效果
猜您可能有这种想法 🤔️
# 如何生成二级菜单(两种模式)
# 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta
属性中加上 showParent: true
即可)
点击查看
// 最简代码,也就是这些字段必须有
const fightingRouter = {
path: "/fighting",
meta: {
title: "励志"
},
children: [
{
path: "/fighting/index",
name: "Fighting",
meta: {
title: "加油",
// 通过设置showParent为true,显示父级
showParent: true
}
}
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
如下图效果
# 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)
点击查看
// 最简代码,也就是这些字段必须有
const fightingRouter = {
path: "/fighting",
meta: {
title: "励志"
},
children: [
{
path: "/fighting/index",
name: "Fighting",
meta: {
title: "加油"
}
},
{
path: "/fighting/effort",
name: "Effort",
meta: {
title: "努力"
}
}
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
如下图效果
# 如何生成三级菜单(两种模式)
# 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta
属性中加上 showParent: true
即可)
点击查看
// 最简代码,也就是这些字段必须有
const nestedRouter = {
path: "/nested",
meta: {
title: "多级菜单"
},
children: [
{
path: "/nested/menu1",
meta: {
title: "菜单1"
},
children: [
{
path: "/nested/menu1/menu1-1/index",
name: "Menu1-1",
meta: {
title: "菜单1-1",
// 通过设置showParent为true,显示父级
showParent: true
}
}
]
}
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
如下图效果
# 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)
点击查看
// 最简代码,也就是这些字段必须有
const nestedRouter = {
path: "/nested",
meta: {
title: "多级菜单"
},
children: [
{
path: "/nested/menu1",
meta: {
title: "菜单1"
},
children: [
{
path: "/nested/menu1/menu1-1/index",
name: "Menu1-1",
meta: {
title: "菜单1-1"
}
},
{
path: "/nested/menu1/menu1-2/index",
name: "Menu1-2",
meta: {
title: "菜单1-2"
}
}
]
}
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
如下图效果
# 如何生成四级菜单(两种模式)
# 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta
属性中加上 showParent: true
即可)
点击查看
// 最简代码,也就是这些字段必须有
const nestedRouter = {
path: "/nested",
meta: {
title: "多级菜单"
},
children: [
{
path: "/nested/menu1",
meta: {
title: "菜单1"
},
children: [
{
path: "/nested/menu1/menu1-1",
meta: {
title: "菜单1-1"
},
children: [
{
path: "/nested/menu1/menu1-1/menu1-1-1/index",
name: "Menu1-1-1",
meta: {
title: "菜单1-1-1",
// 通过设置showParent为true,显示父级
showParent: true
}
}
]
}
]
}
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
如下图效果
# 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)
点击查看
// 最简代码,也就是这些字段必须有
const nestedRouter = {
path: "/nested",
meta: {
title: "多级菜单"
},
children: [
{
path: "/nested/menu1",
meta: {
title: "菜单1"
},
children: [
{
path: "/nested/menu1/menu1-1",
meta: {
title: "菜单1-1"
},
children: [
{
path: "/nested/menu1/menu1-1/menu1-1-1/index",
name: "Menu1-1-1",
meta: {
title: "菜单1-1-1"
}
},
{
path: "/nested/menu1/menu1-1/menu1-1-2/index",
name: "Menu1-1-2",
meta: {
title: "菜单1-1-2"
}
}
]
}
]
}
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
如下图效果
# 注意点
# 动态路由缓存本地
平台在 v3.9.7
版本 默认关闭 CachingAsyncRoutes 动态路由缓存本地 (opens new window) 使其在开发环境下调试更方便,不用每次修改动态路由都要先清空本地缓存的动态路由,更推荐在生产环境开启
开启动态路由缓存本地后会优先读取本地缓存的路由 点击查看具体实现代码 (opens new window) 如果您在开发环境下开启,每次修改动态路由都需要先清空 async-routes
这个键名的 sessionStorage
点击查看参考代码 (opens new window)
# 国际化
只需要在路由 meta
的 title
字段中使用国际化写法即可,比如 title: "menus.hssysManagement"
这个 menus.hssysManagement
必须在 locales
文件夹下进行配置哦,如下图
# 重定向
动态路由无需填写 redirect
重定向属性
平台自动处理,逻辑为:如果子级存在且父级的 redirect
属性不存在,默认取第一个子级的 path
作为父级的 redirect
当然您也可以指定父级的 redirect
重定向属性,平台监测到存在指定父级的 redirect
后,会取指定的属性
# 重置路由
平台的重置路由功能只针对动态路由
# 常用场景
用户切换角色或退出登录时需调用重置路由功能,将路由和菜单恢复到初始化状态
# 基础用法
import { resetRouter } from "@/router";
resetRouter();
2
3
# 具体实现代码
# 静态路由、动态路由相同的配置
下面以动态路由返回举例,静态路由也是相同的配置,区别是静态路由需写在 src/router/modules
里
# 内嵌 iframe
和外链
点击查看
读重点:内嵌 iframe
将地址写进 frameSrc
属性里;外链将地址写进 name
属性里
具体代码实现:内嵌 iframe
(opens new window) 外链 (opens new window)
// 最简代码,也就是这些字段必须有
const frameRouter = {
path: "/iframe",
meta: {
title: "内嵌 iframe 和外链"
},
children: [
// 内嵌 iframe
{
path: "/iframe/pure",
// name必须写,写法随意
name: "anything",
meta: {
title: "内嵌 iframe",
// 需要内嵌页面的地址
frameSrc: "https://pure-admin.cn",
// 内嵌的iframe页面是否开启首次加载动画,默认true,下面可以不写,如果内嵌的iframe页面已经存在首加载动画,可以将frameLoading设为false
// frameLoading: true
}
},
// 外链
{
// path必须写,必须以 / 开头,推荐格式 / 后跟随意单词,不同的外链path不要重复哦
path: "/anything",
// 外链地址写在name属性里
name: "https://pure-admin.cn",
meta: {
title: " 外链"
}
}
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
如下图效果
# 菜单排序 rank
在路由的 meta
里加入 rank
字段即可对菜单进行排序,rank
可不写、可为 null
、可为 undefined
、可为空值,但是平台推荐 rank
最好加上,对菜单进行有序排列使其看起来整齐,而且还能通过排序把您想要第一眼看到的菜单放到最前面
温馨提示
菜单排序 rank
只对顶级菜单生效 点击查看为何如此设计 (opens new window)
# 菜单图标 icon
# 路由页面缓存 keepAlive
在路由的 meta
里加入 keepAlive: true
即可对当前路由页面进行缓存,被缓存的路由页面不会被销毁
生效前提:对应页面 name
必须与路由的 name
保持一致
虽然 vue-router
官方只支持到二级 keep-alive
缓存,但平台对路由进行了 处理 (opens new window),只要对当前路由设置了 keepAlive: true
,并且对应的页面 name
与路由的 name
保持一致,不管层级多深都会支持 keep-alive
缓存
# 页面进场、离场动画定制化 transition
在路由的 meta
里加入下面代码即可定制化单个页面进场、离场动画,平台内置了 Animate.css
,所以您直接在 Animate.css (opens new window) 最右侧复制所需动画即可
transition: {
// 页面进场动画
enterTransition: "animate__fadeInRight",
// 页面离场动画
leaveTransition: "animate__fadeOutDown"
}
2
3
4
5
6
# 页面和按钮级别权限设置
平台内置 RBAC
权限模式,无论静态还是动态路由,配置都会生效,点击查看具体 RBAC 权限讲解
# 常见疑问
# 为什么路由的 name
必写,而且必须唯一
- 没有硬编码的
URL
;params
的自动编码/解码;防止在url
中出现打字错误;绕过路径排序(如显示一个) 点击了解具体 (opens new window) - 虽然跳转路由有很多方式,但平台只推荐使用
name
跳转,如下面写法。优点:一是写法简单,尤其当path
稍长时优点更突出,因为name
只有一个单词;二是规范统一,都用name
跳转也方便维护。尤其当项目过大时对整体维护起关键作用
<script setup lang="ts">
import { useRouter } from "vue-router";
const router = useRouter();
router.push({ name: 'Fighting' })
</script>
2
3
4
5
6
- 平台的某些关键模块(路由模块、标签模块等)都依赖
name
,所以独一无二的name
对平台的稳定性起关键作用
# 为什么路由的 name
需和页面的 name
保持一致?
正常情况下这并不是硬性要求,而是一种规范,方便维护查找,通过路由 name
全局搜索即可知道对应页面代码位置,通过页面对应的 name
也可知道其对应哪个路由
当然,如果当前路由页面需要 路由页面缓存 keepAlive 就必须保持一致
# 更多路由跳转示例代码
效果如下