vue中antd-pro和动态路由的结合使用

什么是ant-design-pro?

  • 具体教程自查
  • 简单来说就是一个系统的、条理清晰的、普适的管理平台网站搭建框架
  • 除了自适应的基本UI框架,andt-pro还清晰地划分了网站的功能模块,包括:api、vuex、router、assets等以及登录注册页、404页、个人页和基本功能页。减少了不少项目开发量,这样的模块分类也有助于培养开发思维
  • antd-pro的UI框架中有页面菜单栏,可通过这里的菜单进行页面路由管理,而这些菜单选项的路由绑定,就是通过动态路由实现的

什么是动态路由?

  • 动态路由概念
  • 分析下:当一个网站的页面路由是根据用户等级来分配,即不同级别或不同类型的用户登录成功后看到的路由菜单不一样,所以能够访问的页面也不一样时,就应该用动态路由来处理各个级别用户的路由权限。而相对的,我们常规的在router里写死路由配置就是静态路由,这部分路由是每种用户都能访问的,可能你并没有给用户提供直接访问这部分静态路由页面的入口,但是如果用户够聪明找到了你的路由路径,就可以直接通过url访问。
  • 在使用动态路由规划的时候,可将路由分为constantRouter(静态路由)和dynamicRouter(动态路由)两部分,其中constantRouter包括login、404、index等每个用户都能访问的页面,而dynamicRouter则处理根据用户权限来获取的不同路由,并通过router.addRoute()来添加路由

动态路由规划具体分析

  • 首先看到main.js同级目录下的permission.js,这就是andt-pro项目的权限管理文件。
    • 开始先进行路由拦截router.beforeEach((to, from, next) => func()), 即未登录的(无ACCESS_TOKEN)用户只能访问login、register、registerResult等免登录页,如果未登录访问需要权限的页面,则在这里重定向到login页。
    • 然后便可根据登录成功后后台返回的menu-list来添加动态路由。store.dispatch('GenerateRoutes', { roles }) 可见这里调用了store的GenerateRoutes异步action来生成路由,而这里的roles就是登录后拿到的menu-list,生成路由完成后,通过router.addRoute(addRouters[i])逐条添加。
// 路由拦截示例代码
router.beforeEach((to, from, next) => {
  NProgress.start() // start progress bar
  to.meta && typeof to.meta.title !== 'undefined' && setDocumentTitle(`${to.meta.title} - ${domTitle}`)
  if (to.path === loginRoutePath) {
    storage.set(ACCESS_TOKEN, '')
  }
  if (storage.get(ACCESS_TOKEN)) {
    if (to.path === loginRoutePath) { // 有token证明已登录,访问login页面可直接跳首页
      next({ path: defaultRoutePath })
      NProgress.done()
    } else {
      const roles = store.state.menuList // vuex中存的路由菜单
      // 调用vuex中的action将后台返回的menuList处理成路由对象,然后router.addRoute进行添加
      store.dispatch('GenerateRoutes', { roles }).then(() => {
        const addRouters = store.getters.addRouters
        for (let i = 0; i < addRouters.length; i++) {
          router.addRoute(addRouters[i])
        }
        const redirect = decodeURIComponent(from.query.redirect || to.path)
        if (to.path === redirect) {
          next({ ...to, replace: true })
        } else {
          // 跳转到目的路由
          next({ path: redirect })
        }
      })
    }
  } else {
    if (allowList.includes(to.name)) {
      // 在免登录名单,直接进入
      next()
    } else {
      next({ path: loginRoutePath, query: { redirect: to.fullPath } })
      NProgress.done()
    }
  }
})
  • 然后我们跳到状态管理的store/modules/async-router.js来查看GenerateRoutes函数,可见整个routers是由generatorDynamicRouter(data)处理返回的router与constantRouter拼接而成。再跳到router/generator-routers.js查看generatorDynamicRouter函数,是根据传入的json数据来配置路由,主要是引入组件,控制菜单隐藏及处理添加重定向。(关于控制菜单隐藏,提出来说下:就是有些子页面路由时不想让它出现在菜单里的,是通过菜单目录里的页面传参之后才能跳转的子页面,一开始用antd-pro的时候,不知道怎么控制子菜单隐藏,所以就在constantRouterMap里添加了一些额外路由,这样写就违背了动态路由用户分级的初衷。复盘才知道只要在配置的json里加一个show字段为false就能隐藏了)
