Skip to content

菜单配置

温馨提示

菜单控制支持2种方式:

菜单格式

在项目 /@/router/route.ts 文件中。这里需要注意,菜单数据内容必须嵌套进顶级节点(作为顶级路由出口)的 children 字段里

ts
{
  // 顶级菜单路径
  path: '/',
  // 顶级菜单 name
  name: '/',
  // 顶级路由出口
  component: () => import('/@/layout/index.vue'),
  // 顶级菜单重定向路径
  redirect: '/home',
  // 顶级附加自定义数据
  meta: {
    // 顶级菜单是否缓存
    isKeepAlive: true,
  },
  // 顶级菜单的子级菜单数据
  children: [
    // 新增的菜单对象写在这里
    {
      ...
    }
  ]
}

附加自定义数据参数说明

代码位置:/@/router/route.ts

菜单路由中的字段说明

ts
{
  // 菜单路径,用于跳转
  path: '/home',
  // 菜单 name,用于界面 keep-alive 路由缓存。
  // 此 name 需要与 component 组件中的 name 值相同(唯一)
  name: 'home',
  // 组件路径
  component: () => import('/@/views/home/index.vue'),
  // 附加自定义数据
  meta: {
    // 菜单标题(国际化写法)
    title: 'message.router.home',
    // 菜单外链链接
    // 开启外链条件,`1、isLink: true 2、链接地址不为空(meta.isLink) 3、isIframe: false`
    isLink: '',
    // 菜单是否隐藏(菜单不显示在界面,但可以进行跳转)
    isHide: false,
    // 菜单是否缓存
    isKeepAlive: true,
    // 菜单是否固定(固定在 tagsView 中,不可进行关闭),右键菜单无 `关闭` 项
    isAffix: true,
    // 是否内嵌
    // 开启条件,`1、isIframe: true 2、链接地址不为空(meta.isLink)`
    isIframe: false,
    // 当前路由权限标识,取角色管理。控制路由显示、隐藏。超级管理员:admin 普通角色:common
    // 之前 auth 取用户(角色下有多个用户)
    roles: ['admin', 'common'],
    // 菜单图标
    icon: 'iconfont icon-shouye',
    // 自行再添加
    ...
  },
}

路径格式

1. 面包屑多级显示

在项目 /@/router/route.ts 文件中,观察 path 字段,有 children 时,path 字段是基于上一级继续拼接。

如下所示:/params/xxx,这样做是为了 breadcrumb-面包屑 的显示问题。

ts
{
  path: '/params',
  redirect: '/params/common',
  ...,
  children: [
    {
      // 面包屑:首页 / 路由参数 / 普通路由
      path: '/params/common',
      ...,
    },
    {
      // 面包屑:首页 / 路由参数 / 普通路由 / 普通路由详情
      path: '/params/common/details',
      ...
    },
  ]
}

2. 面包屑单级显示

children 里的 path 不基于上级 path,注意高亮部分代码的 path

ts
{
  path: '/params',
  redirect: '/params/common',
  ...,
  children: [
    {
      // 面包屑:首页 / 路由参数 / 普通路由
      path: '/params/common',
      ...,
    },
    {
      // 面包屑:首页 / 路由参数 / 普通路由详情
      path: '/params/details',
      ...
    },
    {
      // 面包屑:首页
      path: '/params1/common1/details',
      ...
    },
  ]
}

路由的缓存

路由菜单中的 name 需与组件的 name 相同且 唯一,还有 meta.isKeepAlive 设为 true

1. 页面

  • index.vue,注意 name 值需与 /@/router/route.ts 中的 name 值一致,否则实现不了路由的缓存(keep-alive)
html
<template>
  <div class="personal">
    personal
    ...
  </div>
</template>

<script setup lang="ts" name="personal"> 
import { reactive, toRefs } from 'vue';

// 定义变量内容
const state = reactive({});

<style scoped lang="scss">
.personal {}
</style>

2. 路由代码

在项目 /@/router/route.ts 文件中,需写在 children 字段里(因为布局分为,左菜单,右内容,顶部,底部,侧部分才是我们切换的页面路由),比如我们上面添加个人中心界面,新增如下代码:

ts
{
  path: '/',
  name: '/',
  component: () => import('/@/layout/index.vue'),
  redirect: '/home',
  meta: {
    isKeepAlive: true,
  },
  children: [
    // 新增的菜单对象写在这里
    {
      path: '/personal',
      name: 'personal', 
      component: () => import('/@/views/personal/index.vue'),
      meta: {
        title: 'message.router.personal',
        isLink: '',
        isHide: false,
        isKeepAlive: true,
        isAffix: false,
        isIframe: false,
        roles: ['admin', 'common'],
        icon: 'iconfont icon-gerenzhongxin',
      },
    },
  ]
}

二级菜单

二级菜单与一级菜单的区别:(只要子级里有 children

  • 1、redirect:顶级设置重定向

  • 2、component:顶级为 component: () => import('/@/layout/routerView/parent.vue') 写死路径。component: () => import('/@/layout/routerView/parent.vue') 为路由出口

1. 新建文件夹(同一级菜单)

  • 我们按照建 一级菜单 的步骤建 二级菜单/@/views 下新增 system 文件夹。system 文件夹下新增 menu、user 等文件夹

  • 为了方便管理,我们在 /@/views/system/menu/@/views/system/user 中都添加 index.vue 组件

html
<template>
  <div class="system-menu-container">
    systemMenu
    ...
  </div>
</template>

<script setup lang="ts" name="systemMenu">
import { reactive } from 'vue';

// 定义变量内容
const state = reactive({});

<style scoped lang="scss">
.system-menu-container {}
</style>

路由数据模式

  • 创建路由函数 router/index.ts

创建是加载一些默认的路由

ts
export const router = createRouter({
	history: createWebHashHistory(),
	/**
	 * 说明:
	 * 1、notFoundAndNoPower 默认添加 404、401 界面,防止一直提示 No match found for location with path 'xxx'
	 * 2、backEnd.ts(后端控制路由)、frontEnd.ts(前端控制路由) 中也需要加 notFoundAndNoPower 404、401 界面。
	 *    防止 404、401 不在 layout 布局中,不设置的话,404、401 界面将全屏显示
	 */
	routes: [
		//错误页面路由
		...notFoundAndNoPower,
		//静态固定路由(login) 
		...staticRoutes
	],
});
  • 路由加载前 router/index.ts
  1. NProgress 第三方进度条库
  2. 当进入login页面,必须是未登录失效,其它页面进入,必须是已登录状态,当token失效进入登录页
  3. 已登录时进入login 自动进入控制台页面,其它页面则进行动态在pinia中获取路由
ts
import { useRoutesList } from '/@/stores/routesList';
//路由缓存列表
const storesRoutesList = useRoutesList(pinia);
  1. 当缓存数据为空时,进行 前端控制/后端控制 的方式加载数据,读取 /src/stores/themeConfig.ts 是否开启后端控制路由配置。
ts
//store主题配置缓存
const storesThemeConfig = useThemeConfig(pinia);
//themeConfig对象
const { themeConfig } = storeToRefs(storesThemeConfig);
//themeConfig-isRequestRoutes 属性
const { isRequestRoutes } = themeConfig.value;

前端模式

isRequestRoutes:false

后端模式

isRequestRoutes:true

ts
router.beforeEach(async (to, from, next) => {
	NProgress.configure({ showSpinner: false });
	if (to.meta.title) NProgress.start();
	const token = Session.get('token');
	if (to.path === '/login' && !token) {
		next();
		NProgress.done();
	} else {
		if (!token) {
			next(`/login?redirect=${to.path}&params=${JSON.stringify(to.query ? to.query : to.params)}`);
			Session.clear();
			NProgress.done();
		} else if (token && to.path === '/login') {
			next('/home');
			NProgress.done();
		} else {
			const storesRoutesList = useRoutesList(pinia);
            //将状态存储在 store 中转换为 ref 对象的辅助函数(保持响应式更新)
			const { routesList } = storeToRefs(storesRoutesList);
			if (routesList.value.length === 0) {
				if (isRequestRoutes) {
					// 后端控制路由:路由数据初始化,防止刷新时丢失
					await initBackEndControlRoutes();
					// 解决刷新时,一直跳 404 页面问题,关联问题 No match found for location with path 'xxx'
					// to.query 防止页面刷新时,普通路由带参数时,参数丢失。动态路由(xxx/:id/:name")isDynamic 无需处理
					next({ path: to.path, query: to.query });
				} else {
                    // 前端控制路由
					await initFrontEndControlRoutes();
					next({ path: to.path, query: to.query });
				}
			} else {
				next();
			}
		}
	}
});

Released under the MIT License.