前端权限控制(二):VUE-router.addRoutes根据后台接口传递数据生成动态路由,前端拿到配置并生成侧边栏-实现页面级权限控制
登陆成功之后直接由后端返回异步路由表,然后前端直接通过addRoutes方法进行添加,并且生成侧边栏。大致步骤:1.拦截路由2.取到后台路由数据3.添加并且保存路由(VUEX)。因为后台传回的组件路径‘component’是字符串,所以要把加载组件的过程 封装成一个方法,因为有多级路由的出现,所以要写成遍历递归方法,确保把每个component转成对象。要注意的是Layout组件需要特殊处理。La
-----------------------------------------------
登陆成功之后直接由后端返回异步路由表,然后前端直接通过addRoutes方法进行添加,并且生成侧边栏。
大致步骤:
- 拦截路由
- 取到后台路由数据
- 添加并且保存路由(VUEX)
-----------------------------------------------
该demo是在大神花裤衩的vue-admin-template上进行的修改调整。
https://github.com/PanJiaChen/vue-admin-template
-----------------------------------------------
具体实现步骤:
第一步:模拟路由数据(准备工作)
因该demo暂无数据交互,所以做的模拟数据,已完成后台数据交互的注意传递数据格式后可直接跳到第二步。
路由分为两种:constantRoutes 和 asyncRoutes。
constantRoutes: 代表那些不需要动态判断权限的路由,如登录页、404、等通用页面。直接写在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: '/index',
name: '首页',
meta: { title: 'XXX', icon: 'el-icon-s-help' },
children: [
{
path: 'index',
name: '首页',
component: () => import('@/views/index/index'),
meta: { title: '首页', icon: 'index' }
}
]
}
// 404重定向先注释,稍后步骤里做说明
// { path: '*', redirect: '/404', hidden: true }
]
asyncRoutes: 代表那些需求动态判断权限并通过 addRoutes 动态添加的页面,现通过模拟接口获取。
模拟接口mock/router.js中需要包含的数据格式如下:
const data = {
router: [{
path: '/goodsInfo',
component: 'Layout',
redirect: '/goodsInfo',
name: 'TAB1',
meta: { title: 'TAB1', icon: 'el-icon-s-help' },
children: [
{
path: 'goodsInfo',
name: 'TAB1-1',
component: '/goodsInfo/index',
meta: { title: 'TAB1', icon: 'table', button: ['add'] }
}
]
},
{
path: '/installInfo',
component: 'Layout',
redirect: '/installInfo/installRrecord',
name: 'TAB2',
meta: { title: 'TAB2', icon: 'el-icon-s-help' },
children: [
{
path: 'installRrecord',
name: 'TAB2-1',
component: '/installInfo/installRecord',
meta: { title: 'TAB2-1', icon: 'table', button: ['add'] }
},
{
path: 'summaryStatistics',
name: 'TAB2-2',
component: '/installInfo/summaryStatistics',
meta: { title: 'TAB2-2', icon: 'tree', button: ['add', 'delete'] }
}
]
},
]
}
module.exports = [{
url: '/vue-admin-template/router/list',
type: 'get',
response: config => {
const items = data
return {
code: 20000,
data: {
router: items
}
}
}
}]
第二步:在登录后获取用户权限进行路由请求
在前端权限控制(一):前端权限管理及动态路由配置方案中,与页面级权限控制里的方案二相同,登录login和返回用户路由信息get_router两件事分开请求。
2.1:登录后存储用户role信息
store\modules\user.js中添加onePermission字段,
const getDefaultState = () => {
return {
token: getToken(),
name: '',
avatar: '',
onePermission:null
}
}
在login成功之后 ,存储用户权限
commit('SET_PERMISSION', data.onePermission) // 用户权限标识
2.2:根据onePermission获取权限路由列表
在store\modules\目录下创建permission.js,修改代码如下:
如后台传递格式不符合router规范(例如传递的不是树形结构数据),可自行在此文件中进行处理。
import { getRouterList } from '@/api/router'
const getDefaultState = () => {
return {
router: null,
buttonList:[]
}
}
const state = getDefaultState()
const mutations = {
SET_ROUTER: (state, router) => {
state.router = router
},
SET_BUTTON: (state, button) => {
state.buttonList = button
}
}
const actions = {
// get user router
getRouter({ commit, state }) {
return new Promise((resolve, reject) => {
getRouterList(state.onePermission).then(response => {
const { data } = response
if (!data) {
return reject('get user router,路由获取失败, please Login again.')
}
const { router } = data.router
commit('SET_ROUTER', router) //设置路由变量
resolve(router)
}).catch(error => {
reject(error)
})
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
buttonList 设置为按钮级权限控制,具体实现见:
前端权限控制(三):根据后台接口数据传递页面按钮权限-实现按钮级权限控制
第三步:拦截/获取路由
找到与根目录同级的permission.js,修改代码如下:
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'
// 获取组件、拼接路径的方法
const _import = require('@/router/_import_' + process.env.NODE_ENV)
// 引入架构组件Layout
import Layout from '@/layout'
NProgress.configure({ showSpinner: false }) // 进度条配置
const whiteList = ['/login'] // 没有重定向白名单路由
router.beforeEach(async(to, from, next) => {
// 开始进度条
NProgress.start()
// 设置页面标题
document.title = getPageTitle(to.meta.title)
// 判断用户是否已经登录
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
// 如果已经登录,则重定向到主页
next({ path: '/' })
NProgress.done()
} else {
const hasGetUserRouter = store.getters.router // 是否有路由表
if (hasGetUserRouter) {
// 设置当前页面按钮权限
const button = to.meta.button
store.commit('permission/SET_BUTTON', button)
next()
} else {
try {
// 获取路由表
const list = await store.dispatch('permission/getRouter')
routerGo(to, next, list)
// next()
} catch (error) {
await store.dispatch('user/resetToken')
Message({ type:'error', message: error || 'Has Error' })
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
router.afterEach(() => {
// finish progress bar 进度条
NProgress.done()
})
// 导航到一个新的路由方法封装
function routerGo(to, next, routerList) {
const newRouter = filterAsyncRouter(routerList)
newRouter.push({ path: '*', redirect: '/404', hidden: true }) // 404放在最后
// 必须在addroutes前,使用router.options.routes=XXXXX的方法手动添加,才会显示菜单
router.options.routes = router.options.routes.concat(newRouter);
// router.options.routes = newRouter;
router.addRoutes(newRouter) // 动态添加路由
next({ ...to, replace: true })
}
// 过滤路由方法封装-拼接路径
function filterAsyncRouter(asyncRouterMap) { // 遍历后台传来的路由字符串,转换为组件对象
const accessedRouters = asyncRouterMap.filter(route => {
if (route.component) {
if (route.component === 'Layout') { // 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
}
判断登录后,通过store.getters.router判断是否存有路由表,是的话获取当前页面按钮权限后直接next()进行跳转;否的话用axios后台取一次路由数据并存到vuex。
要点:
- 因为后台传回的组件路径‘component’是字符串,所以要把加载组件的过程 封装成一个方法,因为有多级路由的出现,所以要写成遍历递归方法,确保把每个component转成对象。
要注意的是Layout组件需要特殊处理。Layout 是架构组件,不在后台返回,在文件里单独引入:将后端传回的"component": "Layout", 转为"component": Layout组件对象处理。在router文件夹下新建:_import_development.js和_import_production.js文件。
// router/_import_production.js module.exports = file => () => import('@/views/' + file + '.vue') // router/_import_development.js module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+
-
404
页面一定要最后加载,如果放在constantRouterMap
一同声明了404
,后面的所以页面都会被拦截到404
,详细的问题见:addRoutes when you've got a wildcard route for 404s does not work -
因为vue-admin-template侧边栏菜单是取的router.options.routes数据,所以必须在addroutes前,使用router.options.routes=XXXXX的方法手动添加,才会显示菜单。
-
在
addRoutes()
之后通过next()第一次访问被添加的路由可能出现白屏的情况,也不会报错。大致理解为, 当进入 路由的 前置钩子 (router.beforEach) 的时候, 路由的结构是不会发生变化的, 至少本次跳转, 路由的结构是不会变化的。此时就要使用next({ ...to, replace: true })
重定向当前的路由来确保addRoutes()
时动态添加的路由已经被完全加载上去。
=================================================================
完成:
实现代码如下:
前端VUE权限管理(包含菜单权限和按钮权限),router.addRoutes根据后台接口传递数据生成动态路由-Javascript文档类资源-CSDN下载
更多推荐
所有评论(0)