feat: 动态路由逻辑更改,添加id和parendId用于生成路由树(结构为01-01-01),mock不再返回顶层路由,顶层layout路由改为直接静态导入

This commit is contained in:
wangfan 2025-01-24 13:59:49 +08:00
parent 1f7c398c50
commit b8e0497b1f
12 changed files with 1133 additions and 1016 deletions

View File

@ -1,4 +1,2 @@
#!/usr/bin/env sh #!/usr/bin/env sh
. "$(dirname -- "$0")/husky.sh" . "${0%/*}/h"
npx --no-install commitlint --edit $1

View File

@ -1,4 +1,2 @@
#!/usr/bin/env sh #!/usr/bin/env sh
. "$(dirname "$0")/husky.sh" . "${0%/*}/h"
pnpm run lint:lint-staged

32
src/components.d.ts vendored
View File

@ -5,22 +5,22 @@
// Read more: https://github.com/vuejs/core/pull/3399 // Read more: https://github.com/vuejs/core/pull/3399
export {} export {}
declare module "vue" { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
BarcodeDraw: (typeof import("./components/barcode-draw/index.vue"))["default"]; BarcodeDraw: typeof import('./components/barcode-draw/index.vue')['default']
CodeView: (typeof import("./components/code-view/index.vue"))["default"]; CodeView: typeof import('./components/code-view/index.vue')['default']
ExternalLinkPage: (typeof import("./components/external-link-page/index.vue"))["default"]; ExternalLinkPage: typeof import('./components/external-link-page/index.vue')['default']
FillPage: (typeof import("./components/fill-page/index.vue"))["default"]; FillPage: typeof import('./components/fill-page/index.vue')['default']
InternalLinkPage: (typeof import("./components/internal-link-page/index.vue"))["default"]; InternalLinkPage: typeof import('./components/internal-link-page/index.vue')['default']
LangProvider: (typeof import("./components/lang-provider/index.vue"))["default"]; LangProvider: typeof import('./components/lang-provider/index.vue')['default']
MainTransition: (typeof import("./components/main-transition/index.vue"))["default"]; MainTransition: typeof import('./components/main-transition/index.vue')['default']
PinyinPro: (typeof import("./components/pinyin-pro/index.vue"))["default"]; PinyinPro: typeof import('./components/pinyin-pro/index.vue')['default']
QrcodeDraw: (typeof import("./components/qrcode-draw/index.vue"))["default"]; QrcodeDraw: typeof import('./components/qrcode-draw/index.vue')['default']
RouterLink: (typeof import("vue-router"))["RouterLink"]; RouterLink: typeof import('vue-router')['RouterLink']
RouterView: (typeof import("vue-router"))["RouterView"]; RouterView: typeof import('vue-router')['RouterView']
SelectIcon: (typeof import("./components/select-icon/index.vue"))["default"]; SelectIcon: typeof import('./components/select-icon/index.vue')['default']
SvgAndIcon: (typeof import("./components/svg-and-icon/index.vue"))["default"]; SvgAndIcon: typeof import('./components/svg-and-icon/index.vue')['default']
SvgIcon: (typeof import("./components/svg-icon/index.vue"))["default"]; SvgIcon: typeof import('./components/svg-icon/index.vue')['default']
VerifyCode: (typeof import("./components/verify-code/index.vue"))["default"]; VerifyCode: typeof import('./components/verify-code/index.vue')['default']
} }
} }

4
src/config/index.ts Normal file
View File

@ -0,0 +1,4 @@
// 系统全局配置
// 首页地址(默认)
export const HOME_PATH: string = "/home";

View File

