vue admin template动态路由(vue2后台管理项目动态加载路由)
项目思路描述
1.后台返回一个json格式的路由表 ,我这里直接写死了数据 ,使用Promise返回 ,大家可参考 ,也可以自己造;
2.因为后端传回来的都是字符串格式的 ,但是前端这里需要的是一个组件对象 ,所以要写个方法遍历一下 ,将字符串转换为组件对象;
3.利用vue-router的beforeEach 、addRoutes 、localStorage来配合上边两步实现效果;
4.左侧菜单栏根据拿到转换好的路由列表进行展示;在拦截路由之前 ,我们还得先定义好静态路由 ,然后从后端取到动态路由之后,进行合并 。 修改 router/index.js 静态路由主要是登录页面和重定向页面等 , 生成全局路由变量
import Layout from @/components/layout const constantRoutes = [ { path: /, redirect: /home }, { path: /login, component: () => import(@/views/login.vue), meta: { title: 登录 }, name: Login }, // 后台返回动态路由 // { // path: /home, // component: Layout, // hidden: true, // redirect: /list, // children: [ // { // path: /list, // name: List, // component: () => import(@/views/index.vue), // meta: { // index: 1 , // meta: { title: xxxxx, }, // } // }, // { // path: /shipInfo, // component: () => import(@/views/xxxx.vue), // meta: { title: xxxx}, // name: ShipInfo // }, // { // path: /quality, // component: () => import(@/views/xxxx.vue), // meta: { title: xxxxx }, // name: Quality // }, // ] // }, ]; const router =createRouter({ history:createWebHistory(), routes:constantRoutes , //使用浏览器的回退或者前进时,重新返回时保留页面滚动位置 ,跳转页面的话 ,不触发 。 scrollBehavior(to,from,savePosition){ if(savePosition){ return savePosition; }else{ return {top:0}; } } }); export default router;后端动态路由
路由在实际项目中是通过后端接口返回,在日常的开发中可以根据后端格式写手动写死 ,前后端联调的时候换成接口就行了 。(api下新建menujs)
其实就是一个路由配置项 ,里面的path ,name ,component ,children ,meta都是关键字不能改 ,格式一定要完全一样 ,其中meta里可以带上我们需要的信息 ,比如面包屑,菜单渲染的名字 ,图标 ,是否需要缓存,角色限制等 ,项目中需要用到的都可以存在meta中 。 //模拟获取后台路由(动态路由) export function getRouters() { return new Promise((resolve, reject) => { // menuList 里面得参数自己定义(可以跟后端商量返回自己需要的格式) // 这点一定要和后端商量好 ,这个路由表完全由后端维护,格式正确可以事半功倍哦 let menuList = [ { "path": /home, "component": Layout, "redirect": "/list", "hidden": true, "children": [ { "path": "/list", "name": "List", "component": "index", "meta": { "icon": "monitor", "title": "xxxx" } }, { "path": "/shipInfo", "name": "ShipInfo", "component": "shipInfo", "meta": { "icon": "monitor", "title": "xxxx" } }, { "path": "/quality", "name": "Quality", "component": "qualityAssessment", "meta": { "icon": "monitor", "title": "xxxx" } }, ] } ] resolve(menuList); }) } // 模拟获取登录账号信息(用户登录 、获取权限) export function getInfo() { return new Promise((resolve, reject) => { const data = { code: 200, avatar: "xxxxxxxxxx", username: "nickName111", roles: [admin], permission: [允许操作], msg: "获取用户信息成功" } resolve(data); }) }用vuex实现全局登录 、退出登录等方法
创建 store/modules/user.js ,里面装载 用户登录 、获取权限 、 退出登录清空权限 、存放token和权限菜单等全局变量和方法 。在store/index.js引入
import { emergency } from @/api; import router from @/router; import { message } from ant-design-vue; import { getInfo } from @/api/menu const user= { state: { token: sessionStorage.token || , name: , avatar: , roles: [] }, mutations: { SET_TOKEN(state, token) { // console.log(state, token) state.token = token sessionStorage.setItem(token, state.token) }, SET_AVATAR(state, avatar) { state.avatar = avatar }, SET_NAME(state, name) { state.name = name }, SET_ROLES(state, roles) { state.roles = roles }, }, actions: { // 获取当前登录用户信息 login({ commit }, { params }) { // console.log(params) return new Promise((resolve, reject) => { emergency.login(params).then(res => { // console.log(res) const result = res.data commit(SET_TOKEN, result.token) resolve() }).catch(err => { reject(err) }) }) }, // 获取用户信息 //根据用户的token获取用户的个人信息 ,里面包含了权限信息 getInfo({ commit }) { return new Promise((resolve, reject) => { getInfo().then(res => { // console.log(res) commit(SET_NAME, { name: res.name }) commit(SET_AVATAR, { headImgUrl: res.headImgUrl }) commit(SET_ROLES, { roles: res.roles }) resolve(res) }).catch(err => { reject(err) }) }) }, // 退出登录 userLogout ({ commit }) { // 清空权限菜单 commit(SET_ROLES, []) // 清空token commit(SET_TOKEN, ) // 跳转到登录菜单 router.push({path: /login}) } }, }; export default user;在store/index.js引入
import user from ./modules/user Vue.use(Vuex) export default new Vuex.Store({ modules: { user, permission }, state: {}, mutations: {}, actions: {}, getters: { token: state => state.user.token, name: state => state.user.name, avatar: state => state.user.avatar, addRouters: state => state.permission.addRouters, roles: state => state.user.roles, } })用vuex模块单独写权限路由的判断
创建 store/permission.js , GenerateRoutes 方法判断获取有权限的路由 , hasPerMission 函数用于判断权限主要核心函数 。
import constantRoutes from ../../router/routes import { getRouters } from @/api/menu import Layout from @/components/layout const constantRouterComponents = { Layout } /** * 过滤账户是否拥有某一个权限 ,并将菜单从加载列表移除 * * @param permission * @param route * @returns {boolean} */ function hasPermission(roles, route) { if (route.meta && route.meta.roles) { return roles.some(role => route.meta.roles.includes(role)) } else { return true } } // 遍历后台传来的路由字符串 ,转换为组件对象(过滤符合权限的路由) /** // 原方法 function filterAsyncRouter (routerMap, roles) { const accessedRouters = routerMap.filter(route => { if (hasPermission(roles, route)) { if (route.children && route.children.length) { route.children = filterAsyncRouter(route.children, roles) } return true } return false }) return accessedRouters } */ // 目前没有加入权限控制 filterAsyncRouter是修改过的 *需要的时候加上去即可* function filterAsyncRouter(asyncRouterMap) { const accessedRouters = asyncRouterMap.filter(route => { if (route.children && route.children.length) { route.children = filterAsyncRouter(route.children) } return true }) return accessedRouters } /** * 格式化树形结构数据 生成 vue-router 层级路由表 */ export const generator = (routerMap, parent) => { return routerMap.map(item => { const { title, icon } = item.meta || {} const currentRouter = { // 路由path , path: item.path, // 路由名称 ,建议唯一 name: item.name, // 该路由对应页面的 组件 component: (constantRouterComponents[item.component]) || (() => import(`@/views/${item.component}`)), // meta: 页面标题, 菜单图标 meta: { title: title, icon: icon || undefined, } } // 为了防止出现后端返回结果不规范 ,处理有可能出现拼接出两个 反斜杠 if (!currentRouter.path.startsWith(http)) { currentRouter.path = currentRouter.path.replace(//, /) } // 重定向 item.redirect && (currentRouter.redirect = item.redirect) // 是否有子菜单,并递归处理 if (item.children && item.children.length > 0) { // Recursion currentRouter.children = generator(item.children, currentRouter) } return currentRouter }) } const permission = { state: { routers: constantRoutes, addRouters: [] }, mutations: { SET_ROUTERS: (state, router) => { state.addRouters = router state.routers = constantRoutes.concat(router) } }, actions: { // 生成路由 GenerateRouters({ commit }, roles) { return new Promise(resolve => { getRouters().then(res => { const rdata = JSON.parse(JSON.stringify(res)) const routers = generator(rdata) const rewriteRoutes = filterAsyncRouter(routers) commit(SET_ROUTERS, rewriteRoutes) resolve(rewriteRoutes) }) }) }, } } export default permission;监听路由跳转实现动态加载权限菜单
在src下创建permissionjs文件 ,监听路由的跳转后 ,在mainjs引入
判断是否 有token,没有则跳向登录页 ,
如果有token ,则进行用户权限获取 (在store/user下的getInfo函数),
获取完权限进入 store/permission的GenerateRoutes函数 进行获取符合条件的路由 ,
再通过addRoute (addRoutes已经废弃)加载路由 // 先把路由和vuex引进来使用 import router from ./router import store from ./store import { resetRouter } from ./router/routes const whiteList = [/login] // 不重定向白名单 // console.log(store.getters.token) //路由拦截 router.beforeEach((to, from, next) => { if (store.getters.token) { // to.meta.title && store.dispatch(setTitle, to.meta.title) if (to.path === /login) { // next({ path: }) next() } else { // console.log(store.dispatch(GenerateRouters)) if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完userinfo信息 store.dispatch(getInfo).then((res) => { // console.log(res, userinfo信息) store.dispatch(GenerateRouters).then(accessRoutes => { // 根据roles权限生成可访问的路由表 // router.addRoutes()要使用addRoute 已经废弃 for (let x of accessRoutes) { // console.log(x) router.addRoute(x) } // router.addRoute(store.getters.addRouters) console.log(store.getters.addRouters) next({ ...to, replace: true }) // hack方法 确保addRoute已完成 }) }).catch(err => { //捕捉错误 ,退出登录 // store.dispatch(LogOut).then(() => { // next({ path: / }) // }) }) next() } else { next() } } } else { // 没有token if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单 ,直接进入 next() } else { next(`/login`) // 否则全部重定向到登录页 } } }) 项目中没有注册路由会跳转到空白页 ,需要手动添加404页 ,加在路由得最后创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!