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,需要將 * 攔截的路由放在最后。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容