// 根据json数据生成路由表代码示例
/**
 * 动态生成菜单
 * @param jsonRouter //从后台拿到的该用户可以访问的路由表json数据
 * @returns {Promise<Router>}
 */
 export const generatorDynamicRouter = (jsonRouter) => {
  return new Promise((resolve, reject) => {
    const routers = generator(jsonRouter.roles)
    resolve(routers)
  })
}

/**
 * 格式化树形结构数据 生成 vue-router 层级路由表
 *
 * @param routerMap
 * @param parent
 * @returns {*}
 */
export const generator = (routerMap, parent) => {
  return routerMap.map(item => {
    const { title, show, hideChildren, hiddenHeaderContent, target, icon } = item.meta || {}
    const currentRouter = {
      // 如果路由设置了 path,则作为默认 path,否则 路由地址 动态拼接生成如 /dashboard/workplace
      path: item.path || `${(parent && parent.path) || ''}/${item.key}`,
      // 路由名称,建议唯一
      name: item.name || item.key || '',
      // 该路由对应页面的 组件 :方案1
      // component: constantRouterComponents[item.component || item.key],
      // 该路由对应页面的 组件 :方案2 (动态加载)
      component: constantRouterComponents[item.component || item.key] || (() => import(`@/views/${item.component}`)),

      // meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
      meta: {
        title: title,
        icon: icon || undefined,
        hiddenHeaderContent: hiddenHeaderContent,
        target: target,
        permission: item.name
      }
    }
    if (currentRouter.name === 'index') {
      currentRouter.component = BasicLayout
    }
    // 是否设置了隐藏菜单
    if (show === false) {
      currentRouter.hidden = true
    }
    // 是否设置了隐藏子菜单
    if (hideChildren) {
      currentRouter.hideChildrenInMenu = true
    }
    // 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
    if (!currentRouter.path.startsWith('http')) {
      currentRouter.path = currentRouter.path.replace('//', '/')
    }
    // 重定向
    item.redirect && (currentRouter.redirect = item.redirect)
    // 是否有子菜单,并递归处理
    if (item.children && item.children.length > 0) {
      // Recursion
      currentRouter.children = generator(item.children, currentRouter)
    }
    return currentRouter
  })
}
  • 至此动态路由规划主要部分就基本好了,最后看看后台传的json对象数据的样子
const menu = [{
  path: '/',
  name: 'index',
  component: BasicLayout,
  meta: { title: '首页' },
  redirect: '/path/to/defaultPage',
  children: [{
      path: '/path/to/defaultPage',
      name: 'name1',
      component: 'RouteView',
      redirect: '/path/to/defaultPage',
      meta: { title: '目录名1', icon: 'dashboard' },
      children: [{
        path: '/path/to/sonPage1',
        name: 'sonname1',
        component: 'component1',
        meta: { title: '子目录名1' }
      }]
    }, {
      path: '/path/to/page2',
      name: 'name2',
      component: 'RouteView',
      meta: { title: '目录名2', icon: 'form' },
      children: [{
        path: '/path/to/sonPage2',
        name: 'sonname2',
        component: 'component2',
        meta: { title: '子目录名2', show: false } // 这里这个路径就不会在目录中显示
     }]
    }
  ]
}, {
  path: '*',
  redirect: '/404',
  hidden: true
}]
posted @ 2022-06-13 17:01  Mizuki-Vone  阅读(1925)  评论(0编辑  收藏  举报