Vue Xiaoai admin series of articles (1): using vue-cli3 + mockjs to realize background management authority and three-level menu function

Time:2019-12-10

Recently, I completed the basic functions of my Xiaoai admin background management system, and carried out a new version of the overall layout and style of the page. The implementation of the system permission function has been added. At the same time, I think that all the menus in the background system are set to the left, which will limit the expansion of the menu. Therefore, I improved the display of the three-level menu.

  • Effect demonstration address
  • GitHub address

Realization of authority function

Authority routing idea:
According to the comparison and filtering between the user’s logged in roles and the configured roles in the routing, the accessible routing table is generated, and the accessible routing table is dynamically added through router.addroutes (store. Getters. Addroutes), so as to display the left and top bar menus.

Implementation steps of authority function:

1. Set default roles information for corresponding menus

stayrouter/index.jsSet the default roles information for the corresponding menu;
As follows:

The permissions set for the permission settings menu are:

{
    path: '/permission',
    name: 'permission',
    meta: {
      Title: 'permission setting',
      Roles: ['admin ',' editor '] // different roles can see
    }
}

Give its submenu “page permission” and set the permission as follows:

{
    path: 'page',
    name: 'pagePer',
    meta: {
      Title: 'page permission',
      Roles: ['admin '] // only "admin" can see this menu
    },
    component: () => import('@/page/permission/page'),
}

Set the button permission of its submenu to:

{
    path: 'directive',
    name: 'directivePer',
    meta: {
      Title: 'button permission',
      Roles: ['editor '] // only' editor 'can see this menu
    },
    component: () => import('@/page/permission/directive'),
}

2. Filter the route and intercept the permission through router. Beforeeach();

The code is as follows:

function hasPermission(roles, permissionRoles) {
  if (roles.indexOf('admin') >= 0) return true 
  if (!permissionRoles) return true
  return roles.some(role => permissionRoles.indexOf(role) >= 0)
}
Const whitelist = ['/ Login'] // do not redirect whitelist

