vue路由动态添加

需求:根据登录的角色不一样,实现不同的路由展示,例如:超级管理员登录能访问所有菜单,普通用户登录就只能展示部分菜单。

1.首先在路由里面配置两份路由,一份静态的默认路由,例如登录页面,404页面

export const constantRoutes = [
    {
        path: '/',
        redirect:'/login'
    },
    {
        path: '/login',
        component: () => import('@/view/login')
    }, {
        path: '/404',
        component: () => import('@/view/404')
    }, {
        path: '/main',
        name:'main',
        component: () => import('@/view/main'),
        children: [{
            path: 'home',
            component: () => import('@/view/asyncPage/home')
        }]
    }
]

然后在配置一份动态的路由,每个路由添加上roles属性,表明该路由那些角色能够访问

export const asyncRoutes = [{
    path: 'xtgl',
    id:"2",
    icon:"el-icon-s-tools",
    toPath:'/main/xtgl',
    component: () => import('@/view/asyncPage/系统管理'),
    meta: {
        title: '系统管理',
        roles: ['admin', 'vip']
    },
    children: [{
        path: 'jsgl',
        id:"2-1",
        toPath:'/main/xtgl/jsgl',
        component: () => import('@/view/asyncPage/角色管理'),
        meta: {
            title: '角色管理',
            roles: ['admin', 'vip']
        },
    }, {
        path: 'zhgl',
        id:"2-2",
        toPath:'/main/xtgl/zhgl',
        component: () => import('@/view/asyncPage/账号管理'),
        meta: {
            title: '账号管理',
            roles: ['admin']
        },
    }]
}, {
    path: 'ddgl',
    id:"3",
    toPath:'/main/ddgl',
    component: () => import('@/view/asyncPage/订单管理'),
    meta: {
        title: '订单管理',
        roles: ['admin', 'vip']
    },
    children: [{
            path: 'ddfh',
            id:"3-2",
            toPath:'/main/ddgl/ddfh',
            component: () => import('@/view/asyncPage/订单发货'),
            meta: {
                title: '订单发货',
                roles: ['admin', 'vip']
            },
        },
        {
            path: 'ddlb',
            id:"3-1",
            toPath:'/main/ddgl/ddlb',
            component: () => import('@/view/asyncPage/订单列表'),
            meta: {
                title: '订单列表',
                roles: ['admin']
            },
        }, {
            path: 'wddd',
            id:"3-3",
            toPath:'/main/ddgl/wddd',
            component: () => import('@/view/asyncPage/我的订单'),
            meta: {
                title: '我的订单',
                roles: ['admin','vip']
            },
            children: [{
                    path: 'qbdd',
                    id:"3-3-1",
                    toPath:'/main/ddgl/wddd/qbdd',
                    component: () => import('@/view/asyncPage/全部订单'),
                    meta: {
                        title: '全部订单',
                        roles: ['admin']
                    },
                },
                {
                    path: 'wcdd',
                    id:"3-3-2",
                    toPath:'/main/ddgl/wddd/wcdd',
                    component: () => import('@/view/asyncPage/完成订单'),
                    meta: {
                        title: '完成订单',
                        roles: ['admin', 'vip']
                    },
                }
            ]
        },
    ]
}, {
    path: 'xxgl',
    id:"4",
    toPath:'/main/xxgl',
    redirect:'/main/xxgl/thgl',
    // component: (resolve) => require(['@/view/asyncPage/学校管理'], resolve),//映射的组件
    component: () => import('@/view/asyncPage/学校管理'),
    meta: {
        title: '学校管理',
        roles: ['admin', 'vip']
    },
    children: [{
            path: 'thgl',
            id:"4-1",
            toPath:'/main/xxgl/thgl',
            component: () => import('@/view/asyncPage/教师管理'),
            meta: {
                title: '教师管理',
                roles: ['admin', 'vip']
            },
        },
        {
            path: 'xsgl',
            id:"4-2",
            toPath:'/main/xxgl/xsgl',
            component: () => import('@/view/asyncPage/学生管理'),
            meta: {
                title: '学生管理',
                roles: ['admin']
            },
        }, {
            path: 'jzgl',
            id:"4-3",
            toPath:'/main/xxgl/jzgl',
            component: () => import('@/view/asyncPage/家长管理'),
            meta: {
                title: '家长管理',
                roles: ['admin']
            },
        }
    ]
},{
    path: 'sz',
    id:"5",
    toPath:'/main/sz',
    component: () => import('@/view/asyncPage/设置'),
    meta: {
        title: '设置',
        roles: ['admin', 'vip','user']
    }
}]

