Vue -element-admin dynamic routing and dynamic menu rendering in the understanding, just learning, wrong place, please forgive me

In real work, especially writing background management system, often involving different user roles with different permissions, so long need corresponding to different managers in different dynamic routing render Settings and navigation menu, the backstage management, becomes a urgent need to solve the problem, today long record of vue – element – a at a time Understanding this piece of learning in DMIN

The following code is copied, do not like spray.

Can’t write articles: Let the code speak for itself! Don’t like to see the code forgive ha, send an address: github.com/cgq001/admi…

We learn from each other, I also imitate ha ha ha, do not need stars, pure entertainment ha ha boring just

Route design

1.route/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'

import Layout from '@/layout' / / page layout

import Home from '.. /views/Home.vue'

Vue.use(VueRouter)

// Common page, this configuration does not require permissions
export const constRouter = [
  {
      path: '/login'.component: () = > import('@/views/login/Login'),
      hidden: true // Navigation menu ignores options
  },
  {
      path: '/'.component: Layout, // Application layout page
      redirect: '/home'.meta: {title: 'layout'.icon: 'wx'
      },
      children: [{path: 'home'.component: () = > {
                  import('@/views/Home.vue')},name: "Home".meta: {title: "Home".// Navigate to menu item titles
                  icon: 'qq' // Navigation menu icon}}]}]// Dynamic routing
export const asyncRoutes = [
  {
    path: '/about'.component: Layout,
    redirect: '/about/index'.meta: {title: "About".icon: 'wx'
    },
    children: [{path: 'index'.component: () = > import('@/views/about/About.vue'),
        name: 'about'.meta: {
          title: "About".icon: 'qq'.roles: ['admin'.'editor']  // Role permission configuration}}]}]const router = new VueRouter({
  mode: 'history'.base: process.env.BASE_URL,
  routes: constRouter
})

export default router
Copy the code

2. Global navigation guard

Global guards need to be introduced in main.js

1.main.js
// Global route guard
import './router/permission'
Copy the code
2.router/permission
// The global first place of the route

// Permission control logic
import router from './index'
import store from '.. /store/index'

import { Message } from 'element-ui'
import { getToken } from '@/utils/auth' // Get the token from cookie

const whiteList = ['/login'] // The path to exclude

router.beforeEach(async (to,from,next) => {

    // Get the token to determine whether the user is logged in
    const hasToken = getToken()
    // A token indicates that you have logged in
    if(hasToken){
        if(to.path === '/login') {// Login redirects to home page
            next({path: '/'})}else{
            // If the user role has been attached, the dynamic route has been added
            const hasRoles = store.getters.roles && store.getters.roles.length > 0

            if(hasRoles){
                // The role exists
                next() // Continue
            } else {
                try {
                    // Request the user role first
                    const { roles } = await store.dispatch('user/getInfo')
                
                    // Dynamically generate routes based on the current user role
                    const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
                   
                    // Add these routes to the router
                    router.addRoutes(accessRoutes)

                    // Continue route switching to ensure that addRoutes is completenext({... to,replace: true})}catch(error){
                    // Error requires reset token and re-login (token expired, network error, etc.)
                    await store.dispatch('user/resetToken')
                    Message.error(error || "Network error")
                    next(`/login? redirect=${to.path}`)}}}}else{
        // The user has no token
        if(whiteList.indexOf(to.path) ! = = -1) {// Let the whitelist route pass
            next()
        } else {
            // Redirect to the login page
            next(`/login? redirect=${to.path}`)}}})Copy the code

3.1 store/index

import Vue from 'vue' import Vuex from 'vuex' import permission from './modules/permission' import user from './modules/user' Vue.use(Vuex) export default new Vuex.Store({ state: { }, mutations: { }, actions: { }, modules: Roles :{roles: state => state.user.roles, permission_routes: state => state.permission.routes } })Copy the code

3.1 store/modules/user

Import {getToken, setToken, removeToken} from '@/utils/auth' const state ={token: getToken(), roles: Open mutations ={SET_TOKEN: (state,token) => {state.token = token; }, SET_ROLES: (state,roles) => { state.roles = roles; }}; Const actions = {// User login({commit}, userInfo) {const {username} = userInfo; Return new Promise ((resolve, reject) = > {setTimeout (() = > {the if (username = = = 'admin' | | username = = = 'jerry) {/ / commit('SET_TOKEN',username); // write cookie setToken(username) resolve()}else{reject(' username ')}},1000)})}, // get user role information getInfo({commit, state }){ return new Promise((resolve) => { setTimeout(() => { const roles = state.token === 'admin' ? ['admin'] : ['editor'] commit('SET_ROLES',roles) resolve({roles}) },1000) }) }, Return new Promise(resolve => {commit('SET_TOKEN','') commit('SET_ROLES',[]) removeToken(); resolve() }) } } export default { namespaced: true, state, mutations, actions }Copy the code

3.1 store/modules/permission

// Permission management module import {asyncRoutes, ConstRouter} from '@/router' /** * Determine whether the current user has access rights based on the route meta. Role * @roles The user has a role * @route Route to be determined ** ** / function HasPermission (roles,route){// If (route.meta && route.meta. Roles){// If (route.meta If a user has the role of the role has been included in the backlog in routing table has access to return roles. Some (role = > the route. The meta. Roles. Includes (role)} else {/ / not set roles access without judgement Return true}} /** * Recursive filtering AsyncRoutes routing table * @routes The first route to be filtered is AsyncRoutes * @roles User has a role ** / export function FilterAsyncRoutes (routes,roles){const res = [] routes. ForEach (route => {const TMP = {... // If (hasPermission(roles, TMP)){// If (tmp.children){tmp.children = if(tmp.children) filterAsyncRoutes(tmp.children,roles) } res.push(tmp) } }) return res; } const state = {routes: [], // addRoutes: [] const mutations = {SET_ROUTES: (state, AddRoutes = routes // Complete routing table state.routes = constrouter.concat (routes)}} const actions = { generateRoutes({ commit }, roles) { return new Promise(resolve => { let accessedRoutes; / / the user is administrator has full access to the if (roles. Includes (" admin ")) {accessedRoutes = asyncRoutes | | []} else {/ / otherwise require filtering processing according to user role accessedRoutes = filterAsyncRoutes(asyncRoutes,roles) } commit('SET_ROUTES',accessedRoutes) resolve(accessedRoutes) }) }  } export default { namespaced: true, state, mutations, actions }Copy the code

4. Log in

<template>
    <div>
        <input type="text" v-model="username" />
        <div @click="login">landing</div>
    </div>
   
</template>

<script>
export default {
    data(){
        return {
            username:undefined}},methods: {login(){
            this.$store
                .dispatch('user/login', {username: this.username})
                .then(() = >{
                    // Redirect after successful login
                    this.$router.push({
                        path: this.$route.query.redirect || '/'
                    })
                })
                .catch(err= >{
                    console.log(err)
                })
        }
    }
}
</script>
Copy the code

2. Menu rendering

1.Sidebar/index

<template>
    <div>
  
            <el-menu
                :default-active="activeMenu"
                :background-color="variables.menuBg"
                :text-color="variables.menuText"
                :unique-opened="false"
                :active-text-color="variables.menuActiveText"
                :collapse-transition="false"
                mode="vertical"
                >
                    <SidebarItem
                        v-for="route in permission_routes"
                        :key="route.path"
                        :item='route'
                        :base-path='route.path'
                        >
                    
                    </SidebarItem>
            </el-menu>

    </div>
</template>

<script>
import { mapGetters } from 'vuex';
import SidebarItem from './SidebarItem'
console.log('a')
export default {

    components:{ SidebarItem },
    computed: {... mapGetters(['permission_routes']),
        activeMenu(){
            const route = this.$route;
            const { meta, path } = route
            // Default activation
            if(meta.activeMenu){
                return meta.activeMenu
            }
            return path;
        },
        variables(){
            return {
                menuText: "#bfcbd9".menuActiveText: "#409EFF".menuBg: "# 304156"}}},mounted(){
 
        console.log(this.permission_routes,12)}}</script>
Copy the code

1.Sidebar/SidebarItem

<template>
    <div v-if=! "" item.hidden" class="menu-wrapper">
        <! -- Only one displayable child route, and no grandchild route -->
        <template v-if="hasOneShowingChild(item.children,item) && (! onlyOneChild.children || onlyOneChild.noShowingChildren) && item.alwaysShow">
            <router-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
                <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown' : isNest }">
                   
                    <item :icon="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" :title="onlyOneChild.meta.title" />
                </el-menu-item>
            </router-link>
        </template>
        <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
            <template v-slot:title>
                <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
            </template>
            <sidebar-item
                v-for="child in item.children"
                :key="child.path"
                :is-nest='true'
                :item='child'
                :base-path="resolvePath(child.path)"
                class="nest-menu"
                />
        </el-submenu>
    </div>
</template>

<script>
import path from 'path'
import Item from './Item'

export default {
    name: "SidebarItem".components: { Item },
    props: {
        item: {
            type: Object.required: true
        },
        isNest: {
            type: Boolean.required: false
        },
        basePath: {
            type: String.default: ' '}},data(){
        this.onlyOneChild = null
        return{}},mounted(){
        console.log(this.item)
    },
    methods: {hasOneShowingChild(children = [],parent){
           
            const showingChildren = children.filter(item= >{
                if(item.hidden){
                    return false
                } else {
                    // Set if there is only one submenu
                    this.onlyOneChild = item
                    return true}})// Child routes are displayed by default when there is only one child route
            if(showingChildren.length === 1) {return true
            }
            // Displays the parent route if there are no child routes
            if(showingChildren.length === 0) {this.onlyOneChild = {... parent,path: ' '.noShowingChildren: true }
                return true
            }
            console.log( this.onlyOneChild)
            return false
        },
        resolvePath(routePath){
            return path.resolve(this.basePath, routePath)
        }
    }    
}
</script>
Copy the code

1.Sidebar/index

<script>
export default {
    name: "MenuItem".functional: true.props: {icon: {
            type: String.default: ' '
        },
        title: {
            type: String.default: ' '}},render(h, context){
        const { icon, title } = context.props
        const vnodes = []
      
        if(icon){
            // vnodes.push(<svg-icon icon-class={icon} />)
            vnodes.push(<i class={icon}></i>)}if(title){
  
            vnodes.push(<span slot='title'>{title}</span>)}return vnodes
    }
}
</script>
Copy the code