? 问题,表现形式:
1.以trademark003进行登录,能够确认的是他有商品管理下的品牌管理的权限,children只有1个2.退出trademark003登录,重新以admin进行登录,按道理说他有商品管理下的所有页面的操作权限,应该有5个children,但是现在却仍旧只有1个
? 原因: 因为我们对 alLAsyncRoutes 进行操作,操作的都是原来的数组对象,没有发生改变? 解决: 对alAsyncRoutes进行深拷贝,然后再进行操作,这样,每次aLLAsyncRoutes都是一个副本,不再是原对象
userInfo.ts文件
function filterAsyncRoutes(
allAsyncRoutes: RouteRecordRaw[],
routesName: string[]
) {
// 拿右边比左边(拿allAsyncRoutes比routesName),因为核心算法是递归,右边是有递归的;左边没有递归
return allAsyncRoutes.filter((route) => {
//routesName是一维数组
if (routesName.indexof(route.name as string) === -1) {
// 没有在路由字符串中找到此路由名称
return false;
}
// 找到后,继续判断 动态路由的子路由是否在路由字符串中存在
if(route.children && route.children.length > 0) {
//递归函数的特点: 函数调函数自身,需要有跳出机制,否则死循环
route.children = filterAsyncRoutes(route.children,routesName)
}
return true;
}
}
// 获取用户信息是在/permission.ts授权路由守卫中使用
async getInfo() {
const info = await getuserInfoApi();
this.name =.info.name ;
this.avatar= info.avatar;
this.roles = info.roles;
this.authBtnList = info.buttons;
console.log(info.routes); // 一个一维数组,里面都是string字符串
// 将动态路由进行了递归的条件判断,选择出符合权限的路由对象(调用方法,参数一:动态路由,参数二:接收后端返回的routes字符串数组)
const _allAsyncRoutes = filterAsyncRoutes(cloneDeep(allAsyncRoutes),info.routes); // cloneDeep: 深拷贝的方法
// 路由的合并操作,利用动态路由操作
//addRoutes的参数只包含动态路由和任意路由,并没有静态路由,但是我们想构建的路由其实是静态、动态、任意三者的合并路由
addRoutes([...allAsyncRoutes,anyRoute]);
//为什么不加anyRoute(因为路由是需要实现跳转的,菜单的显示仅仅是菜单的显示而已,虽然菜单的显示内容是从路由中来,但是菜单的显示内容并不是路由)
this.menuRoutes = [...staticRoutes,..._allAsyncRoutes];
}
index.ts文件
import { createRouter, createWebHistory } from "vue-router";
import { staticRoutes } from "@/router/routes";
const router = createRouter({
history: createwebHistory(),
routes:staticRoutes,
scrollBehavior() {
return { top:0, left: 0};
},
});
//导出路由
export default router;
userInfo.ts文件
function addRoutes( routes: RouteRecordRaw[]){
// router是路由对象,而且router里本身已经有一个属性叫routes,并且routes的内容是静态路由
// 说明router已经有了静态路由,所以我们只需要操作动态和异步
routes.forEach((route) => {
router.addRoute(route);
});
}
has.ts文件
//从store仓库中获取按钮的数组列表
import pinia from "@/stores";
import { useUserInfoStore ]from "@/stores/userInfo";
import type { App } from "vue";
//我们当前并不在组件中,所以没办法使用原型链的方式来挂载,所以我们使用插件的方式来挂载
const userInfoStore= useUserInfoStore(pinia);
// 进行一个自定义插件的定义
export default (app: App) => {
// 自定义插件当中可以做的事情有很多,比如全局组件、全局指令、全局方法、实例方法(vue2)
//全局指令
app.directive("has", {
mounted(el, binding){
if (userInfoStore.authBtnList.indexOf(binding.value) === -1) {
// 没有找到匹配的内容,所以元素不应该显示,应该删除
el.parentNode && el.parentNode.removeChild(el);
}
},
});
};
main.ts文件
import { createApp } from "vue";
import pinia from."./stores";
import ElementPlus from "element-plus";
import zhCn from "element-plus/es/locale/lang/zh-cn";
import "element-plus/dist/index.css";
import App from "./App.vue";
import router from "./router";
import "./styles/index.scss";
import ElSvg from "./components/SvgIcon/ElSvg";
import "./permission";
import CategorySelector from "@/components/CategorySelector/index.vue";
import has from "@/directive/has";
const app = createApp(App);
app.component(CategorySelector.name,CategorySelector);
ElSvg(app);
app
.use(pinia)
.use(has)
.use(router)
.use(ElementPlus, {
locale: zhCn,
})
.mount("#app");
index.vue页面使用
修改
permission.ts
import router from "@/router";
import NProgress from "nprogress"; // 进度条
import "nprogress/nprogress.css"
import pinia from "@/stores";
import { useUserInfoStore } from "@/stores/userInfo";
import { ElMessage } from "element-plus";
import getPageTitle from "./utils/get-page-title";
NProgress.configure({ showSpinner: false });
const userInfoStore = useuserInfoStore(pinia);
// 不用进行token检查的白名单路径数组
const whiteList = ["/login"];
// 路由加载前
router.beforeEach(async (to, from, next) => {
// 在显示进度条
NProgress.start();
// 设置整个页面的标题
document.title = getPageTitle(to.meta.title as string);
const token = userInfoStore.token;
// 如果token存在(已经登陆或前面登陆过)
if (token) {
//如果请求的是登陆路由
if (to.path === "/login") {
//直接跳转到根路由,并完成进度条
next({ path: "/" });
NProgress.done();
} else {
// 请求的不是登陆路由
// 是否已经登陆
const hasLogin = !!userInfoStore.name;
// 如果已经登陆直接放行
if (hasLogin) {
next();
} else {
// 如果还没有登陆
try {
// 异步请求获取用户信息(包含权限数据) ===> 动态注册用户的权限路由 => 当次跳转不可见
await userInfoStore.getInfo( );
next(to); // 重新跳转去目标路由,能看到动态添加的异步路由,且不会丢失参数
NProgress.done(); // 结束进度条
} catch (error:any) {
// 如果请求处理过程中出错
// 重置用户信息
await userInfostore.reset():
// 提示错误信息
// ELMessage.error(error.message || 'Has Error') // axios拦截器中已经有提示了
// 跳转到登陆页面,并携带原本要跳转的路由路径,用于登陆成功后跳转
next(`/login?redirect=${to.path}`);
//完成进度条
NProgress.done( );
}
}
}
} else {
// 没有token
// 如果目标路径在白名单中(是不需要token的路径)
if (whiteList.indexOf(to.path) !== -1){
// 放行
next();
} else {
// 如果没在白名单中,跳转到登陆路由携带原目标路径
next(`/login?redirect=${to.path}`);
// 完成进度条 当次跳转中断了要进行个新的跳转了
NProgress.done();
}
}
});
// 路由加载后
router.afterEach(() => {
NProgress .done();
});