4-13:业务落地:解析路由表,获取结构化数据
本小节的目标只有一点,那就是获取到之前明确的结构化数据:
[
{
"title": "个人中心",
"path": ""
},
{
"title": "用户",
"children": [
{
"title": "员工管理",
"path": ""
},
{
"title": "角色列表",
"path": ""
},
{
"title": "权限列表",
"path": ""
}
]
},
{
"title": "文章",
"children": [
{
"title": "文章排名",
"path": ""
},
{
"title": "创建文章",
"path": ""
}
]
}
]
那么想要完成本小节的目标,我们就需要先来看一下,现在的路由表结构是什么样子的。
想要获取路由表数据,那么有两种方式:
- router.options.routes:初始路由列表(新增的路由 无法获取到)
- router.getRoutes():获取所有 路由记录 的完整列表
所以,我们此时使用 router.getRoutes()
在 layout/components/Sidebar/SidebarMenu 下写入以下代码:
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
console.log(router.getRoutes())
</script>
得到返回的数据:
[
{
"path":"/user/info/:id",
"name":"userInfo",
"meta":{
"title":"userInfo"
},
"children":[
]
},
{
"path":"/article/editor/:id",
"meta":{
"title":"articleEditor"
},
"children":[
]
},
{
"path":"/user/manage",
"meta":{
"title":"userManage",
"icon":"personnel-manage"
},
"children":[
]
},
{
"path":"/user/role",
"meta":{
"title":"roleList",
"icon":"role"
},
"children":[
]
},
{
"path":"/user/permission",
"meta":{
"title":"permissionList",
"icon":"permission"
},
"children":[
]
},
{
"path":"/user/import",
"name":"import",
"meta":{
"title":"excelImport"
},
"children":[
]
},
{
"path":"/article/ranking",
"meta":{
"title":"articleRanking",
"icon":"article-ranking"
},
"children":[
]
},
{
"path":"/article/create",
"meta":{
"title":"articleCreate",
"icon":"article-create"
},
"children":[
]
},
{
"path":"/article/:id",
"meta":{
"title":"articleDetail"
},
"children":[
]
},
{
"path":"/login",
"meta":{
},
"children":[
]
},
{
"path":"/profile",
"name":"profile",
"meta":{
"title":"profile",
"icon":"el-icon-user"
},
"children":[
]
},
{
"path":"/404",
"name":"404",
"meta":{
},
"children":[
]
},
{
"path":"/401",
"name":"401",
"meta":{
},
"children":[
]
},
{
"path":"/",
"redirect":"/profile",
"meta":{
},
"children":[
{
"path":"/profile",
"name":"profile",
"meta":{
"title":"profile",
"icon":"el-icon-user"
}
},
{
"path":"/404",
"name":"404"
},
{
"path":"/401",
"name":"401"
}
]
},
{
"path":"/user",
"redirect":"/user/manage",
"meta":{
"title":"user",
"icon":"personnel"
},
"children":[
{
"path":"/user/manage",
"meta":{
"title":"userManage",
"icon":"personnel-manage"
}
},
{
"path":"/user/role",
"meta":{
"title":"roleList",
"icon":"role"
}
},
{
"path":"/user/permission",
"meta":{
"title":"permissionList",
"icon":"permission"
}
},
{
"path":"/user/info/:id",
"name":"userInfo",
"meta":{
"title":"userInfo"
}
},
{
"path":"/user/import",
"name":"import",
"meta":{
"title":"excelImport"
}
}
]
},
{
"path":"/article",
"redirect":"/article/ranking",
"meta":{
"title":"article",
"icon":"article"
},
"children":[
{
"path":"/article/ranking",
"meta":{
"title":"articleRanking",
"icon":"article-ranking"
}
},
{
"path":"/article/:id",
"meta":{
"title":"articleDetail"
}
},
{
"path":"/article/create",
"meta":{
"title":"articleCreate",
"icon":"article-create"
}
},
{
"path":"/article/editor/:id",
"meta":{
"title":"articleEditor"
}
}
]
}
]
从返回的数据来看,它与我们想要的数据结构相去甚远。
出现这个问题的原因,是因为它返回的是一个 完整的路由表
这个路由表距离我们想要的存在两个问题:
- 存在重复的路由数据
- 不满足该条件
meta && meta.title && meta.icon的数据不应该存在
那么接下来我们就应该来处理这两个问题
创建 utils/route 文件,创建两个方法分别处理对应的两个问题:
filterRoutersgenerateMenus
写入以下代码:
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
}
/**
* 处理脱离层级的路由:某个一级路由为其他子路由,则剔除该一级路由,保留路由层级
* @param {*} routes router.getRoutes()
*/
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)
// 路由分离之后,存在同名父路由的情况,需要单独处理
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
}
在 SidebarMenu 中调用该方法
<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(JSON.stringify(routes.value))
</script>
得到该数据结构
[
{
"path":"/profile",
"name":"profile",
"meta":{
"title":"profile",
"icon":"el-icon-user"
},
},
{
"path":"/user",
"redirect":"/user/manage",
"meta":{
"title":"user",
"icon":"personnel"
},
"props":{
"default":false
},
"children":[
{
"path":"/user/manage",
"name":"userManage",
"meta":{
"title":"userManage",
"icon":"personnel-manage"
},
"children":[
]
},
{
"path":"/user/role",
"name":"userRole",
"meta":{
"title":"roleList",
"icon":"role"
},
"children":[
]
},
{
"path":"/user/permission",
"name":"userPermission",
"meta":{
"title":"permissionList",
"icon":"permission"
},
"children":[
]
}
],
},
{
"path":"/article",
"redirect":"/article/ranking",
"meta":{
"title":"article",
"icon":"article"
},
"props":{
"default":false
},
"children":[
{
"path":"/article/ranking",
"name":"articleRanking",
"meta":{
"title":"articleRanking",
"icon":"article-ranking"
},
"children":[
]
},
{
"path":"/article/create",
"name":"articleCreate",
"meta":{
"title":"articleCreate",
"icon":"article-create"
},
"children":[
]
}
],
}
]