Commit 48fcd7684cabff66e8648b71527c6cb4ce7d03be

Authored by Lan
Committed by GitHub
1 parent 791b323d

feat(param-menu): feature: menu with params (#845)

src/locales/lang/en/routes/demo.ts
... ... @@ -81,6 +81,9 @@ export default {
81 81 tab: 'Tab with parameters',
82 82 tab1: 'Tab with parameter 1',
83 83 tab2: 'Tab with parameter 2',
  84 + menu: 'Menu with parameters',
  85 + menu1: 'Menu with parameters 1',
  86 + menu2: 'Menu with parameters 2',
84 87  
85 88 ws: 'Websocket test',
86 89  
... ...
src/locales/lang/zh_CN/routes/demo.ts
... ... @@ -80,6 +80,9 @@ export default {
80 80 tab: 'Tab带参',
81 81 tab1: 'Tab带参1',
82 82 tab2: 'Tab带参2',
  83 + menu: 'Menu带参',
  84 + menu1: 'Menu带参1',
  85 + menu2: 'Menu带参2',
83 86 ws: 'websocket测试',
84 87 breadcrumb: '面包屑导航',
85 88 breadcrumbFlat: '平级模式',
... ...
src/router/guard/index.ts
... ... @@ -11,6 +11,7 @@ import { createPermissionGuard } from './permissionGuard';
11 11 import { createStateGuard } from './stateGuard';
12 12 import nProgress from 'nprogress';
13 13 import projectSetting from '/@/settings/projectSetting';
  14 +import { createParamMenuGuard } from './paramMenuGuard';
14 15  
15 16 // Don't change the order of creation
16 17 export function setupRouterGuard(router: Router) {
... ... @@ -21,6 +22,7 @@ export function setupRouterGuard(router: Router) {
21 22 createMessageGuard(router);
22 23 createProgressGuard(router);
23 24 createPermissionGuard(router);
  25 + createParamMenuGuard(router); // must after createPermissionGuard (menu has been built.)
24 26 createStateGuard(router);
25 27 }
26 28  
... ...
src/router/guard/paramMenuGuard.ts 0 → 100644
  1 +import type { Router } from 'vue-router';
  2 +import { configureDynamicParamsMenu } from '../helper/menuHelper';
  3 +import { Menu } from '../types';
  4 +import { PermissionModeEnum } from '/@/enums/appEnum';
  5 +import { useAppStoreWithOut } from '/@/store/modules/app';
  6 +
  7 +import { usePermissionStoreWithOut } from '/@/store/modules/permission';
  8 +
  9 +export function createParamMenuGuard(router: Router) {
  10 + const permissionStore = usePermissionStoreWithOut();
  11 + router.beforeEach(async (to, _, next) => {
  12 + // filter no name route
  13 + if (!to.name) {
  14 + next();
  15 + return;
  16 + }
  17 +
  18 + // menu has been built.
  19 + if (!permissionStore.getIsDynamicAddedRoute) {
  20 + next();
  21 + return;
  22 + }
  23 +
  24 + let menus: Menu[] = [];
  25 + if (isBackMode()) {
  26 + menus = permissionStore.getBackMenuList;
  27 + } else if (isRouteMappingMode()) {
  28 + menus = permissionStore.getFrontMenuList;
  29 + }
  30 + menus.forEach((item) => configureDynamicParamsMenu(item, to.params));
  31 +
  32 + next();
  33 + });
  34 +}
  35 +
  36 +const getPermissionMode = () => {
  37 + const appStore = useAppStoreWithOut();
  38 + return appStore.getProjectConfig.permissionMode;
  39 +};
  40 +
  41 +const isBackMode = () => {
  42 + return getPermissionMode() === PermissionModeEnum.BACK;
  43 +};
  44 +
  45 +const isRouteMappingMode = () => {
  46 + return getPermissionMode() === PermissionModeEnum.ROUTE_MAPPING;
  47 +};
... ...
src/router/helper/menuHelper.ts
... ... @@ -3,6 +3,8 @@ import type { MenuModule, Menu, AppRouteRecordRaw } from '/@/router/types';
3 3 import { findPath, treeMap } from '/@/utils/helper/treeHelper';
4 4 import { cloneDeep } from 'lodash-es';
5 5 import { isUrl } from '/@/utils/is';
  6 +import { RouteParams } from 'vue-router';
  7 +import { toRaw } from 'vue';
6 8  
7 9 export function getAllParentPath<T = Recordable>(treeData: T[], path: string) {
8 10 const menuList = findPath(treeData, (n) => n.path === path) as Menu[];
... ... @@ -66,3 +68,25 @@ export function transformRouteToMenu(routeModList: AppRouteModule[], routerMappi
66 68 joinParentPath(list);
67 69 return cloneDeep(list);
68 70 }
  71 +
  72 +/**
  73 + * config menu with given params
  74 + */
  75 +const menuParamRegex = /(?<=:)([\s\S]+?)((?=\/)|$)/g;
  76 +export function configureDynamicParamsMenu(menu: Menu, params: RouteParams) {
  77 + const { path, paramPath } = toRaw(menu);
  78 + let realPath = paramPath ? paramPath : path;
  79 + const matchArr = realPath.match(menuParamRegex);
  80 + matchArr?.forEach((it) => {
  81 + if (params[it]) {
  82 + realPath = realPath.replace(`:${it}`, params[it] as string);
  83 + }
  84 + });
  85 + // save original param path.
  86 + if (!paramPath && matchArr && matchArr.length > 0) {
  87 + menu.paramPath = path;
  88 + }
  89 + menu.path = realPath;
  90 + // children
  91 + menu.children?.forEach((item) => configureDynamicParamsMenu(item, params));
  92 +}
... ...
src/router/routes/modules/demo/feat.ts
... ... @@ -265,6 +265,35 @@ const feat: AppRouteModule = {
265 265 },
266 266 ],
267 267 },
  268 + {
  269 + path: 'testParam/:id',
  270 + name: 'TestParam',
  271 + component: getParentLayout('TestParam'),
  272 + meta: {
  273 + title: t('routes.demo.feat.menu'),
  274 + ignoreKeepAlive: true,
  275 + },
  276 + children: [
  277 + {
  278 + path: 'sub1',
  279 + name: 'TestParam_1',
  280 + component: () => import('/@/views/demo/feat/menu-params/index.vue'),
  281 + meta: {
  282 + title: t('routes.demo.feat.menu1'),
  283 + ignoreKeepAlive: true,
  284 + },
  285 + },
  286 + {
  287 + path: 'sub2',
  288 + name: 'TestParam_2',
  289 + component: () => import('/@/views/demo/feat/menu-params/index.vue'),
  290 + meta: {
  291 + title: t('routes.demo.feat.menu2'),
  292 + ignoreKeepAlive: true,
  293 + },
  294 + },
  295 + ],
  296 + },
268 297 ],
269 298 };
270 299  
... ...
src/router/types.ts
... ... @@ -31,6 +31,9 @@ export interface Menu {
31 31  
32 32 path: string;
33 33  
  34 + // path contains param, auto assignment.
  35 + paramPath?: string;
  36 +
34 37 disabled?: boolean;
35 38  
36 39 children?: Menu[];
... ...
src/views/demo/feat/menu-params/index.vue 0 → 100644
  1 +<template>
  2 + <PageWrapper title="带参数菜单(路由)" content="支持多级参数">
  3 + 当前参数:{{ params }}
  4 + <br />
  5 + 输入参数切换路由:
  6 + <Input v-model:value="value" placeholder="建议为url标准字符,输入后点击切换" />
  7 + <a-button type="primary" @click="handleClickGo">切换路由</a-button>
  8 + <br />
  9 + 切换路由后
  10 + <ul>
  11 + <li>可刷新页面测试路由参数情况是否正常。</li>
  12 + <li>可于左侧菜单中展开子菜单,点击测试参数是否携带正常。</li>
  13 + </ul>
  14 + </PageWrapper>
  15 +</template>
  16 +<script lang="ts">
  17 + import { Input } from 'ant-design-vue';
  18 + import { computed, defineComponent, ref, unref } from 'vue';
  19 + import { useRouter } from 'vue-router';
  20 + import { PageWrapper } from '/@/components/Page';
  21 +
  22 + export default defineComponent({
  23 + name: 'TestMenu',
  24 + components: { PageWrapper, Input },
  25 + setup() {
  26 + const { currentRoute, replace } = useRouter();
  27 + const value = ref<string>('');
  28 +
  29 + const handleClickGo = () => {
  30 + const { name } = unref(currentRoute);
  31 + replace({ name: name!, params: { id: unref(value) } });
  32 + };
  33 + return {
  34 + value,
  35 + handleClickGo,
  36 + params: computed(() => {
  37 + return unref(currentRoute).params;
  38 + }),
  39 + };
  40 + },
  41 + });
  42 +</script>
... ...