vue-router 基于后端permissions动态生成导航菜单的示例代码

 更新时间:2021年9月30日 16:00  点击:2547

Vue.js

  • vue-router
  • vuex

1、注册全局守卫

核心逻辑
1、token身份验证(后端) => token失效返回登录页面
2、获取用户权限
3、校验permissions,动态添加路由菜单

router.beforeResolve 注册一个全局守卫。和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。

router.beforeResolve(async (to, from, next) => {
  let hasToken = store.getters['User/accessToken']
  if (!settings.loginInterception) hasToken = true
  if (hasToken) {
    if (to.path === '/auth/sign-in') {
      next({ path: '/' })
    } else {
      const hasPermissions =
        store.getters['User/permissions'] &&
        store.getters['User/permissions'].length > 0
      if (hasPermissions) {
        next()
      } else {
        try {
          let permissions
          if (!constant.loginInterception) {
            // settings.js loginInterception为false时,创建虚拟权限
            await store.dispatch('User/setPermissions', ['admin'])
            permissions = ['admin']
          } else {
            permissions = await store.dispatch('User/getUserInfo')
          }
          let accessRoutes = []
          accessRoutes = await store.dispatch('Routes/setRoutes', permissions)
          // 添加路由
          router.addRoutes(accessRoutes)
          next({ ...to, replace: true })
        } catch {
          await store.dispatch('User/resetAccessToken')
        }
      }
    }
  } else {
    if (settings.routesWhiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      next('/auth/sign-in')
    }
  }
  document.title = getPageTitle(to.meta.title)
})

settings.js 全局设置

export default {
  // 是否开启登录拦截
  loginInterception: true,
  // 不经过token校验的路由
  routesWhiteList: ['/auth/sign-in', '/auth/register', '/401', '/404'],
}

2、Vuex状态管理 全局缓存routes

  • state :对数据的全局存储
  • getter: 可以理解为computed ,对数据进行计算
  • mutations :对数据的同步更改
  • actions:对数据的异步更改(实现异步操作)
  • module: 将 store 分割成模块

/**
 * @author Alan
 * @description 路由拦截状态管理
 */
import { asyncRoutes, constantRoutes } from '@/router'
import { filterAsyncRoutes } from '@/Utils/handleRoutes'

const state = () => ({
  routes: [],
  partialRoutes: []
})
const getters = {
  routes: (state) => state.routes,
  partialRoutes: (state) => state.partialRoutes
}
const mutations = {
  setRoutes (state, routes) {
    state.routes = constantRoutes.concat(routes)
  },

  setPartialRoutes (state, routes) {
    state.partialRoutes = constantRoutes.concat(routes)
  }
}
const actions = {
  async setRoutes ({ commit }, permissions) {
    const finallyAsyncRoutes = await filterAsyncRoutes(
      [...asyncRoutes],
      permissions
    )
    commit('setRoutes', finallyAsyncRoutes)
    return finallyAsyncRoutes
  },
  setPartialRoutes ({ commit }, accessRoutes) {
    commit('setPartialRoutes', accessRoutes)
    return accessRoutes
  }
}
export default { namespaced: true, state, getters, mutations, actions }

3、路由拦截

/**
 * @author Alan
 * @description 判断当前路由是否包含权限
 * @param permissions
 * @param route
 * @returns {boolean|*}
 */
export function hasPermission (permissions, route) {
  if (route.meta && route.meta.permissions) {
    return permissions.some((role) => route.meta.permissions.includes(role))
  } else {
    return true
  }
}

/**
 * @author Alan
 * @description 根据permissions数组拦截路由
 * @param routes
 * @param permissions
 * @returns {[]}
 */
export function filterAsyncRoutes (routes, permissions) {
  const finallyRoutes = []
  routes.forEach((route) => {
    const item = { ...route }
    if (hasPermission(permissions, item)) {
      if (item.children) {
        item.children = filterAsyncRoutes(item.children, permissions)
      }
      finallyRoutes.push(item)
    }
  })
  return finallyRoutes
}

4、路由菜单

