diff --git a/.husky/_/applypatch-msg b/.husky/_/applypatch-msg index 7ac74c3..21c69a4 100644 --- a/.husky/_/applypatch-msg +++ b/.husky/_/applypatch-msg @@ -1,2 +1,2 @@ -#!/usr/bin/env sh +#!/usr/bin/env sh . "${0%/*}/h" \ No newline at end of file diff --git a/.husky/_/commit-msg b/.husky/_/commit-msg index 7ac74c3..21c69a4 100644 --- a/.husky/_/commit-msg +++ b/.husky/_/commit-msg @@ -1,2 +1,2 @@ -#!/usr/bin/env sh +#!/usr/bin/env sh . "${0%/*}/h" \ No newline at end of file diff --git a/src/router/index.ts b/src/router/index.ts index 0f08983..db52dd3 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,5 +1,5 @@ import { createRouter, createWebHashHistory } from "vue-router"; -import { dynamicRoutes, staticRoutes, notFoundAndNoPower } from "@/router/route.ts"; +import { staticRoutes, notFoundAndNoPower } from "@/router/route.ts"; import { initSetRouter } from "@/router/route-output"; import NProgress from "@/config/nprogress"; import pinia from "@/store/index"; @@ -11,7 +11,7 @@ import { useUserInfoStore } from "@/store/user-info"; * @method createRouter(options: RouterOptions): Router * @link 参考:https://next.router.vuejs.org/zh/api/#createrouter */ -export const router = createRouter({ +const router = createRouter({ history: createWebHashHistory(), /** * 说明: @@ -19,7 +19,7 @@ export const router = createRouter({ * 2、后端控制路由中也需要添加 notFoundAndNoPower 404、401界面 * 防止 404、401 不在 layout 布局中,不设置的话,404、401界面将全屏显示 */ - routes: [...dynamicRoutes, ...staticRoutes, ...notFoundAndNoPower] // 这里只需要设置兜底路由即可,其它的路由通过addRoute动态添加 + routes: [...staticRoutes, ...notFoundAndNoPower] // 这里只需要设置兜底路由即可,其它的路由通过addRoute动态添加 }); // 路由加载前 diff --git a/src/router/route-output.ts b/src/router/route-output.ts index 6590d01..e5b91cc 100644 --- a/src/router/route-output.ts +++ b/src/router/route-output.ts @@ -1,13 +1,53 @@ import { dynamicRoutes } from "@/router/route"; import pinia from "@/store/index"; +import router from "@/router/index.ts"; import { storeToRefs } from "pinia"; import { useUserInfoStore } from "@/store/user-info"; -// import { useRoutesListStore } from "@/store/route-list"; +import { useRoutesListStore } from "@/store/route-list"; +/** + * 1、过滤有权限的路由树,缓存,addRoutes添加路由 + * 2、通过有权限的路由树生成路由name数组,缓存 + */ export function initSetRouter() { - // 过滤后的结果 + const store = useRoutesListStore(pinia); + // 根据角色权限过滤树 let filteredData = filterByRole(dynamicRoutes[0].children); - console.log("路由处理完", filteredData); + store.setRouteTree(filteredData); + // 根据树生成一维路由数组 + const flattenedArray = linearArray(filteredData); + // 设置完整的路由,二维路由,顶层路由 + 二级的一维路由 + const twoStoryTree = dynamicRoutes.map(item => { + if (flattenedArray.length > 0) item.redirect = flattenedArray[0].path; + item.children = flattenedArray; + return item; + }); + // 动态添加路由 + twoStoryTree.forEach((route: any) => router.addRoute(route)); + // 根据一维路由设置缓存name + setCacheName(flattenedArray); + const { routeTree, routeNames } = storeToRefs(store); + console.log("一维数组", routeTree.value, routeNames.value); // 缓存需要修改,路由树这里没有数据 +} + +// 设置缓存name +export function setCacheName(flattenedArray: any) { + const store = useRoutesListStore(pinia); + const cacheName = flattenedArray.map((item: any) => item.name); + store.setrouteNames(cacheName); +} + +// 路由转一维数组 +export function linearArray(tree: any) { + const result = []; + while (tree.length) { + const next = tree.pop(); + if (Array.isArray(next.children)) { + tree.push(...next.children); + } + result.push(next); + } + return result.reverse(); } // 过滤路由树,返回有权限的树 diff --git a/src/router/route.ts b/src/router/route.ts index b8855e6..a794d60 100644 --- a/src/router/route.ts +++ b/src/router/route.ts @@ -1,197 +1,197 @@ -/** - * 路由path路径与文件夹名称相同,找文件可以浏览器地址快速查找,方便定位文件 - * - * 路由meta对象参数,我们通常将属性放到meta对象中 - * meta: { - * title: 菜单栏以及 tagsView 栏、菜单搜索名称(国际化) - * isLink: 是否是超链接菜单,开启外链条件:1、 isLink:链接地址不为空 2、isIframe: false - * isHide: 是否隐藏此路由 - * isKeepAlive: 是否缓存组件状态 - * isAffix: 是否固定在 tagsView 栏上 - * isIframe: 是否内嵌窗口,开启条件:1、isIframe:true 2、isLike:链接地址不为空 - * roles: 当前路由权限表示,取角色管理。路由控制显示、隐藏。 超级管理员:admin;普通角色:common - * icon: 菜单、tagsView 图标等 - * } - */ - -/** - * 路由的层级设置 - * 一级children下的一级路由为menu菜单的最外层,例如:首页、系统设置、权限管理,在这里我称它为 `children下的一级路由` - * children下的一级路由内在设置children则是在一级当前菜单下再配置菜单 - * 依此类推 - */ - -export const dynamicRoutes = [ - { - path: "/", - name: "/", - redirect: "/home", - component: () => import("@/layout/index.vue"), // 容器布局-顶层路由 - meat: { - isKeepAlive: true - }, - // 二级路由-主要渲染页面 - children: [ - { - path: "/home", - name: "home", - component: () => import("@/views/home/home.vue"), - meta: { - title: "home", // 国际化 - isHide: false, // 是否隐藏此路由 - isKeepAlive: true, // 缓存组件状态 - isAffix: true, // 固定在tagesView栏上 - isLink: "", // 是否外链 - isIframe: false, // 是否内嵌窗口 - roles: ["admin", "common"], // 路由权限 - icon: "home" // 菜单图标 - } - }, - { - path: "/home2", - name: "home2", - component: () => import("@/views/home/home.vue"), - meta: { - title: "home2", // 国际化 - isHide: false, // 是否隐藏此路由 - isKeepAlive: true, // 缓存组件状态 - isAffix: true, // 固定在tagesView栏上 - isLink: "", // 是否外链 - isIframe: false, // 是否内嵌窗口 - roles: ["common"], // 路由权限 - icon: "home" // 菜单图标 - } - }, - { - path: "/common-components", - name: "common-components", - redirect: "/common-components/form-component", - component: () => import("@/views/common-components/form-component/form-component.vue"), - meta: { - title: "common-components", - isLink: "", - isHide: false, - isKeepAlive: true, - isAffix: true, - isIframe: false, - roles: ["admin"], - icon: "set" - }, - children: [ - { - path: "/common-components/form-component", - name: "form-component", - component: () => import("@/views/common-components/form-component/form-component.vue"), - meta: { - title: "form-component", - isLink: "", - isHide: false, - isKeepAlive: true, - isAffix: false, - isIframe: false, - roles: ["admin"], - icon: "common" - } - } - ] - }, - { - path: "/multilevel-menu", - name: "multilevel-menu", - redirect: "/multilevel-menu/second-menu", - component: () => import("@/views/multilevel-menu/second-menu/second-menu.vue"), - meta: { - title: "multilevel-menu", - isLink: "", - isHide: false, - isKeepAlive: true, - isAffix: true, - isIframe: false, - roles: ["admin"], - icon: "switch" - }, - children: [ - { - path: "/multilevel-menu/second-menu", - name: "second-menu", - component: () => import("@/views/multilevel-menu/second-menu/second-menu.vue"), - meta: { - title: "second-menu", - isLink: "", - isHide: false, - isKeepAlive: true, - isAffix: false, - isIframe: false, - roles: ["admin"], - icon: "common" - }, - children: [ - { - path: "/multilevel-menu/third-menu", - name: "third-menu", - component: () => import("@/views/multilevel-menu/third-menu/third-menu.vue"), - meta: { - title: "third-menu", - isLink: "", - isHide: false, - isKeepAlive: true, - isAffix: false, - isIframe: false, - roles: ["admin"], - icon: "common" - } - } - ] - } - ] - } - ] - } -]; - -/** - * 静态路由 (默认路由) - * 此路由不要动,用于做静态路由定向,如果要添加路由,请在 `dynamicRoutes数组` 中添加 - * @description 前端控制路由 直接改 dynamicRoutes 中的路由,后端控制则不需要,请求接口路由数据时,覆盖 dynamicRoutes 第一个顶层 children 的内容(全屏,不包括 lauyout 中的路由出口) - * @returns 返回路由菜单数据 - */ -export const staticRoutes = [ - { - path: "/login", - name: "login", - component: () => import("@/views/login/login.vue"), - meta: { - title: "登录" - } - } - /** - * 提示:写在这里的为全屏界面,不建议写在这里 - * 非全屏界面,请写在 dynamicRoutes 路由数组中 - */ -]; - -/** - * 定义404、401界面 - * @link 参考:https://router.vuejs.org/zh/guide/essentials/history-mode.html#netlify - */ -export const notFoundAndNoPower = [ - // 未找到页面的时候跳转该页面 - { - path: "/:path(.*)*", - name: "notFound", - component: () => import("@/views/error/404.vue"), - meta: { - title: "notFound", - isHide: true - } - }, - { - path: "/401", - name: "noPower", - component: () => import("@/views/error/401.vue"), - meta: { - title: "notFound", - isHide: true - } - } -]; +/** + * 路由path路径与文件夹名称相同,找文件可以浏览器地址快速查找,方便定位文件 + * + * 路由meta对象参数,我们通常将属性放到meta对象中 + * meta: { + * title: 菜单栏以及 tagsView 栏、菜单搜索名称(国际化) + * isLink: 是否是超链接菜单,开启外链条件:1、 isLink:链接地址不为空 2、isIframe: false + * isHide: 是否隐藏此路由 + * isKeepAlive: 是否缓存组件状态 + * isAffix: 是否固定在 tagsView 栏上 + * isIframe: 是否内嵌窗口,开启条件:1、isIframe:true 2、isLike:链接地址不为空 + * roles: 当前路由权限表示,取角色管理。路由控制显示、隐藏。 超级管理员:admin;普通角色:common + * icon: 菜单、tagsView 图标等 + * } + */ + +/** + * 路由的层级设置 + * 一级children下的一级路由为menu菜单的最外层,例如:首页、系统设置、权限管理,在这里我称它为 `children下的一级路由` + * children下的一级路由内在设置children则是在一级当前菜单下再配置菜单 + * 依此类推 + */ + +export const dynamicRoutes = [ + { + path: "/", + name: "/", + redirect: "/home", + component: () => import("@/layout/index.vue"), // 容器布局-顶层路由 + meat: { + isKeepAlive: true + }, + // 二级路由-主要渲染页面 + children: [ + { + path: "/home", + name: "home", + component: () => import("@/views/home/home.vue"), + meta: { + title: "home", // 国际化 + isHide: false, // 是否隐藏此路由 + isKeepAlive: true, // 缓存组件状态 + isAffix: true, // 固定在tagesView栏上 + isLink: "", // 是否外链 + isIframe: false, // 是否内嵌窗口 + roles: ["admin", "common"], // 路由权限 + icon: "home" // 菜单图标 + } + }, + { + path: "/home2", + name: "home2", + component: () => import("@/views/home/home.vue"), + meta: { + title: "home2", // 国际化 + isHide: false, // 是否隐藏此路由 + isKeepAlive: true, // 缓存组件状态 + isAffix: true, // 固定在tagesView栏上 + isLink: "", // 是否外链 + isIframe: false, // 是否内嵌窗口 + roles: ["common"], // 路由权限 + icon: "home" // 菜单图标 + } + }, + { + path: "/common-components", + name: "common-components", + redirect: "/common-components/form-component", + component: () => import("@/views/common-components/form-component/form-component.vue"), + meta: { + title: "common-components", + isLink: "", + isHide: false, + isKeepAlive: true, + isAffix: true, + isIframe: false, + roles: ["admin"], + icon: "set" + }, + children: [ + { + path: "/common-components/form-component", + name: "form-component", + component: () => import("@/views/common-components/form-component/form-component.vue"), + meta: { + title: "form-component", + isLink: "", + isHide: false, + isKeepAlive: true, + isAffix: false, + isIframe: false, + roles: ["admin"], + icon: "common" + } + } + ] + }, + { + path: "/multilevel-menu", + name: "multilevel-menu", + redirect: "/multilevel-menu/second-menu", + component: () => import("@/views/multilevel-menu/second-menu/second-menu.vue"), + meta: { + title: "multilevel-menu", + isLink: "", + isHide: false, + isKeepAlive: true, + isAffix: true, + isIframe: false, + roles: ["admin"], + icon: "switch" + }, + children: [ + { + path: "/multilevel-menu/second-menu", + name: "second-menu", + component: () => import("@/views/multilevel-menu/second-menu/second-menu.vue"), + meta: { + title: "second-menu", + isLink: "", + isHide: false, + isKeepAlive: true, + isAffix: false, + isIframe: false, + roles: ["admin"], + icon: "common" + }, + children: [ + { + path: "/multilevel-menu/third-menu", + name: "third-menu", + component: () => import("@/views/multilevel-menu/third-menu/third-menu.vue"), + meta: { + title: "third-menu", + isLink: "", + isHide: false, + isKeepAlive: true, + isAffix: false, + isIframe: false, + roles: ["admin"], + icon: "common" + } + } + ] + } + ] + } + ] + } +]; + +/** + * 静态路由 (默认路由) + * 此路由不要动,用于做静态路由定向,如果要添加路由,请在 `dynamicRoutes数组` 中添加 + * @description 前端控制路由 直接改 dynamicRoutes 中的路由,后端控制则不需要,请求接口路由数据时,覆盖 dynamicRoutes 第一个顶层 children 的内容(全屏,不包括 lauyout 中的路由出口) + * @returns 返回路由菜单数据 + */ +export const staticRoutes = [ + { + path: "/login", + name: "login", + component: () => import("@/views/login/login.vue"), + meta: { + title: "登录" + } + } + /** + * 提示:写在这里的为全屏界面,不建议写在这里 + * 非全屏界面,请写在 dynamicRoutes 路由数组中 + */ +]; + +/** + * 定义404、401界面 + * @link 参考:https://router.vuejs.org/zh/guide/essentials/history-mode.html#netlify + */ +export const notFoundAndNoPower = [ + // 未找到页面的时候跳转该页面 + { + path: "/:path(.*)*", + name: "notFound", + component: () => import("@/views/error/404.vue"), + meta: { + title: "notFound", + isHide: true + } + }, + { + path: "/401", + name: "noPower", + component: () => import("@/views/error/401.vue"), + meta: { + title: "notFound", + isHide: true + } + } +]; diff --git a/src/store/route-list.ts b/src/store/route-list.ts index 6b369e2..9948bba 100644 --- a/src/store/route-list.ts +++ b/src/store/route-list.ts @@ -7,15 +7,22 @@ import { defineStore } from "pinia"; */ export const useRoutesListStore = defineStore("routeList", { state: (): any => ({ + routeTree: [], // 路由树 routeList: [], // 路由数据 routeNames: [] // 路由名称 }), actions: { + async setRouteTree(data: any) { + this.routeTree = data; + }, async setRouteList(data: Array) { this.routesList = data; }, async setrouteNames(data: Array) { this.routeNames = data; } + }, + persist: { + enabled: true } }); diff --git a/src/store/user-info.ts b/src/store/user-info.ts index 3313a10..e50f4ba 100644 --- a/src/store/user-info.ts +++ b/src/store/user-info.ts @@ -1,32 +1,32 @@ -import { defineStore } from "pinia"; - -/** - * 用户信息 - * @methods setAccount 设置账号信息 - * @methods setToken 设置token - * @methods logOut 退出登录 - */ -export const useUserInfoStore = defineStore("routeList", { - state: (): any => ({ - account: { - username: "", - roles: [] - }, // 账号信息 - token: "" // token - }), - actions: { - async setAccount(data: Array) { - this.account = data; - }, - async setToken(data: string) { - this.token = data; - }, - async logOut() { - this.account = {}; - this.token = ""; - } - }, - persist: { - enabled: true // 开启数据缓存-默认缓存全部数据 - } -}); +import { defineStore } from "pinia"; + +/** + * 用户信息 + * @methods setAccount 设置账号信息 + * @methods setToken 设置token + * @methods logOut 退出登录 + */ +export const useUserInfoStore = defineStore("userInfo", { + state: (): any => ({ + account: { + username: "", + roles: [] + }, // 账号信息 + token: "" // token + }), + actions: { + async setAccount(data: Array) { + this.account = data; + }, + async setToken(data: string) { + this.token = data; + }, + async logOut() { + this.account = {}; + this.token = ""; + } + }, + persist: { + enabled: true // 开启数据缓存-默认缓存全部数据 + } +});