From 47f7bc26238481e8071a738a34bae24f210d63fd Mon Sep 17 00:00:00 2001 From: "WANGFAN\\wangf" <15871339963@163.com> Date: Sat, 9 Nov 2024 15:37:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A4=9A=E8=A7=92=E8=89=B2=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E6=9D=83=E9=99=90=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/index.ts | 6 +- .../Header/components/header-right/index.vue | 5 + src/mock/_utils.ts | 103 ++++++++++++++++++ src/mock/system/menu.ts | 67 +----------- src/mock/user/index.ts | 24 ++-- src/router/index.ts | 8 +- src/store/modules/route-config.ts | 6 + src/store/modules/user-info.ts | 6 +- 8 files changed, 146 insertions(+), 79 deletions(-) diff --git a/src/api/index.ts b/src/api/index.ts index 664b74c..07e0d2f 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -17,9 +17,9 @@ service.interceptors.request.use( if (localStorage.getItem("user-info")) { userInfo = JSON.parse(localStorage.getItem("user-info") as string); } - if (userInfo?.AdminToken) { + if (userInfo?.token) { // 有token,在请求头中携带token - config.headers.Authorization = userInfo.AdminToken; + config.headers.Authorization = userInfo.token; } return config; }, @@ -53,7 +53,7 @@ service.interceptors.response.use( } }, function (error: any) { - localStorage.removeItem("AdminToken"); + localStorage.removeItem("token"); router.push("/login"); return Promise.reject(error); } diff --git a/src/layout/components/Header/components/header-right/index.vue b/src/layout/components/Header/components/header-right/index.vue index 3620d07..1b97840 100644 --- a/src/layout/components/Header/components/header-right/index.vue +++ b/src/layout/components/Header/components/header-right/index.vue @@ -112,6 +112,7 @@ import { useI18n } from "vue-i18n"; import { Modal } from "@arco-design/web-vue"; import { useRouter } from "vue-router"; import { storeToRefs } from "pinia"; +import { useRoutesConfigStore } from "@/store/modules/route-config"; import { useUserInfoStore } from "@/store/modules/user-info"; import { useThemeConfig } from "@/store/modules/theme-config"; import { useThemeMethods } from "@/hooks/useThemeMethods"; @@ -190,8 +191,12 @@ const logOut = () => { closable: true, onBeforeOk: async () => { try { + // 用户退出 const store = useUserInfoStore(); await store.logOut(); + // 重置路由树 + const route = useRoutesConfigStore(); + await route.resetRoute(); router.replace("/login"); return true; } catch { diff --git a/src/mock/_utils.ts b/src/mock/_utils.ts index bea2637..f5a5a08 100644 --- a/src/mock/_utils.ts +++ b/src/mock/_utils.ts @@ -19,3 +19,106 @@ export const resultError = (data: unknown, message: string, code = 500) => { success: false }); }; + +/** + * 路由树递归排序 + * 1、先给当前层排序 + * 2、若当前层有children则递归排序 + * @param {array} tree 根据角色权限过滤后的路由树 + * @returns 返回排序之后的树 + */ +export const treeSort = (tree: Menu.MenuOptions[]) => { + if (!tree || tree.length == 0) return []; + tree.sort((a: Menu.MenuOptions, b: Menu.MenuOptions) => { + // a和b都是undefined则相等 + if (a.meta.sort != 0 && !a.meta.sort && b.meta.sort != 0 && !b.meta.sort) { + return 0; + } + // a是undefined则a被排在b之后 + if (a.meta.sort != 0 && !a.meta.sort) return 1; + // b是undefined则b被排在a之后 + if (b.meta.sort != 0 && !b.meta.sort) return -1; + return a.meta.sort - b.meta.sort; + }); + + // 深层递归 + return tree.map((item: any) => { + if (item?.children?.length > 0) { + item.children = treeSort(item.children); + } + return item; + }); +}; + +/** + * 过滤路由树,返回有权限的树 + * 1、先过滤停用的菜单,该菜单是不可访问的,直接去掉 + * 2、根据角色权限过滤原始路由树 + * @param {array} tree 根据角色权限过滤原始路由树 + * @returns 返回有权限的树 + */ +export const filterByRole = (tree: any, userRoles: Array) => { + return tree.filter((item: any) => { + // 过滤角色权限 + if (item?.meta?.roles) { + if (!roleBase(item.meta.roles, userRoles)) return false; + } + // 过滤是否禁用 + if (item?.meta?.disable) return false; + if (item.children) item.children = filterByRole(item.children, userRoles); + return true; + }); +}; + +/** + * 校验该角色是否有路由权限 + * @param {array} roles 路由的角色权限 + * @returns 是否有权限 true是 false否 + */ +export const roleBase = (roles: Array, userRoles: Array) => { + return userRoles.some((item: string) => roles.includes(item)); +}; + +/** + * 深拷贝 + * @param { string } data 需要深拷贝的数据 + * @returns 深拷贝的数据 + */ +export function deepClone(data: any) { + let stack = []; + let cloned; + if (Array.isArray(data)) { + cloned = []; + } else if (typeof data === "object" && data !== null) { + cloned = {}; + } else { + return data; + } + stack.push({ + original: data, + copy: cloned + }); + while (stack.length > 0) { + let current: any = stack.pop(); + let original = current.original; + let copy = current.copy; + + for (let key in original) { + if (original.hasOwnProperty(key)) { + let value = original[key]; + + if (typeof value === "object" && value !== null) { + copy[key] = Array.isArray(value) ? [] : {}; + + stack.push({ + original: value, + copy: copy[key] + }); + } else { + copy[key] = value; + } + } + } + } + return cloned; +} diff --git a/src/mock/system/menu.ts b/src/mock/system/menu.ts index 591a736..aaa4f0f 100644 --- a/src/mock/system/menu.ts +++ b/src/mock/system/menu.ts @@ -1,5 +1,6 @@ import type { MockMethod } from "vite-plugin-mock"; -import { resultSuccess, resultError } from "../_utils"; +import { RouteRecordRaw } from "vue-router"; +import { deepClone, filterByRole, treeSort, resultSuccess, resultError } from "../_utils"; import systemMenu from "../_data/system_menu"; /** @@ -29,67 +30,9 @@ export default [ let token = headers.authorization; // 这里模拟两个角色,admin、common let userRoles = token === "Admin-Token" ? ["admin"] : ["common"]; - systemMenu[0].children = treeSort(filterByRole(systemMenu[0].children, userRoles)); - return resultSuccess(systemMenu); + const originTree: RouteRecordRaw[] = deepClone(systemMenu); + originTree[0].children = treeSort(filterByRole(originTree[0].children, userRoles)); + return resultSuccess(originTree); } } ] as MockMethod[]; - -/** - * 路由树递归排序 - * 1、先给当前层排序 - * 2、若当前层有children则递归排序 - * @param {array} tree 根据角色权限过滤后的路由树 - * @returns 返回排序之后的树 - */ -export const treeSort = (tree: Menu.MenuOptions[]) => { - if (!tree || tree.length == 0) return []; - tree.sort((a: Menu.MenuOptions, b: Menu.MenuOptions) => { - // a和b都是undefined则相等 - if (a.meta.sort != 0 && !a.meta.sort && b.meta.sort != 0 && !b.meta.sort) { - return 0; - } - // a是undefined则a被排在b之后 - if (a.meta.sort != 0 && !a.meta.sort) return 1; - // b是undefined则b被排在a之后 - if (b.meta.sort != 0 && !b.meta.sort) return -1; - return a.meta.sort - b.meta.sort; - }); - - // 深层递归 - return tree.map((item: any) => { - if (item?.children?.length > 0) { - item.children = treeSort(item.children); - } - return item; - }); -}; - -/** - * 过滤路由树,返回有权限的树 - * 1、先过滤停用的菜单,该菜单是不可访问的,直接去掉 - * 2、根据角色权限过滤原始路由树 - * @param {array} tree 根据角色权限过滤原始路由树 - * @returns 返回有权限的树 - */ -export const filterByRole = (tree: any, userRoles: Array) => { - return tree.filter((item: any) => { - // 过滤角色权限 - if (item?.meta?.roles) { - if (!roleBase(item.meta.roles, userRoles)) return false; - } - // 过滤是否禁用 - if (item?.meta?.disable) return false; - if (item.children) item.children = filterByRole(item.children, userRoles); - return true; - }); -}; - -/** - * 校验该角色是否有路由权限 - * @param {array} roles 路由的角色权限 - * @returns 是否有权限 true是 false否 - */ -export const roleBase = (roles: Array, userRoles: Array) => { - return userRoles.some((item: string) => roles.includes(item)); -}; diff --git a/src/mock/user/index.ts b/src/mock/user/index.ts index 6f1485c..21e71f2 100644 --- a/src/mock/user/index.ts +++ b/src/mock/user/index.ts @@ -7,12 +7,12 @@ export default [ url: "/mock/login", method: "post", timeout: 300, - response: ({ body }) => { + response: ({ body }: any) => { let { username, password } = body; if (username === "admin" && password === "123456") { return resultSuccess({ token: "Admin-Token" }); } - if(username === "common" && password === "123456"){ + if (username === "common" && password === "123456") { return resultSuccess({ token: "Common-Token" }); } return resultError(null, "账号或者密码错误", 500); @@ -22,11 +22,21 @@ export default [ url: "/mock/user/info", method: "get", timeout: 300, - response: () => { - let data = { - username: "admin", // 用户名 - roles: ["admin"] // 角色 - }; + response: ({ headers }: any) => { + let data = {}; + // 管理员角色 + if (headers.authorization == "Admin-Token") { + data = { + username: "admin", // 用户名 + roles: ["admin"] // 角色 + }; + } else { + // 普通角色 + data = { + username: "common", // 用户名 + roles: ["common"] // 角色 + }; + } return resultSuccess(data); } } diff --git a/src/router/index.ts b/src/router/index.ts index 2af8923..bb47bcd 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -40,16 +40,16 @@ const router = createRouter({ router.beforeEach(async (to, from, next) => { NProgress.start(); // 开启进度条 const store = useUserInfoStore(pinia); - const { AdminToken } = storeToRefs(store); + const { token } = storeToRefs(store); console.log("去", to, "来自", from); // next()内部加了path等于跳转指定路由会再次触发router.beforeEach,内部无参数等于放行,不会触发router.beforeEach - if (to.path === "/login" && !AdminToken.value) { + if (to.path === "/login" && !token.value) { // 1、去登录页,无token,放行 next(); - } else if (!AdminToken.value) { + } else if (!token.value) { // 2、没有token,直接重定向到登录页 next("/login"); - } else if (to.path === "/login" && AdminToken.value) { + } else if (to.path === "/login" && token.value) { // 3、去登录页,有token,直接重定向到home页 next("/home"); // 项目内的跳转,处理跳转路由高亮 diff --git a/src/store/modules/route-config.ts b/src/store/modules/route-config.ts index 32b238b..c6fedee 100644 --- a/src/store/modules/route-config.ts +++ b/src/store/modules/route-config.ts @@ -75,6 +75,12 @@ export const useRoutesConfigStore = defineStore("route-config", { removeRouteNames(list: Array) { this.cacheRoutes = this.cacheRoutes.filter((item: string) => !list.includes(item)); }, + /** + * 重置routeTree路由树 + */ + async resetRoute() { + this.routeTree = []; + }, /** * 路由初始化 * 1、将模块设置为真实模块 diff --git a/src/store/modules/user-info.ts b/src/store/modules/user-info.ts index c2e4029..85879a3 100644 --- a/src/store/modules/user-info.ts +++ b/src/store/modules/user-info.ts @@ -12,18 +12,18 @@ export const useUserInfoStore = defineStore("user-info", { username: "", roles: [] }, // 账号信息 - AdminToken: "" // token + token: "" // token }), actions: { async setAccount(data: Array) { this.account = data; }, async setToken(data: string) { - this.AdminToken = data; + this.token = data; }, async logOut() { this.account = {}; - this.AdminToken = ""; + this.token = ""; } }, persist: persistedstateConfig("user-info")