Vue dynamic routing usage (backend control)

Using VUE to develop a backend management system, the left menu items are completely controlled by the backend.

In the traditional development of backend management systems, the functional requirement of permission control is involved: use the functions of the account according to the different logged-in role accounts. In other words, the menu bar on the left side of the system is not fixed, but depends on the login. Account permissions are dynamically controlled. There are currently two mainstream modes
: 1. Front-end and back-end cooperative control 2. Completely controlled by the back-end

This chapter focuses on the second mode: back-end data controls the front-end menu

  1. Use Ant Design Pro Vue to explain implementation ideas (Ant-Design-Pro Vue is an out-of-the-box enterprise backend management system)
  2. Code cloud address: https://gitee.com/sendya/ant-design-pro-vue.git

1. Implementation ideas

  1. The backend provides a routing interface and the frontend requests data and then processes it twice.

  2. Data requested by the front endInsert image description here

  3. Front-end data processing logic

// eslint-disable-next-line
import * as loginService from '@/api/login'
// eslint-disable-next-line
import {
    
     BasicLayout, BlankLayout, PageView, RouteView } from '@/layouts'

// 前端路由表  
const constantRouterComponents = {
    
    
  // 基础页面 layout 必须引入
  BasicLayout: BasicLayout,
  BlankLayout: BlankLayout,
  RouteView: RouteView,
  PageView: PageView,
  '403': () => import(/* webpackChunkName: "error" */ '@/views/exception/403'),
  '404': () => import(/* webpackChunkName: "error" */ '@/views/exception/404'),
  '500': () => import(/* webpackChunkName: "error" */ '@/views/exception/500'),

  // 你需要动态引入的页面组件
  'Workplace': () => import('@/views/dashboard/Workplace'),
  'Analysis': () => import('@/views/dashboard/Analysis'),

  // form
  'BasicForm': () => import('@/views/form/basicForm'),
  'StepForm': () => import('@/views/form/stepForm/StepForm'),
  'AdvanceForm': () => import('@/views/form/advancedForm/AdvancedForm'),

  // // list
  'TableList': () => import('@/views/list/TableList'),
  // 'StandardList': () => import('@/views/list/StandardList'),
  'CardList': () => import('@/views/list/CardList'),
  'SearchLayout': () => import('@/views/list/search/SearchLayout'),
  'SearchArticles': () => import('@/views/list/search/Article'),
  'SearchProjects': () => import('@/views/list/search/Projects'),
  'SearchApplications': () => import('@/views/list/search/Applications'),
  'ProfileBasic': () => import('@/views/profile/basic'),
  'ProfileAdvanced': () => import('@/views/profile/advanced/Advanced'),

  // result
  'ResultSuccess': () => import(/* webpackChunkName: "result" */ '@/views/result/Success'),
  'ResultFail': () => import(/* webpackChunkName: "result" */ '@/views/result/Error'),

  // exception
  'Exception403': () => import(/* webpackChunkName: "fail" */ '@/views/exception/403'),
  'Exception404': () => import(/* webpackChunkName: "fail" */ '@/views/exception/404'),
  'Exception500': () => import(/* webpackChunkName: "fail" */ '@/views/exception/500'),

  // account
  'AccountCenter': () => import('@/views/account/center'),
  'AccountSettings': () => import('@/views/account/settings/Index'),
  'BaseSettings': () => import('@/views/account/settings/BaseSetting'),
  'SecuritySettings': () => import('@/views/account/settings/Security'),
  'CustomSettings': () => import('@/views/account/settings/Custom'),
  'BindingSettings': () => import('@/views/account/settings/Binding'),
  'NotificationSettings': () => import('@/views/account/settings/Notification'),

  'TestWork': () => import(/* webpackChunkName: "TestWork" */ '@/views/dashboard/TestWork')
}

// 前端未找到页面路由(固定不用改)
const notFoundRouter = {
    
    
  path: '*', redirect: '/404', hidden: true
}

// 根级菜单
const rootRouter = {
    
    
  key: '',
  name: 'index',
  path: '',
  component: 'BasicLayout',
  redirect: '/dashboard',
  meta: {
    
    
    title: '首页'
  },
  children: []
}

