The front end implements dynamic routing (the back end returns authority routing)

insert image description here

Implementation ideas

1. The front end defines a static route (login login page, which is a default route that does not require permissions)
2. When the user logs in, adjust the interface to obtain user information, and then log in to the home page
3. Define the format of the route returned by the front and rear ends
4. In the route navigation hook In beforeEach, adjust the interface to obtain dynamic routing, recursively process the data as routing data available at the front end, and use router.addRouters() to add routing

Step 1: Define a static route on the front end (the default route of the login page that does not require permissions)

router/index.js

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

Vue.use(VueRouter)

// 解决重复点击路由报错的BUG
// 下面这段代码主要解决这个问题 :Uncaught (in promise) Error: Redirected when going from "/login" to "/index" via a navigation guard.
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
    
    
  return originalPush.call(this, location).catch(err => err)
}

// 定义好静态路由
const routes = [
  {
    
    
    path: '/login',
    name: 'login',
    component: () => import('../views/login'),
    hidden: true,
  },
]

const router = new VueRouter({
    
    
  mode: 'history',
  base: process.env.BASE_URL,
  routes,
})

export default router

Step 2: When the user logs in, call the interface to obtain user information, and then log in to the home page

login/index.vue

methods: {
    
    
    login () {
    
    
      this.$refs.userForm.validate((valid) => {
    
    
        if (valid) {
    
    
          // 模拟登录接口去请求用户数据
          setTimeout(() => {
    
    
            // 这里的res就是模拟后台返回的用户数据
            const res = dynamicUserData.filter((item) => item.username === this.user.username)[0]
            console.log(res)
            // 存储用户的信息及token到vuex,并做sessionStorage持久化处理
            this.$store.commit('User/saveUserInfo',res)
            Message({
    
     type: 'success', message: "登录成功", showClose: true, duration: 3000 })
            this.$router.push({
    
     path: "/index" })
          }, 1000)
        } else return false
      })
    }
  }

Attachment: vuex persistence processing: use vuex-persistedstateplug-ins to store the contents of the User warehouse in sessionStorage

import Vue from 'vue'
import Vuex from 'vuex'
import User from './modules/user'
import permission from './modules/permission'
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)

export default new Vuex.Store({
    
    
  state: {
    
    },
  mutations: {
    
    },
  actions: {
    
    },
  modules: {
    
    
    User,
    permission,
  },
  plugins: [
    createPersistedState({
    
    
      storage: window.sessionStorage, // 可选sessionStorage localStorage
      reducer(val) {
    
    
        return {
    
    
          User: val.User,
        }
      },
    }),
  ],
})

Step 3: Define the format returned by the route at the front and back ends