/*
* @author Alan
* @description 公共路由
*/
export const constantRoutes = [
  {
    path: '/auth',
    name: 'auth1',
    component: AuthLayout,
    children: authChildRoutes('auth1'),
    hidden: true // 隐藏菜单
  },
  {
    path: '/',
    name: 'dashboard',
    component: VerticleLayout,
    meta: {
      title: 'Dashboard',
      name: 'sidebar.dashboard',
      is_heading: false,
      is_active: false,
      link: '',
      class_name: '',
      is_icon_class: true,
      icon: 'ri-home-4-line',
      permissions: ['admin']
    },
    children: childRoutes('dashboard')
  }
]

/*
* @author Alan
* @description 异步路由
*/
export const asyncRoutes = [
  {
    path: '/menu-design',
    name: 'horizontal-dashboard',
    component: HorizantalLayout,
    meta: {
      title: 'Menu Design',
      name: 'sidebar.MenuDesign',
      is_heading: false,
      is_active: false,
      link: '',
      class_name: '',
      is_icon_class: true,
      icon: 'ri-menu-3-line',
      permissions: ['admin']
    },
    children: horizontalRoute('dashboard')
  }, {
    path: '/core',
    name: 'core',
    component: VerticleLayout,
    meta: {
      title: 'UI Elements',
      name: 'sidebar.uiElements',
      is_heading: false,
      is_active: false,
      class_name: '',
      link: '',
      is_icon_class: true,
      icon: 'ri-pencil-ruler-line',
      permissions: ['admin']
    },
    children: coreChildRoute('core')
  }
]

5、递归菜单vue组件

<template>
  <b-collapse tag="ul" :class="className" :visible="open" :id="idName" :accordion="accordianName">
    <li v-for="(item,index) in items" :key="index" :class=" !hideListMenuTitle? 'p-0' : item.meta.is_heading ? 'iq-menu-title' :activeLink(item) && item.children ? 'active' : activeLink(item) ? 'active' : ''">
      <template v-if="!item.hidden">
        <i v-if="item.meta.is_heading && hideListMenuTitle" class="ri-subtract-line" />
        <span v-if="item.meta.is_heading && hideListMenuTitle">{{ $t(item.meta.name) }}</span>
        <router-link :to="item.meta.link" v-if="!item.is_heading" :class="`iq-waves-effect ${activeLink(item) && item.children ? 'active' : activeLink(item) ? 'active' : ''}`" v-b-toggle="item.meta.name">
          <i :class="item.meta.icon" v-if="item.meta.is_icon_class"/>
          <template v-else v-html="item.meta.icon">
          </template>
          <span>{{ $t(item.meta.name) }}</span>
          <i v-if="item.children" class="ri-arrow-right-s-line iq-arrow-right" />
          <small v-html="item.meta.append" v-if="hideListMenuTitle" :class="item.meta.append_class" />
        </router-link>
        <List v-if="item.children" :items="item.children" :sidebarGroupTitle="hideListMenuTitle" :open="item.meta.link.name !== '' && activeLink(item) && item.children ? true : !!(item.meta.link.name !== '' && activeLink(item))" :idName="item.meta.name" :accordianName="`sidebar-accordion-${item.meta.class_name}`" :className="`iq-submenu ${item.meta.class_name}`" />
      </template>
    </li>
  </b-collapse>
</template>
<script>
import List from './CollapseMenu' // 自身组件
import { core } from '../../../config/pluginInit'
export default {
  name: 'List',
  props: {
    items: Array,
    className: { type: String, default: 'iq-menu' },
    open: { type: Boolean, default: false },
    idName: { type: String, default: 'sidebar' },
    accordianName: { type: String, default: 'sidebar' },
    sidebarGroupTitle: { type: Boolean, default: true }
  },
  components: {
    List
  },
  computed: {
    hideListMenuTitle () {
      return this.sidebarGroupTitle
    }
  },
  mounted () {
  },
  methods: {
    activeLink (item) {
      return core.getActiveLink(item, this.$route.name)
    }
  }
}
</script>

到此这篇关于vue-router 基于后端permissions动态生成导航菜单的示例代码的文章就介绍到这了,更多相关vue-router permissions导航菜单内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞! 

