Vue-router example code for dynamically generating navigation menus based on backend permissions

Vue-router example code for dynamically generating navigation menus based on backend permissions

Vue.js

  • vue-router
  • vuex

1. Register global guard

Core Logic
1. Token authentication (backend) => Return to login page when token is invalid
2. Obtain user permissions
3. Verify permissions and dynamically add routing menus

router.beforeResolve registers a global guard. Similar to router.beforeEach, except that resolve guards are called before navigation is confirmed and after all component guards and async route components are resolved.

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 When loginInterception is false, create virtual permissions await store.dispatch('User/setPermissions', ['admin'])
            permissions = ['admin']
          } else {
            permissions = await store.dispatch('User/getUserInfo')
          }
          let accessRoutes = []
          accessRoutes = await store.dispatch('Routes/setRoutes', permissions)
          // Add routes 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 global settings

export default {
  // Whether to enable login interception loginInterception: true,
  // Routes that do not pass token verification routesWhiteList: ['/auth/sign-in', '/auth/register', '/401', '/404'],
}

2. Vuex state management global cache routes

  • state: global storage of data
  • Getter: can be understood as computed, which calculates the data
  • mutations: Synchronous changes to data
  • Actions: asynchronous changes to data (implementing asynchronous operations)
  • module: split the store into modules
/**
 * @author Alan
 * @description Routing interception status management*/
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. Routing interception

/**
 * @author Alan
 * @description Determine whether the current route contains permissions* @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 Intercept routes based on the permissions array * @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. Routing menu

/*
* @author Alan
* @description Public routing */
export const constantRoutes = [
  {
    path: '/auth',
    name: 'auth1',
    component: AuthLayout,
    children: authChildRoutes('auth1'),
    hidden: true // hide the menu},
  {
    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 Asynchronous routing */
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. Recursive menu vue component

<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' : ''}`" vb-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' // Self-component 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>

This is the end of this article about the sample code of vue-router dynamically generating navigation menu based on backend permissions. For more relevant vue-router permissions navigation menu content, please search 123WORDPRESS.COM's previous articles or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Use the vue-element-admin framework to dynamically obtain the menu function from the backend
  • Vue dynamic addition of routing and generation of menu method example
  • How to dynamically generate submenus in the Vue sidebar
  • How does Vue get data from the background to generate a dynamic menu list

<<:  Analysis of Apache's common virtual host configuration methods

>>:  MySQL data type optimization principles

Recommend

Let's talk in detail about the props attributes of components in Vue

Table of contents Question 1: How are props used ...

Steps to repair grub.cfg file corruption in Linux system

Table of contents 1. Introduction to grub.cfg fil...

Linux touch command usage examples

Detailed explanation of linux touch command: 1. C...

Web Design Tutorial (8): Web Page Hierarchy and Space Design

<br />Previous article: Web Design Tutorial ...

Analysis of the principle and creation method of Mysql temporary table

This article mainly introduces the principle and ...

jQuery plugin to achieve carousel effect

A jQuery plugin every day - jQuery plugin to impl...

The use and difference between JavaScript pseudo-array and array

Pseudo-arrays and arrays In JavaScript, except fo...

Detailed steps to configure my.ini for mysql5.7 and above

There is no data directory, my-default.ini and my...

js to implement file upload style details

Table of contents 1. Overview 2. Parameters for c...

Three ways to configure Nginx virtual hosts (based on domain names)

Nginx supports three ways to configure virtual ho...

HTML tag full name and function introduction

Alphabetical DTD: Indicates in which XHTML 1.0 DT...

React gets input value and submits 2 methods examples

Method 1: Use the target event attribute of the E...

Three ways to achieve background blur in CSS3 (summary)

1. Normal background blur Code: <Style> htm...

Explanation of MySQL index types Normal, Unique and Full Text

MySQL's index types include normal index, uni...