本来很简单的一件事,给后台差点弄得当场去世。
前言:动态路由就是根据一个json格式的数组来生成Vue中的路由表,实现动态注册。可以前端自己实现,也可以使用后台返回的路由表来生成。
坑1-----------------------------前后台字段不匹配
坑2-----------------------------转换使用异步思想,如果没有添加路由信息便开始跳转,则会出现页面跳转空白和404
说起这个,弄得自己差点就怀疑人生(唉,忍住忍住,不是后台的锅),先说明一下,路由里面,最基础的字段有path,name,component,meta。其中path为跳转时浏览器显示的路径,name为标识,component为该path对应的组件路径,meta为路由携带的信息,里面可以定义任意的字段。使用的最重要的方法为addRoutes()。
要实现一个动态路由有如下几个步骤:
1.准备一个路由表
2.将路由表转换为自己想要的数据格式
3.使用addRoutes添加
由于添加路由addRoutes是一个异步的方法,因此可以配个async和await来使用,效果更好。
1.准备路由表:
leftMenu: [{
"title": "工作台",
"path": "/home",
"icon": "el-icon-house",
"children": [],
id: 1
}, {
"title": "借贷管理",
"path": "/debitManage",
"icon": "el-icon-money",
id: 1,
"children": [{
"title": "提交借款初审",
"path": "/debitManage/firstCon",
id: 1,
"children": [{
"title": "新增借款",
"path": "/debitManage/addDebitItem",
name: "addDebitItem",
id: 1,
"children": []
}, {
"title": "新标维护",
"path": "/debitManage/upholdNewBidItems",
name: "addDebitItem",
id: 1,
"children": []
}, {
"title": "借款审核",
"path": "/debitManage/firstDebitCon",
name: "addDebitItem",
id: 1,
"children": []
}]
}, {
"title": "上架管理",
id: 1,
"path": "/debitManage/secondCon",
"children": [{
"title": "上架初审",
"path": "/debitManage/firstLevelAuditItems",
name: "addDebitItem",
id: 1,
"children": []
}, {
"title": "标的上架",
"path": "/debitManage/secondBidsGroundItems",
name: "addDebitItem",
id: 1,
"children": [
]
}, {
"title": "进行中的标管理",
"path": "/debitManage/runningBidsItems",
name: "addDebitItem",
id: 1,
"children": []
}]
},
{
"title": "复审管理",
"path": "/debitManage/thridCon",
id: 1,
"children": [{
"title": "满标复审",
"path": "/debitManage/reexAuditItems",
name: "addDebitItem",
id: 1,
"children": []
}]
}, {
"title": "记录查看",
"path": "/debitManage/viewLogs",
id: 1,
"children": [{
"title": "所有借款标",
"path": "/debitManage/debitItems",
name: "addDebitItem",
id: 1,
"children": []
}, {
"title": "投资记录",
"path": "/debitManage/investRecords",
name: "addDebitItem",
id: 1,
"children": []
}]
}, {
"title": "管理标类别",
"path": "/debitManage/manageCategory",
id: 1,
"children": [{
"title": "借款标类别",
"path": "/debitManage/debitCategory",
name: "addDebitItem",
id: 1,
"children": []
}]
}
]
}, {
"title": "还款管理",
"path": "/repaymentManage",
"icon": "el-icon-bank-card",
id: 1,
"children": [{
"title": "管理还款项",
"path": "/repaymentManage/repaymentItems",
name: "addDebitItem",
id: 1,
"children": []
},
{
"title": "还款记录",
"path": "/repaymentManage/repaymentRecords",
name: "addDebitItem",
"children": [],
id: 1
}
]
}, {
"title": "资金管理",
"path": "/capitalManage",
id: 1,
"icon": "el-icon-wallet",
"children": [{
"title": "记录查看",
"path": "/capitalManage/logsView",
id: 1,
"children": [{
"title": "充值记录",
"path": "/capitalManage/reChargeLog",
id: 1,
name: "addDebitItem",
"children": []
}, {
"title": "提现记录",
"path": "/capitalManage/withDrawMoneyLog",
id: 1,
name: "addDebitItem",
"children": []
}, {
"title": "用户资金",
"path": "/capitalManage/userCapital",
name: "addDebitItem",
id: 1,
"children": []
}, {
"title": "平台资金",
"path": "/capitalManage/platformCapitalLog",
name: "addDebitItem",
id: 1,
"children": []
}]
}, {
"title": "相关审核",
"path": "/capitalManage/withDrawConfirm",
id: 1,
"children": [{
"title": "提现审核",
"path": "/capitalManage/withDrawCon",
id: 1,
name: "addDebitItem",
"children": []
}]
}]
}, {
"title": "会员管理",
"path": "/memberManage",
"icon": "el-icon-user-solid",
id: 1,
"children": [{
"title": "新增借款用户",
"path": "/memberManage/addDebitUser",
name: "addDebitItem",
id: 1,
"children": []
},
{
"title": "投资用户管理",
"path": "/memberManage/investUserManage",
name: "addDebitItem",
id: 1,
"children": []
},
{
"title": "借款用户管理",
"path": "/memberManage/debitUserManage",
name: "addDebitItem",
id: 1,
"children": []
}
]
}, {
"title": "内容管理",
"path": "/contentManage",
"icon": "el-icon-s-grid",
id: 1,
"children": [{
"title": "轮播管理",
"path": "/contentManage/sliderManage",
id: 1,
"children": [{
"title": "新增轮播",
"path": "/contentManage/sliderManage/addSliderItem",
"children": [],
id: 1
}]
},
{
"title": "文章管理",
"path": "/contentManage/articleManage",
id: 1,
"children": [{
"title": "新增文章",
"path": "/contentManage/articleManage/addArticleItem",
"children": [],
id: 1,
}]
},
{
"title": "意见反馈",
"path": "/contentManage/feedBack",
id: 1,
"children": [{
"title": "反馈处理",
"path": "/contentManage/feedbackManage/addFeedbackItem",
"children": [],
id: 1,
}, ]
}
]
}]
分割线:
2.在cli搭建的项目的router文件夹中替换index.js文件,新建一个index.js文件,在里面写预先定义好的静态路由,并且写上初始化路由的方法
router/index.js
import Vue from "vue";
import Router from "vue-router";
// import Layout from "@/Layout";
import Login from "@/views/Login";
Vue.use(Router);
const originalPush = Router.prototype.push;
Router.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err);
};
export const constRouters = [
{
path: "/login",
component: Login,
hidden: true,
name: "login"
}
];
const createRouter = () =>
new Router({
// mode: "history",
routes: constRouters
});
const index = createRouter();
export function resetRouter() {
const newRouter = createRouter();
index.matcher = newRouter.matcher;
}
export const errorRoute = [
{
path: "*",
component: () => import("@/views/NotFound"),
hidden: true,
name: "pageNotFound",
meta: { title: "页面不见啦" }
},
{
name: "login",
path: "/login",
component: () => import("../views/Login")
}
];
export default index;
3.创建一个转化路由格式的文件:asyncRouter.js
//引入公共路由
export function getAsyncRoutes(routes) {
const res = [];
const keys = ["path", "name", "children"];
if (Array.isArray(routes)) {
routes.forEach(v => {
const newItem = {};
if (v.path) {
//如果m有children,代表它是二级或者n级菜单,不引入组件
if (v.children && v.children.length > 0) {
newItem.component = () => "";
} else if (v.path === "/") {
newItem.component = newItem.component = resolve => {
require([`@/views${v.component}`], resolve);
};
} else {
//如果不是,就替换
newItem.component = resolve => {
require([`@/views${v.path}/index`], resolve);
};
}
}
for (const key in v) {
if (keys.includes(key)) {
//由于没有name字段不合规范,只能替换name字段为path后面的一截
if (key === "name") {
newItem["name"] = v["path"].split("/")[
v["path"].split("/").length - 1
];
} else {
newItem[key] = v[key];
//生成标题字段
newItem.meta = { title: v.name };
}
// } else {
// newItem["name"] = v["path"].split("/")[
// v["path"].split("/").length - 1
// ]+v.id;
// }
}
}
if (v.path) {
if (
(v.children == null || v.children.length === 0) &&
v.path !== "/home"
) {
newItem["name"] = v["path"].split("/")[
v["path"].split("/").length - 1
];
}
}
//如果有children,使用递归继续调用该方法,实现任意深度都可以转换
if (newItem.children && newItem.children.length) {
newItem.children = getAsyncRoutes(v.children);
}
res.push(newItem);
});
}
return res;
}
4.与index.js同级创建一个permission.js文件,拦截并处理跳转加载
import router from "../router";
import store from "../store";
import NProgress from "nprogress";
// console.log(store);
import "nprogress/nprogress.css";
import { resetRouter } from "../router";
import { getAsyncRoutes } from "@/router/asyncRouter";
import { errorRoute } from "../router";
const whileList = ["/login"];
var update = true;
router.beforeEach(async (to, from, next) => {
NProgress.start();
document.title = to.meta.title;
//获取token
const hasToken = (await store.getters.getToken) !== "";
// console.log("token:" + hasToken);
if (hasToken) {
// console.log("有没有token", hasToken);
if (to.path === "/login" || to.path === "/") {
next({ path: "/home" });
} else {
//获取vuex中保存的路由
let hasRoutes = await store.getters.getState;
//如果有路由则直接下一步,如果没有则需要获取路由
//判断是不是点击刷新
// console.log("是否有路由", hasRoutes);
if (hasRoutes && !update) {
next();
} else {
try {
await resetRouter();
const accessRoutes = getAsyncRoutes(await store.getters.getRoutes);
let changedRoutes = accessRoutes.filter(v => {
return v.path !== "/home/index";
});
//---------------------------------------------------===================================================
//-----------------------------------------------------================================================
//在这个下面添加自定义的路由,不要在index.js里面添加
let extraRoutes = [
//在这里写额外的路由
{
path: "/sliderManage/sliderManage/addSlider",
name: "addSlider",
meta: { title: "新增轮播" },
component: () =>
import(
"../views/contentManage/sliderManage/sliderManage/addSlider"
)
},
{
path: "/articleManage/articleManage/addArticle",
name: "addArticle",
meta: { title: "新增文章" },
component: () =>
import(
"../views/contentManage/articleManage/articleManage/addArticle"
)
},
{
path: "/debitManage/Maintenance",
name: "Maintenance",
meta: { title: "上架复审" },
component: () =>
import("../views/debitManage/secondBidsGroundItems/Maintenance")
},
{
path: "/debitManage/RecheckAction",
name: "RecheckAction",
meta: { title: "复审" },
component: () =>
import("../views/debitManage/reexAuditItems/RecheckAction")
},
{
path: "/",
component: () => import("@/views/home/index"),
meta: { title: "工作台" },
name: "index"
},
{
path: "/Pwd",
name: "Pwd",
meta: { title: "密码管理" },
component: () =>
import("@/components/rest/investUserManage/Pwd.vue")
},
{
path: "/luser3",
name: "luser3",
component: () =>
import("@/components/rest/debitUserManage/luser3")
},
{
path: "/Modify",
name: "Modify",
component: () =>
import("@/components/rest/investUserManage/Modify")
},
{
path: "/luser4",
name: "luser4",
component: () =>
import("@/components/rest/debitUserManage/luser4")
}
];
changedRoutes.push(...extraRoutes);
//console.log("转换后的路由:", changedRoutes);
await router.addRoutes([
{
path: "/home",
component: () => import("@/Layout/Home"),
name: "home",
children: changedRoutes
}
]);
await store.commit("setNoRefresh");
update = false;
next({ ...to, replace: true });
} catch (error) {
next(`/login?redirect=${to.path}`);
console.log(error);
NProgress.done();
}
}
}
} else {
// console.log(to.path);
if (whileList.indexOf(to.path) !== -1) {
next();
} else {
next({ path: `/login` });
NProgress.done();
}
}
});
router.afterEach(async () => {
NProgress.done();
// console.log("导航结束");
});
文件中定义了路由的全局前置守卫,当有token并且当前已经生成路由表时才放行;如果没有路由表则要调用getAsyncRoutes方法;
注意:1.文件中引入nprogress插件,页面加载条,启动使用start方法,停止使用done方法2.页面刷新时,路由信息会被清空,需要重新添加3.使用一个状态值来说明是否已经存在路由信息4.addRoutes只接受数组,最好使用解构然后赋值;