实现的效果如下:
用admin账号登录展示
用xiaoxiao账号登录:
并且在xiaoxiao登录的系统中,手动在地址栏输入'/user',请求admin用户才会显示的用户管理页,页面不会展示。
实现思路如下:
1.用户登录,后端返回的data中有菜单数据,把菜单数据存储在vuex中,在侧边栏组件中去获取到存储在vuex中的菜单数据,根据数据动态生成侧边栏。
本项目我是用Mock模拟的数据,后端返回的接口数据如下图:
import Mock from 'mockjs'export default { getMenu: config => { const { username, password } = JSON.parse(config.body) // 先判断用户是否存在 // 判断账号和密码是否对应 if (username === 'admin' && password === 'admin') { return { code: 20000, data: { menu: [ { path: '/home', name: 'home', label: '首页', icon: 's-home', url: 'Home.vue' }, { path: '/mall', name: 'mall', label: '商品管理', icon: 'video-play', url: 'Mall.vue' }, { path: '/user', name: 'user', label: '用户管理', icon: 'user', url: 'User.vue' }, { label: '其他', icon: 'location', children: [ { path: '/page1', name: 'page1', label: '页面1', icon: 'setting', url: 'PageOne.vue' }, { path: '/page2', name: 'page2', label: '页面2', icon: 'setting', url: 'PageTwo.vue' } ] } ], token: Mock.Random.guid(), message: '获取成功' } } } else if (username === 'xiaoxiao' && password === 'xiaoxiao') { return { code: 20000, data: { menu: [ { path: '/home', name: 'home', label: '首页', icon: 's-home', url: 'Home.vue' }, { path: '/video', name: 'video', label: '商品管理', icon: 'video-play', url: 'Mall.vue' } ], token: Mock.Random.guid(), message: '获取成功' } } } else { return { code: -999, data: { message: '密码错误' } } } }}
在store/tab.js的state中定义一个menu数据用来存储后端返回的菜单栏数据,在mutations中定义一个setMenu方法,在登录成功的时候调用该方法,把后端返回的数据存入menu中
export default { state:{ //菜单栏数据 menu:[] }, mutations:{ // 设置menu的数据 setMenu(state,val){ state.menu = val }, }}
在Login.vue组件中,点击登录调用后端接口,拿到后端返回的数据
//登录 submit(){ this.$refs.form.validate((valid) => { if(valid){ getMenu(this.form).then(({ data }) => { console.log(data) if(data.code === 20000){ //获取菜单的数据存入store中 this.$store.commit('setMenu',data.data.menu) }else{ this.$message.error(data.data.message) } }) } }) }
在 CommonAside.vue中动态计算menuData,然后根据menuData生成菜单栏结构
这样就实现了不同账号登录展示不同的侧边栏。但是,当我们在地址栏中手动输入一些路径,仍能够访问到。比如,在xiaoxiao登录的系统中,输入'/user'仍然显示。原因是项目中的路由表是写死了,就像router/index.js中的注释掉的这部分内容这样:
因此,接下来我们要做得就是利用router.addRoute方法,把这部分内容变成动态的。
代码如下:
还是在store/tab.js中
定义动态添加路由的方法,遍历存储在state中的menu数据,把它从这样
menu: [ { path: '/home', name: 'home', label: '首页', icon: 's-home', url: 'Home.vue' }, { path: '/mall', name: 'mall', label: '商品管理', icon: 'video-play', url: 'Mall.vue' }, { path: '/user', name: 'user', label: '用户管理', icon: 'user', url: 'User.vue' }, { label: '其他', icon: 'location', children: [ { path: '/page1', name: 'page1', label: '页面1', icon: 'setting', url: 'PageOne.vue' }, { path: '/page2', name: 'page2', label: '页面2', icon: 'setting', url: 'PageTwo.vue' } ] } ]
变成和注释掉的那部分一样,然后调用router.addRoute()把内容添加到路由表中。
实现代码如下:
export default { state:{ //菜单栏数据 menu:[] }, mutations:{ // 设置menu的数据 setMenu(state,val){ state.menu = val }, //动态注册路由 addMenu(state,router){ if(state.menu.length == 0 ) return //组装动态路由的数据 const menuArray = [] state.menu.forEach(item => { if(item.children){ item.children = item.children.map(childItem => { childItem.component = () => import(`../views/${childItem.url}`) console.log(childItem.component) return childItem }) console.log(item.children) menuArray.push(...item.children) }else{ item.component = () => import(`../views/${item.url}`) menuArray.push(item) } console.log(menuArray,'menuArray') //路由的动态添加 menuArray.forEach(item => { router.addRoute('main',item) }) }) } }}
因为要用到router的api,所以,在定义addMenu方法的时候要把路由实例作为参数传进来。
在Login.vue组件中,登录成功调用addMenu方法:
//登录 submit(){ this.$refs.form.validate((valid) => { if(valid){ getMenu(this.form).then(({ data }) => { console.log(data) if(data.code === 20000){ //token信息存入cookie用于不同页面间的通信 Cookie.set('token', data.data.token) //获取菜单的数据存入store中 this.$store.commit('setMenu',data.data.menu) //动态添加路由表中的数据 this.$store.commit('addMenu',this.$router) //跳转到首页 this.$router.push('/home') }else{ this.$message.error(data.data.message) } }) } }) }
经过上边操作路由表中的数据已经成功动态生成了,接下来设置路由拦截:
在router/index.js中:
//添加前置导航守卫router.beforeEach((to, from, next) => { // 判断token存不存在 const token = Cookie.get('token') // token不存在,说明当前用户是未登录,应该跳转至登录页 if(!token && to.name !== 'login'){ next({name:'login'}) }else if (token && to.name === 'login'){ //token存在,说明用户已经登录了 next({name:'home'}) }else{ next() }})
这样就实现了动态路由,但是存在两个问题:
1.刷新页面,vuex中的menu会初始化为[]
解决方法:作vuex的持久化,可参考我另外一篇博客:
vuex持久化存储插件vuex-persistedstate_奋进的大美的博客-CSDN博客
2.刷新页面,路由表数据也会重置
解决方法:
在main.js中调用addMenu方法: