Recently completed my little love ADMIN background management system basic functions, while the overall layout of the page and style of the new revision. I added the realization of the system permission function, and felt that all the menus in the background system were left, which would limit the extension of the menu, so I improved the display of the three-level menu.

  • Effect demonstration address
  • Making the address

The realization of authority function

Permission routing roadmap: Based on the roles information logged in to and configured in the route, a routing table is generated and filtered, and a routing table is dynamically added using router.addroutes (store.getters. AddRouters) to display menus on the left and top bars.

The realization steps of the permission function:

1. Set the default roles information for the corresponding menu

In router/index.js, set the default roles information for the corresponding menu. As follows:

Set the permissions to the “Permission Settings” menu as follows:

{
    path: '/permission',
	name: 'permission',
	meta: {
        title: 'Permission Settings',
        roles: ['admin'.'editor'[// Different roles can see}}Copy the code

To the submenu “page Permissions “, set the permissions to:

{
	path: 'page',
	name: 'pagePer',
        meta: {
          title: 'Page Permissions',
          roles: ['admin'] / / only"admin"}, Component: () => import('@/page/permission/page'),}Copy the code

Set the submenu “Button Permissions” to:

{
	path: 'directive',
	name: 'directivePer',
        meta: {
    	  title: 'Button Permissions',
    	  roles:['editor'] / / only"editor"}, Component: () => import('@/page/permission/directive'),}Copy the code

2. Use router.beforeeach () to filter routes and intercept permissions.

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 the whitelist router. BeforeEach ((to, from, Next) => {nprogress.start () // set browserHeaderTitle const browserHeaderTitle = to.meta.title store.com MIT ('SET_BROWSERHEADERTITLE', {browserHeaderTitle: browserHeaderTitle}) // When you click login, you get the token and save the cookie to ensure that you can always get the token when the page is refreshedif (getToken('Token')) {
    if(to.path === '/login') {
      next({ path: '/' })  
      NProgress.done() 
    } else{// After the user logs in successfully, every time the user clicks the route, the user determines the role;if (store.getters.roles.length === 0) {
        let token = getToken('Token');
        getUserInfo({"token":token}).then().then(res => {// Pull user information based on tokenlet 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"Router.addroutes (store.getters.addrouters) router.addroutes (store.getters.addrouters) . to, replace:true}) // hack method to make sure 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 dynamically change permissions, you can directly next() delete the lower permissions ↓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 positioned here next()}else {
      next('/login'AfterEach (() => {nprogress.done () // End ProgresssetTimeout(() => {
    const browserHeaderTitle = store.getters.browserHeaderTitle
    setTitle(browserHeaderTitle)
  }, 0)
})
Copy the code

This system permission logic analysis

1. Routing objects distinguish between privileged routing objects and non-privileged routing objects; During initialization, assign a non-privileged route object to the Router. For example, meta:{roles:[‘admin’,’ Editor ‘]} indicates the route permission for roles.

2. Routes are matched and new route objects are generated based on the roles value returned by the user after successful login.

3. When the user successfully logs in and jumps to the home page, render the menu on the left according to the route object just generated; That is, different users see different menus;

Business logic analysis after users click login

1. The user clicks the login button and determines the next jump logic using the router.beforeeach () function as follows:

1.1. The user has logged in successfully and obtained the token value from the cookie; 1.1.1. Users access the login page and directly navigate to the login page. 1.1.1 When a user accesses a non-login page, perform different service logics based on whether the user has roles information: (1) Initially, the roles information is empty. 1. Use the getUserInfo() function to pull user information based on the token. The roles,name, and Avatar information of the user is stored in VUex through store. 2. Use store.dispatch('GenerateRoutes', {roles}) to re-filter and GenerateRoutes, and use router.addroutes () to merge routing tables. 3. If an error occurs while obtaining user information, login to the login page by invoking the store.dispatch('LogOut') interface. (2) The user already has roles information; 1. Click the route page and check hasPermission() based on roles permission. If the user has the route permission, the user can directly go to the corresponding page. If you do not have the permission, go to the 401 prompt page.Copy the code

2. The user clicks exit, and the token is cleared

1. If whitelisted users are configured, the corresponding page is displayed. 2. If no, the login page is displayed.Copy the code

For details, see SRC /permission.js

Permission Demo

Test Account:

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

(2). Username: editor, password: 123456; Editor only pages and buttons with permissions are visible;

Display the top bar of the three-level navigation menu

, as shown in the general background system of secondary navigation menu, I found a lot of background management system has three navigation menu, but if all the level 3 menu on the left side menu to do ladder-like arrangement, is more compact, so I think all the 3 menu on the top is a good choice.

Development needs

Click the left menu to find the corresponding menu (top bar menu) in the top navigation bar;

Development steps

1. Define the top navigation component topmenu.vue

Through element-UI,NavMenu navigation menu to display the top menu, pay attention to the difference between the top bar and the side bar Settings; It is also referenced in the header component headnav.vue;

2. Define the top column 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'}]}]Copy the code

Define topRouterMap as the total number of routes. ParentName is used to establish a connection with the left route. The value of the route in the top column is represented by topmenulist. The value of meta. RouterType is “topMenu” or “leftMenu “to distinguish the top-column route from the left-side route.

3. Prepare the rendering data in headnav. vue

Click the left menu to display the corresponding menu at the top. Because the left menu is connected to the top menu. We know that the navigation menu will filter permissions according to the user’s role information when the user logs in. Then, before filtering the permission routing data, we can add all the three-level menus through addTopRouter(). After adding, we can continue to filter the role to ensure that the top menu that does not have the permission will also be filtered out.

// SRC /store/permission.js, through the loop filter, generates a new secondary menufunction addTopRouter(){
  asyncRouterMap.forEach( (item) => {
    if(item.children && item.children.length >= 1){
      item.children.forEach((sitem) => {
       topRouterMap.forEach((citem) => {
          if(sitem.name === citem.parentName){
              letnewChildren = item.children.concat(citem.topmenulist); item.children = newChildren; }})})return asyncRouterMap;
}
Copy the code

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

In the component Topmenu.vue, the user comes in by default or clicks on the left menu 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 on the left level 2 menu this.$store.dispatch('ClickLeftInnerMenu', {'titleList':titleList});
        this.$store.dispatch('ClickTopMenu', {'title':currentTitle});
    }else{// Click the left level 1 menu this.$store.dispatch('ClickLeftInnerMenu', {'titleList': []}); this.$store.dispatch('ClickTopMenu', {'title':' '}); }}Copy the code

Route this through the current route.The Store triggers the asynchronous action ClickLeftInnerMenu and passes the parameter Name. In THE VUEX area, state. TopRouters = filterTopRouters(Routers,data) are used to filter the current routing information. The code is as follows:

// SRC /store/permission.js, get the top submenu corresponding to the current routefunction filterTopRouters(data){
    let topRouters = topRouterMap.find((item)=>{
       return item.parentName === data.name
    })
    if(! mutils.isEmpty(topRouters)){returntopRouters.topmenulist; }}Copy the code

In topmenu. vue, through computed:{… MapGetters ([‘topRouters’])} displays the top routing data. Every time the user clicks the left menu, the top route is reassigned and rendered to ensure the accuracy of the data.

5. Perfect top menu

When the top menu has too much data, we need to set the horizontal scroll bar and set the style of the scroll bar. As shown in figure:

The mock data

Use a background

In the process of using Easy-Mock to simulate data, it was found that it could not realize the functions of adding, deleting and modifying the fixed data of the table, and because they are free services, the server often could not access them when there are a large number of users. Therefore, mockJS was used for local data simulation.

Introduction and Functions

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

With mockJS you can easily create lots of random text, numbers, booleans, dates, mailboxes, links, images, colors, etc.

2. Mock Ajax requests, generate and return mock data, mockJS for powerful Ajax interception. Can judge the request type, get the URL, request parameters and so on. You can then return mock mock data, or json files you’ve made up yourself. Powerful and easy to use.

3. Generate simulated data based on HTML template

Mockjs is used in this project

1. Install mockjs

npm install mockjs --save-dev
Copy the code

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

As shown in figure:

import Mock from 'mockjs'

import tableAPI from './money'Mock. Setup ({timeout:'300-600'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)
Copy the code

Mockjs /money.js, then define related functions to realize the business logic of simulated data, such as the increase, deletion, change and check of capital flow data; Please refer to the mockJS official website for data generation rules, which have detailed syntax instructions.

3. Introduce the defined MockJS in main.js

As follows:

import './mockjs'/ / reference mockCopy the code

4. Mockjs, API interface encapsulation

SRC/API /money.js, a unified interface encapsulation, call the corresponding function in the page, you can get the corresponding simulation data. 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
  })
}
Copy the code

5. Component, interface call, get data, render page

Vue-cli3.0 Upgrade record

Since vuE-CLI2.0 was used to build the project in the early stage of the project, cumbersome WebPack configuration was required. Vue-cli 3.0 integrates webPack configuration and is greatly optimized for performance improvements. This project uses VUE-CLI3.0 to build and upgrade. The relevant precautions are summarized as follows. For detailed documents, please refer to the official website.

1. Vue-cli3.0 Introduction

Vue CLI package name changed from vue-cli to @vue/ CLI. If you already have an older version of VUE-CLI (1.x or 2.x) installed globally, you need to go through it first

NPM uninstall VUE -CLI -g or YARN Global remove VUE - CLICopy the code

Uninstall it. Vue CLI requires Node.js version 8.9 or later (8.11.0+ recommended). You can use NVM or NVM-Windows to manage multiple versions of Node on the same computer.

2. Vue-cli3.0 installation and use

1. Vue – cli3. X installation

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

If you want to retain the vuE-cli2. x syntax or use the 2.x template, you are advised to install CLI-init

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

2. Create a project using vue-cli3.x

Vue create Project name; Installation Procedure Select related configuration information until the installation is complete.

3. The new project needs to configure environment variables and modes

Create. Env. development and. Env. production files in the root directory to represent the development environment and build environment configurations, respectively. It is used to define environment variables and integrate them into different environments through NPM Run serve or NPM Run Build for interface invocation. The code is as follows:

.env.development

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

.env.production

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

The usage method, as in this project, is configured in utils/env.js with the following code:

// Development and production environments are differentlet app_url = process.env.VUE_APP_URL  
export default {
    app_url
}
Copy the code

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

Since vuE-cli3. x is used to generate the project, the webpack configuration has been integrated into node_module. If you want to configure webpack in detail, you need to create the vue.config.js file in the root directory of the project.

const TerserPlugin = require('terser-webpack-plugin'Const path = require(const path = require()'path');
const resolve = dir => {
  return path.join(__dirname, dir);
};

const env = process.env.NODE_ENV
letVUE_APP_URL // Development and production environments are different module.exports = {publicPath:'/',
  outputDir: './dist',
  lintOnSave: false// turn eslint off // Does not generate.map file productionSourceMap:false,
  devServer: {
    open: true,
    host: '0.0.0.0', port: 8808 // Since the project data is simulated by Easy-mock and MockJS, there is no cross-domain problem and no need to configure proxy; // proxy: { //'/v2': {
    //       target: target,
    //       changeOrigin: true//} //}}, // webPack related configuration chainWebpack: (config) => {config.entry.app = ['./src/main.js'];
    config.resolve.alias
      .set(The '@', resolve('src'))
      .set('cps', resolve('src/components')}, configureWebpack:config => {// Modify the configuration for the production environment...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 the configuration for the development environment... }, // pluginOptions: {}}Copy the code

The project is configured and all dependencies are installed. Execute the development environment package command: NPM run serve, you can run the project; Run the NPM run build command to generate the production environment file.

At the end

Project development so far, some basic functions have been completed, basically can meet the needs of the project. The next article will continue to introduce “implementation details of project sharing function”, “project deployment details and precautions (including how to deploy subdirectories)”,” project performance optimization details “, we hope you will look forward to ~

Technology of answering questions

Project Description:

Small love ADMIN is a completely open source free management system integration scheme, can be directly applied to the relevant background management system template; Many of the key points are carefully annotated and explained. If you also like front-end development, welcome to join our discussion/learning group, where you can ask questions and share learning materials; Welcome to q&A QQ group.