Vue: transformación de proyecto de plantilla de vue-admin-template: obtención dinámica de enrutamiento de menú

Dirección de demostración de GitHub

vista previa en línea

prefacio

1. El proyecto en la demostración ha agregado TagsViewfunciones y control de permisos local.
Acerca de TagsViewla adición de funciones, puede ver: Vue - transformación de proyecto de plantilla de vue-admin-template: agregando la función TagsView
. Acerca rama de control de permisos vue-admin-template del control de permisos local referencia de código relacionado

2. Y el proyecto en la demostración tiene funciones basadas en 1. 增加TopHeader(顶栏)Muestra el título del proyecto y la información del usuario en la barra superior, que puede admitir la pantalla original, y también puede mostrar la barra superior a través de la configuración de
configuración 增加TopHeader(顶栏). puede ver: Vue - Transformación de proyecto de plantilla de Vue-admin-template: agregue TopHeader (barra superior)

Por lo tanto, el código del proyecto puede ser un poco diferente de la versión original vue-admin-template, dirección del código vue-admin-template

Control de permisos locales, específicamente, obtenga roles de usuario consultando la información del usuario, filtre rutas configuradas localmente a través de roles en la protección de rutas y genere una matriz de rutas para rutas que cumplan con los permisos de roles

La idea de obtener dinámicamente el enrutamiento del menú es en realidad la misma, excepto que la matriz de enrutamiento se obtiene del servidor, consultando la lista de menú de un rol y luego convirtiendo la matriz de menú obtenida en una matriz de enrutamiento en la protección de enrutamiento.

La implementación del enrutamiento dinámico se escribe con referencia a los problemas de vue-element-admin , problemas relacionados:
vue-element-admin/issues/167
vue-element-admin/issues/293
vue-element-admin/issues/3326# problemacomentario-832852647

punto clave

Principalmente en la lista del menú de la interfaz, cambie el padre componenta Layoutla cadena 'Layout',
childrenel componente: () => import('@/views/table/index'), cambie a la cadena 'table/index', y luego obtenga transferir de nuevo a los datos
!!!!!!!!!!!! 接口格式可以根据项目需要自定义,不一定非得按照这里的来

Formato de enrutamiento local:

  {
    
    
    path: '/example',
    component: Layout,
    redirect: '/example/table',
    name: 'Example',
    meta: {
    
     title: 'Example', icon: 'el-icon-s-help', roles: ['admin'] },
    children: [
      {
    
    
        path: 'table',
        name: 'Table',
        component: () => import('@/views/table/index'),
        meta: {
    
     title: 'Table', icon: 'table' }
      },
      {
    
    
        path: 'tree',
        name: 'Tree',
        component: () => import('@/views/tree/index'),
        meta: {
    
     title: 'Tree', icon: 'tree' }
      }
    ]
  },

Formato de enrutamiento de interfaz:

  {
    
    
    path: '/example',
    component: 'Layout',
    redirect: '/example/table',
    name: 'Example',
    meta: {
    
     title: '动态Example', icon: 'el-icon-s-help', roles: ['admin'] },
    children: [
      {
    
    
        path: 'table',
        name: 'Table',
        component: 'table/index',
        meta: {
    
     title: '动态Table', icon: 'table' }
      },
      {
    
    
        path: 'tree',
        name: 'Tree',
        component: 'tree/index',
        meta: {
    
     title: '动态Tree', icon: 'tree' }
      }
    ]
  },

Implementación

1. Interfaz

Debido a que es una solicitud de red simulada por simulacro, es necesario agregar una interfaz para obtener la lista del menú de usuario (puede copiar directamente la carpeta de roles debajo de la carpeta simulada en vue-element-admin al proyecto y luego modificarla)

Cree una carpeta de roles y un archivo index.js en el directorio simulado

const Mock = require('mockjs')
const {
    
     asyncRoutes } = require('./routes.js')
