赞
踩
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页,加在路由得最后
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。