/**
 * 动态生成菜单
 * @param token
 * @returns {Promise<Router>}
 */
export const generatorDynamicRouter = (token) => {
    
    
  return new Promise((resolve, reject) => {
    
    
    loginService.getCurrentUserNav(token).then(res => {
    
    
      console.log('res', res) //后端请求到的数据
      const {
    
     result } = res
      const menuNav = []
      const childrenNav = []
      //      后端数据, 根级树数组,  根级 PID
      listToTree(result, childrenNav, 0) //路由数据 第一次处理
      rootRouter.children = childrenNav  
      menuNav.push(rootRouter)
      console.log('menuNav', menuNav)
      const routers = generator(menuNav) //路由数据 第二次处理
      routers.push(notFoundRouter)
      console.log('routers', routers)
      resolve(routers)
    }).catch(err => {
    
    
      reject(err)
    })
  })
}

/**
 * 格式化树形结构数据 生成 vue-router 层级路由表
 *
 * @param routerMap
 * @param parent
 * @returns {*}
 */
 //路由数据第二次处理  匹配加载组件 生成路由path 
export const generator = (routerMap, parent) => {
    
       
  return routerMap.map(item => {
    
    
    const {
    
     title, show, hideChildren, hiddenHeaderContent, target, icon } = item.meta || {
    
    }
    const currentRouter = {
    
    
      // 如果路由设置了 path,则作为默认 path,否则 路由地址 动态拼接生成如 /dashboard/workplace
      path: item.path || `${
      
      parent && parent.path || ''}/${
      
      item.key}`,
      // 路由名称,建议唯一
      name: item.name || item.key || '',
      // 该路由对应页面的 组件 :方案1
      // component: constantRouterComponents[item.component || item.key],
      // 该路由对应页面的 组件 :方案2 (动态加载)
      component: (constantRouterComponents[item.component || item.key]) || (() => import(`@/views/${
      
      item.component}`)),
      // meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
      meta: {
    
    
        title: title,
        icon: icon || undefined,
        hiddenHeaderContent: hiddenHeaderContent,
        target: target,
        permission: item.name
      }
    }
    // 是否设置了隐藏菜单
    if (show === false) {
    
    
      currentRouter.hidden = true
    }
    // 是否设置了隐藏子菜单
    if (hideChildren) {
    
    
      currentRouter.hideChildrenInMenu = true
    }
    // 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
    if (!currentRouter.path.startsWith('http')) {
    
    
      currentRouter.path = currentRouter.path.replace('//', '/')
    }
    // 重定向
    item.redirect && (currentRouter.redirect = item.redirect)
    // 是否有子菜单,并递归处理
    if (item.children && item.children.length > 0) {
    
    
      // Recursion
      currentRouter.children = generator(item.children, currentRouter)
    }
    return currentRouter
  })
}

/**
 * 数组转树形结构
 * @param list 源数组
 * @param tree 树
 * @param parentId 父ID
 */
 // 路由数据第一次 处理 将list结构转化为 tree
const listToTree = (list, tree, parentId) => {
    
    
  list.forEach(item => {
    
    
    // 判断是否为父级菜单
    if (item.parentId === parentId) {
    
    
      const child = {
    
    
        ...item,
        key: item.key || item.name,
        children: []
      }
    
      // 迭代 list, 找到当前菜单相符合的所有子菜单
      listToTree(list, child.children, item.id)
      // 删掉不存在 children 值的属性
      if (child.children.length <= 0) {
    
    
        delete child.children
      }
      // 加入到树中
      tree.push(child)
    }
  })
}

The final generated routing data
Insert image description here

