1.首先确认使用哪一种方式进行动态路由添加:我选择的是addRoutes API
2.选用这种方式的坑:1.重复添加路由,每次退出登录(权限不变),会每次重复添加之前添加过得路由,导致警告;权限改变时,不会清除之前的权限路由,由高级降到低级时,只要知道地址,低级权限也可进入高级权限的页面;2.路由持久化,每次刷新都要重新添加一次路由,否者直接跳转404页面
首先看看router文件夹的结构
addRouter.js : 添加路由
index.js : 路由主文件
routerList.js : 初始路由配置文件
setRouter.js : 动态路由匹配组件页面方法
addRouter.js
import router from "@/router";
//传入整合好的真实路由,添加到vue路由
export function addR(routerMap) {
router.addRoutes(routerMap);
}
index.js
import Vue from "vue";
import Router from "vue-router";
// 引入初始路由配置表
import { routers } from "@/router/routerList";
Vue.use(Router);
const createRouter = () => new Router({
mode: "history",
scrollBehavior: () => ({ y: 0 }),
routes: routers
})
const router = createRouter()
//解决重复登录时重复添加路由 或者 高级权限改低级权限时 某些路由已经注入的问题
export function resetRouter () {
const newRouter = createRouter()
router.matcher = newRouter.matcher
}
export default router
routerList.js
export const routers = [
{
path: "/",
name: "login",
component: resolve => require(["@/views/basics/login.vue"], resolve) //登录
},
{
path: "*",
component: resolve => require(["@/views/basics/404.vue"], resolve) // 404页面
}
];
setRouter.js
import router from '@/router';
export function getList(routerMap, parent){
// 返回一个map对象
return routerMap.map((item,i) => {
const currentRouter = {
// 路由地址 动态拼接生成如 /dashboard/workplace
path: `${parent && parent.path || ''}/${item.path}`,
// 路由名称
name: item.name || item.key || '',
// 该路由对应页面的组件
component: resolve => require(["@/views/" + item.component], resolve),
//标题
title:item.meta.title,
//源信息
meta: item.meta,
//
key:item.key,
}
// 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
currentRouter.path = currentRouter.path.replace('//', '/')
// 路由重定向
item.redirect && (currentRouter.redirect = item.redirect)
// 子菜单递归循环
if (item.children && item.children.length > 0) {
currentRouter.children = getList(item.children, currentRouter)
}
// 返回路由数组
return currentRouter
})
};
到这里为止 基本准备工作已经完成,接下来就是使用了!
我的登录页面方法
//处理前路由数据结构展示
// 处理后路由数据结构展示
首先引入 文件 结构出上下文中的方法
import router from "@/router";
import { getList } from "@/router/setRouter.js";
import { resetRouter } from '@/router/index.js';
this.$api.Login(
{
password: values.psd,
username: values.name
}
).then(res => {
if(res.code == 0){
//存储token
localStorage.setItem("token", res.data.accessToken);
this.savetoken(res.data.accessToken);
//获取后端返回的路由对象
this.$api.Menu().then(res2 => {
if(res2.code == 0){
//自定义Loading组件
this.$Loading.show();
//调用setRouter.js 中抛出的getList方法
//返回一个匹配好的路由表对象
let routerList = getList(res2.data);
//index.js 中抛出的resetRouter方法
// 清空路由防止重复添加 或者清除之前添加的权限路由
resetRouter();
//使用addRoutes API添加路由
router.addRoutes(routerList);
//跳转
}
})
}
})
到此动态路由添加完成,接下来就是对动态路由进行数据持久化
main.js 路由拦截器
router.beforeEach((to, form, next) => {
//如果进入到的路由是登录页或者注册页面,则正常展示
let flag = localStorage.getItem("token");
//判断是否有token
if (to.path == "/") {
if (flag == "" || flag == null) {
//没有token 又访问根路由 就去登录
next();
} else {
//有token 又访问根路由 首页
//获取首页path
let path = localStorage.getItem("sPath");
//获取侧边栏需要的key
let code = localStorage.getItem("sCode");
//设置侧边栏选中高亮
localStorage.setItem("leftCode", code);
//跳转首页
next(path);
}
} else {
//访问非根路由
//判断store.state.rList.length 是否为0
//如果为0 则证明页面刷新过
if (store.state.rList.length === 0) {
let list = [];
axios.get('xxxxx').then(res =>{
if(res.code == 0 ){
list = res.data;
}
})
let routerList = getList(list);
//设置侧边栏渲染所需的数据
store.state.leftVal = routerList;
//上方引入 addR方法 调用方法添加路由
addR(routerList);
//跳转 replace: true 不添加到历史记录
next({ ...to, replace: true });
} else {
// 否则直接跳转
next();
}
}
});
至此动态路由持久化完成
接下来就是把页面按钮权限加入到页面
首先 我们在路由源信息里面加入了 permission 这个参数
我们现在要做的就是从路由中取出它 并与页面权限作对比 有则显示 无则隐藏
在这里 我们使用自定义指令的方式来实现
在src目录下新建一个 power文件夹 在里面新建一个 index.js
//首先引入Vue
import Vue from "vue";
const own = Vue.directive("own", {
//绑定元素插入到DOM中 要做的事
// 参数: 绑定的元素, 信息 , 虚拟节点对象
//binding
//binding:一个对象,包含以下 property:
//name:指令名,不包括 v- 前缀。
//value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
//oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
//expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
//arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
//modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
//vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
//oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
inserted: function(el, binding, vnode) {
let btn = "";
//获取单个按钮权限指令所绑定的值
let btnAdmin = binding.value;
//判断是否有绑定值 没有就直接return
if (btnAdmin) {
btn = btnAdmin;
} else {
//无权限直接返回
return;
}
//从虚拟节点中获取路由元信息
//判断是否为空对象 如果是直接return
if (JSON.stringify(vnode.context.$route.meta) == "{}") {
return;
}
//从传入的虚拟节点中获取按钮权限列表
let list = vnode.context.$route.meta.permission;
// 判断列表中是否含有 这个指令绑定的值 没有就隐藏这个按钮
if (list.indexOf(btn) == -1) {
el.style.display = "none"; //隐藏元素
}
}
});
在main.js中引入own
//自定义权限指令has 必须引入
import own from "./power/index.js";
Vue.use(own)
权限使用
<button @click='goPage' v-own="'edit'">GO cs3</button>
好的 完结撒花
写在最后,第一次写长篇大论,如有写的不好的请多包含,也可以加入QQ群一起交流:742189737
最后会上传一个已经实现动态路由和权限按钮的demo 里面用了 vuex 模块化 mock数据写在vue.config.js中 可以自行修改,自行参考这篇博客 实现持久化路由 请求写在vuex login模块里 可自行修改使用
下载资源