Vue添加動態路由

需求

后臺管理需要根據登陸的用戶權限做菜單控制

思路

后臺返回用戶權限,保存到vuex中,判斷權限是否有慘淡導航,如果有使用addRoutes添加到路由中。
路由聲明處,需要聲明動態路由和公共路由。
由權限去判斷是否應該加入動態路由

store代碼:

-->user.js
function isAddRoute(pers, route) {
  return pers.some(per => per.code.split('_').pop() === route.path.split('/').pop())
}

function removeArrayData(arr) {
  let i = arr.length - 1;
  while (i >= 0) {
    if (arr[i].path !== '/login' && arr[i].path !== '/' && arr[i].path !== '/404' && arr[i].path !== '*') {
      arr.splice(i, 1)
    }
    i--
  }
}


export function filterAsyncRoutes(pers, routes) {
  const res = []
  routes.forEach(route => {
    const tmp = {...route}
    if (isAddRoute(pers, tmp)) {
      // 目前僅控制一級路由,子路由以后控制
      // if (tmp.children) {
      //   tmp.children = filterAsyncRoutes(tmp.children, roles)
      // }
      res.push(tmp)
    }
  });
  return res
}

const state = {
  addRoutes: []
}

const mutations = {
  // 移除用戶
  REMOVE: (state) => {
    state.name = ''
    state.addRoutes = []
    //退出后,改變還原靜態路由
    removeArrayData(constantRoutes)
  },
  ADD_ROUTES: (state, routes) => {
    state.addRoutes = routes
    routes.forEach((i, index) => {
      // 從第3+index個位置插入
      constantRoutes.splice(3 + index, 0, i)
    })
  }
}

