vue2 项目实例 动态路由菜单(四)
动态路由涉及到 router、 store、 beforeEach、 permission权限
1、触发登录事件
Login.vue
监听路由变化,下次登录重定向上次页面
watch: {$route: {handler: function(route) {this.redirect = route.query && route.query.redirect},immediate: true}},
登录按钮事件
handleLogin() {this.$refs.loginForm.validate((valid) => {if (valid) {this.loading = truethis.$store.dispatch('user/login', this.loginForm).then(() => {this.$router.push({ path: this.redirect || '/' })this.loading = false}).catch(() => {this.loading = false})} else {console.log('error submit!!')return false}})}
2、vuex
store/module/user.js
import { login, logout, getInfo } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { resetRouter } from '@/router'const getDefaultState = () => {return {token: getToken(),name: '',avatar: ''}
}const state = getDefaultState()const mutations = {RESET_STATE: (state) => {Object.assign(state, getDefaultState())},SET_TOKEN: (state, token) => {state.token = token},SET_NAME: (state, name) => {state.name = name},SET_AVATAR: (state, avatar) => {state.avatar = avatar}
}const actions = {// user login
login({ commit }, userInfo) {const { username, password } = userInforeturn new Promise((resolve, reject) => {login({ username: username.trim(), password: password }).then((response) => {console.log('login', response)const { data } = responsecommit('SET_TOKEN', data.token)setToken(data.token)resolve()}).catch((error) => {reject(error)})})},// get user info
getInfo({ commit, state }) {return new Promise((resolve, reject) => {getInfo(state.token).then((response) => {const { data } = responseif (!data) {return reject('Verification failed, please Login again.')}const { name, avatar } = datacommit('SET_NAME', name)commit('SET_AVATAR', avatar)resolve(data)}).catch((error) => {reject(error)})})},// user logout
logout({ commit, state }) {return new Promise((resolve, reject) => {logout(state.token).then(() => {removeToken() // must remove token first
resetRouter()commit('RESET_STATE')resolve()}).catch((error) => {reject(error)})})},// remove token
resetToken({ commit }) {return new Promise((resolve) => {removeToken() // must remove token firstcommit('RESET_STATE')resolve()})}
}export default {namespaced: true,state,mutations,actions
}
login 成功 resolve 执行路由跳转
3、路由守卫 permission
import router from './router' import store from './store' import { Message } from 'element-ui' import NProgress from 'nprogress' // progress bar import 'nprogress/nprogress.css' // progress bar style import { getToken } from '@/utils/auth' // get token from cookie import getPageTitle from '@/utils/get-page-title'NProgress.configure({ showSpinner: false }) // NProgress Configurationconst whiteList = ['/login'] // no redirect whitelist router.beforeEach(async(to, from, next) => {// start progress bar NProgress.start()// set page titledocument.title = getPageTitle(to.meta.title)// determine whether the user has logged inconst hasToken = getToken()if (hasToken) {if (to.path === '/login') {// if is logged in, redirect to the home pagenext({ path: '/' })NProgress.done()} else {const hasGetUserInfo = store.getters.nameif (hasGetUserInfo) {next()} else {try {// get user infoawait store.dispatch('user/getInfo')
//获取路由列表,会触发 src/store/modules/permission.jsstore.dispatch('GenerateRoutes').then(accessRoutes => {// 根据roles权限生成可访问的路由表router.addRoutes(accessRoutes) // 动态添加可访问路由表next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 })next()} catch (error) {// remove token and go to login page to re-loginawait store.dispatch('user/resetToken')Message.error(error || 'Has Error')next(`/login?redirect=${to.path}`)NProgress.done()}}}} else {/* has no token*/if (whiteList.indexOf(to.path) !== -1) {// in the free login whitelist, go directly next()} else {// other pages that do not have permission to access are redirected to the login page.next(`/login?redirect=${to.path}`)NProgress.done()}} })router.afterEach(() => {// finish progress bar NProgress.done() })
src/store/modules/permission.js
4、请求路由接口
import {constantRoutes} from '@/router'
import {getRouters} from '@/api/menu'
import Layout from '@/layout/index'const permission = {state: { routes: [], // 路由数据
},mutations: { SET_ROUTES: (state, routes) => {state.routes = constantRoutes.concat(routes)},SET_CURRENT_ROUTES: (state, routes) => {state.currentRoutes = routes}},actions: {// 生成路由
GenerateRoutes({ commit }) {return new Promise(resolve => { // 向后端请求路由数据getRouters().then(res => {const accessedRoutes = filterAsyncRouter(res.data)accessedRoutes.push({ path: '*', redirect: '/404', hidden: true })commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes) // 找到代码 3标题处})})}}
}// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap) {
const res = []for(let i= 0; i < asyncRouterMap.length; i++ ){
// 组装路由模式 ⚠️
const temp = {...asyncRouteMap[i]}
if(asyncRouteMap[i].children){
temp.children = filterAsyncRouter(asyncRouteMap[i].children)
}else{
temp.component=loadView(temp.component)
}
res.push(temp)
}return res
}export const loadView = (view) => { // 路由懒加载return (resolve) => require([`@/views/${view}`], resolve)
}export default permission
5、渲染左侧菜单
<template><div><el-menuclass="el-menu-vertical-demo":collapse="isCollapse"background-color="#545c64"text-color="#fff"active-text-color="#ffd04b"><h4>通用后台管理系统</h4>// 在子组件配置路由<AsideItems :menu="menu" /></el-menu></div> </template><script> import AsideItems from "./components/AsideItems"; export default {name: "CommonAside",components: {AsideItems,},data() {return {menu: [],};},mounted() {const mRoutes = this.$store.state.menu.menu;this.menu = mRoutes;}, }; </script>
<template><div><!-- 一定要加template遍历 递归调用 不然无效 --><template v-for="(item, i) in menu"><!-- 判断没有子路由的 --><el-menu-itemv-if="!item.children":key="i":index="item.name"@click="$router.push({ name: item.name })"><i :class="'el-icon-' + item.meta.icon"></i><span slot="title">{{ item.meta.title }}</span></el-menu-item><!-- 有子路由的导航 --><el-submenu v-else :key="i" :index="item.name"><template slot="title"><i :class="'el-icon-' + item.meta.icon"></i><span slot="title">{{ item.meta.title }}</span></template><!-- 子路由 --><aside-item :menu="item.children"></aside-item></el-submenu></template></div> </template><script> export default {name: "AsideItem",props: {menu: { type: Array },}, }; </script>
