目标:
学习vue-router对象中的addRoutes,用它来动态添加路由配置
思路:
用户能访问到的页面(路由配置)必须是动态的, 所以要先掌握一个可以动态添加路由地址的API
addRoutes基本使用:
格式:
router.addRoutes([路由配置对象])
或者:
this.$router.addRoutes([路由配置对象])
作用:动态添加路由配置
如图所示:
示例 :
<button @click="hAddRoute">addRoute</button>
// 回调
hAddRoute() {
this.$router.addRoutes([{
path: '/abc',
component: () => import('@/views/abc'),
}])
},
效果:点击了按钮之后,就可以在地址中访问/abc了。
改造代码
1.在router/index.js中的路由配置中删除动态路由的部分
const createRouter = () => new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
// routes: constantRoutes
// 合并动态和静态的路由 , ...asyncRoutes
- routes: [...constantRoutes, ...asyncRoutes]
+ routes: [...constantRoutes]
})
2.在permission.js中引入,并使用addRoutes动态添加
把之前在router中直接静态写死的动态路由表改造成通过addRoutes
方法调用添加的形式
// 引入所有的动态路由表(未经过筛选)
+ import router, { asyncRoutes } from '@/router'
const whiteList = ['/login', '/404']
router.beforeEach(async(to, from, next) => {
// 开启进度条
NProgress.start()
// 获取本地token 全局getter
const token = store.getters.token
if (token) {
// 有token
if (to.path === '/login') {
next('/')
} else {
if (!store.getters.userId) {
await store.dispatch('user/getUserInfo')
// 改写成动态添加的方式
+ router.addRoutes(asyncRoutes)
}
next()
}
} else {
// 没有token
if (whiteList.includes(to.path)) {
next()
} else {
next('/login')
}
}
// 结束进度条
NProgress.done()
})
必会遇到的bug
(1)如果我们刷新浏览器,会发现跳到了404页面
原因
现在我们的路由设置中的404页处在中间位置而不是所有路由的末尾了。
解决
把404页改到路由配置的最末尾就可以了
代码
-
从route/index.js中的静态路由中删除
path:'*'
这一项
// 不需要特殊的权限控制就可以访问的页面
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
// 404 page must be placed at the end !!!
- { path: '*', redirect: '/404', hidden: true }
]
2.在permission.js中补充在最后
// if(没有userInfo) {
if (!store.getters.userId) {
// 有token,要去的不是login,就直接放行
// 进一步获取用户信息
// 发ajax---派发action来做
const menus = await store.dispatch('user/getUserInfo')
console.log('当前用户能访问的页面', menus)
console.log('当前系统功能中提供的所有的动态路由页面是', asyncRoutes)
// 根据本用户实际的权限menus去 asyncRoutes 中做过滤,选出本用户能访问的页面
const filterRoutes = asyncRoutes.filter(route => {
const routeName = route.children[0].name
return menus.includes(routeName)
})
// 一定要在进入主页之前去获取用户信息
// 把404加到最后一条
+ filterRoutes.push( // 404 page must be placed at the end !!!
{ path: '*', redirect: '/404', hidden: true })
// addRoutes用来动态添加路由配置
// 只有在这里设置了补充了路由配置,才可能去访问页面
// 它们不会出现左侧
router.addRoutes(filterRoutes)
// 把它们保存在vuex中,在src\layout\components\Sidebar\index.vue
// 生成左侧菜单时,也应该去vuex中拿
store.commit('menu/setMenuList', filterRoutes)
}
(2)对于addRoute添加的路由,在刷新时会白屏
if (!store.getters.userId) {
// 省略其他...
// 解决刷新出现的白屏bug
next({
...to, // next({ ...to })的目的,是保证路由添加完了再进入页面 (可以理解为重进一次)
replace: true // 重进一次, 不保留重复历史
})
} else {
next()
}
(3)退出后,再次登陆,发现菜单异常 (控制台有输出说路由重复)
如下图:
原因
路由设置是通过router.addRoutes(filterRoutes)
来添加的,退出时,并没有清空,再次登陆,又加了一次,所以有重复。
需要将路由权限重置 (恢复默认) 将来登录后再次追加才可以,不然的话,就会重复添加
解决
我们的router/index.js
文件,发现一个重置路由方法
// 重置路由
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // 重新设置路由的可匹配路径
}
这个方法就是将路由重新实例化,相当于换了一个新的路由,之前加的路由
就不存在了,需要在登出的时候, 调用一下即可在store/modules/user.js
import { resetRouter } from '@/router'
// 退出的action操作
logout(context) {
// 1. 移除vuex个人信息
context.commit('removeUserInfo')
// 2. 移除token信息
context.commit('removeToken')
// 3. 重置路由
resetRouter()
}
分配按钮权限
目标
员工A和员工B都可以访问同一个页面(以员工管理为例),但是员工A可以导出excel,员工B就不可以导出excel
思路
用户登陆成功后,用户可以访问的按钮级别权限保存在points数组中。而这个数据我们是保存在vuex中的,所以,就可以在项目的任意地方来中访问。
使用方法:自定义指令
注册格式:
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时inserted会自动执行
inserted: function(el, binding) {
// v-focus="'abc'" ===> binding.value = 'abc'
console.log('focus.... binding', binding.value)
// 聚焦元素
el.focus()
}
})
使用格式
<input v-foucs="'xxxx'" />
解决方法
在main.js中,定义全局指令
// 注册一个全局自定义指令 `v-allow`
Vue.directive('allow', {
inserted: function(el, binding) {
// 从vuex中取出points,
const points = store.state.user.userInfo.roles.points
// 如果points有binding.value则显示
if (points.includes(binding.value)) {
// console.log('判断这个元素是否会显示', el, binding.value)
} else {
el.parentNode.removeChild(el)
// el.style.display = 'none'
}
}
})
使用
<el-button
+ v-allow="'import_employee'"
type="warning"
size="small"
@click="$router.push('/import')"
>导入excel</el-button>
上诉自定义指令的作用就是判断某个数据集是否包含binding.value这个值,如果不包含则删除这个元素自身