• vue-element-template实战(五) 获取后端路由表动态生成权限


    主要思路如下:

    • 用户登录login获取token
    • 拿着token请求用户信息,同时后端返回一个路由表
    • 前端解析后动态添加路由表,同时存储到本地localstorage
    • 刷新页面或者退出登录或者登录过期等时,会进行相应的判断,重新渲染路由

    1、在src/router文件夹下新建_import.js,用于匹配组件,代码如下:

    export default file => {
        return map[file] || null
      }
      const map = {
        'Layout': () => import('@/layout'),
        'table': () => import('@/views/table/index'),
        'tree': () => import('@/views/tree/index'),
        'form': () => import('@/views/form/index'),
        'menu1': () => import('@/views/nested/menu1/index'),
        'menu1-1': () => import('@/views/nested/menu1/menu1-1'),
        'menu1-2': () => import('@/views/nested/menu1/menu1-2'),
        'menu1-2-1': () => import('@/views/nested/menu1/menu1-2/menu1-2-1'),
        'menu1-2-2': () => import('@/views/nested/menu1/menu1-2/menu1-2-2'),
        'menu1-3': () => import('@/views/nested/menu1/menu1-3'),
        'menu2': () => import('@/views/nested/menu2/index')
      }
      

     2、在src/utils文件夹下新建addRouter.js,用于解析后端返回的路由,具体解析方法和数据形式还是要看项目具体来分析,代码如下:

    import _import from '../router/_import'// 获取组件的方法
    
    function addRouter(routerlist) {
      routerlist.forEach(e => {
        // 删除无用属性
        delete e.id
        e.component = _import(e.component) // 动态匹配组件
        if (e.redirect === '') {
          delete e.redirect
        }
        if (e.name === '') {
          delete e.name
        }
        if (e.icon !== '' && e.title !== '') { // 配置 菜单标题 与 图标
          e.meta = {
            title: e.title,
            icon: e.icon
          }
        } else if (e.icon === '' && e.title !== '') {
          e.meta = {
            title: e.title
          }
        }
        delete e.icon
        delete e.title
        if (e.children != null) {
          // 存在子路由就递归
          addRouter(e.children)
        }
      })
      // console.log(routerlist)
      return routerlist
    }
    export { addRouter }

    3、修改src/permission.js,动态添加路由

      3.1 导入addRouter

    import { addRouter } from './utils/addRouter'

      3.2 动态添加路由函数,注意404页面要在这最后添加到路由表中,不能放在router默认的路由中,否则刷新页面就会跳转到404

    function gotoRouter(to, next) {
      try {
        getRouter = addRouter(getRouter) // 解析路由
        const newRouters = router.options.routes.concat(getRouter) // 连接获取到的路由
        newRouters.push({ path: '*', redirect: '/404', hidden: true }) // 最后添加404页面
        console.log('路由:', newRouters)
        // router.options.routes = newRouters
        router.addRoutes(newRouters) // 动态添加路由
        store.commit('SET_ROUTER', newRouters) // 将路由数据传递给VUEX,做侧边栏菜单渲染工作
        next({ to, replace: true })
      } catch (error) {
        localStorage.setItem('login_static', 0)
        store.dispatch('user/resetToken')
        Message.error(error || 'Has Error')
        next(`/login?redirect=${to.path}`)
        NProgress.done()
      }
    }

      3.3 请求路由数据函数以及从本地获取路由表函数

    function setRouterList(to, next) {
      store.dispatch('user/getInfo').then(data => { // 请求路由数据
        localStorage.setItem('router', JSON.stringify(data.routers))
        getRouter = getRouterList('router') // 拿到路由
        gotoRouter(to, next)
      })
    }

      3.4 修改beforeEach

    var getRouter
    router.beforeEach(async(to, from, next) => {
      // start progress bar
      NProgress.start()
    
      // set page title
      document.title = getPageTitle(to.meta.title)
    
      // determine whether the user has logged in
      const hasToken = getToken()
      const hasLogin = getRouterList('login_static')
      console.log('hasToken:', hasToken) // 是否已获取token
      console.log('hasLogin:', hasLogin) // 登录状态
      if (hasToken && hasLogin === 1) {
        if (to.path === '/login') {
          // if is logged in, redirect to the home page
          next({ path: '/' })
          NProgress.done()
        } else {
          const hasGetUserInfo = store.getters.name
          if (hasGetUserInfo) {
            next()
          } else {
            try {
              // get user info
              await store.dispatch('user/getInfo')
    
              next()
            } catch (error) {
              // remove token and go to login page to re-login
              await store.dispatch('user/resetToken')
              Message.error(error || 'Has Error')
              next(`/login?redirect=${to.path}`)
              NProgress.done()
            }
          }
          if (!getRouter) {
            console.log('路由信息不存在')
            const hasRouterData = getRouterList('router')
            if (hasRouterData) {
              console.log('路由信息存在 说明已经请求到路由 直接解析路由信息')
              getRouter = hasRouterData
              await gotoRouter(to, next)
            } else {
              console.log('localStorage不存在路由信息 需要重新请求路由信息 并解析路由')
              setRouterList(to, next)
            }
          } else {
            console.log('路由信息存在 直接通过')
            next()
          }
        }
      } else {
        /* has no token*/
    
        if (whiteList.indexOf(to.path) !== -1) {
          // in the free login whitelist, go directly
          next()
        } else {
          // other pages that do not have permission to access are redirected to the login page.
          next(`/login?redirect=${to.path}`)
          NProgress.done()
        }
      }
    })

    4、把router文件夹中的index.js中的路由删掉,只留默认自带的路由。

    export const constantRoutes = [
      {
        path: '/login',
        component: () => import('@/views/login/index'),
        hidden: true
      },
    
      {
        path: '/404',
        component: () => import('@/views/404'),
        hidden: true
      },
    
      {
        path: '/',
        component: Layout,
        redirect: '/dashboard',
        children: [{
          path: 'dashboard',
          name: 'Dashboard',
          component: () => import('@/views/dashboard/index'),
          meta: { title: 'Dashboard', icon: 'dashboard' }
        }]
      }
    ]

    5、修改store文件夹中index.js,添加路由表状态和获取路由表的方法

    const store = new Vuex.Store({
      state: {
        routerList: [] // 用于存储路由表
      },
      mutations: {
        // 用于获取路由表
        SET_ROUTER(state, routerList) {
          state.routerList = routerList
        }
      },
      modules: {
        app,
        settings,
        user
      },
      getters
    })

    6、修改store/modules中的user.js,在login时保存登录状态,在logout时清除登录状态以及本地保存的路由表

    // user login
      login({ commit }, userInfo) {
        const { username, password } = userInfo
        return new Promise((resolve, reject) => {
          login({ username: username.trim(), password: password }).then(response => {
            const { data } = response
            commit('SET_TOKEN', data.token)
            setToken(data.token)
            localStorage.setItem('login_static', 1) // 保存登录状态
            resolve()
          }).catch(error => {
            reject(error)
          })
        })
      },
    // user logout
      logout({ commit, state }) {
        return new Promise((resolve, reject) => {
          logout(state.token).then(() => {
            removeToken() // must remove  token  first
            localStorage.removeItem('router') // 删除本地存储的路由表
            localStorage.setItem('login_static', 0) // 切换登录状态
            resetRouter()
            commit('RESET_STATE')
            location.reload() // 刷新页面 以重置getRouter
            resolve()
          }).catch(error => {
            reject(error)
          })
        })
      },
    
      // remove token
      resetToken({ commit }) {
        return new Promise(resolve => {
          removeToken() // must remove  token  first
          localStorage.removeItem('router') // 删除本地存储的路由表
          localStorage.setItem('login_static', 0) // 切换登录状态
          commit('RESET_STATE')
          resolve()
        })
      }

    7、修改layout/component/Sliebar中的index.vue

    routes() {
          // return this.$router.options.routes
          return this.$store.state.routerList // 将VUEX中的路由表挂载
        },

    8、使用mock模拟下后端返回的路由信息,在getInfo中与用户信息一起返回的

    // 模拟路由表
    const routerTable = [
      {
        'id': 1,
        'name': 'Example',
        'path': '/example',
        'component': 'Layout',
        'redirect': '/example/table',
        'title': 'Example',
        'icon': 'example',
        'children': [{
          'id': 2,
          'name': 'Table',
          'path': '/example/table',
          'component': 'table',
          'title': 'Table',
          'icon': 'table'
        },
        {
          'id': 3,
          'name': 'Tree',
          'path': '/example/tree',
          'component': 'tree',
          'title': 'Tree',
          'icon': 'tree'
        }]
      },
      {
        'id': 4,
        'path': '/form',
        'component': 'Layout',
        'children': [{
          'id': 5,
          'name': 'Form',
          'path': '/form/index',
          'component': 'form',
          'title': 'Form',
          'icon': 'form'
        }]
      },
      {
        'id': 6,
        'name': 'Nested',
        'path': '/nested',
        'component': 'Layout',
        'redirect': '/nested/menu1/index',
        'title': 'Nested',
        'icon': 'nested',
        'children': [{
          'id': 7,
          'name': 'Menu1',
          'path': '/nested/menu1/index',
          'component': 'menu1',
          'title': 'Menu1',
          'children': [{
            'id': 8,
            'name': 'Menu1-1',
            'path': '/nested/menu1/menu1-1',
            'component': 'menu1-1',
            'title': 'Menu1-1'
          },
          {
            'id': 9,
            'name': 'Menu1-2',
            'path': '/nested/menu1/menu1-2',
            'component': 'menu1-2',
            'title': 'Menu1-2',
            'children': [{
              'id': 10,
              'name': 'Menu1-2-1',
              'path': '/nested/menu1/menu1-2/menu1-2-1',
              'component': 'menu1-2-1',
              'title': 'Menu1-2-1'
            },
            {
              'id': 11,
              'name': 'Menu1-2-2',
              'path': '/nested/menu1/menu1-2/menu1-2-2',
              'component': 'menu1-2-2',
              'title': 'Menu1-2-2'
            }]
          },
          {
            'id': 12,
            'name': 'Menu1-3',
            'path': '/nested/menu1/menu1-3',
            'component': 'menu1-3',
            'title': 'Menu1-3'
          }]
        },
        {
          'id': 13,
          'name': 'Menu2',
          'path': '/nested/menu2/index',
          'component': 'menu2',
          'title': 'Menu2'
        }],
      },
      {
        'id': 14,
        'path': 'external-link',
        'component': 'Layout',
        'children': [{
          'path': 'https://panjiachen.github.io/vue-element-admin-site/#/',
          'title': 'External Link',
          'icon': 'link'
        }]
      }
    ]
    const users = {
      'admin-token': {
        roles: ['admin'],
        introduction: 'I am a super administrator',
        avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
        name: 'Super Admin',
        routers: routerTable
      },
      'editor-token': {
        roles: ['editor'],
        introduction: 'I am an editor',
        avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
        name: 'Normal Editor',
        routers: [
          routerTable[0]
        ]
      }
    }

    至此已完成,亲测可行,第一次登陆会出现找不到路由,再次登陆后正常,待解决。

  • 相关阅读:
    iOS 证书、真机调试、发布 App Store
    iOS 9 适配
    交叉编译tslib1.4
    nau8822 codec driver 录音时mic bias 无法自动打开问题
    nuc900 nand flash mtd 驱动
    在ubuntu14.04上安装oracle java6 java7的方法
    N3292x IBR介绍
    N3292系列资料之RTC介绍
    支持mdev的init脚本片断
    Nginx
  • 原文地址:https://www.cnblogs.com/windok/p/13195268.html
Copyright © 2020-2023  润新知