const routes = asyncRoutes
module.exports = [
  // mock get all routes form server
  {
    
    
    url: '/vue-element-admin/routes',
    type: 'get',
    response: _ => {
    
    
      return {
    
    
        code: 20000,
        data: routes
      }
    }
  }

Cree una carpeta de roles y un archivo route.js en el directorio simulado,
principalmente usando asyncRoutes

// Just a mock data

const constantRoutes = [
  {
    
    
    path: '/redirect',
    component: 'Layout',
    hidden: true,
    children: [
      {
    
    
        path: '/redirect/:path(.*)',
        component: 'redirect/index'
      }
    ]
  },
  {
    
    
    path: '/login',
    component: 'login/index',
    hidden: true
  },
  {
    
    
    path: '/404',
    component: '404',
    hidden: true
  },

  {
    
    
    path: '/',
    component: 'Layout',
    redirect: '/dashboard',
    children: [{
    
    
      path: 'dashboard',
      name: 'Dashboard',
      component: 'dashboard/index',
      meta: {
    
     title: 'Dashboard', icon: 'dashboard', affix: true }
    }]
  }
]

/**
 * asyncRoutes
 * the routes that need to be dynamically loaded based on user roles
 */
const asyncRoutes = [
  {
    
    
    path: '/example',
    component: 'Layout',
    redirect: '/example/table',
    name: 'Example',
    meta: {
    
     title: '动态Example', icon: 'el-icon-s-help', roles: ['admin'] },
    children: [
      {
    
    
        path: 'table',
        name: 'Table',
        component: 'table/index',
        meta: {
    
     title: '动态Table', icon: 'table' }
      },
      {
    
    
        path: 'tree',
        name: 'Tree',
        component: 'tree/index',
        meta: {
    
     title: '动态Tree', icon: 'tree' }
      }
    ]
  },
  {
    
    
    path: '/form',
    component: 'Layout',
    meta: {
    
     roles: ['admin'] },
    children: [
      {
    
    
        path: 'index',
        name: 'Form',
        component: 'form/index',
        meta: {
    
     title: '动态Form', icon: 'form' }
      }
    ]
  },
  {
    
    
    path: '/nested',
    component: 'Layout',
    redirect: '/nested/menu1',
    name: 'Nested',
    meta: {
    
     title: '动态Nested', icon: 'nested', roles: ['admin'] },
    children: [
      {
    
    
        path: 'menu1',
        component: 'nested/menu1/index', // Parent router-view
        name: 'Menu1',
        meta: {
    
     title: '动态Menu1' },
        children: [
          {
    
    
            path: 'menu1-1',
            component: 'nested/menu1/menu1-1',
            name: 'Menu1-1',
            meta: {
    
     title: '动态Menu1-1' }
          },
          {
    
    
            path: 'menu1-2',
            component: 'nested/menu1/menu1-2',
            name: 'Menu1-2',
            meta: {
    
     title: '动态Menu1-2' },
            children: [
              {
    
    
                path: 'menu1-2-1',
                component: 'nested/menu1/menu1-2/menu1-2-1',
                name: 'Menu1-2-1',
                meta: {
    
     title: '动态Menu1-2-1' }
              },
              {
    
    
                path: 'menu1-2-2',
                component: 'nested/menu1/menu1-2/menu1-2-2',
                name: 'Menu1-2-2',
                meta: {
    
     title: '动态Menu1-2-2' }
              }
            ]
          },
          {
    
    
            path: 'menu1-3',
            component: 'nested/menu1/menu1-3',
            name: 'Menu1-3',
            meta: {
    
     title: '动态Menu1-3' }
          }
        ]
      },
      {
    
    
        path: 'menu2',
        component: 'nested/menu2/index',
        meta: {
    
     title: '动态menu2' }
      }
    ]
  },
  {
    
    
    path: 'external-link',
    component: 'Layout',
    children: [
      {
    
    
        path: 'https://panjiachen.github.io/vue-element-admin-site/#/',
        meta: {
    
     title: '动态External Link', icon: 'link' }
      }
    ]
  },
  /** when your routing map is too long, you can split it into small modules **/
  // componentsRouter,
  // chartsRouter,

  // 404 page must be placed at the end !!!
  {
    
     path: '*', redirect: '/404', hidden: true }
]

module.exports = {
    
    
  constantRoutes,
  asyncRoutes
}

Luego, en mock/index.js, cite el rol recién agregado

const user = require('./user')
const role = require('./role/index') // 新加的
const table = require('./table')
const dict = require('./demos/dict')
const tables = require('./demos/tables')

const mocks = [
  ...user,
  ...role, // 新加的
  ...table,
  ...dict,
  ...tables
]

2. El front-end llama a la interfaz del menú y genera una matriz de enrutamiento

Primero implemente la interfaz simulada en src/api/roles.js (cree nuevos roles.j o colóquelo en el usuario)

import request from '@/utils/request'

export function getUserMenus() {
    
    
  return request({
    
    
    url: '/vue-element-admin/routes',
    method: 'get'
  })
}

Luego modifique src/store/modules/permission.js para implementar el método de convertir la matriz del menú de procesamiento en una matriz de enrutamiento

const {
    
     deepClone } = require('@/utils')

// 加载路由
export const loadView = (view) => {
    
    
  // 路由懒加载
  return (resolve) => require([`@/views/${
      
      view}`], resolve)
  // return (resolve) => require([`@${view}`], resolve)
}

/**
 * 通过递归格式化菜单路由 (配置项规则:https://panjiachen.github.io/vue-element-admin-site/zh/guide/essentials/router-and-nav.html#配置项)
 * @param routes
 * @param roles
 */
export function filterAsyncRoutes2(routes) {
    
    
  const res = []
  routes.forEach((route) => {
    
    
    const tmp = deepClone(route)
    if (route.component === 'Layout') {
    
    
      tmp.component = Layout
    } else if (route.component) {
    
    
      tmp.component = loadView(route.component)
    }
    if (route.children && route.children.length > 0) {
    
    
      tmp.children = filterAsyncRoutes2(route.children)
    }
    res.push(tmp)
  })
  return res
}

Agregar una nueva función en acciones

  generateDynamicRoutes({
     
      commit }, menus) {
    
    
    return new Promise(resolve => {
    
    
      const accessedRoutes = filterAsyncRoutes2(menus)
      commit('SET_ROUTES', accessedRoutes) // Todo: 内部拼接constantRoutes,所以查出来的菜单不用包含constantRoutes
      resolve(accessedRoutes)
    })
  }

Luego modifique src\store\modules\user.js
para agregar el método para obtener el menú de usuario

const state = {
    
    
// ```
  menus: [] //这个是我新增的
}

const mutations = {
    
    
	// ```
  SET_MENUS: (state, menus) => {
    
     //这里是新增的
    state.menus = menus
  }
}

  getUserMenus({
     
      commit, state }) {
    
    
    return new Promise((resolve, reject) => {
    
    
      getUserMenus(state.token).then(response => {
    
    
        const {
    
     data } = response
        if (!data) {
    
    
          reject('Verification failed, please Login again.')
        }
        const menus = data
        // roles must be a non-empty array
        if (!menus || menus.length <= 0) {
    
    
          reject('getMenus: menus must be a non-null array!')
        }
        commit('SET_MENUS', menus)
        resolve(menus)
      }).catch(error => {
    
    
        reject(error)
      })
    })
  },

Luego modifique la protección de ruta de src\permission.js
para que se cambie cerca del código store.dispatch('permission/generateRoutes').La ruta del usuario se obtuvo originalmente de la configuración local + roles, y ahora se obtiene del servidor anterior agregandoRutas

He agregado un rol al simulacro aquí editor2. Al editor2iniciar sesión, la ruta dinámica se obtiene del servidor y otros roles obtienen la ruta del local.

 const {
    
     roles } = await store.dispatch('user/getInfo')
 // console.log('roles', JSON.stringify(roles))

 var accessRoutes = []

 if (roles.includes('editor2')) {
    
    
   // 根据服务器获取的用户菜单生成路由
   const menus = await store.dispatch('user/getUserMenus')
   const asyncRoutes = await store.dispatch('permission/generateDynamicRoutes', menus)
   accessRoutes = asyncRoutes
 } else {
    
    
   // generate accessible routes map based on roles
   accessRoutes = await store.dispatch('permission/generateRoutes', roles)
 }
 
 // dynamically add accessible routes
 router.addRoutes(accessRoutes)

Eso es todo.

Guess you like

Origin blog.csdn.net/iotjin/article/details/130400860