vue3 + Element-plus 开发后台管理系统(17)

403 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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>

可以看到此时的数据结构已经基本符合我们的预期了