Ideas for Implementing Permission Function of vue Background Project

In common background systems, there will be different user roles, such as super administrator, ordinary user, whose permissions are different, so the modules that can be manipulated or displayed are also not consistent. The following background management system framework is used as a template here

Address: https://github.com/PanJiaChen/vue-admin-template

Briefly speaking, the background menu is shown below, which requires that there are two types of users, administrators and ordinary employees in the system. Administrator login system can see all menu options, and ordinary users can only see the first menu items.

The Sidebar component code for the left menu of the project is as follows, configure this. $by traversing the current routing instance route with v-for. Router. Options. Routes are passed to <sidebar-item/> for demonstration, which is not detailed here.

<el-scrollbar wrap-class="scrollbar-wrapper">
   <el-menu
    :default-active="activeMenu"
    :collapse="isCollapse"
    :background-color="variables.menuBg"
    :text-color="variables.menuText"
    :unique-opened="false"
    :active-text-color="variables.menuActiveText"
    :collapse-transition="false"
     mode="vertical"
    >
      <sidebar-item v-for="route in routes" 
       :key="route.path" 
       :item="route" 
       :base-path="route.path" 
      />
    </el-menu>
 </el-scrollbar>

<script>
import { mapGetters } from 'vuex'
import Logo from './Logo'
import SidebarItem from './SidebarItem'

export default {
  components: { SidebarItem, Logo },
  computed: {
    ...mapGetters([
      'sidebar'
    ]),
    routes() {
      return this.$router.options.routes;
    },
  }
}

Thus, in order to display the menu bar dynamically, all the routing configuration objects in the project cannot be written together in the routing configuration file. Here, the routing configuration in the project is divided into three categories, as shown below. Constant Routes Constant Routes stores the routes visible to all users, including the home page, the login page, and 404 pages. asyncRoutes asynchronous routing, storing routes filtered out by different users; Any Routes, redirects to 404 pages when the path is wrong. This gives you the flexibility to handle routing configuration information.

router/index.js

export const constantRoutes = [
  {
    path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true
  },

  {
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true
  },
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [{
      path: 'dashboard',
      name: 'Dashboard',
      component: () => import('@/views/dashboard/index'),
      meta: { title: 'home page', icon: 'dashboard' }
    }]
  },
  
]

export const asyncRoutes = [
  {
    name: 'Acl',
    path: '/acl',
    component: Layout,
    redirect: '/acl/user/list',
    meta: {
      title: 'Rights Management',
      icon: 'el-icon-lock'
    },
    children: [
      {
        name: 'User',
        path: 'user/list',
        component: () => import('@/views/acl/user/list'),
        meta: {
          title: 'user management',
        },
      },
      ......
    ]
  },
  {
    path:'/product',
    component:Layout,
    name:'Product',
    meta: { title: 'Commodity Management', icon: 'el-icon-goods' },
    children:[
      {
        path:'tradeMark',
        name:'TradeMark',
        component:() => import('@/views/product/tradeMark'),
        meta:{title:'Brand Management'}
      },
      ......
    ]
  },
];

export const anyRoutes = [
  // 404 page must be placed at the end !!!
  { path: '*', redirect: '/404', hidden: true }
];

const createRouter = () => new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})

After a user logs in to the system, the browser retrieves the user's details based on the user's token information, which includes a list of menus (Array) that the user can access, as well as a list of buttons that the user can manipulate.

The request behavior occurs in vuex's actions, which are dispatched on the login page, and the responding user information is submitted to mutations in aciions.

const getDefaultState = () => {
  return {
    token: getToken(),
    name: '',
    avatar: '',
    routes:[],  // Menu Tag Information Returned by Server
    buttons:[],  // Button tag information returned by the server
    role:[],  // Role information returned by the server
  }
}

const state = getDefaultState()
const mutations = {
  RESET_STATE: (state) => {
    Object.assign(state, getDefaultState())
  },
  USER_INFO: (state,info) => {
    state.name = info.name;
    state.avator = info.avator;
    state.routes = info.routes;   // Menu Permission Marker
    state.buttons = info.buttons;  // Button Permission Marker
    state.role = info.role;  // User role
},
const actions = {
  // get user info
  getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      getInfo(state.token).then(response => {
        const { data } = response
        if (!data) {
          return reject('Verification failed, please Login again.')
        }
        // 
        commit('USER_INFO',data);
        resolve(data)
      }).catch(error => {
        reject(error)
      })
    })
  },
}

Once you have the displayable asynchronous routing flag information returned by the server, all you need to do next is to compare it with all the asynchronous routing information in the project to get the asynchronous routing information that you will ultimately need to show. Here, submit a mutations after you log in, named SEY_RESULTASYNCROUTES, passed to mutations a method computedAsyncRoutes, which compares the information of routes and returns the asynchronous routes that the current user wants to display as return values.

getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      getInfo(state.token).then(response => {
        const { data } = response
        console.log(response.data);
        if (!data) {
          return reject('Verification failed, please Login again.')
        }
        // 
        commit('USER_INFO',data);
        commit('SEY_RESULTASYNCROUTES',computedAsyncRoutes(asyncRoutes,data.routes));
        resolve(data)
      }).catch(error => {
        reject(error)
      })
    })
  },

Define the computedAsyncRoutes method as follows, introduce asynchronous routing asyncRoutes, traverse the asyncRoutes array, compare the name of each route with the tag information routes passed from the server (indexOf method), and filter out the route information contained in the server tag array;

It is worth noting here that since there are secondary or even tertiary routes for routes, which are written in the children attribute of the route configuration object, they also need to be traversed and compared to determine whether they need to be displayed, and the computedAsyncRoutes method needs to be called recursively to filter each and routed children again.

import { resetRouter,asyncRoutes,anyRoutes,constantRoutes } from '@/router'
// Compare the two arrays to figure out which asynchronous routes the current user is displaying
const computedAsyncRoutes = (asyncRoutes,routes) => {
  return asyncRoutes.filter((item) => {
    // Routing information contained in the array returned by the server
    if(routes.indexOf(item.name) !== -1) {
      // If multilevel routing exists, recursively
      if(item.children && item.children.length) {
        item.children = computedAsyncRoutes(item.children,routes);
      }
      return true;
    }
  })
}

SEY_Submitted this way RESULTASYNCROUTES is a mutation in which you can get the routing configuration information asyncRoutes after processing, but you still need to do routing merge by merging the previously defined constant route, any route, and the newly filtered asynchronous route (array concat method) to form a final complete routing information. Add this routing rule router to the current project. AddRoutes (state.resultAllRoutes)

import router from '@/router'
const getDefaultState = () => {
  return {
    token: getToken(),
    name: '',
    avatar: '',
    routes:[],  // Menu Tag Information Returned by Server
    buttons:[],  // Button tag information returned by the server
    role:[],  // Role information returned by the server
    resultAsyncRoutes:[],
    resultAllRoutes:[]
  }
}

const state = getDefaultState();

mutations:{
    // Final computed asynchronous routing
    SEY_RESULTASYNCROUTES(state,asyncRoutes){
        // vuex saves asynchronous routes for the current user
        state.resultAsyncRoutes = asyncRoutes;
        // Calculate all routes for the current user
        state.resultAllRoutes = constantRoutes.concat(state.resultAsyncRoutes,anyRoutes);
        // Add a new route to a route
        router.addRoutes(state.resultAllRoutes);
    }
}

At this time, the menu on the left side is still not displayed correctly after login.

Although the correct route already exists in the router instance at this time, the left navigation bar <Sidebar>component traversal still shows only the Home menu because of routes/index. Registering in JS is a constant route as shown below, while modifying the operation router.addRoutes(state.resultAllRoutes) are the result of an asynchronous operation and pass this. $in the sidebar component Router. Options. Routes can only get initial constant routes

 router/index.js:

const createRouter = () => new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})

sidebar.vue

export default {
  components: { SidebarItem, Logo },
  computed: {
    routes() {
      return this.$router.options.routes;
    },
  }
}

Simply change the access mode to the latest routing configuration information in the repository, this.$store.state.user.resultAllRoutes

sidebar.vue

routes() {
    return this.$store.state.user.resultAllRoutes;
},

This achieves the effect of displaying different menus after users of different identities log in.

In addition, to achieve button permissions in the project, that is, the same button, some users display buttons, some users do not display buttons, as long as you get the above buttons array, use v-show on the corresponding button label to make a judgment.

<button v-show="$store.state.xxxModule.buttons.indexOf('Corresponding button label name')">delete</button>

Keywords: Front-end Vue.js

Added by Amplifier on Tue, 01 Feb 2022 10:17:55 +0200