携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情
搭建Layout架构 解决方案与实现
业务落地:生成项目页面组件
根据我们之前的分析,想要完成动态的 menu,那么我们需要按照以下步骤去实现:
1、创建页面组件
2、生成路由表
3、解析路由表
4、生成 menu 菜单
首先我们来看看创建页面组件
创建页面组件
在 views 文件夹下,创建如下页面
1、个人中心:profile
2、角色列表:role-list
3、错误页面:error-page
-
404
-
401
构建路由表
由于将来我们需要进行用户权限处理,所以我们需要对路由表进行一个划分
- 私有路由表
privateRoutes:权限路由 - 公有路由表
publicRoutes:无权限路由
所以我们生成的路由表大致如下
/**
* 私有路由表
*/
const privateRoutes = [
{
path: '/user',
component: layout,
redirect: '/user/role',
meta: {
title: 'user',
icon: 'personnel'
},
children: [
{
path: '/user/role',
component: () => import('@/views/role-list/index'),
meta: {
title: 'roleList',
icon: 'role'
}
}
]
}
]
/**
* 公开路由表
*/
const publicRoutes = [
{
path: '/login',
component: () => import('@/views/login/index')
},
{
path: '/',
component: layout,
redirect: '/profile',
children: [
{
path: '/profile',
name: 'profile',
component: () => import('@/views/profile/index'),
meta: {
title: 'profile',
icon: 'el-icon-user'
}
},
{
path: '/404',
name: '404',
component: () => import('@/views/error-page/404')
},
{
path: '/401',
name: '401',
component: () => import('@/views/error-page/401')
}
]
}
]
const router = createRouter({
history: createWebHashHistory(),
routes: [...publicRoutes, ...privateRoutes]
})
创建完路由表后,需要在 layout/appMain/index.vue 中设置路由出口
<template>
<div class="app-main">
<router-view></router-view>
</div>
</template>
解析路由表
我们想要获取路由表的数据,有如下两种方式
1、router.options.routes:初始路由列表(无法获取新增的路由)
2、router.getRoutes():获取所有路由记录的完整列表
根据业务情况,我们此时应该使用第二个
在 layout/components/Sidebar/SiderbarMenu.vue 中填写如下代码
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
console.log(router.getRoutes())
</script>
我们打印出来可以发现获得的数据与我们想要的还是有很大差别的,因为其返回的是一个完整的路由表,仔细观察我们会发现它存在两个问题
1、存在重复的路由数据
2、不满足 meta && meta.title && meta.icon 的数据不应该存在
现在我们就来解决这两个问题
创建 utils/route 文件,创建两个方法处理对应的两个问题
1、filterRouters
2、generateMenus
import path from 'path'
/**
* 返回所有子路由
*/
const getChildrenRoutes = routes => {
const result = []
routes.forEach(route => {
if (route.children && route.children.length > 0) {
result.push(...route.children)
}
})
return result
}
/**
* 处理层级错误的路由,比如某个路由为字路由但其却出现在一级路由的位置上,这个就应该删除,保留正确层级的路由
*/
export const filterRouters = routes => {
const childrenRoutes = getChildrenRoutes(routes)
return routes.filter(route => {
return !childrenRoutes.find(childrenRoute => {
return childrenRoute.path === route.path
})
})
}
/**
* 判断数据是否为空值
*/
function isNull(data) {
if (!data) return true
if (JSON.stringify(data) === '{}') return true
if (JSON.stringify(data) === '[]') return true
return false
}
/**
* 根据 routes 数据,返回对应 menu 规则数组
*/
export function generateMenus(routes, basePath = '') {
const result = []
// 遍历路由表
routes.forEach(item => {
// 不存在 children && 不存在 meta 直接 return
if (isNull(item.meta) && isNull(item.children)) return
// 存在 children 不存在 meta,进入迭代
if (isNull(item.meta) && !isNull(item.children)) {
result.push(...generateMenus(item.children))
return
}
// 合并 path 作为跳转路径
const routePath = path.resolve(basePath, item.path)
// 如果由于疏忽多写了一个同 path 的路由则单独处理
let route = result.find(item => item.path === routePath)
if (!route) {
route = {
...item,
path: routePath,
children: []
}
// icon 与 title 必须全部存在
if (route.meta.icon && route.meta.title) {
// meta 存在生成 route 对象,放入 arr
result.push(route)
}
}
// 存在 children 进入迭代到children
if (item.children) {
route.children.push(...generateMenus(item.children, route.path))
}
})
return result
}
在 SiderbarMenu 中调用此方法
<script setup>
import { computed } from 'vue'
import { useRouter } from 'vue-router'
import { filterRouters, generateMenus } from '@/utils/route'
const router = useRouter()
const routes = computed(() => {
const filterRoutes = filterRouters(router.getRoutes())
return generateMenus(filterRoutes)
})
console.log(routes.value)
</script>
可以看到此时的数据结构已经基本符合我们的预期了