router.beforeEach((to, from, next) => {
  NProgress.start()
   //Set browser header
   const browserHeaderTitle = to.meta.title
   store.commit('SET_BROWSERHEADERTITLE', {
     browserHeaderTitle: browserHeaderTitle
   })
  //When you click log in, you get the token and save the cookie to ensure that you can always get the token when the page is refreshed
  if (getToken('Token')) {
    if(to.path === '/login') {
      next({ path: '/' })  
      NProgress.done() 
    } else {
      //After the user logs in successfully, each click on the route makes a role judgment;
      if (store.getters.roles.length === 0) {
        let token = getToken('Token');
        GetUserInfo ({"token": token}). Then(). Then (RES = > {// pull the user information according to the token
          let userList = res.data.userList;
          store.commit("SET_ROLES",userList.roles);
          store.commit("SET_NAME",userList.name);
          store.commit("SET_AVATAR",userList.avatar);
          Store. Dispatch ('generateroutes', {"roles": userlist. Roles}). Then (() = > {// generate an accessible routing table according to the roles permission
            Router. Addroutes (store. Getters. Addroutes) // dynamically add the accessible route table
            Next ({... To, replace: true}) // the hack method ensures that addroutes is complete
          })
        }).catch((err) => {
          store.dispatch('LogOut').then(() => {
            Message.error(err || 'Verification failed, please login again')
            next({ path: '/' })
          })
        })
      } else {
        //If there is no need to change the authority dynamically, you can directly delete the lower authority judgment by next()
        if (hasPermission(store.getters.roles, to.meta.roles)) {
          next()//
        } else {
          next({ path: '/401', replace: true, query: { noGoBack: true }})
        }
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) {
      //When you click exit, it will be located here
      next()
    } else {
      next('/login')
      NProgress.done()
    }
  }
})

router.afterEach(() => {
  Nprogress. Done() // end progress
  setTimeout(() => {
    const browserHeaderTitle = store.getters.browserHeaderTitle
    setTitle(browserHeaderTitle)
  }, 0)
})

Logical analysis of authority of the system

1. Routing objects distinguish permission routing objects from non permission routing objects. During initialization, non permission routing objects are assigned to router. At the same time, meta objects in permission routing are set, such as: meta: {roles: [‘admin ‘,’editor’]}, indicating the routing permission of the role;

2. Match the routes and generate new route objects through the roles returned after the user logs in successfully;

3. When the user successfully logs in and jumps to the home page, the menu on the left will be rendered according to the routing object just generated, that is, different users will see different menus;

Business logic analysis after users click to log in

1. The user clicks the login button to determine the next jump logic through the route navigation hook router. Beforeeach() function, as follows:

1.1. The user has successfully logged in and obtained the token value from the cookie;

 1.1.1 the user accesses the login page and directly locates to the login page;
 
 1.1.1 when users access the non login page, they need to perform different business logic according to whether they have roles information, as follows:
 
    (1) initially, the user roles information is empty;
    
        1. Pull user information according to token through getuserinfo() function, and store the user's roles, name, avatar information in vuex through store;
        
        2. Use store. Dispatch ('generateroutes', {roles}) to re filter and generate routes, and use router. Addroutes() to merge route tables; 
        
        3. If there is an error in obtaining the user information interface, call the store.dispatch ('logout ') interface and return to the login page;
      
    (2) the user already has the roles information;
    
        1. Click Page route, and judge haspermission () through roles permission. If the user has the route permission, directly jump to the corresponding page; if not, jump to the 401 prompt page;

2. When the user clicks exit, the token has been cleared

1. If the whitelist user is set, jump to the corresponding page directly;

2. Otherwise, it will jump to the login page;

For detailed code, please refer to Src / permission.js

Permission presentation

Test account:

(1) Username: admin, password: 123456; admin has the highest permission to view all pages and buttons;

(2) Username: editor, password: 123456; the editor can only be seen by pages and buttons with permission;

Top bar display of three-level navigation menu

Vue Xiaoai admin series of articles (1): using vue-cli3 + mockjs to realize background management authority and three-level menu function

As shown in the figure, after completing the secondary navigation menu function of the general background system, I found that many background management systems actually have three-level navigation menus. However, if the three-level menus are placed on the left side of the menu to make a ladder arrangement, it will be relatively compact, so I think it is a good choice to put all the three-level menus on the top.

Development needs

Click the menu on the left, find the corresponding menu (top bar menu) and place it in the top navigation bar;

Development steps

1. Define the top navigation component topmenu.vue

Through the element UI, navmenu navigation menu to display the top menu, pay attention to the difference between the top bar and side bar settings, and reference it in the head component headnav.vue;

2. Define top bar routing data router / toprouter.js

The format is as follows:

export const topRouterMap = [
    {
        'parentName':'infoShow',
        'topmenulist':[
            {
                path: 'infoShow1',
                name: 'infoShow1',
                meta: {
                    Title: 'personal information submenu 1',
                    icon: 'fa-asterisk',
                    routerType: 'topmenu'
                },
                component: () => import('@/page/fundList/moneyData')
            }
        ]
    },
    {
        'parentName':'chinaTabsList',
        'topmenulist':[
            {
                path:'chinaTabsList1',
                name:'chinaTabsList1',
                meta:{
                    Title: 'regional investment submenu 1',
                    icon:'fa-asterisk',
                    routerType:'topmenu'
                },
                component: () => import('@/page/fundList/moneyData')
            }
        ]
    }
]

Define toproutermap as the total route array; establish contact with the left route through parentname; represent the value of the top route through topmenulist; distinguish the top route or the left route through the value of meta.routertype as “topmenu” or “leftmenu”;

3. Prepare rendering data in headnav.vue

Idea: click the menu on the left to display the corresponding menu at the top. Because the left menu has to be linked to the top menu. We know that when the user logs in, the navigation menu will filter permissions according to the user’s role information; then, before filtering the permission routing data, we can filter and add all the three-level menus through addtoprouter(). After adding, we can continue to filter roles to ensure that the top menu without permission will also be filtered out.

//Src / store / permission.js, through circular filtering, generates a new secondary menu
function addTopRouter(){
  asyncRouterMap.forEach( (item) => {
    if(item.children && item.children.length >= 1){
      item.children.forEach((sitem) => {
       topRouterMap.forEach((citem) => {
          if(sitem.name === citem.parentName){
              let newChildren = item.children.concat(citem.topmenulist);
              item.children = newChildren;
          }
       })
      })
    }
  })
  return asyncRouterMap;
}

4. Click the menu on the left to filter the route and display the corresponding data

In the component topmenu.vue, the user enters or clicks the left menu by default to trigger the setleftinnermenu() function, as follows:

setLeftInnerMenu(){
    const titleList = this.$route.matched[1].meta.titleList;
    const currentTitle = titleList && this.$route.matched[2].meta.title;
    If (titlelist & & this. $route. Matched [1]. Meta. Routertype = = = 'leftmenu') {// click the level 2 menu on the left
        this.$store.dispatch('ClickLeftInnerMenu',{'titleList':titleList});
        this.$store.dispatch('ClickTopMenu',{'title':currentTitle});
    }Else {// click the level 1 menu on the left
        this.$store.dispatch('ClickLeftInnerMenu',{'titleList':[]});
        this.$store.dispatch('ClickTopMenu',{'title':''});
    }
}

According to the value of this. $route.meta.routertype of the current route, the user can click the top menu or the left menu. If you click the top menu, click this. $store to trigger the asynchronous action ‘clickleftinnermenu’ and pass the parameter ‘name’. In vuex, filter the current route information through state.toproutes = filtertoprouter (state.routes, data). The code is as follows:

//Src / store / permission.js, get the corresponding top submenu of the current route
 function filterTopRouters(data){
    let topRouters = topRouterMap.find((item)=>{
       return item.parentName === data.name
    })
    if(!mutils.isEmpty(topRouters)){
       return topRouters.topmenulist;
    }
}

In topmenu.vue, display the corresponding top route data through computed: {… Mapgetters ([‘toproutes’])}. Each time the user clicks the menu on the left, the top route is reassigned and rendered to ensure the accuracy of the data.

5. Perfect top menu

When the amount of data in the top menu is too large, we need to set the horizontal scroll bar and set the style of the scroll bar.
As shown in the picture:
Vue Xiaoai admin series of articles (1): using vue-cli3 + mockjs to realize background management authority and three-level menu function

Mock data

Use background

In the process of using easy mock to simulate data, it is found that it can’t add, delete and modify the fixed data of the table, and because they are free to provide services, leading to a large number of users, the server often can’t access, so we choose to use mockjs to simulate local data.

Introduction and functions

Mock.js is a simulation data generator designed to help the front-end siegers develop independently of the back-end and help write unit tests. The following simulation functions are provided:

1. Generate simulation data according to the data template. With the method provided by mockjs, you can easily create a large number of random text, numbers, Boolean values, dates, mailboxes, links, pictures, colors, etc

2. Simulate Ajax request, generate and return simulation data. Mockjs can perform powerful Ajax interception. It can judge request type, obtain URL, request parameters, etc. then it can return fake data of mocks, or JSON file you have compiled. It is powerful and easy to use

3. Generate simulation data based on HTML template

Mockjs is used in this project

1. Install mockjs

npm install mockjs --save-dev

2. Create the mock folder structure and define the related function modules

As shown in the picture:
Vue Xiaoai admin series of articles (1): using vue-cli3 + mockjs to realize background management authority and three-level menu function

Mock JS / index.js is responsible for defining the relevant mocks interfaces, as follows:

import Mock from 'mockjs'

import tableAPI from './money'

//If there is no delay in setting the global delay, sometimes the data change will not be detected and it is recommended to keep it
Mock.setup({
    timeout: '300-600'
})

//Capital related
Mock.mock(/\/money\/get/, 'get', tableAPI.getMoneyList)
Mock.mock(/\/money\/remove/, 'get', tableAPI.deleteMoney)
Mock.mock(/\/money\/batchremove/, 'get', tableAPI.batchremoveMoney)
Mock.mock(/\/money\/add/, 'get', tableAPI.createMoney)
Mock.mock(/\/money\/edit/, 'get', tableAPI.updateMoney)

For mockjs / money.js, define related functions to realize the business logic of simulation data, such as adding, deleting, modifying and querying fund flow data; for the generation rules of data, please refer to the mockjs official website document, which has a detailed syntax description;

3. Introduce the defined mockjs in main.js

As follows:

Import '. / mockjs' // reference mocks

4. Mockjs, API interface encapsulation

In src/api/money.js, a unified interface encapsulation is implemented, and the corresponding function is invoked in the page, and the corresponding analog data can be obtained. The code is as follows:

import request from '@/utils/axios'

export function getMoneyIncomePay(params) {
  return request({
    url: '/money/get',
    method: 'get',
    params: params
  })
}

export function addMoney(params) {
  return request({
    url: '/money/add',
    method: 'get',
    params: params
  })
}

5. In the component, the interface calls, obtains the data, and renders the page

Vue-cli 3.0 upgrade record

Due to the early use of vue-cli 2.0 to build a project, it needs to carry out cumbersome webback configuration; vue-cli 3.0 integrates webback configuration and makes great optimization in performance improvement. Because this project uses vue-cli 3.0 to build and upgrade. Please refer to the official website for details.

1. Introduction to the use premise of vue-cli 3.0

The package name of Vue cli was changed from Vue cli to @ Vue / cli.
If you have installed an older version of Vue cli (1. X or 2. X) globally, you need to first pass the

NPM uninstall Vue cli - g or yarn global remove Vue cli

Unload it.
Vue cli requires node.js 8.9 or later (8.11.0 + is recommended). You can use NVM or NVM windows to manage multiple node versions on the same computer.

2. Vue-cli 3.0 installation and use

1. Vue-cli3. X installation

npm install -g @vue/cli
# OR
yarn global add @vue/cli

If you want to keep the syntax of vue-cli2. X or use the template of 2. X, it is recommended to install cli init

npm install -g @vue/cli-init
# OR
yarn global add @vue/cli-init

2. Create a project with vue-cli3. X

`
Vue create project name;
`
Select the relevant configuration information in the installation step until it is completed.

3. New projects need to be configured with environment variables and patterns

Create new files. Env.development and. Env.production in the root directory to represent the development environment and generation environment configuration respectively. They are mainly used to define environment variables and integrate them into different environments through NPM run serve or NPM run build for interface calls. The code is as follows:

.env.development

NODE_ENV = development
VUE_APP_URL = "https://easy-mock.com/mock/5cd03667adb0973be6a3d8d1/api"

.env.production

NODE_ENV = production
VUE_APP_URL = "https://easy-mock.com/mock/5cd03667adb0973be6a3d8d1/api"

Usage, such as in this project, is configured in utils / env.js. The code is as follows:

//Development and production environments are different
let app_url = process.env.VUE_APP_URL  
export default {
    app_url
}

4. Use vue.config.js to compile and package detailed configuration

Due to the use of vue-cli3. X to generate a project, the relevant configuration of webpack has been integrated into the node [module]. If you want to make detailed configuration of webpack, you need to create a new file vue.config.js under the root directory of the project. For specific configuration, please refer to the documentation. The following is a basic configuration.

Const terser plugin = require ('terser webpack plugin ') // used to eliminate debuggers and console in the build environment
const path = require('path');
const resolve = dir => {
  return path.join(__dirname, dir);
};

const env = process.env.NODE_ENV
Let target = process. Env. Vue? App? URL // development and production environments are different

module.exports = {
  publicPath: '/',
  outputDir: './dist',
  Lintonsave: false, // close eslint
  //Do not generate. Map file when packaging
  productionSourceMap: false,
  devServer: {
    open: true,
    host: '0.0.0.0',
    port: 8808
    //Because the data of this project is simulated by easy mock and mockjs, there is no cross domain problem and there is no need to configure the agent;
    // proxy: { 
    //   '/v2': {
    //       target: target,
    //       changeOrigin: true
    //   }
    // }
  },
   //Webpack related configuration
  chainWebpack: (config) => {
    config.entry.app = ['./src/main.js'];
    config.resolve.alias
      .set('@', resolve('src'))
      .set('cps', resolve('src/components'))
  },
  configureWebpack:config => {
    //Modify configuration for production
    if (process.env.NODE_ENV === 'production') {
      new TerserPlugin({
        cache: true,
        parallel: true,
        sourceMap: true, // Must be set to true if using source-maps in production
        terserOptions: {
          compress: {
            drop_console: true,
            drop_debugger: true
          }
        }
      })
    } else {
      //Modify configuration for development environment

    }
  },
   //Third party plug-in configuration
  pluginOptions: {

  }
}

The project configuration is completed, and all dependent packages are installed. Execute the development environment package command: NPM run serve to run the project; execute the generate environment package command: NPM run build to generate the production environment file.

Ending

So far, some basic functions of the project have been completed, which can basically meet the needs of the project. The next article will continue“Implementation details of project sharing function“、”Project deployment details and precautions (including how to deploy subdirectories)“、”Project performance optimization details“, please look forward to~

Technical answer

Project Description:

Xiaoai admin is a completely open-source and free management system integration scheme, which can be directly applied to relevant background management system templates; many key points have made detailed comments and explanations. If you like front-end development as well, welcome to join our discussion / learning group, in which you can ask questions and share learning materials;
Welcome to QQ group.

Vue Xiaoai admin series of articles (1): using vue-cli3 + mockjs to realize background management authority and three-level menu function

Recommended Today

Explain idea git branch backoff specified historical version

scene When I submitted this modification to the local and remote branches, I found that there were still some changes missing in this submission, or this modification was totally wrong, but I also pushed it to the remote repository. How to go back? problem How can the content that has been submitted to the repository […]