基本思路
1.请求后端接口,返回一个菜单数组的json格式数据。
2.前端拿到返回值之后将菜单数组中需要的数据进行组装成一个路由认识的json对象数组。
3.路由中某个方法可以设置进行并渲染。
4.以上工作都准备好之后,想一下放在哪里比较合适。
实现
思路1
后端创建菜单数据,我采用的基本json格式如下
{
data:[{
path:'',
component:'',
name:'',
hidden:true,
alwaysShow:true,
icon:'图标',
children:[]
}]
}
思路2
将后端返回的数据进行解析并加工,这里非常关键。因为涉及到动态解析组件,我这里是创建了一个menu-util.js文件把方法都写在这里面然后暴露,大家可以借鉴参考。
import Layout from '@/layout'
export function refreshMenus(menus,router,store) {
const remoteRoutes=handleMenu(menus)
router.addRoutes(remoteRoutes)
router.options.routes=router.options.routes.concat(remoteRoutes)
store.dispatch('app/setRefreshPage');
}
export function handleMenu(menus){
menus=menus.map(item=>{
if(item.component){
let path = item.component;
item.component = resolve=>require([`@/views/${path}`],resolve)
}else{
item.component=Layout
}
item.meta = {title:item.name,icon:item.icon}
if(item.children && item.children.length>0){
item.children=handleMenu(item.children)
}
return item;
})
return menus;
}
这里写了一个刷新菜单方法refreshMenus 和 处理菜单方法handleMenu
handleMenu :将后端接收的菜单数据进行加工的地方
refreshMenus :刷新菜单方法是封装的步骤方法,因为代码中多处地方要用到,所以统一了一下。
思路3
在思路2中的刷新菜单handleMenu 方法中的第2,3行代码体现。
this.$router.addRoutes();
this.$store.options.routes.concat()
思路4
上面的操作完成后基本所有的准备工作都完成就绪,就差一步,思考应该放在哪里比较合适。楼主是放在了2个地方,登录和全局守卫
登录位置
在你自己代码的登录部分,当登录成功后,后端应该要返回当前用户的菜单数据用于里面的菜单渲染。
import {refreshMenus} from '@/utils/menu-util';
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
this.$store.dispatch('user/login', this.loginForm).then((response) => {
let { data:{menus} } = response;
refreshMenus(menus,this.$router,this.$store)
this.$router.push({ path: this.redirect || '/' })
this.loading = false
}).catch(() => {
this.loading = false
})
} else {
console.log('error submit!!')
return false
}
})
}
其实到这里你就能看到你想要的效果了,但是真正的问题才开始展现,这里就是为什么我还要在全局守卫这也要加这种代码。
全局守卫位置
查看vue-admin-template后台模板中,route的beforeEach方法在permission.js 中。我上面为什么说有重大问题,还在兴高采烈的小伙伴这时候可以尝试刷新一下你当前页面,惊喜天上来。
是不是菜单没了!这是因为刷新页面,VueX的数据会丢失,router会被重新创建。那咋搞?楼主是这么想的,既然刷新页面之后VueX里面的值会变,那我就在VueX的store中存储一个值,然后登录的时候设置成其他值,如果页面刷新肯定会把值恢复,这样我就可以根据这个字段来判断是否是刷新页面了。这就是思路2中的refreshMenus 方法中最后一行代码的由来.
store.dispatch('app/setRefreshPage');
将refreshPage默认设置成true ,refreshMenus 方法中会将该属性变成false ,别说看不懂这个代码,看不懂的得去稍微看一下VueX,楼主比你们还菜,js会的也是只有一点点。
关键的代码来了,就是在router.beforeEach方法中如何解决刷新页面路由重置问题。
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 {
if(store.state.app.refreshPage){
getMenus().then(response=>{
refreshMenus(response.data,router,store)
})
}
const hasGetUserInfo = store.getters.name
if (hasGetUserInfo) {
next()
} else {
try {
await store.dispatch('user/getInfo')
next()
} catch (error) {
await store.dispatch('user/resetToken')
Message.error(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()
}
}
})
|