preface

This article mainly introduces the user login and authority management, for background management, user authentication and authority management is a very core function module, so in the implementation of specific business functions before the code, we first masturbate a relatively simple authority management function.

Previous post portal

Build Vite2+Vue3 Family barrel from zero (4)

Build Vite2+Vue3 Family barrel from zero (3)

Build Vite2+Vue3 Family barrel from zero (2)

Build Vite2+Vue3 Family barrel from zero (1)

Implementation approach

  • User login: A user enters the account password and requests to log in to the interface. The server verifies the account password and returns the token after the authentication succeeds.

  • User information: The front-end invokes the interface based on the token to obtain user information (basic user information and permissions).

  • Menu permission: Dynamically renders a menu route by router.addRoute based on the user permission information obtained in the background.

  • Button permission: dynamically render the button with custom instructions according to the user permission information obtained from the background.

The user login

A basic login function is simulated here. Login by account + password. After entering the account password, click the login button.

Click open source >>>

Login.vue

<template> <div class="login-container"> <el-form ref="loginForm" :model="state.loginForm" :rules="state.loginRules" Class ="login-form" auto-complete="on" label-position="left" > <div class="title-container"> <h3 class="title"> Login </h3> </div> <el-form-item prop="username"> <span class="svg-container"> <svg-icon name="user" /> </span> <el-input v-model="state.loginForm.username" placeholder="Username" name="username" type="text" tabindex="1" auto-complete="on" />  </el-form-item> <el-form-item prop="password"> <span class="svg-container"> <svg-icon name="password" icon-class="password" /> </span> <el-input v-model="state.loginForm.password" placeholder="Password" tabindex="2" auto-complete="on" show-password @keyup.enter="handleLogin" /> </el-form-item> <el-button :loading="loading" type="primary" style="width: 100%; margin-bottom: 30px" @click="handleLogin" > </el-button > </el-form> </div> </template> <script setup> import {ref, reactive } from 'vue' import { useStore } from 'vuex' import { ElMessage } from 'element-plus' import { useRouter } from  'vue-router' const router = useRouter() const state = reactive({ loginForm: { username: '', password: '' }, loginRules: { username: [ { required: true, trigger: 'blur', validator: (rule, value, callback) => {if (! Value) {callback(new Error(' please input the login account '))} else {callback()}}}], password: [ { required: true, trigger: 'blur', validator: (rule, value, Callback) => {if (value.trim() === '') {callback(new Error(' please input user password '))} else {callback()}}]}}) const store = useStore() const loginForm = ref(null) const loading = ref(false) function handleLogin() { loginForm.value.validate((valid) => { if (valid) { loading.value = true store .dispatch('user/login', state.loginForm) .then(() => { loading.value = false router.push({ path: '/'})}). The catch (() = > {loading. The value = false ElMessage. Error (' user name or password error ')})}})} < / script >Copy the code

action

login({ commit, dispatch }, userInfo) { return new Promise((resolve, reject) => { login(userInfo) .then((response) => { const { data } = response commit('SET_TOKEN', Data.token) // Store user token resolve()}). Catch ((error) => {reject(error)})})}Copy the code

The user information

After a user passes login authentication, the interface is invoked to obtain the user’s basic information and user permissions.

  • Navigate guard by routerouter.beforeEachTo intercept the route.
  • Check whether the token is valid
  • Check whether the current route is a non-login page
  • Check whether user information has been obtained
const whiteList = ['/login'] router.beforeEach((to, form, Next) => {const hasToken = getToken() if (hasToken) {if (to.path === '/login') { '/'}) NProgress. Done ()} else {try {/ / determine whether has access to user information if (store. Getters. The userInfo. UserName) {next ()} else {/ / Store. Dispatch ('user/getUserInfo'). Then (() => {next({path: '/', replace: true }) }) } } catch (error) { next(`/login? redirect=${to.path}`) NProgress.done() } } } else if (whiteList.includes(to.path)) { next() } else { next(`/login? redirect=${to.path}`) NProgress.done() } })Copy the code

Menu permissions

This section only describes the implementation logic of dynamic routing, and does not involve the development of specific menu display components. Intercept routes for permission control by routing navigation guard router.beforeEach, dynamically rendering menu routes by router.addRoute.

  • Check whether the token is valid
  • Check whether the current route is a non-login page
  • Check whether user information has been obtained
  • Check whether dynamic routes have been loaded
  • Recursive traversal of dynamic render menu routing
const whiteList = ['/login'] const modules = import.meta.glob('.. /**/**/**.vue') Const readNodes = (Nodes = [], routerInfo, parent) => { nodes.forEach((res) => { let name = '' res.component = modules[`../${res.componentPath}.vue`] if (parent) {  routerInfo.addRoute(parent, res) } else { name = res.name routerInfo.addRoute(res) } if (res.children && res.children.length) { readNodes(res.children, router, name) } }) } router.beforeEach((to, form, Next) => {const hasToken = getToken() if (hasToken) {if (to.path === '/login') { '/'}) NProgress. Done ()} else {try {/ / determine whether has access to user information if (store. Getters. The userInfo. UserName) {/ / router. DefaultRouters To customize variables during router initialization, Main storage if the default routing data (router. DefaultRouters. Length. = = = the router getRoutes (.) length) {/ / judgment by default routing data // Call the interface for obtaining User information to obtain user routing information. Store.dispatch ('user/getUserInfo'). Then (() => {// Recursive traversal interface returns the routing data, dynamic routing readNodes rendering into the menu (store) getters) role, menulist, router) next ({path: '/', replace: Store. Dispatch ('user/getUserInfo'). Then (() => {// Recursive traversal interface returns the routing data, dynamic routing readNodes rendering into the menu (store) getters) role, menulist, router) next ({path: '/', replace: true }) }) } } catch (error) { next(`/login?redirect=${to.path}`) NProgress.done() } } } else if (whiteList.includes(to.path)) { next() } else { next(`/login?redirect=${to.path}`) NProgress.done() } })Copy the code

Button permissions

We need to do button permission control according to actual requirements.

  • In the case of few application scenarios, you can use V-IF to manually check button permissions.
  • Most pages generally need to control button permissions, we can encapsulate a custom directive to do permission control.

Permission data Demo

{path: '/order/list', name: 'orderList', meta: {title: 'order', icon: 'Order ', Auth: ['create', 'edit']}, Component: 'views/order/OrderList' }Copy the code

Encapsulate custom directives

// hasPermission.js import route from '@/router/index' export const hasPermission = (el, Binding) => {// Determine whether the button is in the list of button permissions that the current route has. .value && route.currentRoute.value? .meta? .auth) { if (! route.currentRoute.value.meta.auth.includes(binding.value)) { el.remove() } } } export default hasPermissionCopy the code

Encapsulate the custom directive initialization file and create directive/index.js

import hasPermission from './hasPermission'

export default {
  install(app) {
    app.directive('has', {
      mounted: (el, binding) => {
        hasPermission(el, binding)
      }
    })
  }
}

Copy the code

Modify the main js

import directive from './directive'
const app = createApp(App)
app.use(directive)
Copy the code

Vue constructs use directives

<el-button @click="addOrder" v-has="'create'"> </el-button>Copy the code

Here only provides a general permission control implementation ideas, can be optimized according to the actual situation of each company; Wait for free time to sort out a complete series of code into Git.

Portal to the future

Build Vite2+Vue3 Family barrel from zero (4)

Build Vite2+Vue3 Family barrel from zero (3)

Build Vite2+Vue3 Family barrel from zero (2)

Build Vite2+Vue3 Family barrel from zero (1)

Architecture design based on Vue