路由与菜单
前端框架路由使用的是 Vue Router,主路由配置在 src/router/index.ts
中,在这个文件中我们只配置主框架中的路由,各个模块的路由需要在各自模块中配置。
路由要配置为菜单注意什么?
一定要保证路由的 name
需要与菜单的 dataId
相同,否则将无法正常打开页面。(PS:如果在 meta
中配置了 menuDataId
例外,因为会优先读取 menuDataId
的值。
一个路由要配置为多个菜单该怎么做?
路由 meta 中的 menuDataId
属性可以接受一个 (to: RouteLocationNormalized) => string
类型的函数,这个函数要返回一个字符串,返回的字符串就是菜单的 dataId
。
当 target === TargetEnum.FIRST_TAB
,并且一个路由为多个菜单时,并且开启了保活。那么 keyGenerator
也要配置与 menuDataId
一致,否则会出现无法正确展示页面。
{
path: '/stat/custom-sql/custom-sql-report/:id',
name: 'customSqlReport',
component: () => import('@/module/stat/components/custom/CustomSqlReport.vue'),
meta: {
// 自定义SQL统计
title: '数据中心',
target: TargetEnum.FIRST_TAB,
menuDataId: (to: RouteLocationNormalized) => `customSqlReport${to.params.id}`
keyGenerator: (to: RouteLocationNormalized) => `customSqlReport${to.params.id}`
},
}
上面的代码中是接收 id
来区分不同的菜单,故在 bp_menu
表中的 data_id
的值一定是类似 customSqlReport1
、customSqlReport2
这种格式,其中的 1 2
就是 id
的值。 但是为了可读性和用户体验,这里并不建议全都使用数字,根据实际情况可以选择使用字符串来区分。
模块路由文件规则
路由分为 独立页面路由
和 主页面子路由
之分。
独立页面路由:顾名思义,就是打开会占用整个页面的路由。如:登录页面、CCView 监控页面等;
主页面子路由:顾名思义,就是在主页面内以 tab 页模式打开打开子页面的路由。如:工单页面、配置页面等;
独立页面路由
独立页面路由其实就是模块的主路由,需要放在具体模块目录下,我们以 sso 模块为例:
- 在
src/module/sso/router
目录下创建index.ts
文件 - sso 模块的主路由文件为
src/module/sso/router/index.ts
提示
- 文件名必须为
index.ts
才可识别为主路由(即可以单独打开页面,可以使用_SELF
选项)。 - 并不是所有模块都需要这个路由,因为多数模块都是在系统内打开子页面的形式,只有一些需要全屏显示的页面才需要,如:登陆页面、当前项目的主页、CCView(大屏监控页面)、WebCall 客户端等。
export default [
{
path: '/login',
name: 'loginPage',
component: import('@/module/sso/components/Login.vue'),
},
] as Array<RouteRecordRaw>;
主页面子路由
我们多数模块都是使用这种方式创建路由,因为几乎全部模块都需要在主页面内打开 tab(无论是第一个 tab 还是新的 tab)。 子路由配置文件需要放在具体模块目录下,我们以 ticket 模块为例:
- 在
src/module/ticket/router
目录下创建children-router.ts
- ticket 模块的子路由文件为
src/module/ticket/router/children-router.ts
,这里配置的路由都是在主框架内打开。
提示
- 文件名必须为
children-*.ts
才可识别为主页面的子路由(即可以使用 tab 打页面,禁止使用_SELF
选项。 src/module/ticket/router/children-*.ts
,其中的*
可以使用模块的名称children-router-ticket.ts
,也可以直接起名为children-router.ts
export default [
{
path: '/ticket',
name: 'ticket',
component: () => import('@/module/ticket/components/TicketInfo.vue'),
meta: {},
},
] as Array<RouteRecordRaw>;
命名路由
推荐使用 命名路由 的方式跳转页面,因为有以下有点:
- 没有硬编码的 URL
params
的自动编码/解码。- 防止你在 url 中出现打字错误。
- 绕过路径排序(如显示一个)
路由的定义
path
的定义
子模块我们都加上子模块的前缀,如:ticket/create-ticket
、ticket/info
name
的定义
子模块要尽量添加模块名相关的关键字,防止出现冲突,如:createTicket
、ticketInfo
路由页面保活
在日常使用中,用户会打开多个 tab 页,所以就需要将路由页面进行保活操作。我们使用 Vue 的内置组件 KeepAlive 框架中默认为保活,如果想不保活。只需在路由的 meta
参数中配置上 offKeepAlive: true
即可
export default [
{
path: '/ticket/info',
name: 'ticketInfo',
component: () => import('@/module/ticket/components/TicketInfo.vue'),
meta: {
offKeepAlive: true,
},
},
] as Array<RouteRecordRaw>;
meta 可以选配置参数
提示
如果父路由与子路由均设置了 meta,那么子路由会继承父路由的 meta。
interface RouteMeta {
/**
* 是否保活,不配置则默认保活
*/
offKeepAlive?: boolean;
/**
* 不在系统的 tab 中打开,不配置则打开tab
*/
target: TargetEnum;
/**
* 是否关闭身份验证,不配置则默认需要验证
*/
offAuth?: boolean;
/**
* 打开tab的名称,如果需要打开tab,那么此项必填
*/
title?: string;
/**
* 权限控制,只有有此技能的用户才能访问此链接,如果为null则代表所有人均可用访问
*/
skillCode?: string;
/**
* 打开路由时,根据参数生成的唯一 key,这个值会在路由守卫中添加,请勿手动配置。
*
* ### 用途
* - 用于生成 `keep-alive` 保活的 key,如果 key 相同的话,那么这个路由只会被缓存一个,不会出现多个实例。
* - 新 tab 页的唯一 key
*
* ### 生成规则
* - 当 `target === TargetEnum.NEW_TAB || TargetEnum._SELF`,这个值的默认生成方式为 `${routerName}-${query}-${params}` 的 MD5 值
* - 当 `target === TargetEnum.FIRST_TAB`,这个值的默认生成方式为 `${routerName}` 值
* - 当 `keyGenerator` 不为 null 的时候会默认执行 `keyGenerator` 函数,返回一个唯一的 key
*/
key?: string;
/**
* 生成唯一 key 的函数,当自带的算法无法满足时(比如多层嵌套路由想要保活的时候),可以配置这个函数,返回一个唯一的 key。
* @param to 路由信息
*/
keyGenerator?: (to: RouteLocationNormalized) => string;
/**
* 菜单的 data-id,这个选项是用于默认选中菜单的。 当路由为菜单 且 这个路由要通过不同参数区分不同的菜单时才设置该值,这个值要与菜单表的 data_id 相同。
*/
menuDataId?: string | ((to: RouteLocationNormalized) => string);
}
/**
* 打开类型
*/
export enum TargetEnum {
/**
* 当前页面直接打开,此项配置必须使用在父路由上,禁止在子路由上使用
*/
_SELF,
/**
* 打开新的 tab,此项配置一般用于需要保留多个的详单页面,如来电弹屏、工单详情等页面
*/
NEW_TAB,
/**
* 替换第一个标签,此配置是菜单专用项,请勿用于其他页面
*/
FIRST_TAB,
}
将 props 传递给路由组件
这是 Vue Router 的基础功能,就是利用路由的props
将参数传给组件的props
,下面只介绍函数模式
的使用,因为它可以实现混合传输,更加灵活。如果想了解更多可以移步官方文档查看详情
函数模式
你可以创建一个返回 props 的函数。这允许你将参数转换为其他类型,将静态值与基于路由的值相结合等等。
const routes: RouteRecordRaw[] = [
{
path: '/work-order/work-order-info/:orderCode',
name: 'orderInfo',
component: WorkOrderInfo,
props: route => ({
orderCode: route.params.orderCode,
urgeCoid: route.query.urgeCoid,
}),
},
];
URL /work-order/work-order-info/ZX20180001?urgeCoid=coid20180001
将传递 {orderCode: 'ZX20180001', urgeCoid: 'coid20180001'}
作为 props 传给 WorkOrderInfo
组件。
提示
在组件中创建接收数据的 props
const props = defineProps({
orderCode: String,
urgeCoid: String,
});
菜单
菜单分为两部分,主菜单和子菜单。多数情况下每个主菜单下一定会有子菜单,只有个别模块没有子菜单(如:主页)
菜单数据只有一级或三级,原因如下
- 当只有一级时,主菜单自己便是路由
- 第二级始终为子菜单的分组,不需要配置任何路由地址
- 第三级就是子菜单,必须要配置路由地址或 iframe 地址
菜单国际化
菜单的 dataId
就是多语言的 key,固定格式为 menu.{dataId}
。菜单名称(menuName)我们统一使用中文,当无法在多语音文件中找到翻译时会直接显示菜单名称
主菜单
当在配置主菜单时要遵循以下几种规则:
- 主菜单必须配置一个图标
- 主菜单的中文名称不能超过四个汉字,如果为英文则不能超过 8 个字母,超出限制则无法完全显示。
- 当主菜单拥有子菜单时,那么该主菜单一定没有路由地址。
- 当主菜单没有子菜单时,那么该主菜单一定要有路由地址。
子菜单
当在配置子菜单时要遵循以下几种规则:
- 子菜单不需要配置图标
- 子菜单中文名称不能超过九个汉字,英文不得超过 23 个字母,超出限制会自动换行。
- 子菜单必须要配置路由地址或 iframe 地址
- 子菜单路由中的
meta.title
要设置为主菜单的meta.title
,因为菜单项是不需要打开新标签页的,只会在第一个标签内展示内容。
菜单与路由的关系
- 路由的
name
需要与菜单的dataId
相同 - 所有菜单的路由均要配置为
target: TargetEnum.FIRST_TAB
为仅替换第一个标签页,不打开新的标签(PS:是路由,所以跟 iframe 无关)。
iframe 使用方式与注意事项
- 所有的 iframe 都不需要在程序中配置路由,因为他们会默认使用新标签页的形式打开。
- 在菜单表中的 isIframe 字段配置为 1,url 字段配置完整的请求地址。
在浏览器中打开新标签页
有些情况我们需要通过点击菜单后打开新的浏览器标签页去显示内容,如:大屏监控、第三方系统等。
菜单配置方式
- 只需将菜单表中的 isBlank 字段配置为 1,url 字段配置完整的请求地址。
- 如果为本系统路由页面,那么必须将其配置在父路由中,且需要配置为
target: TargetEnum._SELF