2.然后封装一个函数,用来从动态路由中过滤当前角色能够访问的路由

function hasPermission(roles, route) { //判断角色是否有该路由的权限
    if (route.meta && route.meta.roles) {
        return route.meta.roles.includes(roles)
    } else {
        return true
    }
}

function filterAsyncRoutes(asyncRoutes, roles) {
    let r = [];
    asyncRoutes.map((item) => {
        const tmp = { ...item
        };
        if (hasPermission(roles, tmp) && !tmp.children) {
            r.push(tmp)
        } else if (hasPermission(roles, tmp) && tmp.children) {
            r.push({
                path: tmp.path,
                id: tmp.id,
                component: tmp.component,
                meta: tmp.meta,
                toPath:tmp.toPath,
                children: filterAsyncRoutes(tmp.children, roles)
            })
        }
    })
    // 返回角色拥有的权限路由
    return r;
}
export default filterAsyncRoutes;

3.登录的时候更具后台返回的角色(我这里使用mock 模拟后台返回的数据,只定义了3种角色,admin vip 和user),调用刚封装的函数,获取到该角色拥有权限的路由,调用addRoute动态注册该路由,并把路由存到

sessionStorage里面方面后面使用组件渲染。
login(){
    // 登录前预校验
    this.$refs.loginFromRef.validate(res=>{
        if(res){
            this.axios.post('/login',this.loginFrom).then(res=>{
                let {data:{data}} = res;
                let userInfo = data.userInfo;
                window.sessionStorage.setItem('token',userInfo.token);
                window.sessionStorage.setItem('identity',userInfo.identity);
                this.$store.commit('loginIn',userInfo)
                let ro = JSON.stringify(filterAsyncRoutes(asyncRoutes, userInfo.identity))
                console.log("登录重新注册路由",ro);
                window.sessionStorage.setItem('router', ro);
                resetRouter()
                filterAsyncRoutes(asyncRoutes, userInfo.identity).forEach(item => {
                    router.addRoute('main', item)
                })
                //路由添加完成后在动态添加404 ,解决刷新后页面跳404 和路由找不到的时候跳404
                router.addRoute({
                    path: '*',
                    redirect: '/404'
                })
                this.$router.push('main/home');
            }).catch(err=>{
                console.log(err);
            })
        }else{
            return;
        }
    })
},

4,登录后如果刷新页面,路由会失效,所有在路由守卫里面在添加一次动态路由注册,解决该问题

let oneRun = true; //防止路由死循环
router.beforeEach((to, from, next) => {
    let roles = window.sessionStorage.getItem('identity');
    if (to.path == '/login') {
        return next()
    } else {
        let token = window.sessionStorage.getItem('token');
        token ? next() : next('/login')
    }
    // 每次跳转前存入要跳转的路径当成当前选中项的id
    window.sessionStorage.setItem('currIndex',to.path);
    if(_this){
         _this.$store.commit('setCurrIndex',to.path);
         console.log( _this.$store.state.currIndex);
    }
    if(oneRun){
        oneRun=false;
        console.log(asyncRoutes,roles);
        window.sessionStorage.setItem('router', JSON.stringify(filterAsyncRoutes(asyncRoutes, roles)));
        console.log("路由守卫重新注册路由");
        filterAsyncRoutes(asyncRoutes, roles).forEach(item => {
            router.addRoute('main', item)
        })
        //路由添加完成后在动态添加404 ,解决刷新后页面跳404 和路由找不到的时候跳404
        router.addRoute({
            path: '*',
            redirect: '/404'
        })
        next({...to,replace:true})
    }
})

大体的思路就是这样的,源码传到了码云上,给大佬奉上仓库地址:https://gitee.com/wbw1993/vue-dynamic-routing

效果图:登录的用户名是admin能访问所有的菜单,vip只能展示部分菜单,其他用户名都只能看到首页和设置两个菜单

 

 

 

 

 结语:代码未经优化,欢迎各位大佬指出不足的地方

路漫漫其修远兮...

 

posted @ 2022-04-21 22:16  吴百万  阅读(2823)  评论(0编辑  收藏  举报