// 后台返回的数据结构
const dynamicUser = [
  {
    
    
    name: '管理员',
    avatar: 'https://sf3-ttcdn-tos.pstatp.com/img/user-avatar/ccb565eca95535ab2caac9f6129b8b7a~300x300.image',
    desc: '管理员 - admin',
    username: 'admin',
    password: '654321',
    token: 'rtVrM4PhiFK8PNopqWuSjsc1n02oKc3f',
    routes: [
      {
    
    
        id: 1,
        name: '/',
        path: '/',
        component: 'Layout',
        redirect: '/index',
        hidden: false,
        children: [{
    
     name: 'index', path: '/index', meta: {
    
     title: 'index' }, component: 'index/index' }],
      },
      {
    
    
        id: 2,
        name: '/form',
        path: '/form',
        component: 'Layout',
        redirect: '/form/index',
        hidden: false,
        children: [{
    
     name: '/form/index', path: '/form/index', meta: {
    
     title: 'form' }, component: 'form/index' }],
      },
      {
    
    
        id: 3,
        name: '/example',
        path: '/example',
        component: 'Layout',
        redirect: '/example/tree',
        meta: {
    
     title: 'example' },
        hidden: false,
        children: [
          {
    
     name: '/tree', path: '/example/tree', meta: {
    
     title: 'tree' }, component: 'tree/index' },
          {
    
     name: '/copy', path: '/example/copy', meta: {
    
     title: 'copy' }, component: 'tree/copy' },
        ],
      },
      {
    
    
        id: 4,
        name: '/table',
        path: '/table',
        component: 'Layout',
        redirect: '/table/index',
        hidden: false,
        children: [{
    
     name: '/table/index', path: '/table/index', meta: {
    
     title: 'table' }, component: 'table/index' }],
      },
      {
    
    
        id: 5,
        name: '/admin',
        path: '/admin',
        component: 'Layout',
        redirect: '/admin/index',
        hidden: false,
        children: [{
    
     name: '/admin/index', path: '/admin/index', meta: {
    
     title: 'admin' }, component: 'admin/index' }],
      },
      {
    
    
        id: 6,
        name: '/people',
        path: '/people',
        component: 'Layout',
        redirect: '/people/index',
        hidden: false,
        children: [{
    
     name: '/people/index', path: '/people/index', meta: {
    
     title: 'people' }, component: 'people/index' }],
      },
    ],
  },
  {
    
    
    name: '普通用户',
    avatar: 'https://sf1-ttcdn-tos.pstatp.com/img/user-avatar/6364348965908f03e6a2dd188816e927~300x300.image',
    desc: '普通用户 - people',
    username: 'people',
    password: '123456',
    token: '4es8eyDwznXrCX3b3439EmTFnIkrBYWh',
    routes: [
      {
    
    
        id: 1,
        name: '/',
        path: '/',
        component: 'Layout',
        redirect: '/index',
        hidden: false,
        children: [{
    
     name: 'index', path: '/index', meta: {
    
     title: 'index' }, component: 'index/index' }],
      },
      {
    
    
        id: 2,
        name: '/form',
        path: '/form',
        component: 'Layout',
        redirect: '/form/index',
        hidden: false,
        children: [{
    
     name: '/form/index', path: '/form/index', meta: {
    
     title: 'form' }, component: 'form/index' }],
      },
      {
    
    
        id: 3,
        name: '/example',
        path: '/example',
        component: 'Layout',
        redirect: '/example/tree',
        meta: {
    
     title: 'example' },
        hidden: false,
        children: [
          {
    
     name: '/tree', path: '/example/tree', meta: {
    
     title: 'tree' }, component: 'tree/index' },
          {
    
     name: '/copy', path: '/example/copy', meta: {
    
     title: 'copy' }, component: 'tree/copy' },
        ],
      },
      {
    
    
        id: 4,
        name: '/table',
        path: '/table',
        component: 'Layout',
        redirect: '/table/index',
        hidden: false,
        children: [{
    
     name: '/table/index', path: '/table/index', meta: {
    
     title: 'table' }, component: 'table/index' }],
      },
      {
    
    
        id: 6,
        name: '/people',
        path: '/people',
        component: 'Layout',
        redirect: '/people/index',
        hidden: false,
        children: [{
    
     name: '/people/index', path: '/people/index', meta: {
    
     title: 'people' }, component: 'people/index' }],
      },
    ],
  },
]

export default dynamicUser

Step 4: Routing navigation hook beforeEach processing

Routing hook logic:

是否为白名单:
	是:  直接进入
	不是:判断是否有token
		无token:跳转到login登录页
		有token:判断用户路由状态(是否有路由)
			有路由: 直接进入
			无路由: 调接口获取动态路由
					递归处理返回的路由
					将递归处理好的路由存储到vuex,设置用户路由状态为true
					使用router.addRouters()添加路由

Routing navigation guard:

import router from './index'
import Layout from '../layout/index'
import NProgress from 'nprogress' // progress bar
import store from '@/store'
import menu from '@/mock/menu.js'

