9、vuex+vue-router实现:动态路由表+菜单管理

1,290 阅读2分钟

方案思路:

1、在vue-router中通过router.beforeEach做路由拦截;

2、判断有没有登录,通过next({ path: "/login" })跳到登录页面;

3、登录成功回到首页、通过同步的方式获取权限列表,存放到vuex的state.routes中;

store.dispatch("getRoutersByRole")
content.commit('setRoutes', resp.data)

4、功能权限列表生成路由视图

  获取权限列表
  store.state.routes
  变量列表生成路由视图
  router.addRoute(menu)

5、添加404页面

一、vuex

import Vue from 'vue'
import Vuex from 'vuex'
import httpClient from '../utils/httpClient.js'
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)
//配置部分状态持久化,默认存储在localStorage
const PERSIST_PATHS = ['isLogin']
export default new Vuex.Store({
	plugins: [createPersistedState({ paths: PERSIST_PATHS })],
	state: {
		isLogin: false,
		routes: []
	},
	mutations: {
		logout(state) {
			state.isLogin = false;
			state.routes = [];
			localStorage.removeItem('token')
		},
		login(state, token) {
			localStorage.setItem("token", token)
			state.isLogin = true
		},
		setRoutes(state, routes) {
			state.routes = routes;
		},
	},
	actions: {
		getRoutersByRole(content) {
			return new Promise(resolve => {
				httpClient.get("/getRouters").then(resp => {
					if (resp.code == 200) {
						if (process.env.NODE_ENV == 'dev') {
							resp.data.push({
								path: '/menu',
								meta: {
									title: '开发管理',
								},
								children: [{
										path: 'menu',
										name: 'menu',
										meta: {
											title: '菜单管理'
										}
									},
									{
										path: 'example',
										name: 'example',
										meta: {
											title: '使用示例',
										}
									}
								]
							})
						}
						content.commit('setRoutes', resp.data)
						resolve(true);
					}
				})
			})
		}
	},
})

二、vue-router

import Vue from 'vue'
import VueRouter from 'vue-router'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import store from '../store'
import login from '../views/login.vue'
NProgress.configure({ showSpinner: false })
Vue.use(VueRouter)

const routes = [{
		path: '/',
		name: 'login',
		redirect: '/login'
	},
	{
		path: '/login',
		name: 'login',
		component: login
	},
	{
		path: '/404',
		name: '404',
		component: () => import('@/views/404/index')
	},
]

// 解决Vue-Router升级导致的Uncaught(in promise) navigation guard问题("3.0.2"以下没有问题)
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location, onResolve, onReject) {
	if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)
	return originalPush.call(this, location).catch(err => err)
}
VueRouter.prototype.replace = function replace(location, onResolve, onReject) {
	if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)
	return originalPush.call(this, location).catch(err => err)
}


const router = new VueRouter({
	mode: "history",
	routes
})

router.beforeEach(async (to, from, next) => {
	console.log(to.path);
	NProgress.start();
	if (localStorage.getItem("token")) { //没有toeken就跳到登录页
		if (store.state.routes.length > 0) {
			next()
		} else {
			await store.dispatch("getRoutersByRole")
			addRemoteroutes();
			next({ ...to, replace: true }) //由于路由获取是异步的,在没有生成路由视图之前(重新加载直到路由视图完成)
		}
	} else {
		if (to.path == "/login") { //当前路由是登录就放行,否则重定向到登录页(目的是拦截没有登录的时候输入不合法的路由)
			next()
		} else {
			next({ path: "/login" })
		}
	}
})

function addRemoteroutes() {
	genetaeRoutes(store.state.routes);
	router.addRoute({
		path: '*',
		redirect: '404',
	})
	console.log(router.matcher.getRoutes());
}

//生产路由视图
function genetaeRoutes(Remoteroutes) {
	Remoteroutes.forEach(item => {
		let menu = {
			path: item.path,
			name: item.name,
			component: main,
			children: [],
			redirect: ''
		}
		if (item.children) {
			menu.children = item.children.map(item2 => {
				return {
					path: item2.path,
					name: item2.name,
					meta: item2.meta,
					component: () => import(`@/views${item.path}/${item2.path}`)
				}
			})
			menu.redirect=item.path+'/'+item.children[0].path
		}
		router.addRoute(menu);
	})

}


router.afterEach(() => {
	NProgress.done()
})

export default router

三、根据权限列表生成菜单:

setMenus() {
	let temp = [];
	this.$store.state.routes.forEach(item => {
		if (!item.hidden) {
			let menu = {}
			if (item.children.length > 1) {
				menu.icon = this.icons["summary"] //item.path;
				menu.imeName = item.meta.title;
				let subMenu = [];
				item.children.forEach(subItem => {
					if (!subItem.hidden) {
						let sub = {}
						sub.imeName = subItem.meta.title;
						sub.path =item.path+"/"+ subItem.path;
						subMenu.push(sub)
					}
				})
				menu.subMenu = subMenu
			} else {
				menu.icon = this.icons["summary"]; 
				menu.imeName = item.children[0].meta.title;
				menu.path = item.children[0].path;
			}
			temp.push(menu);
		}
		this.menus = temp;
	})
},