Skip to content

Commit 58cafbc

Browse files
committed
feat: 路由meta添加activePath可将某个菜单激活,主要用于通过queryparams传参的路由
1 parent 5d86b71 commit 58cafbc

File tree

9 files changed

+135
-110
lines changed

9 files changed

+135
-110
lines changed

mock/asyncRoutes.ts

+2
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ const tabsRouter = {
179179
meta: {
180180
// 不在menu菜单中显示
181181
showLink: false,
182+
activePath: "/tabs/index",
182183
roles: ["admin", "common"]
183184
}
184185
},
@@ -190,6 +191,7 @@ const tabsRouter = {
190191
meta: {
191192
// 不在menu菜单中显示
192193
showLink: false,
194+
activePath: "/tabs/index",
193195
roles: ["admin", "common"]
194196
}
195197
}

src/layout/components/sidebar/horizontal.vue

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
<script setup lang="ts">
2-
import { ref, nextTick } from "vue";
32
import Search from "../search/index.vue";
43
import Notice from "../notice/index.vue";
54
import SidebarItem from "./sidebarItem.vue";
5+
import { isAllEmpty } from "@pureadmin/utils";
6+
import { ref, nextTick, computed } from "vue";
67
import { useNav } from "@/layout/hooks/useNav";
78
import { useTranslationLang } from "../../hooks/useTranslationLang";
89
import { usePermissionStoreHook } from "@/store/modules/permission";
@@ -27,6 +28,10 @@ const {
2728
getDropdownItemClass
2829
} = useNav();
2930
31+
const defaultActive = computed(() =>
32+
!isAllEmpty(route.meta?.activePath) ? route.meta.activePath : route.path
33+
);
34+
3035
nextTick(() => {
3136
menuRef.value?.handleResize();
3237
});
@@ -46,7 +51,7 @@ nextTick(() => {
4651
ref="menuRef"
4752
mode="horizontal"
4853
class="horizontal-header-menu"
49-
:default-active="route.path"
54+
:default-active="defaultActive"
5055
>
5156
<sidebar-item
5257
v-for="route in usePermissionStoreHook().wholeMenus"

src/layout/components/sidebar/mixNav.vue

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import extraIcon from "./extraIcon.vue";
33
import Search from "../search/index.vue";
44
import Notice from "../notice/index.vue";
5+
import { isAllEmpty } from "@pureadmin/utils";
56
import { useNav } from "@/layout/hooks/useNav";
67
import { transformI18n } from "@/plugins/i18n";
78
import { ref, toRaw, watch, onMounted, nextTick } from "vue";
@@ -36,10 +37,9 @@ function getDefaultActive(routePath) {
3637
const wholeMenus = usePermissionStoreHook().wholeMenus;
3738
/** 当前路由的父级路径 */
3839
const parentRoutes = getParentPaths(routePath, wholeMenus)[0];
39-
defaultActive.value = findRouteByPath(
40-
parentRoutes,
41-
wholeMenus
42-
)?.children[0]?.path;
40+
defaultActive.value = !isAllEmpty(route.meta?.activePath)
41+
? route.meta.activePath
42+
: findRouteByPath(parentRoutes, wholeMenus)?.children[0]?.path;
4343
}
4444
4545
onMounted(() => {

src/layout/components/sidebar/vertical.vue

+12-6
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { emitter } from "@/utils/mitt";
55
import SidebarItem from "./sidebarItem.vue";
66
import leftCollapse from "./leftCollapse.vue";
77
import { useNav } from "@/layout/hooks/useNav";
8-
import { storageLocal } from "@pureadmin/utils";
98
import { responsiveStorageNameSpace } from "@/config";
9+
import { storageLocal, isAllEmpty } from "@pureadmin/utils";
1010
import { findRouteByPath, getParentPaths } from "@/router/utils";
1111
import { usePermissionStoreHook } from "@/store/modules/permission";
1212
import { ref, computed, watch, onMounted, onBeforeUnmount } from "vue";
@@ -32,7 +32,13 @@ const loading = computed(() =>
3232
pureApp.layout === "mix" ? false : menuData.value.length === 0 ? true : false
3333
);
3434
35-
function getSubMenuData(path: string) {
35+
const defaultActive = computed(() =>
36+
!isAllEmpty(route.meta?.activePath) ? route.meta.activePath : route.path
37+
);
38+
39+
function getSubMenuData() {
40+
let path = "";
41+
path = defaultActive.value;
3642
subMenuData.value = [];
3743
// path的上级路由组成的数组
3844
const parentPathArr = getParentPaths(
@@ -48,18 +54,18 @@ function getSubMenuData(path: string) {
4854
subMenuData.value = parenetRoute?.children;
4955
}
5056
51-
getSubMenuData(route.path);
52-
5357
watch(
5458
() => [route.path, usePermissionStoreHook().wholeMenus],
5559
() => {
5660
if (route.path.includes("/redirect")) return;
57-
getSubMenuData(route.path);
61+
getSubMenuData();
5862
menuSelect(route.path);
5963
}
6064
);
6165
6266
onMounted(() => {
67+
getSubMenuData();
68+
6369
emitter.on("logoChange", key => {
6470
showLogo.value = key;
6571
});
@@ -87,7 +93,7 @@ onBeforeUnmount(() => {
8793
mode="vertical"
8894
class="outer-most select-none"
8995
:collapse="isCollapse"
90-
:default-active="route.path"
96+
:default-active="defaultActive"
9197
:collapse-transition="false"
9298
>
9399
<sidebar-item

src/layout/components/tag/index.vue

+3-3
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ function onFresh() {
166166
path: "/redirect" + fullPath,
167167
query
168168
});
169-
handleAliveRoute(route as toRouteType, "refresh");
169+
handleAliveRoute(route as ToRouteType, "refresh");
170170
}
171171
172172
function deleteDynamicTag(obj: any, current: any, tag?: string) {
@@ -239,7 +239,7 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
239239
240240
function deleteMenu(item, tag?: string) {
241241
deleteDynamicTag(item, item.path, tag);
242-
handleAliveRoute(route as toRouteType);
242+
handleAliveRoute(route as ToRouteType);
243243
}
244244
245245
function onClickDrop(key, item, selectRoute?: RouteConfigs) {
@@ -287,7 +287,7 @@ function onClickDrop(key, item, selectRoute?: RouteConfigs) {
287287
length: multiTags.value.length
288288
});
289289
router.push(topPath);
290-
handleAliveRoute(route as toRouteType);
290+
handleAliveRoute(route as ToRouteType);
291291
break;
292292
case 6:
293293
// 整体页面全屏

src/router/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ const whiteList = ["/login"];
101101

102102
const { VITE_HIDE_HOME } = import.meta.env;
103103

104-
router.beforeEach((to: toRouteType, _from, next) => {
104+
router.beforeEach((to: ToRouteType, _from, next) => {
105105
if (to.meta?.keepAlive) {
106106
handleAliveRoute(to, "add");
107107
// 页面整体刷新和点击标签页刷新

src/router/utils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ function formatTwoStageRoutes(routesList: RouteRecordRaw[]) {
256256
}
257257

258258
/** 处理缓存路由(添加、删除、刷新) */
259-
function handleAliveRoute({ name }: toRouteType, mode?: string) {
259+
function handleAliveRoute({ name }: ToRouteType, mode?: string) {
260260
switch (mode) {
261261
case "add":
262262
usePermissionStoreHook().cacheOperate({

types/global.d.ts

-93
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import type {
77
import type { ECharts } from "echarts";
88
import type { IconifyIcon } from "@iconify/vue";
99
import type { TableColumns } from "@pureadmin/table";
10-
import { type RouteComponent, type RouteLocationNormalized } from "vue-router";
1110

1211
/**
1312
* 全局类型声明,无需引入直接在 `.vue` 、`.ts` 、`.tsx` 文件使用即可获得类型提示
@@ -166,98 +165,6 @@ declare global {
166165
tags?: Array<any>;
167166
}
168167

169-
/**
170-
* `src/router` 文件夹里的类型声明
171-
*/
172-
interface toRouteType extends RouteLocationNormalized {
173-
meta: {
174-
roles: Array<string>;
175-
keepAlive?: boolean;
176-
dynamicLevel?: string;
177-
};
178-
}
179-
180-
/**
181-
* @description 完整子路由配置表
182-
*/
183-
interface RouteChildrenConfigsTable {
184-
/** 子路由地址 `必填` */
185-
path: string;
186-
/** 路由名字(对应不要重复,和当前组件的`name`保持一致)`必填` */
187-
name?: string;
188-
/** 路由重定向 `可选` */
189-
redirect?: string;
190-
/** 按需加载组件 `可选` */
191-
component?: RouteComponent;
192-
meta?: {
193-
/** 菜单名称(兼容国际化、非国际化,如何用国际化的写法就必须在根目录的`locales`文件夹下对应添加) `必填` */
194-
title: string;
195-
/** 菜单图标 `可选` */
196-
icon?: string | FunctionalComponent | IconifyIcon;
197-
/** 菜单名称右侧的额外图标 */
198-
extraIcon?: string | FunctionalComponent | IconifyIcon;
199-
/** 是否在菜单中显示(默认`true`)`可选` */
200-
showLink?: boolean;
201-
/** 是否显示父级菜单 `可选` */
202-
showParent?: boolean;
203-
/** 页面级别权限设置 `可选` */
204-
roles?: Array<string>;
205-
/** 按钮级别权限设置 `可选` */
206-
auths?: Array<string>;
207-
/** 路由组件缓存(开启 `true`、关闭 `false`)`可选` */
208-
keepAlive?: boolean;
209-
/** 内嵌的`iframe`链接 `可选` */
210-
frameSrc?: string;
211-
/** `iframe`页是否开启首次加载动画(默认`true`)`可选` */
212-
frameLoading?: boolean;
213-
/** 页面加载动画(有两种形式,一种直接采用vue内置的`transitions`动画,另一种是使用`animate.css`写进、离场动画)`可选` */
214-
transition?: {
215-
/**
216-
* @description 当前路由动画效果
217-
* @see {@link https://next.router.vuejs.org/guide/advanced/transitions.html#transitions}
218-
* @see animate.css {@link https://animate.style}
219-
*/
220-
name?: string;
221-
/** 进场动画 */
222-
enterTransition?: string;
223-
/** 离场动画 */
224-
leaveTransition?: string;
225-
};
226-
// 是否不添加信息到标签页,(默认`false`)
227-
hiddenTag?: boolean;
228-
/** 动态路由可打开的最大数量 `可选` */
229-
dynamicLevel?: number;
230-
};
231-
/** 子路由配置项 */
232-
children?: Array<RouteChildrenConfigsTable>;
233-
}
234-
235-
/**
236-
* @description 整体路由配置表(包括完整子路由)
237-
*/
238-
interface RouteConfigsTable {
239-
/** 路由地址 `必填` */
240-
path: string;
241-
/** 路由名字(保持唯一)`可选` */
242-
name?: string;
243-
/** `Layout`组件 `可选` */
244-
component?: RouteComponent;
245-
/** 路由重定向 `可选` */
246-
redirect?: string;
247-
meta?: {
248-
/** 菜单名称(兼容国际化、非国际化,如何用国际化的写法就必须在根目录的`locales`文件夹下对应添加)`必填` */
249-
title: string;
250-
/** 菜单图标 `可选` */
251-
icon?: string | FunctionalComponent | IconifyIcon;
252-
/** 是否在菜单中显示(默认`true`)`可选` */
253-
showLink?: boolean;
254-
/** 菜单升序排序,值越高排的越后(只针对顶级路由)`可选` */
255-
rank?: number;
256-
};
257-
/** 子路由配置项 */
258-
children?: Array<RouteChildrenConfigsTable>;
259-
}
260-
261168
/**
262169
* 平台里所有组件实例都能访问到的全局属性对象的类型声明
263170
*/

types/router.d.ts

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// 全局路由类型声明
2+
3+
import { type RouteComponent, type RouteLocationNormalized } from "vue-router";
4+
5+
declare global {
6+
interface ToRouteType extends RouteLocationNormalized {
7+
meta: CustomizeRouteMeta;
8+
}
9+
10+
/**
11+
* @description 完整子路由的`meta`配置表
12+
*/
13+
interface CustomizeRouteMeta {
14+
/** 菜单名称(兼容国际化、非国际化,如何用国际化的写法就必须在根目录的`locales`文件夹下对应添加) `必填` */
15+
title: string;
16+
/** 菜单图标 `可选` */
17+
icon?: string | FunctionalComponent | IconifyIcon;
18+
/** 菜单名称右侧的额外图标 */
19+
extraIcon?: string | FunctionalComponent | IconifyIcon;
20+
/** 是否在菜单中显示(默认`true`)`可选` */
21+
showLink?: boolean;
22+
/** 是否显示父级菜单 `可选` */
23+
showParent?: boolean;
24+
/** 页面级别权限设置 `可选` */
25+
roles?: Array<string>;
26+
/** 按钮级别权限设置 `可选` */
27+
auths?: Array<string>;
28+
/** 路由组件缓存(开启 `true`、关闭 `false`)`可选` */
29+
keepAlive?: boolean;
30+
/** 内嵌的`iframe`链接 `可选` */
31+
frameSrc?: string;
32+
/** `iframe`页是否开启首次加载动画(默认`true`)`可选` */
33+
frameLoading?: boolean;
34+
/** 页面加载动画(有两种形式,一种直接采用vue内置的`transitions`动画,另一种是使用`animate.css`写进、离场动画)`可选` */
35+
transition?: {
36+
/**
37+
* @description 当前路由动画效果
38+
* @see {@link https://next.router.vuejs.org/guide/advanced/transitions.html#transitions}
39+
* @see animate.css {@link https://animate.style}
40+
*/
41+
name?: string;
42+
/** 进场动画 */
43+
enterTransition?: string;
44+
/** 离场动画 */
45+
leaveTransition?: string;
46+
};
47+
// 是否不添加信息到标签页,(默认`false`)
48+
hiddenTag?: boolean;
49+
/** 动态路由可打开的最大数量 `可选` */
50+
dynamicLevel?: number;
51+
/** 将某个菜单激活
52+
* (主要用于通过`query`或`params`传参的路由,当它们通过配置`showLink: false`后不在菜单中显示,就不会有任何菜单高亮,
53+
* 而通过设置`activePath`指定激活菜单即可获得高亮,`activePath`为指定激活菜单的`path`)
54+
*/
55+
activePath?: string;
56+
}
57+
58+
/**
59+
* @description 完整子路由配置表
60+
*/
61+
interface RouteChildrenConfigsTable {
62+
/** 子路由地址 `必填` */
63+
path: string;
64+
/** 路由名字(对应不要重复,和当前组件的`name`保持一致)`必填` */
65+
name?: string;
66+
/** 路由重定向 `可选` */
67+
redirect?: string;
68+
/** 按需加载组件 `可选` */
69+
component?: RouteComponent;
70+
meta?: CustomizeRouteMeta;
71+
/** 子路由配置项 */
72+
children?: Array<RouteChildrenConfigsTable>;
73+
}
74+
75+
/**
76+
* @description 整体路由配置表(包括完整子路由)
77+
*/
78+
interface RouteConfigsTable {
79+
/** 路由地址 `必填` */
80+
path: string;
81+
/** 路由名字(保持唯一)`可选` */
82+
name?: string;
83+
/** `Layout`组件 `可选` */
84+
component?: RouteComponent;
85+
/** 路由重定向 `可选` */
86+
redirect?: string;
87+
meta?: {
88+
/** 菜单名称(兼容国际化、非国际化,如何用国际化的写法就必须在根目录的`locales`文件夹下对应添加)`必填` */
89+
title: string;
90+
/** 菜单图标 `可选` */
91+
icon?: string | FunctionalComponent | IconifyIcon;
92+
/** 是否在菜单中显示(默认`true`)`可选` */
93+
showLink?: boolean;
94+
/** 菜单升序排序,值越高排的越后(只针对顶级路由)`可选` */
95+
rank?: number;
96+
};
97+
/** 子路由配置项 */
98+
children?: Array<RouteChildrenConfigsTable>;
99+
}
100+
}
101+
102+
// https://router.vuejs.org/zh/guide/advanced/meta.html#typescript
103+
declare module "vue-router" {
104+
interface RouteMeta extends CustomizeRouteMeta {}
105+
}

0 commit comments

Comments
 (0)