@ -14,10 +14,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import { useThemeConfig } from "@/store/modules/theme-config"; import { useThemeConfig } from "@/store/modules/theme-config";
import { useRoutesConfigStore } from "@/store/modules/route-config";
import { useDevicesSize } from "@/hooks/useDevicesSize"; import { useDevicesSize } from "@/hooks/useDevicesSize";
import { HOME_PATH } from "@/config/index";
const themeStore = useThemeConfig(); const themeStore = useThemeConfig();
const { isBreadcrumb, transitionPage } = storeToRefs(themeStore); const { isBreadcrumb, transitionPage } = storeToRefs(themeStore);
const routesConfigStore = useRoutesConfigStore();
const { routeList } = storeToRefs(routesConfigStore);
const { isMobile } = useDevicesSize(); const { isMobile } = useDevicesSize();
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
@ -25,16 +28,17 @@ const router = useRouter();
/** /**
* 获取面包屑 * 获取面包屑
* 根据当前路由信息获取route.matched可以获取当前路由的所有父级路由信息 * 根据当前路由信息获取route.matched可以获取当前路由的所有父级路由信息
* 如果当前路由是顶层的重定向路由则只返回当前路由信息(说明当前就是顶层) * 如果当前路由是home路由则只返回当前路由信息(说明当前就是顶层)
* 否则返回所有父级路由信息顶层路由重写为首页 * 否则返回所有父级路由信息顶层路由重写为首页
*/ */
const breadcrumb = computed(() => { const breadcrumb = computed(() => {
if (route.path === route.matched[0].redirect) { //
return [route]; if (route.path === HOME_PATH) return [route];
}
//
return route.matched.map((item: any) => { return route.matched.map((item: any) => {
if (item.name == "/") { if (item.name == "layout") {
return item.children[0]; return routeList.value.find((item: any) => item.path == HOME_PATH);
} else { } else {
return item; return item;
} }
@ -55,7 +59,7 @@ const transition = computed(() => {
// //
const onBreadcrumb = (route: any) => { const onBreadcrumb = (route: any) => {
let path = route.redirect || route.path; let path = route.redirect || route.path;
router.replace((path as string) || ""); router.replace((path as string) || HOME_PATH);
}; };
</script> </script>

File diff suppressed because it is too large Load Diff

View File

@ -14,9 +14,8 @@ import systemMenu from "../_data/system_menu";
* 1 * 1
* 2 * 2
* 3 * 3
* 4 + * 4
* 5 * 5
* 6
*/ */
// post请求body,get请求query // post请求body,get请求query
@ -30,8 +29,7 @@ export default [
// 这里模拟两个角色admin、common // 这里模拟两个角色admin、common
let userRoles = token === "Admin-Token" ? ["admin"] : ["common"]; let userRoles = token === "Admin-Token" ? ["admin"] : ["common"];
const originTree: any = deepClone(systemMenu); const originTree: any = deepClone(systemMenu);
originTree[0].children = treeSort(filterByRole(originTree[0].children, userRoles)); return resultSuccess(treeSort(filterByRole(originTree, userRoles)));
return resultSuccess(originTree);
} }
} }
] as MockMethod[]; ] as MockMethod[];

View File

@ -17,10 +17,10 @@ const router = createRouter({
history: createWebHashHistory(), history: createWebHashHistory(),
/** /**
* addRoute动态添加 * addRoute动态添加
* 1staticRoutes登录页 * 1staticRoutes登录页layout页('/')
* 2notFoundAndNoPower 404401 No match found for location with path 'xxx' * 2notFoundAndNoPower 401500 No match found for location with path 'xxx'
* 2 notFoundAndNoPower 404401 * 2 notFoundAndNoPower 401500
* notFoundAndNoPower 404401 * notFoundAndNoPower 401500
* notFoundAndNoPower layout容器展示 * notFoundAndNoPower layout容器展示
*/ */
routes: [...staticRoutes, ...notFoundAndNoPower] routes: [...staticRoutes, ...notFoundAndNoPower]

View File

@ -4,7 +4,6 @@ import { useRoutesConfigStore } from "@/store/modules/route-config";
import { useThemeConfig } from "@/store/modules/theme-config"; import { useThemeConfig } from "@/store/modules/theme-config";
import { deepClone, arrayFlattened } from "@/utils/index"; import { deepClone, arrayFlattened } from "@/utils/index";
import { useRoutingMethod } from "@/hooks/useRoutingMethod"; import { useRoutingMethod } from "@/hooks/useRoutingMethod";
import Layout from "@/layout/index.vue";
/** /**
* *
@ -67,19 +66,13 @@ export const moduleReplacement = (tree: any) => {
/** /**
* *
* 1 views .vue * 1 views .vue
* 2layoutlayout为应用的基础结构 * 2views下的所有文件路径
* 3views下的所有文件路径 * 3
* 4
*/ */
// 匹配views里面所有的.vue文件 // 匹配views里面所有的.vue文件
const modules = import.meta.glob("@/views/**/*.vue"); const modules = import.meta.glob("@/views/**/*.vue");
export const moduleMatch = (item: any) => { export const moduleMatch = (item: any) => {
// 若是layout则直接给予顶层layout // 匹配每个views文件夹下的文件路径
if (item.component === "layout") {
// 布局组件是应用的基础结构,在应用启动时就需要被加载,因此不需要按需引入
return (item.component = Layout);
}
// 其它情况下匹配每个views文件夹下的文件路径
for (const key in modules) { for (const key in modules) {
const dir = key.split("views/")[1].replace(".vue", ""); const dir = key.split("views/")[1].replace(".vue", "");
// 若匹配上,则替换真实模块 // 若匹配上,则替换真实模块

View File

@ -1,29 +1,35 @@
import { HOME_PATH } from "@/config/index";
import Layout from "@/layout/index.vue";
/** /**
* path路径与文件夹名称相同便 * path路径与文件夹名称相同便
* *
* meta对象参数meta对象中 * meta对象参数meta对象中
* meta: { * meta: {
* title: 菜单栏以及 tabsView * title: 菜单栏以及 tabsView
* hide: 是否隐藏此路由访 * hide: 是否隐藏此路由访
* disable: 是否停用访 * disable: 是否停用访
* keepAlive: 是否缓存组件状态 * keepAlive: 是否缓存组件状态
* affix: 是否固定在 tabsView * affix: 是否固定在 tabsView
* link: 是否是超链接菜单1 link 2iframe: false * link: 是否是超链接菜单1 link 2iframe: false
* iframe: 是否内嵌窗口1iframetrue 2link * iframe: 是否内嵌窗口1iframetrue 2link
* roles: 当前路由权限表示 admincommon * roles: 当前路由权限表示 admincommon
* icon: 菜单tabsView * icon: 菜单tabsView
* svgIcon: svg图标 * svgIcon: svg图标
* sort: * sort: 菜单顺
* } * }
*/ */
/** /**
* *
* `dynamicRoutes数组` * `layout-children`
* @description dynamicRoutes dynamicRoutes children lauyout * @description mock/_data/system_menu
* @returns * @returns
*/ */
export const staticRoutes = [ export const staticRoutes = [
{
path: "/",
redirect: HOME_PATH
},
{ {
path: "/login", path: "/login",
name: "login", name: "login",
@ -31,10 +37,18 @@ export const staticRoutes = [
meta: { meta: {
title: "login" title: "login"
} }
},
{
path: "/layout",
name: "layout",
redirect: HOME_PATH,
component: Layout, // 容器布局-顶层路由
// 二级路由-主要渲染页面
children: []
} }
/** /**
* * layout.children
* dynamicRoutes *
*/ */
]; ];

View File

@ -86,9 +86,8 @@ export const routesConfigStore = () => {
* 2 * 2
* 3 * 3
* 4 * 4
* 5 + * 5
* 6 * 6
* 7
*/ */
async function initSetRouter() { async function initSetRouter() {
// 1、获取过滤角色权限后的树后端做排序处理 // 1、获取过滤角色权限后的树后端做排序处理
@ -96,15 +95,13 @@ export const routesConfigStore = () => {
// 2、将模块设置为真实模块 // 2、将模块设置为真实模块
let tree = await moduleReplacement(data); let tree = await moduleReplacement(data);
// 3、存储路由树用于生成菜单 // 3、存储路由树用于生成菜单
routeTree.value = tree[0].children; routeTree.value = tree;
// 4、根据树生成一维路由数组 // 4、根据树生成一维路由数组
tree[0].children = linearArray(tree[0].children); tree = linearArray(tree);
// 5、设置完整的路由二维路由顶层路由 + 二级的一维路由 // 5、动态添加路由
tree[0].redirect = tree[0].children[0].path; tree.forEach((route: any) => router.addRoute("layout", route));
// 6、动态添加路由 // 6、缓存一维路由
tree.forEach((route: any) => router.addRoute(route)); routeList.value = tree;
// 7、缓存一维路由
routeList.value = tree[0].children;
} }
return { return {

View File

@ -95,7 +95,7 @@ import MenuItemIcon from "@/layout/components/Menu/menu-item-icon.vue";
import dynamicRoutes from "@/mock/_data/system_menu"; import dynamicRoutes from "@/mock/_data/system_menu";
const tableData = computed(() => { const tableData = computed(() => {
return dynamicRoutes[0].children; return dynamicRoutes;
}); });
const formData = reactive({ const formData = reactive({
form: { form: {