{
    
    
    path: '/',
    name: 'index',
    component: BasicLayout,
    meta: {
    
     title: 'menu.home' },
    redirect: '/dashboard/workplace',
    children: [
      // dashboard
      {
    
    
        path: '/dashboard',
        name: 'dashboard',
        redirect: '/dashboard/workplace',
        component: RouteView,
        meta: {
    
     title: 'menu.dashboard', keepAlive: true, icon: bxAnaalyse, permission: [ 'dashboard' ] },
        children: [
          {
    
    
            path: '/dashboard/analysis/:pageNo([1-9]\\d*)?',
            name: 'Analysis',
            component: () => import('@/views/dashboard/Analysis'),
            meta: {
    
     title: 'menu.dashboard.analysis', keepAlive: false, permission: [ 'dashboard' ] }
          },
          // 外部链接
          {
    
    
            path: 'https://www.baidu.com/',
            name: 'Monitor',
            meta: {
    
     title: 'menu.dashboard.monitor', target: '_blank' }
          },
          {
    
    
            path: '/dashboard/workplace',
            name: 'Workplace',
            component: () => import('@/views/dashboard/Workplace'),
            meta: {
    
     title: 'menu.dashboard.workplace', keepAlive: true, permission: [ 'dashboard' ] }
          }
        ]
      },

      // forms
      {
    
    
        path: '/form',
        redirect: '/form/base-form',
        component: RouteView,
        meta: {
    
     title: '表单页', icon: 'form', permission: [ 'form' ] },
        children: [
          {
    
    
            path: '/form/base-form',
            name: 'BaseForm',
            component: () => import('@/views/form/basicForm'),
            meta: {
    
     title: '基础表单', keepAlive: true, permission: [ 'form' ] }
          },
          {
    
    
            path: '/form/step-form',
            name: 'StepForm',
            component: () => import('@/views/form/stepForm/StepForm'),
            meta: {
    
     title: '分步表单', keepAlive: true, permission: [ 'form' ] }
          },
          {
    
    
            path: '/form/advanced-form',
            name: 'AdvanceForm',
            component: () => import('@/views/form/advancedForm/AdvancedForm'),
            meta: {
    
     title: '高级表单', keepAlive: true, permission: [ 'form' ] }
          }
        ]
      },

      // list
      {
    
    
        path: '/list',
        name: 'list',
        component: RouteView,
        redirect: '/list/table-list',
        meta: {
    
     title: '列表页', icon: 'table', permission: [ 'table' ] },
        children: [
          {
    
    
            path: '/list/table-list/:pageNo([1-9]\\d*)?',
            name: 'TableListWrapper',
            hideChildrenInMenu: true, // 强制显示 MenuItem 而不是 SubMenu
            component: () => import('@/views/list/TableList'),
            meta: {
    
     title: '查询表格', keepAlive: true, permission: [ 'table' ] }
          },
          {
    
    
            path: '/list/basic-list',
            name: 'BasicList',
            component: () => import('@/views/list/BasicList'),
            meta: {
    
     title: '标准列表', keepAlive: true, permission: [ 'table' ] }
          },
          {
    
    
            path: '/list/card',
            name: 'CardList',
            component: () => import('@/views/list/CardList'),
            meta: {
    
     title: '卡片列表', keepAlive: true, permission: [ 'table' ] }
          },
          {
    
    
            path: '/list/search',
            name: 'SearchList',
            component: () => import('@/views/list/search/SearchLayout'),
            redirect: '/list/search/article',
            meta: {
    
     title: '搜索列表', keepAlive: true, permission: [ 'table' ] },
            children: [
              {
    
    
                path: '/list/search/article',
                name: 'SearchArticles',
                component: () => import('../views/list/search/Article'),
                meta: {
    
     title: '搜索列表(文章)', permission: [ 'table' ] }
              },
              {
    
    
                path: '/list/search/project',
                name: 'SearchProjects',
                component: () => import('../views/list/search/Projects'),
                meta: {
    
     title: '搜索列表(项目)', permission: [ 'table' ] }
              },
              {
    
    
                path: '/list/search/application',
                name: 'SearchApplications',
                component: () => import('../views/list/search/Applications'),
                meta: {
    
     title: '搜索列表(应用)', permission: [ 'table' ] }
              }
            ]
          }
        ]
      },

      // profile
      {
    
    
        path: '/profile',
        name: 'profile',
        component: RouteView,
        redirect: '/profile/basic',
        meta: {
    
     title: '详情页', icon: 'profile', permission: [ 'profile' ] },
        children: [
          {
    
    
            path: '/profile/basic',
            name: 'ProfileBasic',
            component: () => import('@/views/profile/basic'),
            meta: {
    
     title: '基础详情页', permission: [ 'profile' ] }
          },
          {
    
    
            path: '/profile/advanced',
            name: 'ProfileAdvanced',
            component: () => import('@/views/profile/advanced/Advanced'),
            meta: {
    
     title: '高级详情页', permission: [ 'profile' ] }
          }
        ]
      },

      // result
      {
    
    
        path: '/result',
        name: 'result',
        component: RouteView,
        redirect: '/result/success',
        meta: {
    
     title: '结果页', icon: 'check-circle-o', permission: [ 'result' ] },
        children: [
          {
    
    
            path: '/result/success',
            name: 'ResultSuccess',
            component: () => import(/* webpackChunkName: "result" */ '@/views/result/Success'),
            meta: {
    
     title: '成功', keepAlive: false, hiddenHeaderContent: true, permission: [ 'result' ] }
          },
          {
    
    
            path: '/result/fail',
            name: 'ResultFail',
            component: () => import(/* webpackChunkName: "result" */ '@/views/result/Error'),
            meta: {
    
     title: '失败', keepAlive: false, hiddenHeaderContent: true, permission: [ 'result' ] }
          }
        ]
      },

      // Exception
      {
    
    
        path: '/exception',
        name: 'exception',
        component: RouteView,
        redirect: '/exception/403',
        meta: {
    
     title: '异常页', icon: 'warning', permission: [ 'exception' ] },
        children: [
          {
    
    
            path: '/exception/403',
            name: 'Exception403',
            component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/403'),
            meta: {
    
     title: '403', permission: [ 'exception' ] }
          },
          {
    
    
            path: '/exception/404',
            name: 'Exception404',
            component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/404'),
            meta: {
    
     title: '404', permission: [ 'exception' ] }
          },
          {
    
    
            path: '/exception/500',
            name: 'Exception500',
            component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/500'),
            meta: {
    
     title: '500', permission: [ 'exception' ] }
          }
        ]
      },

      // account
      {
    
    
        path: '/account',
        component: RouteView,
        redirect: '/account/center',
        name: 'account',
        meta: {
    
     title: '个人页', icon: 'user', keepAlive: true, permission: [ 'user' ] },
        children: [
          {
    
    
            path: '/account/center',
            name: 'center',
            component: () => import('@/views/account/center'),
            meta: {
    
     title: '个人中心', keepAlive: true, permission: [ 'user' ] }
          },
          {
    
    
            path: '/account/settings',
            name: 'settings',
            component: () => import('@/views/account/settings/Index'),
            meta: {
    
     title: '个人设置', hideHeader: true, permission: [ 'user' ] },
            redirect: '/account/settings/base',
            hideChildrenInMenu: true,
            children: [
              {
    
    
                path: '/account/settings/base',
                name: 'BaseSettings',
                component: () => import('@/views/account/settings/BaseSetting'),
                meta: {
    
     title: '基本设置', hidden: true, permission: [ 'user' ] }
              },
              {
    
    
                path: '/account/settings/security',
                name: 'SecuritySettings',
                component: () => import('@/views/account/settings/Security'),
                meta: {
    
     title: '安全设置', hidden: true, keepAlive: true, permission: [ 'user' ] }
              },
              {
    
    
                path: '/account/settings/custom',
                name: 'CustomSettings',
                component: () => import('@/views/account/settings/Custom'),
                meta: {
    
     title: '个性化设置', hidden: true, keepAlive: true, permission: [ 'user' ] }
              },
              {
    
    
                path: '/account/settings/binding',
                name: 'BindingSettings',
                component: () => import('@/views/account/settings/Binding'),
                meta: {
    
     title: '账户绑定', hidden: true, keepAlive: true, permission: [ 'user' ] }
              },
              {
    
    
                path: '/account/settings/notification',
                name: 'NotificationSettings',
                component: () => import('@/views/account/settings/Notification'),
                meta: {
    
     title: '新消息通知', hidden: true, keepAlive: true, permission: [ 'user' ] }
              }
            ]
          }
        ]
      }

Front-end page display
Insert image description here

Guess you like

Origin blog.csdn.net/weixin_43835425/article/details/107432273