Vue添加動(dòng)態(tài)路由

需求

后臺(tái)管理需要根據(jù)登陸的用戶權(quán)限做菜單控制

思路

后臺(tái)返回用戶權(quán)限,保存到vuex中,判斷權(quán)限是否有慘淡導(dǎo)航视卢,如果有使用addRoutes添加到路由中。
路由聲明處廊驼,需要聲明動(dòng)態(tài)路由和公共路由据过。
由權(quán)限去判斷是否應(yīng)該加入動(dòng)態(tài)路由

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)) {
      // 目前僅控制一級(jí)路由颊埃,子路由以后控制
      // 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 = []
    //退出后,改變還原靜態(tài)路由
    removeArrayData(constantRoutes)
  },
  ADD_ROUTES: (state, routes) => {
    state.addRoutes = routes
    routes.forEach((i, index) => {
      // 從第3+index個(gè)位置插入
      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)
        // 將角色權(quán)限保存到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,如果已經(jīng)登陸蝶俱,還要訪問登陸頁面班利,則重定向到首頁
      next({
        path: '/'
      })
      NProgress.done()
    } else {
      // 在vuex中保存有用戶信息的,直接通過
      const hasGetUserInfo = store.getters.name
      if (hasGetUserInfo) {
        // 判斷路由是否在 database榨呆,在該路由下罗标,即發(fā)送數(shù)據(jù)請(qǐng)求,保存到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)) {
              // 當(dāng)路由來到database時(shí)积蜻,發(fā)送獲取數(shù)據(jù)庫分類請(qǐng)求闯割,保存到vuex中
              ajax_get_db_types()
            }
            // 首次保存用戶信息 // 第一次請(qǐng)求保存用戶信息到vuex中,并進(jìn)入index頁面竿拆,此時(shí)不做進(jìn)入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}
    }]
  },
  // 匹配所有路由宙拉,如果匹配了上面的路由,就會(huì)直接跳轉(zhuǎn)丙笋,否則跳轉(zhuǎn)到404路由
  // {path: '*', redirect: '/404', hidden: true}
]

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

  {
    path: '/intelligence',
    component: Layout,
    redirect: '/intelligence/intelclass',
    name: '情報(bào)智庫管理',
    meta: {title: '情報(bào)智庫管理', icon: '數(shù)據(jù)情報(bào)'},
    children: [
      {
        path: 'intellist',
        name: 'Tree',
        component: () => import('@/views/intelligence/IntelList'),
        meta: {title: '情報(bào)列表', icon: '列表'}
      },
      {
        path: 'intelclass',
        name: 'IntelClass',
        component: () => import('@/views/intelligence/IntelClass'),
        meta: {title: '情報(bào)分類', 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: '會(huì)員'}
      },
      {
        path: 'rules',
        name: 'Rules',
        component: () => import('@/views/permission/Rules'),
        meta: {title: '權(quán)限列表', icon: '權(quán)限'}
      }
    ]
  },

  {
    path: '/vip',
    component: Layout,
    redirect: '/vip/viplist',
    name: '會(huì)員管理',
    meta: {title: '會(huì)員管理', icon: '會(huì)員中心'},
    children: [
      {
        path: 'viplist',
        name: 'VipList',
        component: () => import('@/views/vip/VipList'),
        meta: {title: '會(huì)員列表', icon: '列表'}
      },
      {
        path: 'vippower',
        name: 'VipPower',
        component: () => import('@/views/vip/VipPower'),
        meta: {title: '會(huì)員權(quán)限', icon: '權(quán)限'}
      },
      {
        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: '前臺(tái)首頁輪播圖', 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: '價(jià)格上浮管理', icon: '數(shù)據(jù)情報(bào)'}
      },
      {
        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: '數(shù)據(jù)圖庫', icon: '數(shù)據(jù)情報(bào)'}
      },
      {
        path: 'intelligence',
        name: 'intelligence',
        component: () => import('@/views/gallery/Intelligence'),
        meta: {title: '情報(bào)圖庫', icon: 'eye'}
      }
    ]
  },

]

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

const router = createRouter()

問題:

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市怠肋,隨后出現(xiàn)的幾起案子敬鬓,更是在濱河造成了極大的恐慌,老刑警劉巖笙各,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钉答,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡杈抢,警方通過查閱死者的電腦和手機(jī)数尿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來春感,“玉大人砌创,你說我怎么就攤上這事虏缸■昀粒” “怎么了?”我有些...
    開封第一講書人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵刽辙,是天一觀的道長窥岩。 經(jīng)常有香客問我,道長宰缤,這世上最難降的妖魔是什么颂翼? 我笑而不...
    開封第一講書人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任晃洒,我火速辦了婚禮,結(jié)果婚禮上朦乏,老公的妹妹穿的比我還像新娘球及。我一直安慰自己,他們只是感情好呻疹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開白布吃引。 她就那樣靜靜地躺著,像睡著了一般刽锤。 火紅的嫁衣襯著肌膚如雪镊尺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,775評(píng)論 1 307
  • 那天并思,我揣著相機(jī)與錄音庐氮,去河邊找鬼。 笑死宋彼,一個(gè)胖子當(dāng)著我的面吹牛弄砍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播输涕,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼输枯,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了占贫?” 一聲冷哼從身側(cè)響起桃熄,我...
    開封第一講書人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎型奥,沒想到半個(gè)月后瞳收,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡厢汹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年螟深,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烫葬。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡界弧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出搭综,到底是詐尸還是另有隱情垢箕,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布兑巾,位于F島的核電站条获,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蒋歌。R本人自食惡果不足惜帅掘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一委煤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧修档,春花似錦碧绞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至癣诱,卻和暖如春计维,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背撕予。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來泰國打工鲫惶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人实抡。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓欠母,卻偏偏與公主長得像,于是被迫代替她去往敵國和親吆寨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赏淌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

推薦閱讀更多精彩內(nèi)容