// 路由拼接
function loadView(view) {
    
    
  return () => import(`@/views/${
      
      view}`)
}
// 路由过滤   遍历路由 转换为组件对象和路径
function filterASyncRoutes(data) {
    
    
  // console.log(data)
  const routes = data.filter(item => {
    
    
    if (item['component'] === 'Layout') {
    
    
      item.component = Layout
    } else {
    
    
      item['component'] = loadView(item['component'])
    }
    // 路由递归,转换组件对象和路径
    if (item['children'] && item['children'].length > 0) {
    
    
      item['children'] = filterASyncRoutes(item.children)
    }
    return true
  })
  // 排序
  routes.sort((a, b) => a['id'] - b['id'])
  return routes
}

NProgress.configure({
    
     showSpinner: false }) // NProgress Configuration

// 白名单页面直接进入
const whiteList = ['/login']

router.beforeEach((to, from, next) => {
    
    
  NProgress.start()
  // 白名单页面,不管是否有token,是否登录都直接进入
  if (whiteList.indexOf(to.path) !== -1) {
    
    
    next()
    return false
  }
  // 有token(代表了有用户信息,但是不确定有没有路由信息)
  if (store.state.User.token) {
    
    
    // 判断当前用户是否是登录状态, 是登录状态则一定有路由,直接放行,不是登录状态则去获取路由菜单登录
    // 刷新时store.state.routerList.hasRoutes会重置为false,重新去获取 异步路由
    if (!store.state.routerList.hasRoutes) {
    
    
      setTimeout(() => {
    
    
        const res = menu.filter(item => item.token === store.state.User.token)[0].routes
        const asyncRouter = filterASyncRoutes(res) // 递归处理后台返回的路由
        store.commit('routerList/setRouterList', asyncRouter) // 存储到异步的路由到vuex
        store.commit('routerList/setHasRoutes', true) // 设置登录状态为true
        router.addRoutes(asyncRouter) // 动态添加可访问路由表
        next({
    
     ...to, replace: true }) // hack方法 router.addRoutes之后的next()可能会失效,可能next()的时候路由并没有完全add完成 通过next(to)解决
      }, 500)
    } else {
    
    
      next() //当有用户权限的时候,说明所有可访问路由已生成 如访问没权限的全面会自动进入404页面
    }
  }else {
    
    
    next({
    
    path:'/login'})
  }
})

router.afterEach(() => {
    
    
  // finish progress bar
  NProgress.done()
})

注意
Here, the data of asynchronous routing and routing status are placed in routerList.js, but here is the reason why there is no caching
: when the user refreshes, the data here is reset, and then when the hook is re-routed, the interface will be adjusted to obtain routing information. .
(Why not put the route in sessionStorage? The reason is that the components in the route array cannot be saved in sessionStorage, () => import(@/views/${userAuth.component}) cannot be saved in sessionStorage)

The obtained user information needs to be stored in vuex and persisted, but the obtained menu is not persisted in vuex, so that the user will clear it when refreshing, and re-take the interface of obtaining the menu, which is the following code

if (!store.state.routerList.hasRoutes) {
    
    
 setTimeout(() => {
    
    
    const res = menu.filter(item => item.token === store.state.User.token)[0].routes
    const asyncRouter = filterASyncRoutes(res) // 递归处理后台返回的路由
    store.commit('routerList/setRouterList', asyncRouter) // 存储到异步的路由到vuex
    store.commit('routerList/setHasRoutes', true) // 设置登录状态为true
    router.addRoutes(asyncRouter) // 动态添加可访问路由表
    next({
    
     ...to, replace: true }) // hack方法 router.addRoutes之后的next()可能会失效,可能next()的时候路由并没有完全add完成 通过next(to)解决
  }, 500)
}

Our company is currently using back-end return routing to do authority management

hope it can help you

The reference code is in github Give me a star

Guess you like

Origin blog.csdn.net/weixin_44582045/article/details/132578620