mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-01-23 09:40:25 +08:00
feat: add 'maxNumOfOpenTab' to limit the maximum number of tabs with the same name
This commit is contained in:
parent
bcd5e49117
commit
480580f104
@ -38,7 +38,8 @@
|
|||||||
"title": "Features",
|
"title": "Features",
|
||||||
"hideChildrenInMenu": "Hide Menu Children",
|
"hideChildrenInMenu": "Hide Menu Children",
|
||||||
"loginExpired": "Login Expired",
|
"loginExpired": "Login Expired",
|
||||||
"tabs": "Tabs"
|
"tabs": "Tabs",
|
||||||
|
"tabDetail": "Tab Detail Page"
|
||||||
},
|
},
|
||||||
"breadcrumb": {
|
"breadcrumb": {
|
||||||
"navigation": "Breadcrumb Navigation",
|
"navigation": "Breadcrumb Navigation",
|
||||||
|
@ -40,7 +40,8 @@
|
|||||||
"title": "功能",
|
"title": "功能",
|
||||||
"hideChildrenInMenu": "隐藏子菜单",
|
"hideChildrenInMenu": "隐藏子菜单",
|
||||||
"loginExpired": "登录过期",
|
"loginExpired": "登录过期",
|
||||||
"tabs": "标签页"
|
"tabs": "标签页",
|
||||||
|
"tabDetail": "标签详情页"
|
||||||
},
|
},
|
||||||
"breadcrumb": {
|
"breadcrumb": {
|
||||||
"navigation": "面包屑导航",
|
"navigation": "面包屑导航",
|
||||||
|
@ -107,6 +107,18 @@ const routes: RouteRecordRaw[] = [
|
|||||||
title: $t('page.demos.features.tabs'),
|
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',
|
name: 'HideChildrenInMenuParent',
|
||||||
path: 'hide-menu-children',
|
path: 'hide-menu-children',
|
||||||
|
@ -28,6 +28,11 @@ function openTab() {
|
|||||||
router.push({ name: 'VbenAbout' });
|
router.push({ name: 'VbenAbout' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openTabWithParams(id: number) {
|
||||||
|
// 这里就是路由跳转,也可以用path
|
||||||
|
router.push({ name: 'FeatureTabDetailDemo', params: { id } });
|
||||||
|
}
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
newTabTitle.value = '';
|
newTabTitle.value = '';
|
||||||
resetTabTitle();
|
resetTabTitle();
|
||||||
@ -92,5 +97,19 @@ function reset() {
|
|||||||
<Button @click="reset"> 重置 </Button>
|
<Button @click="reset"> 重置 </Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="card-box mt-5 p-5">
|
||||||
|
<div class="text-lg font-semibold">最大打开数量</div>
|
||||||
|
<div class="text-foreground/80 my-3">
|
||||||
|
限制带参数的tab打开的最大数量,由 `route.meta.maxNumOfOpenTab` 控制
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap items-center gap-3">
|
||||||
|
<template v-for="item in 5" :key="item">
|
||||||
|
<Button type="primary" @click="openTabWithParams(item)">
|
||||||
|
打开{{ item }}详情页
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
29
apps/web-antd/src/views/demos/features/tabs/tab-detail.vue
Normal file
29
apps/web-antd/src/views/demos/features/tabs/tab-detail.vue
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
import { useTabs } from '@vben/hooks';
|
||||||
|
|
||||||
|
defineOptions({ name: 'FeatureTabDetailDemo' });
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const { setTabTitle } = useTabs();
|
||||||
|
|
||||||
|
const index = computed(() => {
|
||||||
|
return route.params?.id ?? -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
setTabTitle(`No.${index.value} - 详情信息`);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="p-5">
|
||||||
|
<div class="card-box p-5">
|
||||||
|
<h1 class="text-xl font-semibold">标签详情页</h1>
|
||||||
|
<div class="text-foreground/80 mt-2">
|
||||||
|
<div>{{ index }} - 详情页内容在此</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
@ -121,6 +121,7 @@ const customConfig: Linter.FlatConfig[] = [
|
|||||||
files: ['apps/backend-mock/**/**'],
|
files: ['apps/backend-mock/**/**'],
|
||||||
rules: {
|
rules: {
|
||||||
'@typescript-eslint/no-extraneous-class': 'off',
|
'@typescript-eslint/no-extraneous-class': 'off',
|
||||||
|
'n/no-extraneous-import': 'off',
|
||||||
'n/prefer-global/buffer': 'off',
|
'n/prefer-global/buffer': 'off',
|
||||||
'no-console': 'off',
|
'no-console': 'off',
|
||||||
'unicorn/prefer-module': 'off',
|
'unicorn/prefer-module': 'off',
|
||||||
|
@ -100,6 +100,23 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (tabIndex === -1) {
|
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);
|
this.tabs.push(tab);
|
||||||
} else {
|
} else {
|
||||||
// 页面已经存在,不重复添加选项卡,只更新选项卡参数
|
// 页面已经存在,不重复添加选项卡,只更新选项卡参数
|
||||||
|
@ -85,6 +85,11 @@ interface RouteMeta {
|
|||||||
* 路由是否已经加载过
|
* 路由是否已经加载过
|
||||||
*/
|
*/
|
||||||
loaded?: boolean;
|
loaded?: boolean;
|
||||||
|
/**
|
||||||
|
* 标签页最大打开数量
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
maxNumOfOpenTab?: number;
|
||||||
/**
|
/**
|
||||||
* 菜单可以看到,但是访问会被重定向到403
|
* 菜单可以看到,但是访问会被重定向到403
|
||||||
*/
|
*/
|
||||||
|
@ -50,7 +50,7 @@ export function useTabbar() {
|
|||||||
toggleTabPin,
|
toggleTabPin,
|
||||||
} = useTabs();
|
} = useTabs();
|
||||||
const currentActive = computed(() => {
|
const currentActive = computed(() => {
|
||||||
return route.path;
|
return route.fullPath;
|
||||||
});
|
});
|
||||||
|
|
||||||
const { locale } = useI18n();
|
const { locale } = useI18n();
|
||||||
|
@ -18,6 +18,10 @@
|
|||||||
"dependsOn": ["^build"],
|
"dependsOn": ["^build"],
|
||||||
"outputs": ["dist/**"]
|
"outputs": ["dist/**"]
|
||||||
},
|
},
|
||||||
|
"@vben/backend-mock#build": {
|
||||||
|
"dependsOn": ["^build"],
|
||||||
|
"outputs": [".nitro/**", ".output/**"]
|
||||||
|
},
|
||||||
"stub": {},
|
"stub": {},
|
||||||
"dev": {
|
"dev": {
|
||||||
"dependsOn": [],
|
"dependsOn": [],
|
||||||
|
Loading…
Reference in New Issue
Block a user