diff --git a/apps/web-antd/src/locales/langs/en-US.json b/apps/web-antd/src/locales/langs/en-US.json
index dec360db0..014aabaa8 100644
--- a/apps/web-antd/src/locales/langs/en-US.json
+++ b/apps/web-antd/src/locales/langs/en-US.json
@@ -38,7 +38,8 @@
"title": "Features",
"hideChildrenInMenu": "Hide Menu Children",
"loginExpired": "Login Expired",
- "tabs": "Tabs"
+ "tabs": "Tabs",
+ "tabDetail": "Tab Detail Page"
},
"breadcrumb": {
"navigation": "Breadcrumb Navigation",
diff --git a/apps/web-antd/src/locales/langs/zh-CN.json b/apps/web-antd/src/locales/langs/zh-CN.json
index 515e45bb1..08b3fa1f9 100644
--- a/apps/web-antd/src/locales/langs/zh-CN.json
+++ b/apps/web-antd/src/locales/langs/zh-CN.json
@@ -40,7 +40,8 @@
"title": "功能",
"hideChildrenInMenu": "隐藏子菜单",
"loginExpired": "登录过期",
- "tabs": "标签页"
+ "tabs": "标签页",
+ "tabDetail": "标签详情页"
},
"breadcrumb": {
"navigation": "面包屑导航",
diff --git a/apps/web-antd/src/router/routes/modules/demos.ts b/apps/web-antd/src/router/routes/modules/demos.ts
index d9cc9afef..c0e369a4a 100644
--- a/apps/web-antd/src/router/routes/modules/demos.ts
+++ b/apps/web-antd/src/router/routes/modules/demos.ts
@@ -107,6 +107,18 @@ const routes: RouteRecordRaw[] = [
title: $t('page.demos.features.tabs'),
},
},
+ {
+ name: 'FeatureTabDetailDemo',
+ path: 'tabs/detail/:id',
+ component: () =>
+ import('#/views/demos/features/tabs/tab-detail.vue'),
+ meta: {
+ activePath: '/demos/features/tabs',
+ hideInMenu: true,
+ maxNumOfOpenTab: 3,
+ title: $t('page.demos.features.tabDetail'),
+ },
+ },
{
name: 'HideChildrenInMenuParent',
path: 'hide-menu-children',
diff --git a/apps/web-antd/src/views/demos/features/tabs/index.vue b/apps/web-antd/src/views/demos/features/tabs/index.vue
index df98a4b8b..48d52ce36 100644
--- a/apps/web-antd/src/views/demos/features/tabs/index.vue
+++ b/apps/web-antd/src/views/demos/features/tabs/index.vue
@@ -28,6 +28,11 @@ function openTab() {
router.push({ name: 'VbenAbout' });
}
+function openTabWithParams(id: number) {
+ // 这里就是路由跳转,也可以用path
+ router.push({ name: 'FeatureTabDetailDemo', params: { id } });
+}
+
function reset() {
newTabTitle.value = '';
resetTabTitle();
@@ -92,5 +97,19 @@ function reset() {
+
+
+
最大打开数量
+
+ 限制带参数的tab打开的最大数量,由 `route.meta.maxNumOfOpenTab` 控制
+
+
+
+
+
+
+
diff --git a/apps/web-antd/src/views/demos/features/tabs/tab-detail.vue b/apps/web-antd/src/views/demos/features/tabs/tab-detail.vue
new file mode 100644
index 000000000..809cf9107
--- /dev/null
+++ b/apps/web-antd/src/views/demos/features/tabs/tab-detail.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+
标签详情页
+
+
{{ index }} - 详情页内容在此
+
+
+
+
diff --git a/internal/lint-configs/eslint-config/src/custom-config.ts b/internal/lint-configs/eslint-config/src/custom-config.ts
index 0ebff774f..d871d8bc2 100644
--- a/internal/lint-configs/eslint-config/src/custom-config.ts
+++ b/internal/lint-configs/eslint-config/src/custom-config.ts
@@ -121,6 +121,7 @@ const customConfig: Linter.FlatConfig[] = [
files: ['apps/backend-mock/**/**'],
rules: {
'@typescript-eslint/no-extraneous-class': 'off',
+ 'n/no-extraneous-import': 'off',
'n/prefer-global/buffer': 'off',
'no-console': 'off',
'unicorn/prefer-module': 'off',
diff --git a/packages/@core/forward/stores/src/modules/tabbar.ts b/packages/@core/forward/stores/src/modules/tabbar.ts
index 53dcbdae6..770b47b60 100644
--- a/packages/@core/forward/stores/src/modules/tabbar.ts
+++ b/packages/@core/forward/stores/src/modules/tabbar.ts
@@ -100,6 +100,23 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
});
if (tabIndex === -1) {
+ // 获取动态路由打开数,超过 0 即代表需要控制打开数
+ const maxNumOfOpenTab = (routeTab?.meta?.maxNumOfOpenTab ??
+ -1) as number;
+ // 如果动态路由层级大于 0 了,那么就要限制该路由的打开数限制了
+ // 获取到已经打开的动态路由数, 判断是否大于某一个值
+ if (
+ maxNumOfOpenTab > 0 &&
+ this.tabs.filter((tab) => tab.name === routeTab.name).length >=
+ maxNumOfOpenTab
+ ) {
+ // 关闭第一个
+ const index = this.tabs.findIndex(
+ (item) => item.name === routeTab.name,
+ );
+ index !== -1 && this.tabs.splice(index, 1);
+ }
+
this.tabs.push(tab);
} else {
// 页面已经存在,不重复添加选项卡,只更新选项卡参数
diff --git a/packages/@core/shared/typings/src/vue-router.d.ts b/packages/@core/shared/typings/src/vue-router.d.ts
index 60d19bb6e..e905c2cb6 100644
--- a/packages/@core/shared/typings/src/vue-router.d.ts
+++ b/packages/@core/shared/typings/src/vue-router.d.ts
@@ -85,6 +85,11 @@ interface RouteMeta {
* 路由是否已经加载过
*/
loaded?: boolean;
+ /**
+ * 标签页最大打开数量
+ * @default false
+ */
+ maxNumOfOpenTab?: number;
/**
* 菜单可以看到,但是访问会被重定向到403
*/
diff --git a/packages/effects/layouts/src/basic/tabbar/use-tabbar.ts b/packages/effects/layouts/src/basic/tabbar/use-tabbar.ts
index 6aaca73b4..655339389 100644
--- a/packages/effects/layouts/src/basic/tabbar/use-tabbar.ts
+++ b/packages/effects/layouts/src/basic/tabbar/use-tabbar.ts
@@ -50,7 +50,7 @@ export function useTabbar() {
toggleTabPin,
} = useTabs();
const currentActive = computed(() => {
- return route.path;
+ return route.fullPath;
});
const { locale } = useI18n();
diff --git a/turbo.json b/turbo.json
index dca7fc29b..e2881e138 100644
--- a/turbo.json
+++ b/turbo.json
@@ -18,6 +18,10 @@
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
+ "@vben/backend-mock#build": {
+ "dependsOn": ["^build"],
+ "outputs": [".nitro/**", ".output/**"]
+ },
"stub": {},
"dev": {
"dependsOn": [],