为什么要动态路由绑定
因为,如果我们的导航栏没有这个权限,输入对应网址,一样可以获取对应的页面,为了解决这个问题,有两种解决方案,一种是动态路由绑定(导航有多少个,就有多少个路由,在路由修改之前,先进行一个导航路由的加载和路由的动态绑定,然后看是否有这个路由,有就跳转),一种是权限的校验(路由修改之前,进行一个拦截,对用户的权限进行一个校验),这两种方法都可以在路由页面里,使用beforeEach来进行操作,这里是动态路由绑定
思路
将这个用户的能显示显示的导航栏对应的路由,和我们的路由配置进行一个绑定和校验,这样的话,如果说没有这个用户对应的路由,那么就无法跳转到对应的页面
步骤(1-3步是准备步骤)
1.使用mock.js返回对应的菜单栏和用户权限
因为还没写后端,所以使用mock.js来模拟一下
Mock.mock('/sys/menu/nav', 'get', () => {
let nav = [
{
name: 'SysManga',
title: '系统管理',
icon: 'el-icon-s-operation',
component: "",
path: '',
children: [
{
name: 'SysUser',
title: '用户管理',
icon: 'el-icon-s-custom',
component: "sys/User",
path: '/sys/user',
children: []
}
]
},
{
name: 'SysTools',
title: '系统工具',
icon: 'el-icon-s-tools',
path: '',
component: "",
children: [
{
name: 'SysDict',
title: '数字字典',
icon: 'el-icon-s-order',
path: '/sys/dicts',
component: "sys/Dict",
children: []
},
]
},
]
let authoritys = [];
Result.data = {
nav: nav,
authoritys: authoritys
}
return Result;
});
2.在store里面写一个menu.js的页
将我们需要再其他页面使用的变量和方法写入
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default {
state: {
menuList: [],
authoritys: [],
validation: false,
},
getters: {
},
mutations: {
// 菜单列表
setMenuList(state, menu) {
state.menuList = menu
},
// 权限列表
setPermList(state, perms) {
state.authoritys = perms
},
setValidation(state, validation) {
state.validation = validation;
}
},
modules: {
},
actions: {
},
}
3.给对应的页面插入对应的元素
<template>
<el-menu
class="el-menu-vertical-demo"
background-color="#545c64"
text-color="#fff"
active-text-color="#ffd04b"
@select="handleSelect"
>
<!-- 首页菜单项 -->
<el-menu-item index="Index">
<template #title>
<i class="el-icon-s-home"></i>
首页
</template>
</el-menu-item>
<!-- 系统管理子菜单 -->
<el-submenu :index="menu.name" v-for="menu in menuList" :key="menu.name">
<template #title>
<i :class="menu.icon"></i>
<span>{{ menu.title }}</span>
</template>
<!-- 用户管理子菜单 -->
<el-menu-item
v-for="item in menu.children"
:key="item.name"
:index="item.path"
>
<template #title>
<i :class="item.icon"></i>
<span>{{ item.title }}</span>
</template>
</el-menu-item>
</el-submenu>
</el-menu>
</template>
<script>
export default {
name: "SideMenu",
data() {
return {
}
},
computed: {
menuList: {
get() {
return this.$store.state.menu.menuList
},
}
},
methods: {
handleSelect(index) {
this.$router.push(index);
},
}
}
</script>
<style scoped>
.el-menu-vertical-demo {
flex: 1; /* 菜单项占据 el-aside 的剩余空间 */
overflow-y: auto; /* 如果内容过多,显示滚动条 */
width: 200px;
}
</style>
4.在路由页面写入beforeEach方法(有注释的)
思路:
1.在路由改变时发一个请求,拿到对应的菜单栏和权限
2.动态绑定路由
2.1 因为这里是子路由,所以先遍历,排除没有子路由的路由
2.2 再遍历子路由,看有没有component(具体的页面),排除没有的
2.3 将有component的对象里的元素封装为一个主路由(我这里主路由名为Home)的子路由
2.4 但是现在有个问题,每次路由修改之后我都要发请求,浪费,所以,在store中的页面里定义一个布尔类型的validation,给个默认值false,当请求完成,修改为true,下次就不会发请求,节约性能
router.beforeEach((to, from, next) => {
const validation = store.state.menu.validation;
if (!validation) {
axios.get("/sys/menu/nav", {
headers: {
Authorization: localStorage.getItem("token"),
}
}).then(res => {
// 拿到 menuList
store.commit("setMenuList", res.data.data.nav);
// 拿到用户权限
store.commit("setPermList", res.data.data.permList);
// 动态绑定路由
store.state.menu.menuList.forEach(item => {
if (item.children) {
item.children.forEach(e => {
if (e.component) {
router.addRoute("Home", {
path: e.path,
name: e.name,
component: () => import(`@/views/${e.component}`),
});
}
});
}
});
// 更新 validation 状态
console.log(123456)
store.commit('setValidation', true);
// 确保在异步操作完成后继续导航
next({path: to.fullPath});
}).catch(error => {
console.error('Error fetching menu:', error);
// 处理请求失败的情况,例如重定向到登录页面
next('/login');
});
} else {
// 如果已经验证过,直接继续导航
next();
}
});