const actions = {
  // get user info
  getInfo({commit, state}) {
    return new Promise((resolve, reject) => {
      getInfo(state.token).then(res => {
        const {data} = res
        let pers = []
        let admin_pers = []
        if (!data) {
          reject('Verification failed, please Login again.')
        }
        // const {name, phone, active, create_time, avatar} = data
        commit('SET_INFO', data)
        admin_pers = data.roles.map(item => item.pers)
        for (let i in admin_pers) {
          for (let j in admin_pers[i]) {
            let target = pers.filter(item => item.code === admin_pers[i][j].code)
            if (target.length === 0) {
              // 判斷如果角色中有相同的
              pers.push(admin_pers[i][j])
            }
          }
        }
        const accessedRoutes = filterAsyncRoutes(pers, asyncRoutes)
        // 將角色權限保存到vuex
        commit('ADD_ROUTES', accessedRoutes)
        resolve(data)
      }).catch(error => {
        reject(error)
      })
    })
  },

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


-->main.js

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 {
      // 在vuex中保存有用戶信息的,直接通過
      const hasGetUserInfo = store.getters.name
      if (hasGetUserInfo) {
        // 判斷路由是否在 database,在該路由下,即發送數據請求,保存到vuex
        if (/database/.test(to.path)) {
          ajax_get_db_types()
          // await store.dispatch('db/action_get_types')
        }
        const hasAddRoutes = store.getters.addRoutes && store.getters.addRoutes.length > 0
        if (hasAddRoutes) {
          next()
        }
      } else {
        /*沒有用戶信息*/
        const hasAddRoutes = store.getters.addRoutes && store.getters.addRoutes.length > 0
        if (hasAddRoutes) {
          next()
        } else {
          // 判斷用戶角色
          try {
            if (/database/.test(to.path)) {
              // 當路由來到database時,發送獲取數據庫分類請求,保存到vuex中
              ajax_get_db_types()
            }
            // 首次保存用戶信息 // 第一次請求保存用戶信息到vuex中,并進入index頁面,此時不做進入database的判斷
            await store.dispatch('user/getInfo');
            // // constantRoutes.options.routes = newRoutes
            router.addRoutes(store.getters.addRoutes)
            // 解決路由刷新404界面
            router.addRoutes([{path: '*', redirect: '/404', hidden: true}])
            next({...to, replace: true})
          } 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.js

import Layout from '@/layout'

export const constantRoutes = [
  {
    path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true
  },

  {
    name: '404',
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true, meta: {title: '404'}
  },
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [{
      path: '/dashboard',
      name: '首頁',
      component: () => import('@/views/dashboard/index'),
      meta: {title: '首頁', icon: 'dashboard', noCache: true, affix: true}
    }]
  },
  // 匹配所有路由,如果匹配了上面的路由,就會直接跳轉,否則跳轉到404路由
  // {path: '*', redirect: '/404', hidden: true}
]

export const asyncRoutes = [
  {
    path: '/database',
    component: Layout,
    redirect: '/database/databaselist',
    name: '數據庫管理',
    meta: {title: '數據庫管理', icon: 'nested'},
    children: [
      {
        path: 'databaselist',
        name: 'DatabaseList',
        component: () => import('@/views/database/DatabaseList'),
        meta: {title: '數據庫分類', icon: '列表'}
      },
      {
        path: 'databaserset',
        name: 'DatabaseSet',
        component: () => import('@/views/database/DatabaseSet'),
        meta: {title: '數據庫設置', icon: '設置'}
      }
    ]
  },
  {
    path: '/news',
    component: Layout,
    redirect: '/news/newslist',
    name: '資訊快報管理',
    meta: {title: '資訊快報管理', icon: '資訊'},
    children: [
      {
        path: 'newslist',
        name: 'NewsList',
        component: () => import('@/views/news/NewsList'),
        meta: {title: '資訊快報列表', icon: '列表'}
      },
      {
        path: 'newsset',
        name: 'NewsSet',
        component: () => import('@/views/news/NewsSet'),
        meta: {title: '資訊快報設置', icon: '設置'}
      },
    ]
  },

  {
    path: '/intelligence',
    component: Layout,
    redirect: '/intelligence/intelclass',
    name: '情報智庫管理',
    meta: {title: '情報智庫管理', icon: '數據情報'},
    children: [
      {
        path: 'intellist',
        name: 'Tree',
        component: () => import('@/views/intelligence/IntelList'),
        meta: {title: '情報列表', icon: '列表'}
      },
      {
        path: 'intelclass',
        name: 'IntelClass',
        component: () => import('@/views/intelligence/IntelClass'),
        meta: {title: '情報分類', icon: 'link'}
      }
    ]
  },

  {
    path: '/permission',
    component: Layout,
    redirect: '/permission/admins',
    name: '管理員管理',
    meta: {title: '管理員管理', icon: '用戶,管理員_jurassic'},
    children: [
      {
        path: 'admins',
        name: 'Admins',
        component: () => import('@/views/permission/Admins'),
        meta: {title: '管理員列表', icon: '列表'}
      },
      {
        path: 'roles',
        name: 'Roles',
        component: () => import('@/views/permission/Roles'),
        meta: {title: '角色列表', icon: '會員'}
      },
      {
        path: 'rules',
        name: 'Rules',
        component: () => import('@/views/permission/Rules'),
        meta: {title: '權限列表', icon: '權限'}
      }
    ]
  },

  {
    path: '/vip',
    component: Layout,
    redirect: '/vip/viplist',
    name: '會員管理',
    meta: {title: '會員管理', icon: '會員中心'},
    children: [
      {
        path: 'viplist',
        name: 'VipList',
        component: () => import('@/views/vip/VipList'),
        meta: {title: '會員列表', icon: '列表'}
      },
      {
        path: 'vippower',
        name: 'VipPower',
        component: () => import('@/views/vip/VipPower'),
        meta: {title: '會員權限', icon: '權限'}
      },
      {
        path: 'viporders',
        name: 'VipOrders',
        component: () => import('@/views/vip/VipOrders'),
        meta: {title: '用戶訂單', icon: '訂單'}
      }
    ]
  },

  {
    path: '/swiper',
    component: Layout,
    redirect: '/swiper/homeswiper',
    name: '輪播圖管理',
    meta: {title: '輪播圖管理', icon: 'eye-open'},
    children: [
      {
        path: 'homeswiper',
        name: 'HomeSwiper',
        component: () => import('@/views/swiper/HomeSwiper'),
        meta: {title: '前臺首頁輪播圖', icon: '首頁輪播圖'}
      },
      {
        path: 'newsswiper',
        name: 'NewsSwiper',
        component: () => import('@/views/swiper/NewsSwiper'),
        meta: {title: '資訊首頁輪播文章', icon: '首頁輪播圖'}
      }
    ]
  },

  {
    path: '/tools',
    component: Layout,
    redirect: '/tools/pricecontrol',
    name: '工具管理',
    meta: {title: '工具管理', icon: 'example'},
    children: [
      {
        path: 'pricecontrol',
        name: 'PriceControl',
        component: () => import('@/views/tools/PriceControl'),
        meta: {title: '價格上浮管理', icon: '數據情報'}
      },
      {
        path: 'viewlog',
        name: 'Viewlog',
        component: () => import('@/views/tools/Viewlog'),
        meta: {title: '日志查看', icon: 'eye'}
      }
    ]
  },
  {
    path: '/gallery',
    component: Layout,
    redirect: '/gallery/database',
    name: '圖庫管理',
    meta: {title: '圖庫管理', icon: 'example'},
    children: [
      {
        path: 'database',
        name: 'database',
        component: () => import('@/views/gallery/Database'),
        meta: {title: '數據圖庫', icon: '數據情報'}
      },
      {
        path: 'intelligence',
        name: 'intelligence',
        component: () => import('@/views/gallery/Intelligence'),
        meta: {title: '情報圖庫', icon: 'eye'}
      }
    ]
  },

]

const createRouter = () => new Router({
  mode: 'history',
  scrollBehavior: () => ({y: 0}),
  routes: constantRoutes
})

const router = createRouter()

問題:

一:刷新404,不能在靜態路由中添加 * 攔截沒有的路由,需要手動傳入router.addRoutes([{path: '*', redirect: '/404', hidden: true}])
二:訪問動態路由404,需要將 * 攔截的路由放在最后。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,836評論 6 540
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,275評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,904評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,633評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,368評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,736評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,740評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,919評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,481評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,235評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,427評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,968評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,656評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,055評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,348評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,160評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,380評論 2 379

推薦閱讀更多精彩內容