vue-element-admin 實現動態路由(從後臺查詢出菜單列表綁定側邊欄)

1. 在路由實例中保留基礎路由

router/index.js中只需要保留基礎路由,其他的都刪了

2. 獲取用戶菜單,並保存到Vuex中

stroe/modules/user.js中,有個getInfo方法查詢用戶基本信息,返回了用戶的菜單列表

import { login, logout, getInfo } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { resetRouter } from '@/router'

const getDefaultState = () => {
  return {
    token: getToken(),
    name: '',
    avatar: ''
  }
}

const state = getDefaultState()

const mutations = {
  RESET_STATE: (state) => {
    Object.assign(state, getDefaultState())
  },
  SET_TOKEN: (state, token) => {
    state.token = token
  },
  SET_NAME: (state, name) => {
    state.name = name
  },
  SET_AVATAR: (state, avatar) => {
    state.avatar = avatar
  },
  SET_MENUS: (state, menus) => {
    state.menus = menus
  }
}

const actions = {
  // 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)
        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  },

  // get user info
  getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      getInfo(state.token).then(response => {
        const { data } = response
        if (!data) {
          reject('Verification failed, please Login again.')
        }
        //模擬請求回來的欄目樹數據,後端根據角色遞歸組裝
        const menus =
          [
            {
              path: '/a',
              component: 'Layout',
              meta: { title: '請求欄目', icon: 'form' },
              children: [
                {
                  path: '/form',
                  name: 'form',
                  component: 'form/index',    //該組件位置在views/form/index.vue,
                  // form前加不加 /,取決於你的router/_import_  文件中的@/view、後面是否有/
                  meta: { title: '通訊錄', icon: 'form' }
                },
                {
                  path: '/table',
                  name: 'table',
                  component: 'table/index',    //該組件位置在views/form/index.vue,
                  // form前加不加 /,取決於你的router/_import_  文件中的@/view、後面是否有/
                  meta: { title: '列表', icon: 'table' }
                }
              ]
            }
          ]

        //如果需要404 頁面,請在此處添加
        menus.push(
          {
            path: '/404',
            component: '404',
            hidden: true
          },
          {
            path: '*',
            redirect: '/404',
            hidden: true
          })
        const { name, avatar } = data
        commit('SET_NAME', name)
        commit('SET_AVATAR', avatar)
        commit('SET_MENUS', menus)
        resolve(data)
      }).catch(error => {
        reject(error)
      })
    })
  },

  // user logout
  logout({ commit, state }) {
    return new Promise((resolve, reject) => {
      logout(state.token).then(() => {
        removeToken() // must remove  token  first
        resetRouter()
        commit('RESET_STATE')
        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  },

  // remove token
  resetToken({ commit }) {
    return new Promise(resolve => {
      removeToken() // must remove  token  first
      commit('RESET_STATE')
      resolve()
    })
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

 store/getter.js

const getters = {
  sidebar: state => state.app.sidebar,
  device: state => state.app.device,
  token: state => state.user.token,
  avatar: state => state.user.avatar,
  name: state => state.user.name,
  cmpname: state => state.user.cmpname,
  userid: state => state.user.userid,
  menus: state => state.user.menus
}
export default getters

3.動態生成權限路由(核心)

根據環境配置導入組件,在vue中,將菜單路徑作爲參數,實現路由地址的注入

在 src/router 文件夾下,建立兩個文件,各只需添加一行代碼, 定義導入方法

src/router/_import_development.js
//開發環境導入組件
module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+

---------------------------------------------------------------------

src/router/_import_production.js
//生產環境導入組件
module.exports = file => () => import('@/views/' + file + '.vue')

 說明:組件導入 —— _import

//獲取組件的方法
const _import = require('./router/_import_' + process.env.NODE_ENV)

// .......

//導入路徑下的組件
route.component = _import(route.path)

4.使用

在路由鉤子中,過濾路由,並生成路由

核心在src目錄下的permission.js中,router.beforeEach路由鉤子

import router from './router'
import store from './store'
import {
  Message
} from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import {
  getToken
} from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'
import Layout from '@/layout'
const _import = require('./router/_import_' + process.env.NODE_ENV) // 獲取組件的方法

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

const whiteList = ['/login'] // no redirect whitelist

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()

  if (hasToken) {
    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')
          if (store.getters.menus.length < 1) {
            global.antRouter = []
            next()
          }
          const menus = filterAsyncRouter(store.getters.menus) // 1.過濾路由
          router.addRoutes(menus) // 2.動態添加路由
          global.antRouter = menus // 3.將路由數據傳遞給全局變量,做側邊欄菜單渲染工作
          next({
            ...to,
            replace: true
          }) // hack方法 確保addRoutes已完成 ,set the replace
        } 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()
        }
      }
    }
  } 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()
    }
  }
})

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

// 遍歷後臺傳來的路由字符串,轉換爲組件對象
function filterAsyncRouter(asyncRouterMap) {
  const accessedRouters = asyncRouterMap.filter(route => {
    if (route.component) {
      if (route.component === 'Layout') {
        route.component = Layout
      } else {
        route.component = _import(route.component) // 導入組件
      }
    }
    if (route.children && route.children.length) {
      route.children = filterAsyncRouter(route.children)
    }
    return true
  })

  return accessedRouters
}

5.最後一步,合併路由

 

轉載地址:https://www.cnblogs.com/langhaoabcd/p/11346227.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章