[!--infotagslink--]

相关文章

  • Vue-Router的routes配置详解

    在使用vue-router的项目中,实例化VueRouter是其配置选项routes该选项指定路由与视图的组件的关系或者路由与其他路由的关系,Router配置选项中是其中最重要的配置。本文就详细的介绍一下...2021-10-25
  • Vue.js实战之利用vue-router实现跳转页面

    对于单页应用,官方提供了vue-router进行路由跳转的处理,这篇文章主要给大家介绍了Vue.js实战之利用vue-router实现跳转页面的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧。...2017-04-03
  • vue-router history模式服务器端配置过程记录

    vue路由有hash和history两种模式,这篇文章主要给大家介绍了关于vue-router history模式服务器端配置的相关资料,需要的朋友可以参考下...2021-06-08
  • vue-router 基于后端permissions动态生成导航菜单的示例代码

    本文主要介绍了vue-router 基于后端permissions动态生成导航菜单的示例代码,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-30
  • vue-router懒加载的3种方式汇总

    这篇文章主要给大家介绍了关于vue-router懒加载的3种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-28
  • jQuery实现二级导航菜单的示例

    这篇文章主要介绍了jQuery实现二级导航菜单的示例,帮助大家理解和制作网页特效,感兴趣的朋友可以了解下...2020-10-01
  • vue.js使用Element-ui中实现导航菜单

    这篇文章主要为大家详细介绍了vue.js使用Element-ui中实现导航菜单,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-27
  • Vue-router中hash模式与history模式的区别详解

    这篇文章主要给大家介绍了关于Vue-router中hash模式与history模式区别的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-12-16
  • 分享使用圆形导航菜单的优秀网页设计效果图20p

    本文我们分享20个国外优秀网页设计师设计圆形导航菜单效果图,此设计相对国内来说比较个性大胆,但是又不失圆滑细腻,我们的设计师可以在这找到不少灵感。 1701 Franci...2016-09-20
  • 基于vue-router的matched实现面包屑功能

    本文主要介绍了基于vue-router的matched实现面包屑功能,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-26
  • vue-router中hash模式与history模式的区别

    为了构建 SPA(单页面应用),需要引入前端路由系统,这就是 Vue-Router 存在的意义,而这篇文章主要给大家介绍了关于vue-router中两种模式区别的相关资料,分别是hash模式、history模式,需要的朋友可以参考下...2021-06-24
  • vue-router路由懒加载及实现的3种方式

    这篇文章主要给大家介绍了关于vue-router路由懒加载及实现的3种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-28
  • WordPress导航菜单的滚动和淡入淡出效果的实现要点

    这篇文章主要介绍了WordPress导航菜单的滚动和淡入淡出效果的实现要点,讲解了根据WordPress默认主题的PHP编写技巧,需要的朋友可以参考下...2015-12-16
  • WordPress中制作导航菜单的PHP核心方法讲解

    这篇文章主要介绍了WordPress中制作导航菜单的PHP核心方法,即wp_get_nav_menu的相关参数的作用和用法,需要的朋友可以参考下...2015-12-14
  • Element NavMenu导航菜单的使用方法

    这篇文章主要介绍了Element NavMenu导航菜单的使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 以JavaScript来实现WordPress中的二级导航菜单的方法

    这篇文章主要介绍了以JavaScript来实现WordPress中的二级导航菜单的方法,文中首先对WordPress基本的PHP导航菜单的做法给出了说明来作为基础,需要的朋友可以参考下...2015-12-17
  • vue3+TypeScript+vue-router的使用方法

    本文详细讲解了vue3+TypeScript+vue-router的使用方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2022-01-05
  • vue中vue-router的使用说明(包括在ssr中的使用)

    这篇文章主要介绍了vue中vue-router的使用说明(包括在ssr中的使用),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2022-05-25
  • vue-router传参的4种方式超详细讲解

    我们在组件切换时经常会有传递一些数据的需求,这样就涉及到了路由传参的问题,下面这篇文章主要给大家介绍了关于vue-router传参的4种超详细方式,文中通过实例代码介绍的非常详细,需要的朋友可以参考下...2022-07-22