From 526223331289f2e29e5ef8a99858ecabaf28d8a4 Mon Sep 17 00:00:00 2001 From: Netfan Date: Thu, 6 Feb 2025 19:33:10 +0800 Subject: [PATCH] feat: tabbar support max count limit (#5490) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 标签栏支持限制打开的最大数量 --- .../__snapshots__/config.test.ts.snap | 1 + packages/@core/preferences/src/config.ts | 1 + packages/@core/preferences/src/types.ts | 2 ++ .../preferences/blocks/layout/tabbar.vue | 12 ++++++++++ .../preferences/blocks/number-field-item.vue | 12 ++++++++-- .../preferences/preferences-drawer.vue | 2 ++ .../locales/src/langs/en-US/preferences.json | 2 ++ .../locales/src/langs/zh-CN/preferences.json | 2 ++ packages/stores/package.json | 1 + packages/stores/src/modules/tabbar.ts | 10 +++++++- pnpm-lock.yaml | 23 +++++++++++++++++++ 11 files changed, 65 insertions(+), 3 deletions(-) diff --git a/packages/@core/preferences/__tests__/__snapshots__/config.test.ts.snap b/packages/@core/preferences/__tests__/__snapshots__/config.test.ts.snap index 486bf0487d0..d537d22e937 100644 --- a/packages/@core/preferences/__tests__/__snapshots__/config.test.ts.snap +++ b/packages/@core/preferences/__tests__/__snapshots__/config.test.ts.snap @@ -80,6 +80,7 @@ exports[`defaultPreferences immutability test > should not modify the config obj "enable": true, "height": 38, "keepAlive": true, + "maxCount": 0, "middleClickToClose": false, "persist": true, "showIcon": true, diff --git a/packages/@core/preferences/src/config.ts b/packages/@core/preferences/src/config.ts index 8f82e4fc453..2c3f6586839 100644 --- a/packages/@core/preferences/src/config.ts +++ b/packages/@core/preferences/src/config.ts @@ -80,6 +80,7 @@ const defaultPreferences: Preferences = { enable: true, height: 38, keepAlive: true, + maxCount: 0, middleClickToClose: false, persist: true, showIcon: true, diff --git a/packages/@core/preferences/src/types.ts b/packages/@core/preferences/src/types.ts index b554b219a40..9c7e87bc4e5 100644 --- a/packages/@core/preferences/src/types.ts +++ b/packages/@core/preferences/src/types.ts @@ -168,6 +168,8 @@ interface TabbarPreferences { height: number; /** 开启标签页缓存功能 */ keepAlive: boolean; + /** 限制最大数量 */ + maxCount: number; /** 是否点击中键时关闭标签 */ middleClickToClose: boolean; /** 是否持久化标签 */ diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/layout/tabbar.vue b/packages/effects/layouts/src/widgets/preferences/blocks/layout/tabbar.vue index 4c116e5a124..ea533dad7fb 100644 --- a/packages/effects/layouts/src/widgets/preferences/blocks/layout/tabbar.vue +++ b/packages/effects/layouts/src/widgets/preferences/blocks/layout/tabbar.vue @@ -5,6 +5,7 @@ import { computed } from 'vue'; import { $t } from '@vben/locales'; +import NumberFieldItem from '../number-field-item.vue'; import SelectItem from '../select-item.vue'; import SwitchItem from '../switch-item.vue'; @@ -22,6 +23,7 @@ const tabbarWheelable = defineModel('tabbarWheelable'); const tabbarStyleType = defineModel('tabbarStyleType'); const tabbarShowMore = defineModel('tabbarShowMore'); const tabbarShowMaximize = defineModel('tabbarShowMaximize'); +const tabbarMaxCount = defineModel('tabbarMaxCount'); const tabbarMiddleClickToClose = defineModel( 'tabbarMiddleClickToClose', ); @@ -54,6 +56,16 @@ const styleItems = computed((): SelectOption[] => [ {{ $t('preferences.tabbar.persist') }} + + {{ $t('preferences.tabbar.maxCount') }} + {{ $t('preferences.tabbar.draggable') }} diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/number-field-item.vue b/packages/effects/layouts/src/widgets/preferences/blocks/number-field-item.vue index aeb099bc12d..3878f812b63 100644 --- a/packages/effects/layouts/src/widgets/preferences/blocks/number-field-item.vue +++ b/packages/effects/layouts/src/widgets/preferences/blocks/number-field-item.vue @@ -23,10 +23,12 @@ withDefaults( disabled?: boolean; items?: SelectOption[]; placeholder?: string; + tip?: string; }>(), { disabled: false, placeholder: '', + tip: '', items: () => [], }, ); @@ -47,11 +49,17 @@ const slots = useSlots(); - + - + + + diff --git a/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue b/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue index 6761e8a3288..88e83815b86 100644 --- a/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue +++ b/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue @@ -116,6 +116,7 @@ const tabbarPersist = defineModel('tabbarPersist'); const tabbarDraggable = defineModel('tabbarDraggable'); const tabbarWheelable = defineModel('tabbarWheelable'); const tabbarStyleType = defineModel('tabbarStyleType'); +const tabbarMaxCount = defineModel('tabbarMaxCount'); const tabbarMiddleClickToClose = defineModel( 'tabbarMiddleClickToClose', ); @@ -365,6 +366,7 @@ async function handleReset() { v-model:tabbar-show-more="tabbarShowMore" v-model:tabbar-style-type="tabbarStyleType" v-model:tabbar-wheelable="tabbarWheelable" + v-model:tabbar-max-count="tabbarMaxCount" v-model:tabbar-middle-click-to-close="tabbarMiddleClickToClose" /> diff --git a/packages/locales/src/langs/en-US/preferences.json b/packages/locales/src/langs/en-US/preferences.json index c115d94a4fc..029a9a549bd 100644 --- a/packages/locales/src/langs/en-US/preferences.json +++ b/packages/locales/src/langs/en-US/preferences.json @@ -62,6 +62,8 @@ "showMore": "Show More Button", "showMaximize": "Show Maximize Button", "persist": "Persist Tabs", + "maxCount": "Max Count of Tabs", + "maxCountTip": "When the number of tabs exceeds the maximum,\nthe oldest tab will be closed.\n Set to 0 to disable count checking.", "draggable": "Enable Draggable Sort", "wheelable": "Support Mouse Wheel", "middleClickClose": "Close Tab when Mouse Middle Button Click", diff --git a/packages/locales/src/langs/zh-CN/preferences.json b/packages/locales/src/langs/zh-CN/preferences.json index 7e90a1da431..fde3368a10f 100644 --- a/packages/locales/src/langs/zh-CN/preferences.json +++ b/packages/locales/src/langs/zh-CN/preferences.json @@ -62,6 +62,8 @@ "showMore": "显示更多按钮", "showMaximize": "显示最大化按钮", "persist": "持久化标签页", + "maxCount": "最大标签数", + "maxCountTip": "每次打开新的标签时如果超过最大标签数,\n会自动关闭一个最先打开的标签\n设置为 0 则不限制", "draggable": "启动拖拽排序", "wheelable": "启用纵向滚轮响应", "middleClickClose": "点击鼠标中键关闭标签页", diff --git a/packages/stores/package.json b/packages/stores/package.json index 83f8a1004d0..fe79f8e5a60 100644 --- a/packages/stores/package.json +++ b/packages/stores/package.json @@ -20,6 +20,7 @@ } }, "dependencies": { + "@vben-core/preferences": "workspace:*", "@vben-core/shared": "workspace:*", "@vben-core/typings": "workspace:*", "pinia": "catalog:", diff --git a/packages/stores/src/modules/tabbar.ts b/packages/stores/src/modules/tabbar.ts index 3c5dbfe033b..0555437769c 100644 --- a/packages/stores/src/modules/tabbar.ts +++ b/packages/stores/src/modules/tabbar.ts @@ -4,6 +4,7 @@ import type { TabDefinition } from '@vben-core/typings'; import { toRaw } from 'vue'; +import { preferences } from '@vben-core/preferences'; import { openRouteInNewWindow, startProgress, @@ -107,6 +108,7 @@ export const useTabbarStore = defineStore('core-tabbar', { }); if (tabIndex === -1) { + const maxCount = preferences.tabbar.maxCount; // 获取动态路由打开数,超过 0 即代表需要控制打开数 const maxNumOfOpenTab = (routeTab?.meta?.maxNumOfOpenTab ?? -1) as number; @@ -122,8 +124,14 @@ export const useTabbarStore = defineStore('core-tabbar', { (item) => item.name === routeTab.name, ); index !== -1 && this.tabs.splice(index, 1); + } else if (maxCount > 0 && this.tabs.length >= maxCount) { + // 关闭第一个 + const index = this.tabs.findIndex( + (item) => + !Reflect.has(item.meta, 'affixTab') || !item.meta.affixTab, + ); + index !== -1 && this.tabs.splice(index, 1); } - this.tabs.push(tab); } else { // 页面已经存在,不重复添加选项卡,只更新选项卡参数 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 561df83ca57..527ae0c589a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1727,6 +1727,9 @@ importers: packages/stores: dependencies: + '@vben-core/preferences': + specifier: workspace:* + version: link:../@core/preferences '@vben-core/shared': specifier: workspace:* version: link:../@core/base/shared @@ -1997,24 +2000,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@ast-grep/napi-linux-arm64-musl@0.32.3': resolution: {integrity: sha512-7+u7F5rzaV0/N5WdP2q+kGl3v+l8iGFRx4p7NUcbNumYqGDS2mkfRkaesRDSd7BH94ZulGtJnpmu3imX7spolQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@ast-grep/napi-linux-x64-gnu@0.32.3': resolution: {integrity: sha512-XwUjw+W1QWDAPjx+Hsa8ZwONN3MDPINdRkRM6Q1vV3pl0p9YrMpwL72xrWQA1G7r7ej9BI1fLiXWB4YEOeYzJw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@ast-grep/napi-linux-x64-musl@0.32.3': resolution: {integrity: sha512-894fQNqBDUfCP/qYbrPcK6+tMTEskc+vV2IKOKqgCfDryeptaiJJTJL9+Vbj38rO1LWhY8MIZ8W5ZyjxuhDRBA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@ast-grep/napi-win32-arm64-msvc@0.32.3': resolution: {integrity: sha512-T8nrZm3E+h2VgHuQ3THQLvqhou4MkSbNcyIOgLZ0l2NatHIckeHuly5fmnkd6KQsGP/AqAEGxZBoOVYvoDl7DA==} @@ -3713,36 +3720,42 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.1': resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.1': resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.1': resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] + libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.1': resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.1': resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] + libc: [musl] '@parcel/watcher-wasm@2.5.1': resolution: {integrity: sha512-RJxlQQLkaMMIuWRozy+z2vEqbaQlCuaCgVZIUCzQLYggY22LZbP5Y1+ia+FD724Ids9e+XIyOLXLrLgQSHIthw==} @@ -3966,51 +3979,61 @@ packages: resolution: {integrity: sha512-lfqTpWjSvbgQP1vqGTXdv+/kxIznKXZlI109WkIFPbud41bjigjNmOAAKoazmRGx+k9e3rtIdbq2pQZPV1pMig==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.34.2': resolution: {integrity: sha512-RGjqULqIurqqv+NJTyuPgdZhka8ImMLB32YwUle2BPTDqDoXNgwFjdjQC59FbSk08z0IqlRJjrJ0AvDQ5W5lpw==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.34.2': resolution: {integrity: sha512-ZvkPiheyXtXlFqHpsdgscx+tZ7hoR59vOettvArinEspq5fxSDSgfF+L5wqqJ9R4t+n53nyn0sKxeXlik7AY9Q==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.34.2': resolution: {integrity: sha512-UlFk+E46TZEoxD9ufLKDBzfSG7Ki03fo6hsNRRRHF+KuvNZ5vd1RRVQm8YZlGsjcJG8R252XFK0xNPay+4WV7w==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.34.2': resolution: {integrity: sha512-hJhfsD9ykx59jZuuoQgYT1GEcNNi3RCoEmbo5OGfG8RlHOiVS7iVNev9rhLKh7UBYq409f4uEw0cclTXx8nh8Q==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.34.2': resolution: {integrity: sha512-g/O5IpgtrQqPegvqopvmdCF9vneLE7eqYfdPWW8yjPS8f63DNam3U4ARL1PNNB64XHZDHKpvO2Giftf43puB8Q==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.34.2': resolution: {integrity: sha512-bSQijDC96M6PuooOuXHpvXUYiIwsnDmqGU8+br2U7iPoykNi9JtMUpN7K6xml29e0evK0/g0D1qbAUzWZFHY5Q==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-s390x-gnu@4.34.2': resolution: {integrity: sha512-49TtdeVAsdRuiUHXPrFVucaP4SivazetGUVH8CIxVsNsaPHV4PFkpLmH9LeqU/R4Nbgky9lzX5Xe1NrzLyraVA==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.34.2': resolution: {integrity: sha512-j+jFdfOycLIQ7FWKka9Zd3qvsIyugg5LeZuHF6kFlXo6MSOc6R1w37YUVy8VpAKd81LMWGi5g9J25P09M0SSIw==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.34.2': resolution: {integrity: sha512-aDPHyM/D2SpXfSNCVWCxyHmOqN9qb7SWkY1+vaXqMNMXslZYnwh9V/UCudl6psyG0v6Ukj7pXanIpfZwCOEMUg==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.34.2': resolution: {integrity: sha512-LQRkCyUBnAo7r8dbEdtNU08EKLCJMgAk2oP5H3R7BnUlKLqgR3dUjrLBVirmc1RK6U6qhtDw29Dimeer8d5hzQ==}