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 = true this.$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 } = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password })
.then((response) => {
console.log('login', response)
const { data } = response
commit('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 } = response
if (!data) {
return reject('Verification failed, please Login again.')
}
const { name, avatar } = data
commit('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 first
commit('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 Configuration const whiteList = ['/login'] // no redirect whitelist router.beforeEach(async(to, from, next) => { // start progress bar NProgress.start() // set page title document.title = getPageTitle(to.meta.title) // determine whether the user has logged in const hasToken = getToken() if (hasToken) { if (to.path === '/login') { // if is logged in, redirect to the home page next({ path: '/' }) NProgress.done() } else { const hasGetUserInfo = store.getters.name if (hasGetUserInfo) { next() } else { try { // get user info await store.dispatch('user/getInfo')
//获取路由列表,会触发 src/store/modules/permission.js store.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-login await 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、请求路由接口
path,name,component,children,meta 路由格式
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-menu
class="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-item
v-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>
如果按照 vue-element-admin 是通过路由过滤实现的,具体可以看下代码
浙公网安备 33010602011771号