diff --git a/src/locales/lang/en/routes/demo.ts b/src/locales/lang/en/routes/demo.ts index c57124ca..5fb7950d 100644 --- a/src/locales/lang/en/routes/demo.ts +++ b/src/locales/lang/en/routes/demo.ts @@ -81,6 +81,9 @@ export default { tab: 'Tab with parameters', tab1: 'Tab with parameter 1', tab2: 'Tab with parameter 2', + menu: 'Menu with parameters', + menu1: 'Menu with parameters 1', + menu2: 'Menu with parameters 2', ws: 'Websocket test', diff --git a/src/locales/lang/zh_CN/routes/demo.ts b/src/locales/lang/zh_CN/routes/demo.ts index 3908052e..f3d439a8 100644 --- a/src/locales/lang/zh_CN/routes/demo.ts +++ b/src/locales/lang/zh_CN/routes/demo.ts @@ -80,6 +80,9 @@ export default { tab: 'Tab带参', tab1: 'Tab带参1', tab2: 'Tab带参2', + menu: 'Menu带参', + menu1: 'Menu带参1', + menu2: 'Menu带参2', ws: 'websocket测试', breadcrumb: '面包屑导航', breadcrumbFlat: '平级模式', diff --git a/src/router/guard/index.ts b/src/router/guard/index.ts index 8bd42796..c5677499 100644 --- a/src/router/guard/index.ts +++ b/src/router/guard/index.ts @@ -11,6 +11,7 @@ import { createPermissionGuard } from './permissionGuard'; import { createStateGuard } from './stateGuard'; import nProgress from 'nprogress'; import projectSetting from '/@/settings/projectSetting'; +import { createParamMenuGuard } from './paramMenuGuard'; // Don't change the order of creation export function setupRouterGuard(router: Router) { @@ -21,6 +22,7 @@ export function setupRouterGuard(router: Router) { createMessageGuard(router); createProgressGuard(router); createPermissionGuard(router); + createParamMenuGuard(router); // must after createPermissionGuard (menu has been built.) createStateGuard(router); } diff --git a/src/router/guard/paramMenuGuard.ts b/src/router/guard/paramMenuGuard.ts new file mode 100644 index 00000000..1c75157b --- /dev/null +++ b/src/router/guard/paramMenuGuard.ts @@ -0,0 +1,47 @@ +import type { Router } from 'vue-router'; +import { configureDynamicParamsMenu } from '../helper/menuHelper'; +import { Menu } from '../types'; +import { PermissionModeEnum } from '/@/enums/appEnum'; +import { useAppStoreWithOut } from '/@/store/modules/app'; + +import { usePermissionStoreWithOut } from '/@/store/modules/permission'; + +export function createParamMenuGuard(router: Router) { + const permissionStore = usePermissionStoreWithOut(); + router.beforeEach(async (to, _, next) => { + // filter no name route + if (!to.name) { + next(); + return; + } + + // menu has been built. + if (!permissionStore.getIsDynamicAddedRoute) { + next(); + return; + } + + let menus: Menu[] = []; + if (isBackMode()) { + menus = permissionStore.getBackMenuList; + } else if (isRouteMappingMode()) { + menus = permissionStore.getFrontMenuList; + } + menus.forEach((item) => configureDynamicParamsMenu(item, to.params)); + + next(); + }); +} + +const getPermissionMode = () => { + const appStore = useAppStoreWithOut(); + return appStore.getProjectConfig.permissionMode; +}; + +const isBackMode = () => { + return getPermissionMode() === PermissionModeEnum.BACK; +}; + +const isRouteMappingMode = () => { + return getPermissionMode() === PermissionModeEnum.ROUTE_MAPPING; +}; diff --git a/src/router/helper/menuHelper.ts b/src/router/helper/menuHelper.ts index 21ed7f07..3f5f4776 100644 --- a/src/router/helper/menuHelper.ts +++ b/src/router/helper/menuHelper.ts @@ -3,6 +3,8 @@ import type { MenuModule, Menu, AppRouteRecordRaw } from '/@/router/types'; import { findPath, treeMap } from '/@/utils/helper/treeHelper'; import { cloneDeep } from 'lodash-es'; import { isUrl } from '/@/utils/is'; +import { RouteParams } from 'vue-router'; +import { toRaw } from 'vue'; export function getAllParentPath(treeData: T[], path: string) { const menuList = findPath(treeData, (n) => n.path === path) as Menu[]; @@ -66,3 +68,25 @@ export function transformRouteToMenu(routeModList: AppRouteModule[], routerMappi joinParentPath(list); return cloneDeep(list); } + +/** + * config menu with given params + */ +const menuParamRegex = /(?<=:)([\s\S]+?)((?=\/)|$)/g; +export function configureDynamicParamsMenu(menu: Menu, params: RouteParams) { + const { path, paramPath } = toRaw(menu); + let realPath = paramPath ? paramPath : path; + const matchArr = realPath.match(menuParamRegex); + matchArr?.forEach((it) => { + if (params[it]) { + realPath = realPath.replace(`:${it}`, params[it] as string); + } + }); + // save original param path. + if (!paramPath && matchArr && matchArr.length > 0) { + menu.paramPath = path; + } + menu.path = realPath; + // children + menu.children?.forEach((item) => configureDynamicParamsMenu(item, params)); +} diff --git a/src/router/routes/modules/demo/feat.ts b/src/router/routes/modules/demo/feat.ts index 3094ead9..b295400e 100644 --- a/src/router/routes/modules/demo/feat.ts +++ b/src/router/routes/modules/demo/feat.ts @@ -265,6 +265,35 @@ const feat: AppRouteModule = { }, ], }, + { + path: 'testParam/:id', + name: 'TestParam', + component: getParentLayout('TestParam'), + meta: { + title: t('routes.demo.feat.menu'), + ignoreKeepAlive: true, + }, + children: [ + { + path: 'sub1', + name: 'TestParam_1', + component: () => import('/@/views/demo/feat/menu-params/index.vue'), + meta: { + title: t('routes.demo.feat.menu1'), + ignoreKeepAlive: true, + }, + }, + { + path: 'sub2', + name: 'TestParam_2', + component: () => import('/@/views/demo/feat/menu-params/index.vue'), + meta: { + title: t('routes.demo.feat.menu2'), + ignoreKeepAlive: true, + }, + }, + ], + }, ], }; diff --git a/src/router/types.ts b/src/router/types.ts index 7b5ef05d..a8d9546a 100644 --- a/src/router/types.ts +++ b/src/router/types.ts @@ -31,6 +31,9 @@ export interface Menu { path: string; + // path contains param, auto assignment. + paramPath?: string; + disabled?: boolean; children?: Menu[]; diff --git a/src/views/demo/feat/menu-params/index.vue b/src/views/demo/feat/menu-params/index.vue new file mode 100644 index 00000000..1a566b21 --- /dev/null +++ b/src/views/demo/feat/menu-params/index.vue @@ -0,0 +1,42 @@ + +