查看代码
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
import { App } from "vue";
import Cookies from "js-cookie";
import store from "../store";
import { getAdminInfo } from "../request/http";
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: "/login",
name: "login",
component: () => import("../views/login/Login.vue"),
},
],
});
// 封装动态路由
const genRoutesConfig = () => {
// 添加动态路由
// 1.获取vuex里面的getters数据
// 2.循环getters数据
// 不知道修改vuex里面的数据,先将数据放去一个变量里再修改
let getNewMenus: NewMenusType = store.getters.getNewMenus;
// 第一个for in循环用来循环一级菜单对象
for (let key in getNewMenus) {
// 创建一个变量去接收你想要路由的样子,接口 RouteRecordRaw 是routes内部自带的接口类型定义,用来约束路由的
let newRouteObj: RouteRecordRaw = {
// 按照以前配路由的方式书写路由,只是把静态数据变成动态数据 getNewMenus[key].name是每一个一级路由的名字
path: "/" + getNewMenus[key].name,
name: getNewMenus[key].name,
// router-view是在homepage里面的组件,所有的组件都是在router-view展示,所以一级路由名字怎么变其实都是在homepage而已
component: () => import("../views/homepage/Homepage.vue"),
// children默认值是空数组,约束好类型,一会循环把二级标签循环进去
children: [],
};
// 为什么这里循环前需要判断一下呢?主要是让js识别一定会有getNewMenus[key].children这个值,其实不写问题也不是很大,严谨一些而已,毕竟上面的children是个空数组,理论上是不可能是个undefined
if (getNewMenus[key].children) {
// 第二个for循环是根据每个一级路由的children的数量进行循环,这里children为什么使用!号,因为是想让ts肯定为有值的,并不是可能为undefined,ts约束规则比较繁琐,方便日后维护
for (let i = 0; i < getNewMenus[key].children!.length; i++) {
// 二级路由规则
// 二级路由的规则跟一级路由差不多,不同的地方有两点:1.path前面不用添加"/",因为"/"代表的是从根目录出发,而二级路由的路径是通过一级路由拼接而成的,不需要从根目录开始出发
let subRouteObj: RouteRecordRaw = {
path: getNewMenus[key].children![i].name,
name: getNewMenus[key].children![i].name,
// 2.路径的拼接,二级路由的路径是由一级路径/二级路径名字拼接而成,所以我们不能写死一级路由名字跟二级路由名字
component: () =>
import(
`../views/${getNewMenus[key].name}/${
getNewMenus[key].children![i].name
}.vue`
),
};
// subRouteObj 是接收循环路由配置的变量,每次配置完就将它放进 newRouteObj.children这个数组里面
newRouteObj.children!.push(subRouteObj);
}
}
// console.log(newRouteObj);
// 动态添加路由
// 每次循环完一级路由及一级路由的children数组的二级路由的时候,就往router添加路由,直至循环结束
router.addRoute(newRouteObj);
}
// 这里为什么会写两个静态数据的路由添加上去呢,而不是直接在上面写呢?是因为当客户不是通过除登录页面进去的,一律不让其他人通过直接输入url路径访问后台,毕竟后台管理系统不是对所有人公开,只有通过正规的login页面登录成功进去才行,真正让人知道进入的页面其实只有一个login,其他都是动态生成的.
router.addRoute({
path: "/",
name: "homepage",
// 一级路由界面,动态生成不让通过输入url方式进入
component: () => import("../views/homepage/Homepage.vue"),
// "/"重定向"index",一进来首页的内容就放去index文件里面,它其实也是属于homepage的子路由,它的数据不在vuex里面,自己生成个就好
redirect: "/index",
children: [
{
path: "index",
name: "index",
component: () => import("../views/index/index.vue"),
},
],
});
};
// 前置路由守卫
// 前置路由守护有什么用?
// 1.可以让数据不会因为刷新而丢失
// 2.不让直接输入url路径进入网页
// 3.通过设置条件实现各种功能
router.beforeEach((to, from, next) => {
// console.log(to, from); //刷新后 to:/homepage from:/
// 获取token值用来做判断条件
const token = Cookies.get("token");
// 重新获取当前登录用户的信息
// 当存在token并且store.state.menus没有数据的时候(长度为0),进入
if (token && store.state.menus.length === 0) {
// 发起请求获取数据
getAdminInfo().then((res) => {
if (res.code === 200) {
// 使用updateMenus将请求回来的数据更新到vuex里面
store.commit("updateMenus", res.data.menus);
// 调用封装动态路由
genRoutesConfig();
// 出口出去到当前页面,回到当前页面后,条件不成立不进入这条路线
next(to);
}
});
// 当存在token并且路径是从/login来的 去到/home 会进入该判断 home为不存在的页面,为的是让第一次登录进来的数据先通过这里进入,然后再去到index主页,让数据一定更新到主页上
} else if (token && from.path === "/login" && to.path === "/home") {
// 调用封装动态路由
genRoutesConfig();
// 去到index主页
next("/index");
// 如果不存在token并且去的路径不是/login,跳转到/login.为什么要设置这个判断呢?因为不想用户手动删除token后刷新依然在非登录页,还有就是直接跳过login页直接输入url进来的
} else if (!token && to.path !== "/login") {
// 去到login主页
next("/login");
// 当存在token并且要去到/login时候的判断,明明是通过登录页登录进来了,还在url输入/login跳转回登录页,这样会存在两个token可能会对后面有影响,也不符合逻辑,所以当有token的时候想通过url跳转打login,就让他原地踏步就好
} else if (token && to.path === "/login") {
// 去到当前页面
next(from);
// 当以上判断都不成立的时候,就实行,路由守卫一定要有next()出口的,不然一直出不去
} else {
next();
}
});
// export default router
export const initRouter = (app: App<Element>) => {
app.use(router);
};