From 893f74dc3ef994876057c7c7a3937216271f5120 Mon Sep 17 00:00:00 2001 From: zouawen <846027729@qq.com> Date: Mon, 9 Feb 2026 16:31:29 +0800 Subject: [PATCH 01/13] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E6=A8=AA?= =?UTF-8?q?=E5=90=91=E5=B8=83=E5=B1=80=E6=97=B6=E8=8F=9C=E5=8D=95=E6=BF=80?= =?UTF-8?q?=E6=B4=BB=E6=88=96=E8=81=9A=E7=84=A6=E6=97=B6=E8=83=8C=E6=99=AF?= =?UTF-8?q?=E8=89=B2=EF=BC=8C=E6=A0=87=E7=AD=BE=E5=B7=A5=E5=85=B7=E6=A0=8F?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=88=B7=E6=96=B0=E6=8C=89=E9=92=AE=EF=BC=8C?= =?UTF-8?q?=E5=85=B6=E4=BB=96=E6=A0=B7=E5=BC=8F=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/@core/base/icons/src/lucide.ts | 2 + packages/@core/preferences/src/config.ts | 1 + packages/@core/preferences/src/constants.ts | 3 -- packages/@core/preferences/src/types.ts | 2 + .../ui-kit/menu-ui/src/components/menu.vue | 53 +++++++++++-------- .../menu-ui/src/components/sub-menu.vue | 1 + .../src/components/tabs-chrome/tabs.vue | 2 +- .../tabs-ui/src/components/tabs/tabs.vue | 2 +- .../tabs-ui/src/components/widgets/index.ts | 1 + .../src/components/widgets/tool-more.vue | 4 +- .../src/components/widgets/tool-refresh.vue | 18 +++++++ .../@core/ui-kit/tabs-ui/src/tabs-view.vue | 6 +-- packages/effects/layouts/src/basic/layout.vue | 2 +- .../layouts/src/basic/tabbar/tabbar.vue | 13 ++++- .../preferences/blocks/theme/builtin.vue | 1 - .../plugins/src/vxe-table/use-vxe-grid.vue | 6 ++- 16 files changed, 79 insertions(+), 38 deletions(-) create mode 100644 packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-refresh.vue diff --git a/packages/@core/base/icons/src/lucide.ts b/packages/@core/base/icons/src/lucide.ts index a167aea0..97325131 100644 --- a/packages/@core/base/icons/src/lucide.ts +++ b/packages/@core/base/icons/src/lucide.ts @@ -29,6 +29,7 @@ export { FoldHorizontal, Fullscreen, Github, + Grid, Grip, GripVertical, Menu as IconDefault, @@ -36,6 +37,7 @@ export { Info, InspectionPanel, Languages, + LayoutGrid, LoaderCircle, LockKeyhole, LogOut, diff --git a/packages/@core/preferences/src/config.ts b/packages/@core/preferences/src/config.ts index f788e895..43cc73fa 100644 --- a/packages/@core/preferences/src/config.ts +++ b/packages/@core/preferences/src/config.ts @@ -105,6 +105,7 @@ const defaultPreferences: Preferences = { showIcon: true, showMaximize: true, showMore: true, + showRefresh: true, styleType: 'chrome', visitHistory: true, wheelable: true, diff --git a/packages/@core/preferences/src/constants.ts b/packages/@core/preferences/src/constants.ts index 7ec2007d..562a7afa 100644 --- a/packages/@core/preferences/src/constants.ts +++ b/packages/@core/preferences/src/constants.ts @@ -38,12 +38,10 @@ const BUILT_IN_THEME_PRESETS: BuiltinThemePreset[] = [ primaryColor: 'hsl(240 5.9% 10%)', type: 'zinc', }, - { color: 'hsl(181 84% 32%)', type: 'deep-green', }, - { color: 'hsl(211 91% 39%)', type: 'deep-blue', @@ -56,7 +54,6 @@ const BUILT_IN_THEME_PRESETS: BuiltinThemePreset[] = [ color: 'hsl(0 75% 42%)', type: 'rose', }, - { color: 'hsl(0 0% 25%)', darkPrimaryColor: 'hsl(0 0% 98%)', diff --git a/packages/@core/preferences/src/types.ts b/packages/@core/preferences/src/types.ts index e1ef0a38..6db64af2 100644 --- a/packages/@core/preferences/src/types.ts +++ b/packages/@core/preferences/src/types.ts @@ -222,6 +222,8 @@ interface TabbarPreferences { showMaximize: boolean; /** 显示更多按钮 */ showMore: boolean; + /** 显示刷新按钮 */ + showRefresh: boolean; /** 标签页风格 */ styleType: TabsStyleType; /** 是否开启访问历史记录 */ diff --git a/packages/@core/ui-kit/menu-ui/src/components/menu.vue b/packages/@core/ui-kit/menu-ui/src/components/menu.vue index 644ff59e..4d3ed617 100644 --- a/packages/@core/ui-kit/menu-ui/src/components/menu.vue +++ b/packages/@core/ui-kit/menu-ui/src/components/menu.vue @@ -463,33 +463,33 @@ $namespace: vben; &.is-dark { --menu-background-color: hsl(var(--menu)); // --menu-submenu-opened-background-color: hsl(var(--menu-opened-dark)); - --menu-item-background-color: var(--menu-background-color); --menu-item-color: hsl(var(--foreground) / 80%); + --menu-item-background-color: var(--menu-background-color); --menu-item-hover-color: hsl(var(--accent-foreground)); --menu-item-hover-background-color: hsl(var(--accent)); --menu-item-active-color: hsl(var(--accent-foreground)); --menu-item-active-background-color: hsl(var(--accent)); - --menu-submenu-hover-color: hsl(var(--foreground)); - --menu-submenu-hover-background-color: hsl(var(--accent)); - --menu-submenu-active-color: hsl(var(--foreground)); - --menu-submenu-active-background-color: transparent; --menu-submenu-background-color: var(--menu-background-color); + --menu-submenu-hover-color: hsl(var(--accent-foreground)); + --menu-submenu-hover-background-color: hsl(var(--accent)); + --menu-submenu-active-color: hsl(var(--accent-foreground)); + --menu-submenu-active-background-color: transparent; } &.is-light { --menu-background-color: hsl(var(--menu)); // --menu-submenu-opened-background-color: hsl(var(--menu-opened)); + --menu-item-color: hsl(var(--accent-foreground)); --menu-item-background-color: var(--menu-background-color); - --menu-item-color: hsl(var(--foreground)); --menu-item-hover-color: var(--menu-item-color); --menu-item-hover-background-color: hsl(var(--accent)); --menu-item-active-color: hsl(var(--primary)); --menu-item-active-background-color: hsl(var(--primary) / 15%); + --menu-submenu-background-color: var(--menu-background-color); --menu-submenu-hover-color: hsl(var(--primary)); --menu-submenu-hover-background-color: hsl(var(--accent)); --menu-submenu-active-color: hsl(var(--primary)); --menu-submenu-active-background-color: transparent; - --menu-submenu-background-color: var(--menu-background-color); } &.is-rounded { @@ -518,25 +518,33 @@ $namespace: vben; --menu-background-color: transparent; &.is-dark { + --menu-background-color: hsl(var(--menu)); + --menu-item-color: hsl(var(--foreground) / 80%); + --menu-item-background-color: var(--menu-background-color); --menu-item-hover-color: hsl(var(--accent-foreground)); --menu-item-hover-background-color: hsl(var(--accent)); --menu-item-active-color: hsl(var(--accent-foreground)); --menu-item-active-background-color: hsl(var(--accent)); - --menu-submenu-active-color: hsl(var(--foreground)); - --menu-submenu-active-background-color: hsl(var(--accent)); + --menu-submenu-background-color: var(--menu-background-color); --menu-submenu-hover-color: hsl(var(--accent-foreground)); --menu-submenu-hover-background-color: hsl(var(--accent)); + --menu-submenu-active-color: hsl(var(--accent-foreground)); + --menu-submenu-active-background-color: hsl(var(--accent)); } &.is-light { + --menu-background-color: hsl(var(--menu)); + --menu-item-color: hsl(var(--accent-foreground)); + --menu-item-background-color: var(--menu-background-color); + --menu-item-hover-color: hsl(var(--menu-item-color)); + --menu-item-hover-background-color: hsl(var(--accent)); --menu-item-active-color: hsl(var(--primary)); --menu-item-active-background-color: hsl(var(--primary) / 15%); - --menu-item-hover-background-color: hsl(var(--accent)); - --menu-item-hover-color: hsl(var(--primary)); - --menu-submenu-active-color: hsl(var(--primary)); - --menu-submenu-active-background-color: hsl(var(--primary) / 15%); + --menu-submenu-background-color: var(--menu-background-color); --menu-submenu-hover-color: hsl(var(--primary)); --menu-submenu-hover-background-color: hsl(var(--accent)); + --menu-submenu-active-color: hsl(var(--primary)); + --menu-submenu-active-background-color: hsl(var(--primary) / 15%); } } } @@ -862,16 +870,17 @@ $namespace: vben; padding-right: 12px !important; } - // &:not(.is-active):hover { - &:hover { - color: var(--menu-submenu-hover-color); - text-decoration: none; - cursor: pointer; - background: var(--menu-submenu-hover-background-color) !important; + &:not(.is-active):hover { + &:hover { + //color: var(--menu-submenu-hover-color); + text-decoration: none; + cursor: pointer; + background: var(--menu-submenu-hover-background-color) !important; - // svg { - // fill: var(--menu-submenu-hover-color); - // } + // svg { + // fill: var(--menu-submenu-hover-color); + // } + } } } diff --git a/packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue b/packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue index 6dfeb77f..5c8c8a86 100644 --- a/packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue +++ b/packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue @@ -210,6 +210,7 @@ onBeforeUnmount(() => { opened ? '' : 'hidden', 'overflow-auto', 'max-h-[calc(var(--reka-hover-card-content-available-height)-20px)]', + mode === 'horizontal' ? 'is-horizontal' : '', ]" :content-props="contentProps" :open="true" diff --git a/packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue b/packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue index 941ecfe2..dfb4a5e1 100644 --- a/packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue +++ b/packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue @@ -158,7 +158,7 @@ function onMouseDown(e: MouseEvent, tab: TabConfig) { diff --git a/packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue b/packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue index effa93cf..a573f449 100644 --- a/packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue +++ b/packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue @@ -132,7 +132,7 @@ function onMouseDown(e: MouseEvent, tab: TabConfig) { diff --git a/packages/@core/ui-kit/tabs-ui/src/components/widgets/index.ts b/packages/@core/ui-kit/tabs-ui/src/components/widgets/index.ts index a26899ef..f7634ce8 100644 --- a/packages/@core/ui-kit/tabs-ui/src/components/widgets/index.ts +++ b/packages/@core/ui-kit/tabs-ui/src/components/widgets/index.ts @@ -1,2 +1,3 @@ export { default as TabsToolMore } from './tool-more.vue'; +export { default as TabsToolRefresh } from './tool-refresh.vue'; export { default as TabsToolScreen } from './tool-screen.vue'; diff --git a/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-more.vue b/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-more.vue index 24f43a9e..045a670b 100644 --- a/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-more.vue +++ b/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-more.vue @@ -1,7 +1,7 @@ + + diff --git a/packages/@core/ui-kit/tabs-ui/src/tabs-view.vue b/packages/@core/ui-kit/tabs-ui/src/tabs-view.vue index fbc6b0d9..b5464f00 100644 --- a/packages/@core/ui-kit/tabs-ui/src/tabs-view.vue +++ b/packages/@core/ui-kit/tabs-ui/src/tabs-view.vue @@ -2,7 +2,7 @@ import type { TabsEmits, TabsProps } from './types'; import { useForwardPropsEmits } from '@vben-core/composables'; -import { ChevronLeft, ChevronRight } from '@vben-core/icons'; +import { ChevronsLeft, ChevronsRight } from '@vben-core/icons'; import { VbenScrollbar } from '@vben-core/shadcn-ui'; import { Tabs, TabsChrome } from './components'; @@ -60,7 +60,7 @@ useTabsDrag(props, emit); class="border-r px-2" @click="scrollDirection('left')" > - +
- +
diff --git a/packages/effects/layouts/src/basic/layout.vue b/packages/effects/layouts/src/basic/layout.vue index 45b97833..b8912c09 100644 --- a/packages/effects/layouts/src/basic/layout.vue +++ b/packages/effects/layouts/src/basic/layout.vue @@ -411,7 +411,7 @@ const headerSlots = computed(() => { diff --git a/packages/effects/layouts/src/basic/tabbar/tabbar.vue b/packages/effects/layouts/src/basic/tabbar/tabbar.vue index 8e4f33ba..367604ea 100644 --- a/packages/effects/layouts/src/basic/tabbar/tabbar.vue +++ b/packages/effects/layouts/src/basic/tabbar/tabbar.vue @@ -6,7 +6,12 @@ import { useContentMaximize, useTabs } from '@vben/hooks'; import { preferences } from '@vben/preferences'; import { useTabbarStore } from '@vben/stores'; -import { TabsToolMore, TabsToolScreen, TabsView } from '@vben-core/tabs-ui'; +import { + TabsToolMore, + TabsToolRefresh, + TabsToolScreen, + TabsView, +} from '@vben-core/tabs-ui'; import { useTabbar } from './use-tabbar'; @@ -19,7 +24,7 @@ defineProps<{ showIcon?: boolean; theme?: string }>(); const route = useRoute(); const tabbarStore = useTabbarStore(); const { contentIsMaximize, toggleMaximize } = useContentMaximize(); -const { unpinTab } = useTabs(); +const { refreshTab, unpinTab } = useTabs(); const { createContextMenus, @@ -65,6 +70,10 @@ if (!preferences.tabbar.persist) { />
+ { diff --git a/packages/@core/ui-kit/layout-ui/src/vben-layout.ts b/packages/@core/ui-kit/layout-ui/src/vben-layout.ts index 9b77ba96..b6b41375 100644 --- a/packages/@core/ui-kit/layout-ui/src/vben-layout.ts +++ b/packages/@core/ui-kit/layout-ui/src/vben-layout.ts @@ -146,6 +146,11 @@ interface VbenLayoutProps { * @default dark */ sidebarTheme?: ThemeModeType; + /** + * 侧边栏子栏 + * @default dark + */ + sidebarThemeSub?: ThemeModeType; /** * 侧边栏宽度 * @default 210 diff --git a/packages/@core/ui-kit/layout-ui/src/vben-layout.vue b/packages/@core/ui-kit/layout-ui/src/vben-layout.vue index a35ed349..b51be1e6 100644 --- a/packages/@core/ui-kit/layout-ui/src/vben-layout.vue +++ b/packages/@core/ui-kit/layout-ui/src/vben-layout.vue @@ -56,6 +56,7 @@ const props = withDefaults(defineProps(), { sidebarHidden: false, sidebarMixedWidth: 80, sidebarTheme: 'dark', + sidebarThemeSub: 'dark', sidebarWidth: 180, sideCollapseWidth: 60, tabbarEnable: true, @@ -502,6 +503,7 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT; :mixed-width="sidebarMixedWidth" :show="showSidebar" :theme="sidebarTheme" + :theme-sub="sidebarThemeSub" :width="getSidebarWidth" :z-index="sidebarZIndex" @leave="() => emit('sideMouseLeave')" diff --git a/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue b/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue index 86e86c91..11661386 100644 --- a/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue +++ b/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue @@ -151,10 +151,12 @@ $namespace: vben; } &__name { + width: 100%; margin-top: 8px; margin-bottom: 0; font-size: calc(var(--font-size-base, 16px) * 0.75); font-weight: 400; + text-align: center; transition: all 0.25s ease; } } diff --git a/packages/effects/layouts/src/basic/layout.vue b/packages/effects/layouts/src/basic/layout.vue index b8912c09..687fd647 100644 --- a/packages/effects/layouts/src/basic/layout.vue +++ b/packages/effects/layouts/src/basic/layout.vue @@ -60,6 +60,11 @@ const sidebarTheme = computed(() => { return dark ? 'dark' : 'light'; }); +const sidebarThemeSub = computed(() => { + const dark = isDark.value || preferences.theme.semiDarkSidebarSub; + return dark ? 'dark' : 'light'; +}); + const headerTheme = computed(() => { const dark = isDark.value || preferences.theme.semiDarkHeader; return dark ? 'dark' : 'light'; @@ -240,6 +245,7 @@ const headerSlots = computed(() => { :sidebar-hidden="preferences.sidebar.hidden" :sidebar-mixed-width="preferences.sidebar.mixedWidth" :sidebar-theme="sidebarTheme" + :sidebar-theme-sub="sidebarThemeSub" :sidebar-width="preferences.sidebar.width" :side-collapse-width="preferences.sidebar.collapseWidth" :tabbar-enable="preferences.tabbar.enable" @@ -355,7 +361,7 @@ const headerSlots = computed(() => { :collapse="preferences.sidebar.extraCollapse" :menus="wrapperMenus(extraMenus)" :rounded="isMenuRounded" - :theme="sidebarTheme" + :theme="sidebarThemeSub" /> From 03ebbea46aed722f6999c7cda6834452b9edfe88 Mon Sep 17 00:00:00 2001 From: AxiosLeo <13862149+AxiosLeo@users.noreply.github.com> Date: Thu, 12 Feb 2026 22:22:53 +0800 Subject: [PATCH 09/13] fix(menu): update hover color variable to use the correct reference (#7544) * fix(menu): update hover color variable to use the correct reference Medium Severity In the horizontal .is-light menu section, --menu-item-hover-color is set to hsl(var(--menu-item-color)), but --menu-item-color is already defined as hsl(var(--accent-foreground)). This results in hsl(hsl(...)) at computed-value time, which is invalid CSS. The non-horizontal .is-light block correctly uses var(--menu-item-color) without the extra hsl() wrapper. * fix(menu): simplify hover styles by removing redundant nested hover rules Low Severity The SCSS &:not(.is-active):hover { &:hover { ... } } compiles to a :hover:hover pseudo-class chain, which is functionally identical to a single :hover. The inner &:hover nesting is redundant and adds unnecessary complexity compared to placing styles directly inside the &:not(.is-active):hover block. --- .../ui-kit/menu-ui/src/components/menu.vue | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/@core/ui-kit/menu-ui/src/components/menu.vue b/packages/@core/ui-kit/menu-ui/src/components/menu.vue index 4d3ed617..d7894a17 100644 --- a/packages/@core/ui-kit/menu-ui/src/components/menu.vue +++ b/packages/@core/ui-kit/menu-ui/src/components/menu.vue @@ -536,7 +536,7 @@ $namespace: vben; --menu-background-color: hsl(var(--menu)); --menu-item-color: hsl(var(--accent-foreground)); --menu-item-background-color: var(--menu-background-color); - --menu-item-hover-color: hsl(var(--menu-item-color)); + --menu-item-hover-color: var(--menu-item-color); --menu-item-hover-background-color: hsl(var(--accent)); --menu-item-active-color: hsl(var(--primary)); --menu-item-active-background-color: hsl(var(--primary) / 15%); @@ -871,16 +871,14 @@ $namespace: vben; } &:not(.is-active):hover { - &:hover { - //color: var(--menu-submenu-hover-color); - text-decoration: none; - cursor: pointer; - background: var(--menu-submenu-hover-background-color) !important; + //color: var(--menu-submenu-hover-color); + text-decoration: none; + cursor: pointer; + background: var(--menu-submenu-hover-background-color) !important; - // svg { - // fill: var(--menu-submenu-hover-color); - // } - } + // svg { + // fill: var(--menu-submenu-hover-color); + // } } } From 01508d5e42f929e18ed4cd697bafc2c0283a305c Mon Sep 17 00:00:00 2001 From: Jin Mao Date: Thu, 26 Feb 2026 05:45:36 +0800 Subject: [PATCH 10/13] fix: fix lint --- packages/stores/src/modules/tabbar.ts | 58 ++++++++++++++++++++------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/packages/stores/src/modules/tabbar.ts b/packages/stores/src/modules/tabbar.ts index 8d18d442..66a3758b 100644 --- a/packages/stores/src/modules/tabbar.ts +++ b/packages/stores/src/modules/tabbar.ts @@ -1,5 +1,9 @@ import type { ComputedRef } from 'vue'; -import type { RouteLocationNormalized, Router, RouteRecordNormalized } from 'vue-router'; +import type { + RouteLocationNormalized, + Router, + RouteRecordNormalized, +} from 'vue-router'; import type { TabDefinition } from '@vben-core/typings'; @@ -66,7 +70,9 @@ export const useTabbarStore = defineStore('core-tabbar', { */ async _bulkCloseByKeys(keys: string[]) { const keySet = new Set(keys); - this.tabs = this.tabs.filter((item) => !keySet.has(getTabKeyFromTab(item))); + this.tabs = this.tabs.filter( + (item) => !keySet.has(getTabKeyFromTab(item)), + ); if (isVisitHistory()) { this.visitHistory.remove(...keys); } @@ -130,20 +136,25 @@ export const useTabbarStore = defineStore('core-tabbar', { if (tabIndex === -1) { const maxCount = preferences.tabbar.maxCount; // 获取动态路由打开数,超过 0 即代表需要控制打开数 - const maxNumOfOpenTab = (routeTab?.meta?.maxNumOfOpenTab ?? -1) as number; + const maxNumOfOpenTab = (routeTab?.meta?.maxNumOfOpenTab ?? + -1) as number; // 如果动态路由层级大于 0 了,那么就要限制该路由的打开数限制了 // 获取到已经打开的动态路由数, 判断是否大于某一个值 if ( maxNumOfOpenTab > 0 && - this.tabs.filter((tab) => tab.name === routeTab.name).length >= maxNumOfOpenTab + this.tabs.filter((tab) => tab.name === routeTab.name).length >= + maxNumOfOpenTab ) { // 关闭第一个 - const index = this.tabs.findIndex((item) => item.name === routeTab.name); + const index = this.tabs.findIndex( + (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 + (item) => + !Reflect.has(item.meta, 'affixTab') || !item.meta.affixTab, ); index !== -1 && this.tabs.splice(index, 1); } @@ -183,7 +194,9 @@ export const useTabbarStore = defineStore('core-tabbar', { this.tabs = newTabs.length > 0 ? newTabs : [...this.tabs].splice(0, 1); // 设置访问历史记录 if (isVisitHistory()) { - this.visitHistory.retain(this.tabs.map((item) => getTabKeyFromTab(item))); + this.visitHistory.retain( + this.tabs.map((item) => getTabKeyFromTab(item)), + ); } await this._goToDefaultTab(router); this.updateCacheTabs(); @@ -220,7 +233,9 @@ export const useTabbarStore = defineStore('core-tabbar', { for (const key of closeKeys) { if (key !== getTabKeyFromTab(tab)) { - const closeTab = this.tabs.find((item) => getTabKeyFromTab(item) === key); + const closeTab = this.tabs.find( + (item) => getTabKeyFromTab(item) === key, + ); if (!closeTab) { continue; } @@ -290,12 +305,14 @@ export const useTabbarStore = defineStore('core-tabbar', { break; } } - await (previousTab ? this._goToTab(previousTab, router) : this._goToDefaultTab(router)); + await (previousTab + ? this._goToTab(previousTab, router) + : this._goToDefaultTab(router)); return; } // 未开启访问历史记录,直接跳转下一个或上一个tab const index = this.getTabs.findIndex( - (item) => getTabKeyFromTab(item) === getTabKey(currentRoute.value) + (item) => getTabKeyFromTab(item) === getTabKey(currentRoute.value), ); const before = this.getTabs[index - 1]; @@ -319,7 +336,9 @@ export const useTabbarStore = defineStore('core-tabbar', { */ async closeTabByKey(key: string, router: Router) { const originKey = decodeURIComponent(key); - const index = this.tabs.findIndex((item) => getTabKeyFromTab(item) === originKey); + const index = this.tabs.findIndex( + (item) => getTabKeyFromTab(item) === originKey, + ); if (index === -1) { return; } @@ -335,7 +354,9 @@ export const useTabbarStore = defineStore('core-tabbar', { * @param key */ getTabByKey(key: string) { - return this.getTabs.find((item) => getTabKeyFromTab(item) === key) as TabDefinition; + return this.getTabs.find( + (item) => getTabKeyFromTab(item) === key, + ) as TabDefinition; }, /** * @zh_CN 新窗口打开标签页 @@ -656,14 +677,21 @@ function isTabShown(tab: TabDefinition) { * @param tab */ function getTabKey(tab: RouteLocationNormalized | RouteRecordNormalized) { - const { fullPath, path, meta: { fullPathKey } = {}, query = {} } = tab as RouteLocationNormalized; + const { + fullPath, + path, + meta: { fullPathKey } = {}, + query = {}, + } = tab as RouteLocationNormalized; // pageKey可能是数组(查询参数重复时可能出现) - const pageKey = Array.isArray(query.pageKey) ? query.pageKey[0] : query.pageKey; + const pageKey = Array.isArray(query.pageKey) + ? query.pageKey[0] + : query.pageKey; let rawKey; if (pageKey) { rawKey = pageKey; } else { - rawKey = fullPathKey === false ? path : fullPath ?? path; + rawKey = fullPathKey === false ? path : (fullPath ?? path); } try { return decodeURIComponent(rawKey); From 05920cd66d447198680590f8e6dc143d6f941d83 Mon Sep 17 00:00:00 2001 From: moil-xm <64048303+moil-xm@users.noreply.github.com> Date: Thu, 26 Feb 2026 06:14:12 +0800 Subject: [PATCH 11/13] feat(vite-config): vite export typing (#7569) * feat(vite-config): vite export typing * feat(vite-config): add type --------- Co-authored-by: Jin Mao <50581550+jinmao88@users.noreply.github.com> --- internal/vite-config/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/vite-config/src/index.ts b/internal/vite-config/src/index.ts index 352a3235..c9c2b20d 100644 --- a/internal/vite-config/src/index.ts +++ b/internal/vite-config/src/index.ts @@ -1,4 +1,5 @@ export * from './config'; export * from './options'; export * from './plugins'; +export type * from './typing'; export { loadAndConvertEnv } from './utils/env'; From 191fd90f0695c4227291398c3511da96315dec00 Mon Sep 17 00:00:00 2001 From: Wu Clan Date: Thu, 26 Feb 2026 06:17:04 +0800 Subject: [PATCH 12/13] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0=E8=A1=A8?= =?UTF-8?q?=E5=8D=95=E6=8F=8F=E8=BF=B0=E6=98=BE=E7=A4=BA=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=20(#6938)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../@core/ui-kit/form-ui/src/form-render/form-field.vue | 6 +++--- playground/src/views/examples/form/basic.vue | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue b/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue index 53c731e7..cc610d59 100644 --- a/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue +++ b/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue @@ -381,10 +381,10 @@ onUnmounted(() => {
- - -
+ + + diff --git a/playground/src/views/examples/form/basic.vue b/playground/src/views/examples/form/basic.vue index 52d04319..05be1f3a 100644 --- a/playground/src/views/examples/form/basic.vue +++ b/playground/src/views/examples/form/basic.vue @@ -67,6 +67,13 @@ const [BaseForm, baseFormApi] = useVbenForm({ label: '字符串', rules: 'required', }, + { + component: 'Input', + fieldName: 'desc', + // 界面显示的description + description: '这是表单描述', + label: '字符串(带描述)', + }, { // 组件需要在 #/adapter.ts内注册,并加上类型 component: 'ApiSelect', From 45b843f3441b3b45cae6192981f5422ff1310831 Mon Sep 17 00:00:00 2001 From: ming4762 Date: Thu, 26 Feb 2026 06:21:08 +0800 Subject: [PATCH 13/13] fix: fix bug where `renderEcharts` gets stuck in a dead loop (#7561) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 触发条件:echart所在页面开启keepalive 在其他页面切换颜色模式 --- .../plugins/src/echarts/use-echarts.ts | 27 ++++++++++++++++--- .../src/router/routes/modules/dashboard.ts | 1 + 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/effects/plugins/src/echarts/use-echarts.ts b/packages/effects/plugins/src/echarts/use-echarts.ts index dc74fd46..4f1989ce 100644 --- a/packages/effects/plugins/src/echarts/use-echarts.ts +++ b/packages/effects/plugins/src/echarts/use-echarts.ts @@ -6,7 +6,17 @@ import type { Nullable } from '@vben/types'; import type EchartsUI from './echarts-ui.vue'; -import { computed, nextTick, watch } from 'vue'; +import { + computed, + nextTick, + onActivated, + onBeforeUnmount, + onDeactivated, + onMounted, + ref, + unref, + watch, +} from 'vue'; import { usePreferences } from '@vben/preferences'; @@ -27,6 +37,8 @@ type EchartsThemeType = 'dark' | 'light' | null; function useEcharts(chartRef: Ref) { let chartInstance: echarts.ECharts | null = null; let cacheOptions: EChartsOption = {}; + // echart是否处于激活状态 + const isActiveRef = ref(false); const { isDark } = usePreferences(); const { height, width } = useWindowSize(); @@ -42,6 +54,11 @@ function useEcharts(chartRef: Ref) { return maybeComponent.$el ?? null; }; + onMounted(() => (isActiveRef.value = true)); + onActivated(() => (isActiveRef.value = true)); + onDeactivated(() => (isActiveRef.value = false)); + onBeforeUnmount(() => (isActiveRef.value = false)); + const isElHidden = (el: HTMLElement | null): boolean => { if (!el) return true; return el.offsetHeight === 0 || el.offsetWidth === 0; @@ -71,6 +88,9 @@ function useEcharts(chartRef: Ref) { options: EChartsOption, clear = true, ): Promise> => { + if (!unref(isActiveRef)) { + return Promise.resolve(null); + } cacheOptions = options; const currentOptions = { ...options, @@ -154,8 +174,8 @@ function useEcharts(chartRef: Ref) { useResizeObserver(chartRef as never, resizeHandler); - watch(isDark, () => { - if (chartInstance) { + watch([isDark, isActiveRef], () => { + if (chartInstance && unref(isActiveRef)) { chartInstance.dispose(); initCharts(); renderEcharts(cacheOptions); @@ -168,6 +188,7 @@ function useEcharts(chartRef: Ref) { chartInstance?.dispose(); }); return { + isActive: isActiveRef, renderEcharts, resize, updateData, diff --git a/playground/src/router/routes/modules/dashboard.ts b/playground/src/router/routes/modules/dashboard.ts index 5254dc65..7ef3cfb6 100644 --- a/playground/src/router/routes/modules/dashboard.ts +++ b/playground/src/router/routes/modules/dashboard.ts @@ -20,6 +20,7 @@ const routes: RouteRecordRaw[] = [ affixTab: true, icon: 'lucide:area-chart', title: $t('page.dashboard.analytics'), + keepAlive: true, }, }, {