Skip to content

Commit 077a43e

Browse files
committed
fix: narrow plugin tab cleanup matching
1 parent 41584bf commit 077a43e

2 files changed

Lines changed: 71 additions & 25 deletions

File tree

apps/lina-vben/apps/web-antd/src/plugins/tabbar-cleanup.test.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,17 @@ vi.mock('./page-registry', () => ({
1313
pluginId: 'media',
1414
routePath: 'media',
1515
},
16+
{
17+
pluginId: 'media-library',
18+
routePath: 'media-library',
19+
},
1620
],
1721
}));
1822

1923
function tab(
2024
path: string,
2125
title: string,
22-
meta: Partial<TabDefinition['meta']> = {},
26+
meta: Record<string, unknown> = {},
2327
) {
2428
const routeMeta = {
2529
...meta,
@@ -46,6 +50,7 @@ describe('plugin tabbar cleanup', () => {
4650
tab('/dashboard/analytics', 'Analytics', { affixTab: true }),
4751
tab('/system/plugin', 'Plugin Management'),
4852
tab('/media', 'Media'),
53+
tab('/media-library', 'Media Library'),
4954
tab('/watermark-service', 'Watermark', {
5055
authority: ['watermark-service:page'],
5156
}),
@@ -56,6 +61,7 @@ describe('plugin tabbar cleanup', () => {
5661
expect(tabbarStore.getTabs.map((item) => item.path)).toEqual([
5762
'/dashboard/analytics',
5863
'/system/plugin',
64+
'/media-library',
5965
'/watermark-service',
6066
]);
6167
});
@@ -77,4 +83,20 @@ describe('plugin tabbar cleanup', () => {
7783
'/system/plugin',
7884
]);
7985
});
86+
87+
it('closes plugin tabs by string authority metadata', async () => {
88+
const tabbarStore = useTabbarStore();
89+
tabbarStore.tabs = [
90+
tab('/system/plugin', 'Plugin Management'),
91+
tab('/dynamic-media', 'Dynamic Media', {
92+
authority: 'media:page',
93+
}),
94+
];
95+
96+
await closePluginTabs('media');
97+
98+
expect(tabbarStore.getTabs.map((item) => item.path)).toEqual([
99+
'/system/plugin',
100+
]);
101+
});
80102
});

apps/lina-vben/apps/web-antd/src/plugins/tabbar-cleanup.ts

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ function normalizePath(value: unknown) {
88
if (typeof value !== 'string') {
99
return '';
1010
}
11-
return value.trim().replace(/^\//, '');
11+
const normalized = value.trim().replaceAll('\\', '/').split(/[?#]/u)[0] ?? '';
12+
return normalized.replace(/^\/+/, '').replace(/\/+$/u, '');
1213
}
1314

1415
function extractAssetPluginId(value: unknown) {
@@ -20,10 +21,13 @@ function extractAssetPluginId(value: unknown) {
2021

2122
function authorityMatchesPlugin(tab: TabDefinition, pluginId: string) {
2223
const authority = tab.meta?.authority;
23-
if (!Array.isArray(authority)) {
24-
return false;
25-
}
26-
return authority.some(
24+
const authorityItems =
25+
typeof authority === 'string'
26+
? [authority]
27+
: Array.isArray(authority)
28+
? authority
29+
: [];
30+
return authorityItems.some(
2731
(item) =>
2832
typeof item === 'string' &&
2933
(item === pluginId || item.startsWith(`${pluginId}:`)),
@@ -38,19 +42,22 @@ function pathMatchesPluginRoute(path: string, pluginId: string) {
3842
);
3943
}
4044

41-
function pluginRoutePaths(pluginId: string) {
42-
return new Set(
43-
getPluginPages()
44-
.filter((page) => page.pluginId === pluginId)
45-
.map((page) => normalizePath(page.routePath))
46-
.filter(Boolean),
47-
);
45+
function pluginRoutePathOwners() {
46+
const routePathOwners = new Map<string, string>();
47+
for (const page of getPluginPages()) {
48+
const routePath = normalizePath(page.routePath);
49+
if (routePath) {
50+
routePathOwners.set(routePath, page.pluginId);
51+
}
52+
}
53+
return routePathOwners;
4854
}
4955

5056
function tabMatchesPlugin(
5157
tab: TabDefinition,
5258
pluginId: string,
5359
registeredRoutePaths: Set<string>,
60+
routePathOwners: Map<string, string>,
5461
) {
5562
const tabPaths = [
5663
tab.path,
@@ -60,16 +67,6 @@ function tabMatchesPlugin(
6067
...(tab.matched?.map((item) => item.path) ?? []),
6168
].map((item) => normalizePath(item));
6269

63-
if (
64-
tabPaths.some(
65-
(path) =>
66-
path &&
67-
(registeredRoutePaths.has(path) || pathMatchesPluginRoute(path, pluginId)),
68-
)
69-
) {
70-
return true;
71-
}
72-
7370
if (
7471
[
7572
tab.fullPath,
@@ -81,6 +78,23 @@ function tabMatchesPlugin(
8178
return true;
8279
}
8380

81+
const registeredOwners = tabPaths
82+
.map((path) => routePathOwners.get(path))
83+
.filter((owner): owner is string => typeof owner === 'string' && !!owner);
84+
if (registeredOwners.length > 0) {
85+
return registeredOwners.includes(pluginId);
86+
}
87+
88+
if (
89+
tabPaths.some(
90+
(path) =>
91+
path &&
92+
(registeredRoutePaths.has(path) || pathMatchesPluginRoute(path, pluginId)),
93+
)
94+
) {
95+
return true;
96+
}
97+
8498
return authorityMatchesPlugin(tab, pluginId);
8599
}
86100

@@ -91,11 +105,21 @@ export async function closePluginTabs(pluginId: string) {
91105
}
92106

93107
const tabbarStore = useTabbarStore();
94-
const registeredRoutePaths = pluginRoutePaths(normalizedPluginId);
108+
const routePathOwners = pluginRoutePathOwners();
109+
const registeredRoutePaths = new Set(
110+
[...routePathOwners.entries()]
111+
.filter(([, pluginId]) => pluginId === normalizedPluginId)
112+
.map(([routePath]) => routePath),
113+
);
95114
const staleKeys = tabbarStore.getTabs
96115
.filter((tab) => !tab.meta?.affixTab)
97116
.filter((tab) =>
98-
tabMatchesPlugin(tab, normalizedPluginId, registeredRoutePaths),
117+
tabMatchesPlugin(
118+
tab,
119+
normalizedPluginId,
120+
registeredRoutePaths,
121+
routePathOwners,
122+
),
99123
)
100124
.map((tab) => tab.key)
101125
.filter((key): key is string => typeof key === 'string' && key !== '');

0 commit comments

Comments
 (0)