Browse Source

fix: layout error

vben 9 months ago
parent
commit
640ad6d9e7
58 changed files with 670 additions and 671 deletions
  1. 1 1
      apps/web-antd/package.json
  2. 2 2
      internal/lint-configs/commitlint-config/package.json
  3. 1 1
      internal/lint-configs/prettier-config/package.json
  4. 1 1
      internal/vite-config/package.json
  5. 88 0
      packages/@core/forward/helpers/src/find-menu-by-path.test.ts
  6. 3 5
      packages/@core/forward/helpers/src/find-menu-by-path.ts
  7. 1 0
      packages/@core/forward/helpers/src/index.ts
  8. 1 1
      packages/@core/forward/preferences/package.json
  9. 3 3
      packages/@core/forward/preferences/src/config.ts
  10. 2 2
      packages/@core/forward/preferences/src/types.ts
  11. 5 3
      packages/@core/forward/preferences/src/use-preferences.ts
  12. 2 2
      packages/@core/shared/typings/src/app.d.ts
  13. 1 3
      packages/@core/uikit/layout-ui/package.json
  14. 2 2
      packages/@core/uikit/layout-ui/src/components/index.ts
  15. 3 3
      packages/@core/uikit/layout-ui/src/components/layout-content.vue
  16. 1 11
      packages/@core/uikit/layout-ui/src/components/layout-footer.vue
  17. 11 25
      packages/@core/uikit/layout-ui/src/components/layout-header.vue
  18. 62 101
      packages/@core/uikit/layout-ui/src/components/layout-sidebar.vue
  19. 0 3
      packages/@core/uikit/layout-ui/src/components/layout-tabbar.vue
  20. 2 2
      packages/@core/uikit/layout-ui/src/components/widgets/index.ts
  21. 0 63
      packages/@core/uikit/layout-ui/src/components/widgets/side-collapse-button.vue
  22. 0 64
      packages/@core/uikit/layout-ui/src/components/widgets/side-pin-button.vue
  23. 26 0
      packages/@core/uikit/layout-ui/src/components/widgets/sidebar-collapse-button.vue
  24. 26 0
      packages/@core/uikit/layout-ui/src/components/widgets/sidebar-fixed-button.vue
  25. 23 28
      packages/@core/uikit/layout-ui/src/vben-layout.ts
  26. 114 111
      packages/@core/uikit/layout-ui/src/vben-layout.vue
  27. 1 1
      packages/@core/uikit/menu-ui/package.json
  28. 1 1
      packages/@core/uikit/shadcn-ui/package.json
  29. 1 0
      packages/@core/uikit/shadcn-ui/src/components/index.ts
  30. 1 0
      packages/@core/uikit/shadcn-ui/src/components/scrollbar/index.ts
  31. 45 0
      packages/@core/uikit/shadcn-ui/src/components/scrollbar/scrollbar.vue
  32. 3 3
      packages/@core/uikit/shadcn-ui/src/components/sheet/sheet.vue
  33. 0 2
      packages/@core/uikit/tabs-ui/src/components/index.ts
  34. 1 1
      packages/@core/uikit/tabs-ui/src/index.ts
  35. 2 0
      packages/@core/uikit/tabs-ui/src/widgets/index.ts
  36. 0 0
      packages/@core/uikit/tabs-ui/src/widgets/tool-more.vue
  37. 0 0
      packages/@core/uikit/tabs-ui/src/widgets/tool-screen.vue
  38. 2 2
      packages/business/common-ui/package.json
  39. 3 3
      packages/business/common-ui/src/global-search/search-panel.vue
  40. 8 6
      packages/business/common-ui/src/notification/notification.vue
  41. 7 7
      packages/business/common-ui/src/preferences/blocks/layout/layout.vue
  42. 5 5
      packages/business/common-ui/src/preferences/blocks/layout/sidebar.vue
  43. 2 2
      packages/business/common-ui/src/preferences/icons/index.ts
  44. 0 0
      packages/business/common-ui/src/preferences/icons/sidebar-mixed-nav.vue
  45. 0 0
      packages/business/common-ui/src/preferences/icons/sidebar-nav.vue
  46. 7 7
      packages/business/common-ui/src/preferences/preferences-widget.vue
  47. 5 5
      packages/business/common-ui/src/preferences/preferences.vue
  48. 1 0
      packages/business/layouts/package.json
  49. 45 42
      packages/business/layouts/src/basic/layout.vue
  50. 5 10
      packages/business/layouts/src/basic/menu/mixed-menu.vue
  51. 7 7
      packages/business/layouts/src/basic/menu/use-extra-menu.ts
  52. 1 1
      packages/business/layouts/src/basic/menu/use-mixed-menu.ts
  53. 3 0
      packages/business/layouts/src/basic/tabbar/index.ts
  54. 3 3
      packages/business/layouts/src/basic/tabbar/tabbar-tools.vue
  55. 1 1
      packages/business/layouts/src/basic/tabbar/tabbar.vue
  56. 0 0
      packages/business/layouts/src/basic/tabbar/use-tabs.ts
  57. 0 3
      packages/business/layouts/src/basic/tabs/index.ts
  58. 129 122
      pnpm-lock.yaml

+ 1 - 1
apps/web-antd/package.json

@@ -35,7 +35,7 @@
     "@vben/styles": "workspace:*",
     "@vben/types": "workspace:*",
     "@vben/utils": "workspace:*",
-    "@vueuse/core": "^10.10.0",
+    "@vueuse/core": "^10.10.1",
     "ant-design-vue": "^4.2.3",
     "axios": "^1.7.2",
     "dayjs": "^1.11.11",

+ 2 - 2
internal/lint-configs/commitlint-config/package.json

@@ -34,7 +34,7 @@
     "@commitlint/cli": "^19.3.0",
     "@commitlint/config-conventional": "^19.2.2",
     "@vben/node-utils": "workspace:*",
-    "cz-git": "^1.9.2",
-    "czg": "^1.9.2"
+    "cz-git": "^1.9.3",
+    "czg": "^1.9.3"
   }
 }

+ 1 - 1
internal/lint-configs/prettier-config/package.json

@@ -32,6 +32,6 @@
   },
   "dependencies": {
     "prettier": "3.3.0",
-    "prettier-plugin-tailwindcss": "^0.6.2"
+    "prettier-plugin-tailwindcss": "^0.6.3"
   }
 }

+ 1 - 1
internal/vite-config/package.json

@@ -46,7 +46,7 @@
     "dayjs": "^1.11.11",
     "dotenv": "^16.4.5",
     "rollup-plugin-visualizer": "^5.12.0",
-    "sass": "^1.77.4",
+    "sass": "^1.77.5",
     "unplugin-turbo-console": "^1.8.6",
     "vite": "6.0.0-alpha.17",
     "vite-plugin-compression": "^0.5.1",

+ 88 - 0
packages/@core/forward/helpers/src/find-menu-by-path.test.ts

@@ -0,0 +1,88 @@
+import { describe, expect, it } from 'vitest';
+
+import { findMenuByPath, findRootMenuByPath } from './find-menu-by-path';
+
+// 示例菜单数据
+const menus: any[] = [
+  { path: '/', children: [] },
+  { path: '/about', children: [] },
+  {
+    path: '/contact',
+    children: [
+      { path: '/contact/email', children: [] },
+      { path: '/contact/phone', children: [] },
+    ],
+  },
+  {
+    path: '/services',
+    children: [
+      { path: '/services/design', children: [] },
+      {
+        path: '/services/development',
+        children: [{ path: '/services/development/web', children: [] }],
+      },
+    ],
+  },
+];
+
+describe('menu Finder Tests', () => {
+  it('finds a top-level menu', () => {
+    const menu = findMenuByPath(menus, '/about');
+    expect(menu).toBeDefined();
+    expect(menu?.path).toBe('/about');
+  });
+
+  it('finds a nested menu', () => {
+    const menu = findMenuByPath(menus, '/services/development/web');
+    expect(menu).toBeDefined();
+    expect(menu?.path).toBe('/services/development/web');
+  });
+
+  it('returns null for a non-existent path', () => {
+    const menu = findMenuByPath(menus, '/non-existent');
+    expect(menu).toBeNull();
+  });
+
+  it('handles empty menus list', () => {
+    const menu = findMenuByPath([], '/about');
+    expect(menu).toBeNull();
+  });
+
+  it('handles menu items without children', () => {
+    const menu = findMenuByPath(
+      [{ path: '/only', children: undefined }] as any[],
+      '/only',
+    );
+    expect(menu).toBeDefined();
+    expect(menu?.path).toBe('/only');
+  });
+
+  it('finds root menu by path', () => {
+    const { findMenu, rootMenu, rootMenuPath } = findRootMenuByPath(
+      menus,
+      '/services/development/web',
+    );
+
+    expect(findMenu).toBeDefined();
+    expect(rootMenu).toBeUndefined();
+    expect(rootMenuPath).toBeUndefined();
+    expect(findMenu?.path).toBe('/services/development/web');
+  });
+
+  it('returns null for undefined or empty path', () => {
+    const menuUndefinedPath = findMenuByPath(menus);
+    const menuEmptyPath = findMenuByPath(menus, '');
+    expect(menuUndefinedPath).toBeNull();
+    expect(menuEmptyPath).toBeNull();
+  });
+
+  it('checks for root menu when path does not exist', () => {
+    const { findMenu, rootMenu, rootMenuPath } = findRootMenuByPath(
+      menus,
+      '/non-existent',
+    );
+    expect(findMenu).toBeNull();
+    expect(rootMenu).toBeUndefined();
+    expect(rootMenuPath).toBeUndefined();
+  });
+});

+ 3 - 5
packages/business/layouts/src/basic/menu/helper.ts → packages/@core/forward/helpers/src/find-menu-by-path.ts

@@ -8,11 +8,9 @@ function findMenuByPath(
     if (menu.path === path) {
       return menu;
     }
-    if (menu?.children?.length) {
-      const findMenu = findMenuByPath(menu.children, path);
-      if (findMenu) {
-        return findMenu;
-      }
+    const findMenu = menu.children && findMenuByPath(menu.children, path);
+    if (findMenu) {
+      return findMenu;
     }
   }
   return null;

+ 1 - 0
packages/@core/forward/helpers/src/index.ts

@@ -1,3 +1,4 @@
+export * from './find-menu-by-path';
 export * from './flatten-object';
 export * from './generator-menus';
 export * from './generator-routes';

+ 1 - 1
packages/@core/forward/preferences/package.json

@@ -36,7 +36,7 @@
     "@vben-core/helpers": "workspace:*",
     "@vben-core/toolkit": "workspace:*",
     "@vben-core/typings": "workspace:*",
-    "@vueuse/core": "^10.10.0",
+    "@vueuse/core": "^10.10.1",
     "vue": "3.4.27"
   }
 }

+ 3 - 3
packages/@core/forward/preferences/src/config.ts

@@ -12,7 +12,7 @@ const defaultPreferences: Preferences = {
       'https://cdn.jsdelivr.net/npm/@vbenjs/static-source@0.1.0/source/avatar-v1.webp',
     dynamicTitle: true,
     isMobile: false,
-    layout: 'side-nav',
+    layout: 'sidebar-nav',
     locale: 'zh-CN',
     name: 'Vben Admin Pro',
     semiDarkMenu: true,
@@ -47,8 +47,8 @@ const defaultPreferences: Preferences = {
   },
   shortcutKeys: { enable: true },
   sidebar: {
-    collapse: false,
-    collapseShowTitle: true,
+    collapsed: false,
+    collapsedShowTitle: true,
     enable: true,
     expandOnHover: true,
     extraCollapse: true,

+ 2 - 2
packages/@core/forward/preferences/src/types.ts

@@ -94,9 +94,9 @@ interface NavigationPreferences {
 
 interface SidebarPreferences {
   /** 侧边栏是否折叠 */
-  collapse: boolean;
+  collapsed: boolean;
   /** 侧边栏折叠时,是否显示title */
-  collapseShowTitle: boolean;
+  collapsedShowTitle: boolean;
   /** 侧边栏是否可见 */
   enable: boolean;
   /** 菜单自动展开状态 */

+ 5 - 3
packages/@core/forward/preferences/src/use-preferences.ts

@@ -33,7 +33,7 @@ function usePreferences() {
    * @zh_CN 布局方式
    */
   const layout = computed(() =>
-    appPreferences.value.isMobile ? 'side-nav' : appPreferences.value.layout,
+    appPreferences.value.isMobile ? 'sidebar-nav' : appPreferences.value.layout,
   );
 
   /**
@@ -46,13 +46,15 @@ function usePreferences() {
   /**
    * @zh_CN 是否侧边导航模式
    */
-  const isSideNav = computed(() => appPreferences.value.layout === 'side-nav');
+  const isSideNav = computed(
+    () => appPreferences.value.layout === 'sidebar-nav',
+  );
 
   /**
    * @zh_CN 是否侧边混合模式
    */
   const isSideMixedNav = computed(
-    () => appPreferences.value.layout === 'side-mixed-nav',
+    () => appPreferences.value.layout === 'sidebar-mixed-nav',
   );
 
   /**

+ 2 - 2
packages/@core/shared/typings/src/app.d.ts

@@ -4,8 +4,8 @@ type LayoutType =
   | 'full-content'
   | 'header-nav'
   | 'mixed-nav'
-  | 'side-mixed-nav'
-  | 'side-nav';
+  | 'sidebar-mixed-nav'
+  | 'sidebar-nav';
 
 type ThemeModeType = 'auto' | 'dark' | 'light';
 

+ 1 - 3
packages/@core/uikit/layout-ui/package.json

@@ -41,12 +41,10 @@
     }
   },
   "dependencies": {
-    "@vben-core/design": "workspace:*",
     "@vben-core/iconify": "workspace:*",
     "@vben-core/shadcn-ui": "workspace:*",
-    "@vben-core/toolkit": "workspace:*",
     "@vben-core/typings": "workspace:*",
-    "@vueuse/core": "^10.10.0",
+    "@vueuse/core": "^10.10.1",
     "vue": "3.4.27"
   }
 }

+ 2 - 2
packages/@core/uikit/layout-ui/src/components/index.ts

@@ -1,5 +1,5 @@
 export { default as LayoutContent } from './layout-content.vue';
 export { default as LayoutFooter } from './layout-footer.vue';
 export { default as LayoutHeader } from './layout-header.vue';
-export { default as LayoutSide } from './layout-side.vue';
-export { default as LayoutTabs } from './layout-tabs.vue';
+export { default as LayoutSidebar } from './layout-sidebar.vue';
+export { default as LayoutTabbar } from './layout-tabbar.vue';

+ 3 - 3
packages/@core/uikit/layout-ui/src/components/layout-content.vue

@@ -42,8 +42,6 @@ interface Props {
   paddingTop?: number;
 }
 
-defineOptions({ name: 'LayoutContent' });
-
 const props = withDefaults(defineProps<Props>(), {
   contentCompact: 'wide',
   contentCompactWidth: 1200,
@@ -65,7 +63,9 @@ const style = computed((): CSSProperties => {
   } = props;
 
   const compactStyle: CSSProperties =
-    contentCompact === 'compact' ? { margin: '0 auto', width: `1200px` } : {};
+    contentCompact === 'compact'
+      ? { margin: '0 auto', width: `${props.contentCompactWidth}px` }
+      : {};
   return {
     ...compactStyle,
     flex: 1,

+ 1 - 11
packages/@core/uikit/layout-ui/src/components/layout-footer.vue

@@ -2,8 +2,6 @@
 import type { CSSProperties } from 'vue';
 import { computed } from 'vue';
 
-import { useNamespace } from '@vben-core/toolkit';
-
 interface Props {
   /**
    * 背景颜色
@@ -36,8 +34,6 @@ interface Props {
   zIndex?: number;
 }
 
-defineOptions({ name: 'LayoutFooter' });
-
 const props = withDefaults(defineProps<Props>(), {
   backgroundColor: 'hsl(var(--color-background))',
   fixed: true,
@@ -47,8 +43,6 @@ const props = withDefaults(defineProps<Props>(), {
   zIndex: 0,
 });
 
-const { b } = useNamespace('footer');
-
 const style = computed((): CSSProperties => {
   const { backgroundColor, fixed, height, show, width, zIndex } = props;
   return {
@@ -63,11 +57,7 @@ const style = computed((): CSSProperties => {
 </script>
 
 <template>
-  <footer
-    :class="b()"
-    :style="style"
-    class="bottom-0 w-full transition-all duration-200"
-  >
+  <footer :style="style" class="bottom-0 w-full transition-all duration-200">
     <slot></slot>
   </footer>
 </template>

+ 11 - 25
packages/@core/uikit/layout-ui/src/components/layout-header.vue

@@ -41,15 +41,12 @@ interface Props {
    * @default true
    */
   showToggleBtn?: boolean;
-  /**
-   * 侧边是否显示
-   */
-  sideHidden?: boolean;
+
   /**
    * 侧边菜单宽度
    * @default 0
    */
-  sideWidth?: number;
+  sidebarWidth?: number;
   /**
    * 宽度
    * @default 100%
@@ -62,8 +59,6 @@ interface Props {
   zIndex?: number;
 }
 
-defineOptions({ name: 'LayoutHeader' });
-
 const props = withDefaults(defineProps<Props>(), {
   backgroundColor: 'hsl(var(--color-background))',
   // fixed: true,
@@ -71,12 +66,12 @@ const props = withDefaults(defineProps<Props>(), {
   isMixedNav: false,
   show: true,
   showToggleBtn: false,
-  sideWidth: 0,
+  sidebarWidth: 0,
   width: '100%',
   zIndex: 0,
 });
 
-const emit = defineEmits<{ openMenu: []; toggleMenu: [] }>();
+const emit = defineEmits<{ openMenu: []; toggleSidebar: [] }>();
 
 const slots = useSlots();
 
@@ -95,16 +90,16 @@ const style = computed((): CSSProperties => {
 
 const logoStyle = computed((): CSSProperties => {
   return {
-    minWidth: `${props.isMobile ? 40 : props.sideWidth}px`,
+    minWidth: `${props.isMobile ? 40 : props.sidebarWidth}px`,
   };
 });
 
 function handleToggleMenu() {
-  emit('toggleMenu');
-}
-
-function handleOpenMenu() {
-  emit('openMenu');
+  if (props.isMobile) {
+    emit('openMenu');
+  } else {
+    emit('toggleSidebar');
+  }
 }
 </script>
 
@@ -117,21 +112,12 @@ function handleOpenMenu() {
       <slot name="logo"></slot>
     </div>
     <VbenIconButton
-      v-if="showToggleBtn"
+      v-if="showToggleBtn || isMobile"
       class="my-0 ml-2 mr-1 rounded"
       @click="handleToggleMenu"
     >
       <IcRoundMenu class="size-5" />
     </VbenIconButton>
-
-    <VbenIconButton
-      v-if="isMobile"
-      class="my-0 ml-2 mr-1 rounded"
-      @click="handleOpenMenu"
-    >
-      <IcRoundMenu class="size-5" />
-    </VbenIconButton>
-
     <slot></slot>
   </header>
 </template>

+ 62 - 101
packages/@core/uikit/layout-ui/src/components/layout-side.vue → packages/@core/uikit/layout-ui/src/components/layout-sidebar.vue

@@ -1,12 +1,10 @@
 <script setup lang="ts">
 import type { CSSProperties } from 'vue';
-// import { onClickOutside } from '@vueuse/core';
-import { computed, ref, shallowRef, useSlots, watchEffect } from 'vue';
+import { computed, shallowRef, useSlots, watchEffect } from 'vue';
 
-import { ScrollArea } from '@vben-core/shadcn-ui';
-import { useNamespace } from '@vben-core/toolkit';
+import { VbenScrollbar } from '@vben-core/shadcn-ui';
 
-import { SideCollapseButton, SidePinButton } from './widgets';
+import { SidebarCollapseButton, SidebarFixedButton } from './widgets';
 
 interface Props {
   /**
@@ -50,7 +48,7 @@ interface Props {
    * 是否侧边混合模式
    * @default false
    */
-  isSideMixed?: boolean;
+  isSidebarMixed?: boolean;
   /**
    * 混合菜单宽度
    * @default 80
@@ -88,8 +86,6 @@ interface Props {
   zIndex?: number;
 }
 
-defineOptions({ name: 'LayoutSide' });
-
 const props = withDefaults(defineProps<Props>(), {
   collapseHeight: 42,
   collapseWidth: 48,
@@ -113,11 +109,9 @@ const expandOnHovering = defineModel<boolean>('expandOnHovering');
 const expandOnHover = defineModel<boolean>('expandOnHover');
 const extraVisible = defineModel<boolean>('extraVisible');
 
-const { b, e, is } = useNamespace('side');
 const slots = useSlots();
 
 const asideRef = shallowRef<HTMLDivElement | null>();
-const scrolled = ref(false);
 
 const hiddenSideStyle = computed((): CSSProperties => {
   return calcMenuWidthStyle(true);
@@ -244,54 +238,51 @@ function handleMouseleave() {
   collapse.value = true;
   extraVisible.value = false;
 }
-
-function handleScroll(event: Event) {
-  const target = event.target as HTMLElement;
-  scrolled.value = (target?.scrollTop ?? 0) > 0;
-}
 </script>
 
 <template>
-  <div v-if="domVisible" :class="e('hide')" :style="hiddenSideStyle"></div>
+  <div
+    v-if="domVisible"
+    :style="hiddenSideStyle"
+    class="h-full transition-all duration-200"
+  ></div>
   <aside
-    :class="[b(), is(theme, true)]"
     :style="style"
+    class="fixed left-0 top-0 h-full transition-all duration-200"
     @mouseenter="handleMouseenter"
     @mouseleave="handleMouseleave"
   >
-    <SidePinButton
-      v-if="!collapse && !isSideMixed"
+    <SidebarFixedButton
+      v-if="!collapse && !isSidebarMixed"
       v-model:expand-on-hover="expandOnHover"
       :theme="theme"
     />
     <div v-if="slots.logo" :style="headerStyle">
       <slot name="logo"></slot>
     </div>
-    <ScrollArea :on-scroll="handleScroll" :style="contentStyle">
-      <div :class="[e('shadow'), { scrolled }]"></div>
+    <VbenScrollbar :style="contentStyle">
       <slot></slot>
-    </ScrollArea>
+    </VbenScrollbar>
 
     <div :style="collapseStyle"></div>
-    <SideCollapseButton
-      v-if="showCollapseButton && !isSideMixed"
-      v-model:collapse="collapse"
+    <SidebarCollapseButton
+      v-if="showCollapseButton && !isSidebarMixed"
+      v-model:collapsed="collapse"
       :theme="theme"
     />
     <div
-      v-if="isSideMixed"
+      v-if="isSidebarMixed"
       ref="asideRef"
-      :class="e('extra')"
       :style="extraStyle"
-      class="transition-[width] duration-200"
+      class="fixed top-0 h-full overflow-hidden transition-all duration-200"
     >
-      <SideCollapseButton
-        v-if="isSideMixed && expandOnHover"
-        v-model:collapse="extraCollapse"
+      <SidebarCollapseButton
+        v-if="isSidebarMixed && expandOnHover"
+        v-model:collapsed="extraCollapse"
         :theme="theme"
       />
 
-      <SidePinButton
+      <SidebarFixedButton
         v-if="!extraCollapse"
         v-model:expand-on-hover="expandOnHover"
         :theme="theme"
@@ -299,79 +290,49 @@ function handleScroll(event: Event) {
       <div v-if="!extraCollapse" :style="extraTitleStyle">
         <slot name="extra-title"></slot>
       </div>
-      <ScrollArea
-        :class="e('extra-content')"
-        :on-scroll="handleScroll"
-        :style="extraContentStyle"
-      >
-        <div :class="[e('shadow'), { scrolled }]"></div>
+      <VbenScrollbar :style="extraContentStyle" class="py-4">
         <slot name="extra"></slot>
-      </ScrollArea>
+      </VbenScrollbar>
     </div>
   </aside>
 </template>
 
 <style scoped lang="scss">
-@import '@vben-core/design/global';
-
-@include b('side') {
-  --color-surface: var(--color-menu);
-
-  position: fixed;
-  top: 0;
-  left: 0;
-  height: 100%;
-  transition: all 0.2s ease 0s;
-
-  @include is('dark') {
-    --color-surface: var(--color-menu-dark);
-  }
-
-  @include e('shadow') {
-    position: absolute;
-    top: 0;
-    z-index: 1;
-    inline-size: 100%;
-    block-size: 40px;
-    height: 50px;
-    pointer-events: none;
-    background: linear-gradient(
-      to bottom,
-      hsl(var(--color-surface)),
-      transparent
-    );
-    opacity: 0;
-    transition: opacity 0.15s ease-in-out;
-    will-change: opacity;
-
-    &.scrolled {
-      opacity: 1;
-    }
-  }
-
-  @include is('dark') {
-    .#{$namespace}-side__extra {
-      &-content {
-        border-color: hsl(var(--color-dark-border)) !important;
-      }
-    }
-  }
-
-  @include e('hide') {
-    height: 100%;
-    transition: all 0.2s ease 0s;
-  }
-
-  @include e('extra') {
-    position: fixed;
-    top: 0;
-    height: 100%;
-    overflow: hidden;
-    transition: all 0.2s ease 0s;
-
-    &-content {
-      padding: 4px 0;
-    }
-  }
-}
+// @include b('sidebar') {
+//   --color-surface: var(--color-menu);
+
+//   @include is('dark') {
+//     --color-surface: var(--color-menu-dark);
+//   }
+
+//   @include e('shadow') {
+//     position: absolute;
+//     top: 0;
+//     z-index: 1;
+//     inline-size: 100%;
+//     block-size: 40px;
+//     height: 50px;
+//     pointer-events: none;
+//     background: linear-gradient(
+//       to bottom,
+//       hsl(var(--color-surface)),
+//       transparent
+//     );
+//     opacity: 0;
+//     transition: opacity 0.15s ease-in-out;
+//     will-change: opacity;
+
+//     &.scrolled {
+//       opacity: 1;
+//     }
+//   }
+
+//   @include is('dark') {
+//     .#{$namespace}-side__extra {
+//       &-content {
+//         border-color: hsl(var(--color-dark-border)) !important;
+//       }
+//     }
+//   }
+// }
 </style>

+ 0 - 3
packages/@core/uikit/layout-ui/src/components/layout-tabs.vue → packages/@core/uikit/layout-ui/src/components/layout-tabbar.vue

@@ -14,8 +14,6 @@ interface Props {
   height?: number;
 }
 
-defineOptions({ name: 'LayoutTabs' });
-
 const props = withDefaults(defineProps<Props>(), {
   backgroundColor: 'hsl(var(--color-background))',
   fixed: true,
@@ -34,7 +32,6 @@ const style = computed((): CSSProperties => {
   return {
     ...hiddenStyle.value,
     backgroundColor,
-    display: 'flex',
   };
 });
 </script>

+ 2 - 2
packages/@core/uikit/layout-ui/src/components/widgets/index.ts

@@ -1,2 +1,2 @@
-export { default as SideCollapseButton } from './side-collapse-button.vue';
-export { default as SidePinButton } from './side-pin-button.vue';
+export { default as SidebarCollapseButton } from './sidebar-collapse-button.vue';
+export { default as SidebarFixedButton } from './sidebar-fixed-button.vue';

+ 0 - 63
packages/@core/uikit/layout-ui/src/components/widgets/side-collapse-button.vue

@@ -1,63 +0,0 @@
-<script setup lang="ts">
-import { MdiMenuClose, MdiMenuOpen } from '@vben-core/iconify';
-import { useNamespace } from '@vben-core/toolkit';
-
-interface Props {
-  theme: string;
-}
-
-defineOptions({ name: 'SideCollapseButton' });
-
-withDefaults(defineProps<Props>(), {});
-
-const collapse = defineModel<boolean>('collapse');
-
-const { b, is } = useNamespace('side-collapse');
-
-function handleCollapse() {
-  collapse.value = !collapse.value;
-}
-</script>
-
-<template>
-  <div :class="[b(), is(theme, true)]" @click.stop="handleCollapse">
-    <MdiMenuClose v-if="collapse" />
-    <MdiMenuOpen v-else />
-  </div>
-</template>
-
-<style scoped lang="scss">
-@import '@vben-core/design/global';
-
-@include b('side-collapse') {
-  position: absolute;
-  bottom: 6px;
-  left: 10px;
-  z-index: 1;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 5px;
-  color: hsl(var(--color-foreground) / 60%);
-  cursor: pointer;
-  background: hsl(var(--color-accent)) !important;
-  border-radius: 4px;
-  opacity: 1;
-  transition: all 0.3s ease;
-
-  @include is('dark') {
-    color: hsl(var(--color-dark-foreground) / 60%) !important;
-    background: hsl(var(--color-dark-accent)) !important;
-
-    &:hover {
-      color: hsl(var(--color-dark-foreground)) !important;
-      background: hsl(var(--color-dark-accent-hover)) !important;
-    }
-  }
-
-  &:hover {
-    color: hsl(var(--color-foreground));
-    background: hsl(var(--color-accent-hover));
-  }
-}
-</style>

+ 0 - 64
packages/@core/uikit/layout-ui/src/components/widgets/side-pin-button.vue

@@ -1,64 +0,0 @@
-<script setup lang="ts">
-import { MdiPin, MdiPinOff } from '@vben-core/iconify';
-import { useNamespace } from '@vben-core/toolkit';
-
-interface Props {
-  theme: string;
-}
-
-defineOptions({ name: 'SidePinButton' });
-
-withDefaults(defineProps<Props>(), {});
-
-const expandOnHover = defineModel<boolean>('expandOnHover');
-
-const { b, is } = useNamespace('side-pin');
-
-function togglePined() {
-  expandOnHover.value = !expandOnHover.value;
-}
-</script>
-
-<template>
-  <div :class="[b(), is(theme, true)]" @click="togglePined">
-    <MdiPinOff v-if="!expandOnHover" />
-    <MdiPin v-else />
-  </div>
-</template>
-
-<style scoped lang="scss">
-@import '@vben-core/design/global';
-
-@include b('side-pin') {
-  position: absolute;
-  right: 10px;
-  bottom: 6px;
-  z-index: 1;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 5px;
-  color: hsl(var(--color-foreground) / 60%);
-  cursor: pointer;
-  background: hsl(var(--color-accent)) !important;
-  border-radius: 4px;
-  opacity: 1;
-  transition: all 0.3s ease;
-
-  @include is('dark') {
-    color: hsl(var(--color-dark-foreground) / 60%) !important;
-    background: unset;
-    background: hsl(var(--color-dark-accent)) !important;
-
-    &:hover {
-      color: hsl(var(--color-dark-foreground)) !important;
-      background: hsl(var(--color-dark-accent-hover)) !important;
-    }
-  }
-
-  &:hover {
-    color: hsl(var(--color-foreground));
-    background: hsl(var(--color-accent-hover));
-  }
-}
-</style>

+ 26 - 0
packages/@core/uikit/layout-ui/src/components/widgets/sidebar-collapse-button.vue

@@ -0,0 +1,26 @@
+<script setup lang="ts">
+import { MdiMenuClose, MdiMenuOpen } from '@vben-core/iconify';
+
+interface Props {
+  theme: string;
+}
+
+withDefaults(defineProps<Props>(), {});
+
+const collapsed = defineModel<boolean>('collapsed');
+
+function handleCollapsed() {
+  collapsed.value = !collapsed.value;
+}
+</script>
+
+<template>
+  <div
+    :data-theme="theme"
+    class="flex-center hover:text-foreground text-foreground/60 hover:bg-accent-hover bg-accent absolute bottom-2 left-3 z-10 cursor-pointer rounded-sm p-1 transition-all duration-300 data-[theme=dark]:bg-[hsl(var(--color-dark-accent))] data-[theme=dark]:text-[hsl(var(--color-dark-foreground)/60%)] data-[theme=dark]:hover:bg-[hsl(var(--color-dark-accent-hover))] data-[theme=dark]:hover:text-[hsl(var(--color-dark-foreground))]"
+    @click.stop="handleCollapsed"
+  >
+    <MdiMenuClose v-if="collapsed" />
+    <MdiMenuOpen v-else />
+  </div>
+</template>

+ 26 - 0
packages/@core/uikit/layout-ui/src/components/widgets/sidebar-fixed-button.vue

@@ -0,0 +1,26 @@
+<script setup lang="ts">
+import { MdiPin, MdiPinOff } from '@vben-core/iconify';
+
+interface Props {
+  theme: string;
+}
+
+withDefaults(defineProps<Props>(), {});
+
+const expandOnHover = defineModel<boolean>('expandOnHover');
+
+function toggleFixed() {
+  expandOnHover.value = !expandOnHover.value;
+}
+</script>
+
+<template>
+  <div
+    :data-theme="theme"
+    class="flex-center hover:text-foreground text-foreground/60 hover:bg-accent-hover bg-accent absolute bottom-2 right-3 z-10 cursor-pointer rounded-sm p-1 transition-all duration-300 data-[theme=dark]:bg-[hsl(var(--color-dark-accent))] data-[theme=dark]:text-[hsl(var(--color-dark-foreground)/60%)] data-[theme=dark]:hover:bg-[hsl(var(--color-dark-accent-hover))] data-[theme=dark]:hover:text-[hsl(var(--color-dark-foreground))]"
+    @click="toggleFixed"
+  >
+    <MdiPinOff v-if="!expandOnHover" />
+    <MdiPin v-else />
+  </div>
+</template>

+ 23 - 28
packages/@core/uikit/layout-ui/src/vben-layout.ts

@@ -99,64 +99,64 @@ interface VbenLayoutProps {
   isMobile?: boolean;
   /**
    * 布局方式
-   * side-nav 侧边菜单布局
+   * sidebar-nav 侧边菜单布局
    * header-nav 顶部菜单布局
    * mixed-nav 侧边&顶部菜单布局
-   * side-mixed-nav 侧边混合菜单布局
+   * sidebar-mixed-nav 侧边混合菜单布局
    * full-content 全屏内容布局
-   * @default side-nav
+   * @default sidebar-nav
    */
   layout?: LayoutType;
+  /**
+   *  侧边菜单折叠宽度
+   * @default 48
+   */
+  sideCollapseWidth?: number;
   /**
    * 侧边菜单折叠状态
    * @default false
    */
-  sideCollapse?: boolean;
+  sidebarCollapse?: boolean;
   /**
    * 侧边菜单是否折叠时,是否显示title
    * @default true
    */
-  sideCollapseShowTitle?: boolean;
+  sidebarCollapseShowTitle?: boolean;
   /**
-   *  侧边菜单折叠宽度
-   * @default 48
+   * 侧边栏是否可见
+   * @default true
    */
-  sideCollapseWidth?: number;
+  sidebarEnable?: boolean;
   /**
    * 侧边栏是否隐藏
    * @default false
    */
-  sideHidden?: boolean;
-  /**
-   * 混合侧边扩展区域是否可见
-   * @default false
-   */
-  sideMixedExtraVisible?: boolean;
+  sidebarHidden?: boolean;
   /**
    * 混合侧边栏宽度
    * @default 80
    */
-  sideMixedWidth?: number;
+  sidebarMixedWidth?: number;
   /**
    * 侧边栏是否半深色
    * @default false
    */
-  sideSemiDark?: boolean;
+  sidebarSemiDark?: boolean;
   /**
    * 侧边栏
    * @default dark
    */
-  sideTheme?: ThemeModeType;
-  /**
-   * 侧边栏是否可见
-   * @default true
-   */
-  sideVisible?: boolean;
+  sidebarTheme?: ThemeModeType;
   /**
    * 侧边栏宽度
    * @default 210
    */
-  sideWidth?: number;
+  sidebarWidth?: number;
+  /**
+   * tab是否可见
+   * @default true
+   */
+  tabbarEnable?: boolean;
   /**
    * footer背景颜色
    * @default #fff
@@ -167,11 +167,6 @@ interface VbenLayoutProps {
    * @default 30
    */
   tabsHeight?: number;
-  /**
-   * tab是否可见
-   * @default true
-   */
-  tabsVisible?: boolean;
   /**
    * zIndex
    * @default 100

+ 114 - 111
packages/@core/uikit/layout-ui/src/vben-layout.vue

@@ -8,8 +8,8 @@ import {
   LayoutContent,
   LayoutFooter,
   LayoutHeader,
-  LayoutSide,
-  LayoutTabs,
+  LayoutSidebar,
+  LayoutTabbar,
 } from './components';
 import { VbenLayoutProps } from './vben-layout';
 
@@ -38,27 +38,27 @@ const props = withDefaults(defineProps<Props>(), {
   headerMode: 'fixed',
   headerVisible: true,
   isMobile: false,
-  layout: 'side-nav',
-  sideCollapseShowTitle: false,
+  layout: 'sidebar-nav',
   // sideCollapse: false,
   sideCollapseWidth: 60,
-  sideHidden: false,
-  sideMixedWidth: 80,
-  sideSemiDark: true,
-  sideTheme: 'dark',
-  sideWidth: 180,
+  sidebarCollapseShowTitle: false,
+  sidebarHidden: false,
+  sidebarMixedWidth: 80,
+  sidebarSemiDark: true,
+  sidebarTheme: 'dark',
+  sidebarWidth: 180,
+  tabbarEnable: true,
   // tabsBackgroundColor: 'hsl(var(--color-background))',
   tabsHeight: 36,
-  tabsVisible: true,
   zIndex: 200,
 });
 
-const emit = defineEmits<{ sideMouseLeave: [] }>();
-const sideCollapse = defineModel<boolean>('sideCollapse');
-const sideExtraVisible = defineModel<boolean>('sideExtraVisible');
-const sideExtraCollapse = defineModel<boolean>('sideExtraCollapse');
-const sideExpandOnHover = defineModel<boolean>('sideExpandOnHover');
-const sideVisible = defineModel<boolean>('sideVisible', { default: true });
+const emit = defineEmits<{ sideMouseLeave: []; toggleSidebar: [] }>();
+const sidebarCollapse = defineModel<boolean>('sidebarCollapse');
+const sidebarExtraVisible = defineModel<boolean>('sidebarExtraVisible');
+const sidebarExtraCollapse = defineModel<boolean>('sidebarExtraCollapse');
+const sidebarExpandOnHover = defineModel<boolean>('sidebarExpandOnHover');
+const sidebarEnable = defineModel<boolean>('sidebarEnable', { default: true });
 
 const {
   arrivedState,
@@ -69,12 +69,12 @@ const {
 const { y: mouseY } = useMouse({ type: 'client' });
 
 // side是否处于hover状态展开菜单中
-const sideExpandOnHovering = ref(false);
+const sidebarExpandOnHovering = ref(false);
 // const sideHidden = ref(false);
 const headerIsHidden = ref(false);
 
 const realLayout = computed(() => {
-  return props.isMobile ? 'side-nav' : props.layout;
+  return props.isMobile ? 'sidebar-nav' : props.layout;
 });
 
 /**
@@ -85,7 +85,9 @@ const fullContent = computed(() => realLayout.value === 'full-content');
 /**
  * 是否侧边混合模式
  */
-const isSideMixedNav = computed(() => realLayout.value === 'side-mixed-nav');
+const isSidebarMixedNav = computed(
+  () => realLayout.value === 'sidebar-mixed-nav',
+);
 
 /**
  * 是否为头部导航模式
@@ -123,25 +125,25 @@ const headerWrapperHeight = computed(() => {
   if (props.headerVisible && !props.headerHidden) {
     height += getHeaderHeight.value;
   }
-  if (props.tabsVisible) {
+  if (props.tabbarEnable) {
     height += props.tabsHeight;
   }
-
   return height;
 });
 
 const getSideCollapseWidth = computed(() => {
-  const { sideCollapseShowTitle, sideCollapseWidth, sideMixedWidth } = props;
-  return sideCollapseShowTitle || isSideMixedNav
-    ? sideMixedWidth
+  const { sideCollapseWidth, sidebarCollapseShowTitle, sidebarMixedWidth } =
+    props;
+  return sidebarCollapseShowTitle || isSidebarMixedNav.value
+    ? sidebarMixedWidth
     : sideCollapseWidth;
 });
 
 /**
  * 动态获取侧边区域是否可见
  */
-const sideVisibleState = computed(() => {
-  return !isHeaderNav.value && sideVisible.value;
+const sidebarEnableState = computed(() => {
+  return !isHeaderNav.value && sidebarEnable.value;
 });
 
 /**
@@ -155,27 +157,27 @@ const sidePaddingTop = computed(() => {
 /**
  * 动态获取侧边宽度
  */
-const getSideWidth = computed(() => {
-  const { isMobile, sideHidden, sideMixedWidth, sideWidth } = props;
+const getSidebarWidth = computed(() => {
+  const { isMobile, sidebarHidden, sidebarMixedWidth, sidebarWidth } = props;
   let width = 0;
 
-  if (sideHidden) {
+  if (sidebarHidden) {
     return width;
   }
 
   if (
-    !sideVisibleState.value ||
-    (sideHidden && !isSideMixedNav.value && !isMixedNav.value)
+    !sidebarEnableState.value ||
+    (sidebarHidden && !isSidebarMixedNav.value && !isMixedNav.value)
   ) {
     return width;
   }
 
-  if (isSideMixedNav.value && !isMobile) {
-    width = sideMixedWidth;
-  } else if (sideCollapse.value) {
+  if (isSidebarMixedNav.value && !isMobile) {
+    width = sidebarMixedWidth;
+  } else if (sidebarCollapse.value) {
     width = isMobile ? 0 : getSideCollapseWidth.value;
   } else {
-    width = sideWidth;
+    width = sidebarWidth;
   }
   return width;
 });
@@ -184,37 +186,37 @@ const getSideWidth = computed(() => {
  * 获取扩展区域宽度
  */
 const getExtraWidth = computed(() => {
-  const { sideWidth } = props;
-  return sideExtraCollapse.value ? getSideCollapseWidth.value : sideWidth;
+  const { sidebarWidth } = props;
+  return sidebarExtraCollapse.value ? getSideCollapseWidth.value : sidebarWidth;
 });
 
 /**
  * 是否侧边栏模式,包含混合侧边
  */
 const isSideMode = computed(() =>
-  ['mixed-nav', 'side-mixed-nav', 'side-nav'].includes(realLayout.value),
+  ['mixed-nav', 'sidebar-mixed-nav', 'sidebar-nav'].includes(realLayout.value),
 );
 
-const showSide = computed(() => {
+const showSidebar = computed(() => {
   // if (isMixedNav.value && !props.sideHidden) {
   //   return false;
   // }
-  return isSideMode.value && sideVisible.value;
+  return isSideMode.value && sidebarEnable.value;
 });
 
-const sideFace = computed(() => {
-  const { sideSemiDark, sideTheme } = props;
-  const isDark = sideTheme === 'dark' || sideSemiDark;
+const sidebarFace = computed(() => {
+  const { sidebarSemiDark, sidebarTheme } = props;
+  const isDark = sidebarTheme === 'dark' || sidebarSemiDark;
 
   let backgroundColor = '';
   let extraBackgroundColor = '';
 
   if (isDark) {
-    backgroundColor = isSideMixedNav.value
+    backgroundColor = isSidebarMixedNav.value
       ? 'hsl(var(--color-menu-dark-darken))'
       : 'hsl(var(--color-menu-dark))';
   } else {
-    backgroundColor = isSideMixedNav.value
+    backgroundColor = isSidebarMixedNav.value
       ? 'hsl(var(--color-menu-darken))'
       : 'hsl(var(--color-menu))';
   }
@@ -233,7 +235,7 @@ const sideFace = computed(() => {
 /**
  * 遮罩可见性
  */
-const maskVisible = computed(() => !sideCollapse.value && props.isMobile);
+const maskVisible = computed(() => !sidebarCollapse.value && props.isMobile);
 
 /**
  * header fixed值
@@ -247,53 +249,55 @@ const headerFixed = computed(() => {
 
 const mainStyle = computed(() => {
   let width = '100%';
-  let sidebarWidth = 'unset';
+  let sidebarAndExtraWidth = 'unset';
   if (
     headerFixed.value &&
     !['header-nav', 'mixed-nav'].includes(realLayout.value) &&
-    showSide.value &&
+    showSidebar.value &&
     !props.isMobile
   ) {
-    // pin模式下生效
+    // fixed模式下生效
     const isSideNavEffective =
-      isSideMixedNav.value && sideExpandOnHover.value && sideExtraVisible.value;
+      isSidebarMixedNav.value &&
+      sidebarExpandOnHover.value &&
+      sidebarExtraVisible.value;
 
     if (isSideNavEffective) {
-      const sideCollapseWidth = sideCollapse.value
+      const sideCollapseWidth = sidebarCollapse.value
         ? getSideCollapseWidth.value
-        : props.sideMixedWidth;
-      const sideWidth = sideExtraCollapse.value
+        : props.sidebarMixedWidth;
+      const sideWidth = sidebarExtraCollapse.value
         ? getSideCollapseWidth.value
-        : props.sideWidth;
+        : props.sidebarWidth;
 
       // 100% - 侧边菜单混合宽度 - 菜单宽度
-      sidebarWidth = `${sideCollapseWidth + sideWidth}px`;
-      width = `calc(100% - ${sidebarWidth})`;
+      sidebarAndExtraWidth = `${sideCollapseWidth + sideWidth}px`;
+      width = `calc(100% - ${sidebarAndExtraWidth})`;
     } else {
-      sidebarWidth =
-        sideExpandOnHovering.value && !sideExpandOnHover.value
+      sidebarAndExtraWidth =
+        sidebarExpandOnHovering.value && !sidebarExpandOnHover.value
           ? `${getSideCollapseWidth.value}px`
-          : `${getSideWidth.value}px`;
-      width = `calc(100% - ${sidebarWidth})`;
+          : `${getSidebarWidth.value}px`;
+      width = `calc(100% - ${sidebarAndExtraWidth})`;
     }
   }
   return {
-    sidebarWidth,
+    sidebarAndExtraWidth,
     width,
   };
 });
 
-const tabsStyle = computed((): CSSProperties => {
+const tabbarStyle = computed((): CSSProperties => {
   let width = '';
   let marginLeft = 0;
 
   if (!isMixedNav.value) {
     width = '100%';
-  } else if (sideVisible.value) {
-    marginLeft = sideCollapse.value
+  } else if (sidebarEnable.value) {
+    marginLeft = sidebarCollapse.value
       ? getSideCollapseWidth.value
-      : props.sideWidth;
-    width = `calc(100% - ${getSideWidth.value}px)`;
+      : props.sidebarWidth;
+    width = `calc(100% - ${getSidebarWidth.value}px)`;
   } else {
     width = '100%';
   }
@@ -304,14 +308,6 @@ const tabsStyle = computed((): CSSProperties => {
   };
 });
 
-const footerWidth = computed(() => {
-  if (!props.footerFixed) {
-    return '100%';
-  }
-
-  return mainStyle.value.width;
-});
-
 const contentStyle = computed((): CSSProperties => {
   const fixed = headerFixed.value;
 
@@ -337,7 +333,7 @@ const headerWrapperStyle = computed((): CSSProperties => {
   const fixed = headerFixed.value;
   return {
     height: fullContent.value ? '0' : `${headerWrapperHeight.value}px`,
-    left: isMixedNav.value ? 0 : mainStyle.value.sidebarWidth,
+    left: isMixedNav.value ? 0 : mainStyle.value.sidebarAndExtraWidth,
     position: fixed ? 'fixed' : 'static',
     top:
       headerIsHidden.value || fullContent.value
@@ -351,26 +347,31 @@ const headerWrapperStyle = computed((): CSSProperties => {
 /**
  * 侧边栏z-index
  */
-const sideZIndex = computed(() => {
+const sidebarZIndex = computed(() => {
   const { isMobile, zIndex } = props;
   const offset = isMobile || isSideMode.value ? 1 : -1;
   return zIndex + offset;
 });
 
+const footerWidth = computed(() => {
+  if (!props.footerFixed) {
+    return '100%';
+  }
+
+  return mainStyle.value.width;
+});
+
 const maskStyle = computed((): CSSProperties => {
-  return {
-    zIndex: props.zIndex,
-  };
+  return { zIndex: props.zIndex };
 });
 
 const showHeaderToggleButton = computed(() => {
   return (
     isSideMode.value &&
-    !isSideMixedNav.value &&
+    !isSidebarMixedNav.value &&
     !isMixedNav.value &&
     !props.isMobile
   );
-  // return false;
 });
 
 const showHeaderLogo = computed(() => {
@@ -380,7 +381,7 @@ const showHeaderLogo = computed(() => {
 watch(
   () => props.isMobile,
   (val) => {
-    sideCollapse.value = val;
+    sidebarCollapse.value = val;
   },
 );
 
@@ -445,49 +446,48 @@ watch(
 }
 
 function handleClickMask() {
-  sideCollapse.value = true;
+  sidebarCollapse.value = true;
 }
 
-function handleToggleMenu() {
-  // sideVisible.value = !sideVisible.value;
-  // sideHidden.value = !sideHidden.value;
+function handleToggleSidebar() {
+  emit('toggleSidebar');
 }
 
 function handleOpenMenu() {
-  sideCollapse.value = false;
+  sidebarCollapse.value = false;
 }
 </script>
 
 <template>
   <div class="relative flex min-h-full w-full">
     <slot name="preferences"></slot>
-    <slot name="floating-button-group"></slot>
-    <LayoutSide
-      v-if="sideVisibleState"
-      v-model:collapse="sideCollapse"
-      v-model:expand-on-hover="sideExpandOnHover"
-      v-model:expand-on-hovering="sideExpandOnHovering"
-      v-model:extra-collapse="sideExtraCollapse"
-      v-model:extra-visible="sideExtraVisible"
+    <slot name="floating-groups"></slot>
+    <LayoutSidebar
+      v-if="sidebarEnableState"
+      v-model:collapse="sidebarCollapse"
+      v-model:expand-on-hover="sidebarExpandOnHover"
+      v-model:expand-on-hovering="sidebarExpandOnHovering"
+      v-model:extra-collapse="sidebarExtraCollapse"
+      v-model:extra-visible="sidebarExtraVisible"
       :collapse-width="getSideCollapseWidth"
       :dom-visible="!isMobile"
       :extra-width="getExtraWidth"
-      :fixed-extra="sideExpandOnHover"
+      :fixed-extra="sidebarExpandOnHover"
       :header-height="isMixedNav ? 0 : getHeaderHeight"
-      :is-side-mixed="isSideMixedNav"
-      :mixed-width="sideMixedWidth"
+      :is-sidebar-mixed="isSidebarMixedNav"
+      :mixed-width="sidebarMixedWidth"
       :padding-top="sidePaddingTop"
-      :show="showSide"
-      :width="getSideWidth"
-      :z-index="sideZIndex"
-      v-bind="sideFace"
+      :show="showSidebar"
+      :width="getSidebarWidth"
+      :z-index="sidebarZIndex"
+      v-bind="sidebarFace"
       @leave="() => emit('sideMouseLeave')"
     >
       <template v-if="isSideMode && !isMixedNav" #logo>
         <slot name="logo"></slot>
       </template>
 
-      <template v-if="isSideMixedNav">
+      <template v-if="isSidebarMixedNav">
         <slot name="mixed-menu"></slot>
       </template>
       <template v-else>
@@ -500,7 +500,7 @@ function handleOpenMenu() {
       <template #extra-title>
         <slot name="side-extra-title"></slot>
       </template>
-    </LayoutSide>
+    </LayoutSidebar>
 
     <div
       class="flex flex-1 flex-col overflow-hidden transition-all duration-300 ease-in"
@@ -517,12 +517,11 @@ function handleOpenMenu() {
           :is-mobile="isMobile"
           :show="!fullContent && !headerHidden"
           :show-toggle-btn="showHeaderToggleButton"
-          :side-hidden="sideHidden"
-          :side-width="sideWidth"
+          :sidebar-width="sidebarWidth"
           :width="mainStyle.width"
           :z-index="headerZIndex"
           @open-menu="handleOpenMenu"
-          @toggle-menu="handleToggleMenu"
+          @toggle-sidebar="handleToggleSidebar"
         >
           <template v-if="showHeaderLogo" #logo>
             <slot name="logo"></slot>
@@ -530,12 +529,16 @@ function handleOpenMenu() {
           <slot name="header"></slot>
         </LayoutHeader>
 
-        <LayoutTabs v-if="tabsVisible" :height="tabsHeight" :style="tabsStyle">
-          <slot name="tabs"></slot>
+        <LayoutTabbar
+          v-if="tabbarEnable"
+          :height="tabsHeight"
+          :style="tabbarStyle"
+        >
+          <slot name="tabbar"></slot>
           <template #toolbar>
-            <slot name="tabs-toolbar"></slot>
+            <slot name="tabbar-tools"></slot>
           </template>
-        </LayoutTabs>
+        </LayoutTabbar>
       </div>
 
       <!-- </div> -->

+ 1 - 1
packages/@core/uikit/menu-ui/package.json

@@ -46,7 +46,7 @@
     "@vben-core/shadcn-ui": "workspace:*",
     "@vben-core/toolkit": "workspace:*",
     "@vben-core/typings": "workspace:*",
-    "@vueuse/core": "^10.10.0",
+    "@vueuse/core": "^10.10.1",
     "vue": "3.4.27"
   }
 }

+ 1 - 1
packages/@core/uikit/shadcn-ui/package.json

@@ -50,7 +50,7 @@
     "@vben-core/iconify": "workspace:*",
     "@vben-core/toolkit": "workspace:*",
     "@vben-core/typings": "workspace:*",
-    "@vueuse/core": "^10.10.0",
+    "@vueuse/core": "^10.10.1",
     "class-variance-authority": "^0.7.0",
     "clsx": "2.1.1",
     "radix-vue": "^1.8.3",

+ 1 - 0
packages/@core/uikit/shadcn-ui/src/components/index.ts

@@ -17,6 +17,7 @@ export * from './logo';
 export * from './menu-badge';
 export * from './pin-input';
 export * from './popover';
+export * from './scrollbar';
 export * from './segmented';
 export * from './sheet';
 export * from './spinner';

+ 1 - 0
packages/@core/uikit/shadcn-ui/src/components/scrollbar/index.ts

@@ -0,0 +1 @@
+export { default as VbenScrollbar } from './scrollbar.vue';

+ 45 - 0
packages/@core/uikit/shadcn-ui/src/components/scrollbar/scrollbar.vue

@@ -0,0 +1,45 @@
+<script setup lang="ts">
+import type { HTMLAttributes } from 'vue';
+import { ref } from 'vue';
+
+import { ScrollArea } from '#/components/ui/scroll-area';
+import { cn } from '#/lib/utils';
+
+interface Props {
+  class?: HTMLAttributes['class'];
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  class: '',
+});
+
+const isAtTop = ref(true);
+const isAtBottom = ref(false);
+
+function handleScroll(event: Event) {
+  const target = event.target as HTMLElement;
+  const scrollTop = target?.scrollTop ?? 0;
+  const offsetHeight = target?.offsetHeight ?? 0;
+  const scrollHeight = target?.scrollHeight ?? 0;
+  isAtTop.value = scrollTop <= 0;
+  isAtBottom.value = scrollTop + offsetHeight >= scrollHeight;
+}
+</script>
+
+<template>
+  <ScrollArea
+    :class="[
+      cn(props.class),
+      {
+        // 'shadow-none': isAtTop && isAtBottom,
+        // shadow: !isAtTop || !isAtBottom,
+        // 'dark:shadow-white/20': !isAtTop || !isAtBottom,
+        // 'shadow-inner': !isAtBottom,
+        // 'dark:shadow-inner-white/20': !isAtBottom,
+      },
+    ]"
+    :on-scroll="handleScroll"
+  >
+    <slot></slot>
+  </ScrollArea>
+</template>

+ 3 - 3
packages/@core/uikit/shadcn-ui/src/components/sheet/sheet.vue

@@ -4,7 +4,7 @@ import { computed, useSlots } from 'vue';
 import { Cross2Icon } from '@radix-icons/vue';
 
 import { VbenButton, VbenIconButton } from '#/components/button';
-import { ScrollArea } from '#/components/ui/scroll-area';
+import { VbenScrollbar } from '#/components/scrollbar';
 import {
   Sheet,
   SheetClose,
@@ -89,9 +89,9 @@ function handlerSubmit() {
         </SheetClose>
       </SheetHeader>
       <div class="h-full pb-16">
-        <ScrollArea class="h-full">
+        <VbenScrollbar class="h-full">
           <slot></slot>
-        </ScrollArea>
+        </VbenScrollbar>
       </div>
       <SheetFooter v-if="showFooter || slots.footer" as-child>
         <div

+ 0 - 2
packages/@core/uikit/tabs-ui/src/components/index.ts

@@ -1,3 +1 @@
 export { default as ChromeTabs } from './chrome-tabs/tabs.vue';
-export { default as TabsMore } from './tabs-more.vue';
-export { default as TabsScreen } from './tabs-screen.vue';

+ 1 - 1
packages/@core/uikit/tabs-ui/src/index.ts

@@ -1,3 +1,3 @@
-export { TabsMore, TabsScreen } from './components';
 export { default as TabsView } from './tabs-view.vue';
+export * from './widgets';
 export type { IContextMenuItem } from '@vben-core/shadcn-ui';

+ 2 - 0
packages/@core/uikit/tabs-ui/src/widgets/index.ts

@@ -0,0 +1,2 @@
+export { default as TabsToolMore } from './tool-more.vue';
+export { default as TabsToolScreen } from './tool-screen.vue';

+ 0 - 0
packages/@core/uikit/tabs-ui/src/components/tabs-more.vue → packages/@core/uikit/tabs-ui/src/widgets/tool-more.vue


+ 0 - 0
packages/@core/uikit/tabs-ui/src/components/tabs-screen.vue → packages/@core/uikit/tabs-ui/src/widgets/tool-screen.vue


+ 2 - 2
packages/business/common-ui/package.json

@@ -50,8 +50,8 @@
     "@vben-core/shadcn-ui": "workspace:*",
     "@vben-core/toolkit": "workspace:*",
     "@vben/locales": "workspace:*",
-    "@vueuse/core": "^10.10.0",
-    "@vueuse/integrations": "^10.10.0",
+    "@vueuse/core": "^10.10.1",
+    "@vueuse/integrations": "^10.10.1",
     "qrcode": "^1.5.3",
     "vue": "3.4.27",
     "vue-router": "^4.3.3"

+ 3 - 3
packages/business/common-ui/src/global-search/search-panel.vue

@@ -6,7 +6,7 @@ import { useRouter } from 'vue-router';
 
 import { $t } from '@vben/locales';
 import { IcRoundClose, IcRoundSearchOff } from '@vben-core/iconify';
-import { ScrollArea, VbenIcon } from '@vben-core/shadcn-ui';
+import { VbenIcon, VbenScrollbar } from '@vben-core/shadcn-ui';
 import { mapTree, traverseTreeValues } from '@vben-core/toolkit';
 
 import { onKeyStroke, useLocalStorage, useThrottleFn } from '@vueuse/core';
@@ -214,7 +214,7 @@ onMounted(() => {
 </script>
 
 <template>
-  <ScrollArea>
+  <VbenScrollbar>
     <div class="!flex h-full justify-center px-4 sm:max-h-[450px]">
       <!-- 无搜索结果 -->
       <div
@@ -275,5 +275,5 @@ onMounted(() => {
         </li>
       </ul>
     </div>
-  </ScrollArea>
+  </VbenScrollbar>
 </template>

+ 8 - 6
packages/business/common-ui/src/notification/notification.vue

@@ -7,10 +7,10 @@ import {
   IcRoundNotificationsNone,
 } from '@vben-core/iconify';
 import {
-  ScrollArea,
   VbenButton,
   VbenIconButton,
   VbenPopover,
+  VbenScrollbar,
 } from '@vben-core/shadcn-ui';
 
 import { useToggle } from '@vueuse/core';
@@ -81,7 +81,7 @@ function handleClick(item: NotificationItem) {
     </template>
 
     <div class="relative">
-      <div class="flex items-center justify-between border-b p-4 py-3">
+      <div class="flex items-center justify-between p-4 py-3">
         <div class="text-foreground">{{ $t('widgets.notifications') }}</div>
         <VbenIconButton
           :tooltip="$t('widgets.make-all-as-read')"
@@ -90,11 +90,11 @@ function handleClick(item: NotificationItem) {
           <IcRoundMarkEmailRead />
         </VbenIconButton>
       </div>
-      <ScrollArea v-if="notifications.length > 0">
+      <VbenScrollbar v-if="notifications.length > 0">
         <ul class="!flex max-h-[360px] w-full flex-col">
           <template v-for="item in notifications" :key="item.title">
             <li
-              class="hover:bg-accent relative flex w-full cursor-pointer items-start gap-5 border-t px-3 py-3"
+              class="hover:bg-accent border-border relative flex w-full cursor-pointer items-start gap-5 border-t px-3 py-3"
               @click="handleClick(item)"
             >
               <span
@@ -123,7 +123,7 @@ function handleClick(item: NotificationItem) {
             </li>
           </template>
         </ul>
-      </ScrollArea>
+      </VbenScrollbar>
 
       <template v-else>
         <div class="flex-center text-muted-foreground min-h-[150px] w-full">
@@ -131,7 +131,9 @@ function handleClick(item: NotificationItem) {
         </div>
       </template>
 
-      <div class="flex items-center justify-between border-t px-4 py-3">
+      <div
+        class="border-border flex items-center justify-between border-t px-4 py-3"
+      >
         <VbenButton size="sm" variant="ghost" @click="handleClear">
           {{ $t('widgets.clear-notifications') }}
         </VbenButton>

+ 7 - 7
packages/business/common-ui/src/preferences/blocks/layout/layout.vue

@@ -11,8 +11,8 @@ import {
   FullContent,
   HeaderNav,
   MixedNav,
-  SideMixedNav,
-  SideNav,
+  SidebarMixedNav,
+  SidebarNav,
 } from '../../icons';
 
 interface PresetItem {
@@ -25,26 +25,26 @@ defineOptions({
   name: 'PreferenceLayout',
 });
 
-const modelValue = defineModel<LayoutType>({ default: 'side-nav' });
+const modelValue = defineModel<LayoutType>({ default: 'sidebar-nav' });
 
 const components: Record<LayoutType, Component> = {
   'full-content': FullContent,
   'header-nav': HeaderNav,
   'mixed-nav': MixedNav,
-  'side-mixed-nav': SideMixedNav,
-  'side-nav': SideNav,
+  'sidebar-mixed-nav': SidebarMixedNav,
+  'sidebar-nav': SidebarNav,
 };
 
 const PRESET = computed((): PresetItem[] => [
   {
     name: $t('preference.vertical'),
     tip: $t('preference.vertical-tip'),
-    type: 'side-nav',
+    type: 'sidebar-nav',
   },
   {
     name: $t('preference.two-column'),
     tip: $t('preference.two-column-tip'),
-    type: 'side-mixed-nav',
+    type: 'sidebar-mixed-nav',
   },
   {
     name: $t('preference.horizontal'),

+ 5 - 5
packages/business/common-ui/src/preferences/blocks/layout/sidebar.vue

@@ -10,21 +10,21 @@ defineOptions({
 defineProps<{ disabled: boolean }>();
 
 const sidebarEnable = defineModel<boolean>('sidebarEnable');
-const sidebarCollapseShowTitle = defineModel<boolean>(
-  'sidebarCollapseShowTitle',
+const sidebarCollapsedShowTitle = defineModel<boolean>(
+  'sidebarCollapsedShowTitle',
 );
-const sidebarCollapse = defineModel<boolean>('sidebarCollapse');
+const sidebarCollapsed = defineModel<boolean>('sidebarCollapsed');
 </script>
 
 <template>
   <SwitchItem v-model="sidebarEnable" :disabled="disabled">
     {{ $t('preference.side-visible') }}
   </SwitchItem>
-  <SwitchItem v-model="sidebarCollapse" :disabled="!sidebarEnable || disabled">
+  <SwitchItem v-model="sidebarCollapsed" :disabled="!sidebarEnable || disabled">
     {{ $t('preference.collapse') }}
   </SwitchItem>
   <SwitchItem
-    v-model="sidebarCollapseShowTitle"
+    v-model="sidebarCollapsedShowTitle"
     :disabled="!sidebarEnable || disabled"
   >
     {{ $t('preference.collapse-show-title') }}

+ 2 - 2
packages/business/common-ui/src/preferences/icons/index.ts

@@ -3,8 +3,8 @@ import HeaderNav from './header-nav.vue';
 export { default as ContentCompact } from './content-compact.vue';
 export { default as FullContent } from './full-content.vue';
 export { default as MixedNav } from './mixed-nav.vue';
-export { default as SideMixedNav } from './side-mixed-nav.vue';
-export { default as SideNav } from './side-nav.vue';
+export { default as SidebarMixedNav } from './sidebar-mixed-nav.vue';
+export { default as SidebarNav } from './sidebar-nav.vue';
 
 const ContentWide = HeaderNav;
 export { ContentWide, HeaderNav };

+ 0 - 0
packages/business/common-ui/src/preferences/icons/side-mixed-nav.vue → packages/business/common-ui/src/preferences/icons/sidebar-mixed-nav.vue


+ 0 - 0
packages/business/common-ui/src/preferences/icons/side-nav.vue → packages/business/common-ui/src/preferences/icons/sidebar-nav.vue


+ 7 - 7
packages/business/common-ui/src/preferences/preferences-widget.vue

@@ -22,7 +22,7 @@ import Preferences from './preferences.vue';
     :breadcrumb-hide-only-one="preferences.breadcrumb.hideOnlyOne"
     :breadcrumb-home="preferences.breadcrumb.showHome"
     :breadcrumb-icon="preferences.breadcrumb.showIcon"
-    :breadcrumb-style="preferences.breadcrumb.styleType"
+    :breadcrumb-style-type="preferences.breadcrumb.styleType"
     :color-primary-presets="COLOR_PRIMARY_RESETS"
     :footer-enable="preferences.footer.enable"
     :footer-fixed="preferences.footer.fixed"
@@ -32,8 +32,8 @@ import Preferences from './preferences.vue';
     :navigation-split="preferences.navigation.split"
     :navigation-style-type="preferences.navigation.styleType"
     :shortcut-keys-enable="preferences.shortcutKeys.enable"
-    :sidebar-collapse="preferences.sidebar.collapse"
-    :sidebar-collapse-show-title="preferences.sidebar.collapseShowTitle"
+    :sidebar-collapsed="preferences.sidebar.collapsed"
+    :sidebar-collapsed-show-title="preferences.sidebar.collapsedShowTitle"
     :sidebar-enable="preferences.sidebar.enable"
     :tabbar-enable="preferences.tabbar.enable"
     :tabbar-show-icon="preferences.tabbar.showIcon"
@@ -103,11 +103,11 @@ import Preferences from './preferences.vue';
     @update:shortcut-keys-enable="
       (val) => updatePreferences({ shortcutKeys: { enable: val } })
     "
-    @update:sidebar-collapse="
-      (val) => updatePreferences({ sidebar: { collapse: val } })
+    @update:sidebar-collapsed="
+      (val) => updatePreferences({ sidebar: { collapsed: val } })
     "
-    @update:sidebar-collapse-show-title="
-      (val) => updatePreferences({ sidebar: { collapseShowTitle: val } })
+    @update:sidebar-collapsed-show-title="
+      (val) => updatePreferences({ sidebar: { collapsedShowTitle: val } })
     "
     @update:sidebar-enable="
       (val) => updatePreferences({ sidebar: { enable: val } })

+ 5 - 5
packages/business/common-ui/src/preferences/preferences.vue

@@ -70,9 +70,9 @@ const transitionEnable = defineModel<boolean>('transitionEnable');
 const themeColorPrimary = defineModel<string>('themeColorPrimary');
 
 const sidebarEnable = defineModel<boolean>('sidebarEnable');
-const sidebarCollapse = defineModel<boolean>('sidebarCollapse');
-const sidebarCollapseShowTitle = defineModel<boolean>(
-  'sidebarCollapseShowTitle',
+const sidebarCollapsed = defineModel<boolean>('sidebarCollapsed');
+const sidebarCollapsedShowTitle = defineModel<boolean>(
+  'sidebarCollapsedShowTitle',
 );
 
 const headerEnable = defineModel<boolean>('headerEnable');
@@ -216,8 +216,8 @@ function handleReset() {
 
             <Block :title="$t('preference.sidebar')">
               <Sidebar
-                v-model:side-collapse-show-title="sidebarCollapseShowTitle"
-                v-model:sidebar-collapse="sidebarCollapse"
+                v-model:sidebar-collapsed="sidebarCollapsed"
+                v-model:sidebar-collapsed-show-title="sidebarCollapsedShowTitle"
                 v-model:sidebar-enable="sidebarEnable"
                 :disabled="!isSideMode"
               />

+ 1 - 0
packages/business/layouts/package.json

@@ -41,6 +41,7 @@
     }
   },
   "dependencies": {
+    "@vben-core/helpers": "workspace:*",
     "@vben-core/iconify": "workspace:*",
     "@vben-core/layout-ui": "workspace:*",
     "@vben-core/menu-ui": "workspace:*",

+ 45 - 42
packages/business/layouts/src/basic/layout.vue

@@ -9,11 +9,7 @@ import {
   updatePreferences,
   usePreferences,
 } from '@vben-core/preferences';
-import {
-  VbenBackTop,
-  // VbenFloatingButtonGroup,
-  VbenLogo,
-} from '@vben-core/shadcn-ui';
+import { VbenBackTop, VbenLogo } from '@vben-core/shadcn-ui';
 import { mapTree } from '@vben-core/toolkit';
 import { MenuRecordRaw } from '@vben-core/typings';
 
@@ -27,7 +23,7 @@ import {
   useExtraMenu,
   useMixedMenu,
 } from './menu';
-import { LayoutTabs, LayoutTabsToolbar } from './tabs';
+import { LayoutTabbar, LayoutTabbarTools } from './tabbar';
 import { Breadcrumb } from './widgets';
 
 defineOptions({ name: 'BasicLayout' });
@@ -45,8 +41,8 @@ const theme = computed(() => {
 });
 
 const logoClass = computed(() => {
-  const { collapse, collapseShowTitle } = preferences.sidebar;
-  return collapseShowTitle && collapse && !isMixedNav.value ? 'mx-auto' : '';
+  const { collapsed, collapsedShowTitle } = preferences.sidebar;
+  return collapsedShowTitle && collapsed && !isMixedNav.value ? 'mx-auto' : '';
 });
 
 const isMenuRounded = computed(() => {
@@ -59,12 +55,12 @@ const logoCollapse = computed(() => {
   }
 
   const { isMobile } = preferences.app;
-  const { collapse } = preferences.sidebar;
+  const { collapsed } = preferences.sidebar;
 
-  if (!collapse && isMobile) {
+  if (!collapsed && isMobile) {
     return false;
   }
-  return collapse || isSideMixedNav.value;
+  return collapsed || isSideMixedNav.value;
 });
 
 const showHeaderNav = computed(() => {
@@ -74,11 +70,11 @@ const showHeaderNav = computed(() => {
 const {
   extraActiveMenu,
   extraMenus,
-  extraVisible,
   handleDefaultSelect,
   handleMenuMouseEnter,
   handleMixedMenuSelect,
   handleSideMouseLeave,
+  sidebarExtraVisible,
 } = useExtraMenu();
 
 const {
@@ -92,17 +88,22 @@ const {
 
 function wrapperMenus(menus: MenuRecordRaw[]) {
   return mapTree(menus, (item) => {
-    return {
-      ...item,
-      name: $t(item.name),
-    };
+    return { ...item, name: $t(item.name) };
+  });
+}
+
+function toggleSidebar() {
+  updatePreferences({
+    sidebar: {
+      hidden: !preferences.sidebar.hidden,
+    },
   });
 }
 </script>
 
 <template>
   <VbenAdminLayout
-    v-model:side-extra-visible="extraVisible"
+    v-model:sidebar-extra-visible="sidebarExtraVisible"
     :content-compact="preferences.app.contentCompact"
     :footer-enable="preferences.footer.enable"
     :footer-fixed="preferences.footer.fixed"
@@ -111,37 +112,38 @@ function wrapperMenus(menus: MenuRecordRaw[]) {
     :header-visible="preferences.header.enable"
     :is-mobile="preferences.app.isMobile"
     :layout="layout"
-    :side-collapse="preferences.sidebar.collapse"
-    :side-collapse-show-title="preferences.sidebar.collapseShowTitle"
-    :side-expand-on-hover="preferences.sidebar.expandOnHover"
-    :side-extra-collapse="preferences.sidebar.extraCollapse"
-    :side-hidden="preferences.sidebar.hidden"
-    :side-semi-dark="preferences.app.semiDarkMenu"
-    :side-theme="theme"
-    :side-visible="sideVisible"
-    :side-width="preferences.sidebar.width"
-    :tabs-visible="preferences.tabbar.enable"
+    :sidebar-collapse="preferences.sidebar.collapsed"
+    :sidebar-collapse-show-title="preferences.sidebar.collapsedShowTitle"
+    :sidebar-enable="sideVisible"
+    :sidebar-expand-on-hover="preferences.sidebar.expandOnHover"
+    :sidebar-extra-collapse="preferences.sidebar.extraCollapse"
+    :sidebar-hidden="preferences.sidebar.hidden"
+    :sidebar-semi-dark="preferences.app.semiDarkMenu"
+    :sidebar-theme="theme"
+    :sidebar-width="preferences.sidebar.width"
+    :tabbar-enable="preferences.tabbar.enable"
     @side-mouse-leave="handleSideMouseLeave"
-    @update:side-collapse="
-      (value: boolean) => updatePreferences({ sidebar: { collapse: value } })
+    @toggle-sidebar="toggleSidebar"
+    @update:sidebar-collapse="
+      (value: boolean) => updatePreferences({ sidebar: { collapsed: value } })
     "
-    @update:side-expand-on-hover="
+    @update:sidebar-enable="
+      (value: boolean) => updatePreferences({ sidebar: { enable: value } })
+    "
+    @update:sidebar-expand-on-hover="
       (value: boolean) =>
         updatePreferences({ sidebar: { expandOnHover: value } })
     "
-    @update:side-extra-collapse="
+    @update:sidebar-extra-collapse="
       (value: boolean) =>
         updatePreferences({ sidebar: { extraCollapse: value } })
     "
-    @update:side-visible="
-      (value: boolean) => updatePreferences({ sidebar: { enable: value } })
-    "
   >
     <template v-if="preferences.app.showPreference" #preferences>
       <PreferencesWidget />
     </template>
 
-    <template #floating-button-group>
+    <template #floating-groups>
       <VbenBackTop />
       <!-- <VbenFloatingButtonGroup /> -->
     </template>
@@ -194,8 +196,8 @@ function wrapperMenus(menus: MenuRecordRaw[]) {
     <template #menu>
       <LayoutMenu
         :accordion="preferences.navigation.accordion"
-        :collapse="preferences.sidebar.collapse"
-        :collapse-show-title="preferences.sidebar.collapseShowTitle"
+        :collapse="preferences.sidebar.collapsed"
+        :collapse-show-title="preferences.sidebar.collapsedShowTitle"
         :default-active="sideActive"
         :menus="wrapperMenus(sideMenus)"
         :rounded="isMenuRounded"
@@ -207,7 +209,8 @@ function wrapperMenus(menus: MenuRecordRaw[]) {
     <template #mixed-menu>
       <LayoutMixedMenu
         :active-path="extraActiveMenu"
-        :collapse="!preferences.sidebar.collapseShowTitle"
+        :collapse="!preferences.sidebar.collapsedShowTitle"
+        :menus="wrapperMenus(headerMenus)"
         :rounded="isMenuRounded"
         :theme="theme"
         @default-select="handleDefaultSelect"
@@ -234,14 +237,14 @@ function wrapperMenus(menus: MenuRecordRaw[]) {
       />
     </template>
 
-    <template #tabs>
-      <LayoutTabs
+    <template #tabbar>
+      <LayoutTabbar
         v-if="preferences.tabbar.enable"
         :show-icon="preferences.tabbar.showIcon"
       />
     </template>
-    <template #tabs-toolbar>
-      <LayoutTabsToolbar v-if="preferences.tabbar.enable" />
+    <template #tabbar-tools>
+      <LayoutTabbarTools v-if="preferences.tabbar.enable" />
     </template>
 
     <!-- 主体内容 -->

+ 5 - 10
packages/business/layouts/src/basic/menu/mixed-menu.vue

@@ -2,17 +2,15 @@
 import type { NormalMenuProps } from '@vben-core/menu-ui';
 import type { MenuRecordRaw } from '@vben-core/typings';
 
-import { computed, onBeforeMount } from 'vue';
+import { onBeforeMount } from 'vue';
 import { useRoute } from 'vue-router';
 
+import { findMenuByPath } from '@vben-core/helpers';
 import { NormalMenu } from '@vben-core/menu-ui';
-import { useAccessStore } from '@vben-core/stores';
-
-import { findMenuByPath } from './helper';
 
 interface Props extends NormalMenuProps {}
 
-defineProps<Props>();
+const props = defineProps<Props>();
 
 const emit = defineEmits<{
   defaultSelect: [MenuRecordRaw, MenuRecordRaw?];
@@ -20,19 +18,16 @@ const emit = defineEmits<{
   select: [MenuRecordRaw];
 }>();
 
-const accessStore = useAccessStore();
 const route = useRoute();
 
-const menus = computed(() => accessStore.getAccessMenus);
-
 function handleSelect(menu: MenuRecordRaw) {
   emit('select', menu);
 }
 
 onBeforeMount(() => {
-  const menu = findMenuByPath(menus.value, route.path);
+  const menu = findMenuByPath(props.menus || [], route.path);
   if (menu) {
-    const rootMenu = menus.value.find(
+    const rootMenu = (props.menus || []).find(
       (item) => item.path === menu.parents?.[0],
     );
     emit('defaultSelect', menu, rootMenu);

+ 7 - 7
packages/business/layouts/src/basic/menu/use-extra-menu.ts

@@ -3,10 +3,10 @@ import type { MenuRecordRaw } from '@vben-core/typings';
 import { computed, ref } from 'vue';
 import { useRoute } from 'vue-router';
 
+import { findRootMenuByPath } from '@vben-core/helpers';
 import { preferences } from '@vben-core/preferences';
 import { useAccessStore } from '@vben-core/stores';
 
-import { findRootMenuByPath } from './helper';
 import { useNavigation } from './use-navigation';
 
 function useExtraMenu() {
@@ -17,7 +17,7 @@ function useExtraMenu() {
 
   const route = useRoute();
   const extraMenus = ref<MenuRecordRaw[]>([]);
-  const extraVisible = ref<boolean>(false);
+  const sidebarExtraVisible = ref<boolean>(false);
   const extraActiveMenu = ref('');
 
   /**
@@ -29,7 +29,7 @@ function useExtraMenu() {
     extraActiveMenu.value = menu.parents?.[0] ?? menu.path;
     const hasChildren = extraMenus.value.length > 0;
 
-    extraVisible.value = hasChildren;
+    sidebarExtraVisible.value = hasChildren;
     if (!hasChildren) {
       await navigation(menu.path);
     }
@@ -48,7 +48,7 @@ function useExtraMenu() {
     extraActiveMenu.value = menu.parents?.[0] ?? menu.path;
 
     if (preferences.sidebar.expandOnHover) {
-      extraVisible.value = extraMenus.value.length > 0;
+      sidebarExtraVisible.value = extraMenus.value.length > 0;
     }
   };
 
@@ -59,7 +59,7 @@ function useExtraMenu() {
     if (preferences.sidebar.expandOnHover) {
       return;
     }
-    extraVisible.value = false;
+    sidebarExtraVisible.value = false;
 
     const { findMenu, rootMenu, rootMenuPath } = findRootMenuByPath(
       menus.value,
@@ -74,18 +74,18 @@ function useExtraMenu() {
       const { findMenu } = findRootMenuByPath(menus.value, menu.path);
       extraMenus.value = findMenu?.children ?? [];
       extraActiveMenu.value = menu.parents?.[0] ?? menu.path;
-      extraVisible.value = extraMenus.value.length > 0;
+      sidebarExtraVisible.value = extraMenus.value.length > 0;
     }
   };
 
   return {
     extraActiveMenu,
     extraMenus,
-    extraVisible,
     handleDefaultSelect,
     handleMenuMouseEnter,
     handleMixedMenuSelect,
     handleSideMouseLeave,
+    sidebarExtraVisible,
   };
 }
 

+ 1 - 1
packages/business/layouts/src/basic/menu/use-mixed-menu.ts

@@ -3,10 +3,10 @@ import type { MenuRecordRaw } from '@vben-core/typings';
 import { computed, onBeforeMount, ref } from 'vue';
 import { useRoute } from 'vue-router';
 
+import { findRootMenuByPath } from '@vben-core/helpers';
 import { preferences, usePreferences } from '@vben-core/preferences';
 import { useAccessStore } from '@vben-core/stores';
 
-import { findRootMenuByPath } from './helper';
 import { useNavigation } from './use-navigation';
 
 function useMixedMenu() {

+ 3 - 0
packages/business/layouts/src/basic/tabbar/index.ts

@@ -0,0 +1,3 @@
+export { default as LayoutTabbar } from './tabbar.vue';
+export { default as LayoutTabbarTools } from './tabbar-tools.vue';
+export * from './use-tabs';

+ 3 - 3
packages/business/layouts/src/basic/tabs/tabs-toolbar.vue → packages/business/layouts/src/basic/tabbar/tabbar-tools.vue

@@ -3,7 +3,7 @@ import { computed } from 'vue';
 import { useRoute } from 'vue-router';
 
 import { preferences, updatePreferences } from '@vben-core/preferences';
-import { TabsMore, TabsScreen } from '@vben-core/tabs-ui';
+import { TabsToolMore, TabsToolScreen } from '@vben-core/tabs-ui';
 
 import { useTabs } from './use-tabs';
 
@@ -29,8 +29,8 @@ function handleScreenChange(screen: boolean) {
 
 <template>
   <div class="flex-center h-full">
-    <TabsMore :menus="menus" />
-    <TabsScreen
+    <TabsToolMore :menus="menus" />
+    <TabsToolScreen
       :screen="preferences.sidebar.hidden"
       @change="handleScreenChange"
       @update:screen="handleScreenChange"

+ 1 - 1
packages/business/layouts/src/basic/tabs/tabs.vue → packages/business/layouts/src/basic/tabbar/tabbar.vue

@@ -4,7 +4,7 @@ import { TabsView } from '@vben-core/tabs-ui';
 import { useTabs } from './use-tabs';
 
 defineOptions({
-  name: 'LayoutTabs',
+  name: 'LayoutTabbar',
 });
 
 defineProps<{ showIcon?: boolean }>();

+ 0 - 0
packages/business/layouts/src/basic/tabs/use-tabs.ts → packages/business/layouts/src/basic/tabbar/use-tabs.ts


+ 0 - 3
packages/business/layouts/src/basic/tabs/index.ts

@@ -1,3 +0,0 @@
-export { default as LayoutTabs } from './tabs.vue';
-export { default as LayoutTabsToolbar } from './tabs-toolbar.vue';
-export * from './use-tabs';

+ 129 - 122
pnpm-lock.yaml

@@ -81,13 +81,13 @@ importers:
         version: 5.4.5
       unbuild:
         specifier: ^2.0.0
-        version: 2.0.0(sass@1.77.4)(typescript@5.4.5)(vue-tsc@2.0.21(typescript@5.4.5))
+        version: 2.0.0(sass@1.77.5)(typescript@5.4.5)(vue-tsc@2.0.21(typescript@5.4.5))
       vite:
         specifier: 6.0.0-alpha.17
-        version: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+        version: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
       vitest:
         specifier: ^2.0.0-beta.3
-        version: 2.0.0-beta.3(@types/node@20.14.2)(jsdom@24.1.0)(sass@1.77.4)(terser@5.31.0)
+        version: 2.0.0-beta.3(@types/node@20.14.2)(jsdom@24.1.0)(sass@1.77.5)(terser@5.31.0)
       vue-tsc:
         specifier: ^2.0.21
         version: 2.0.21(typescript@5.4.5)
@@ -134,8 +134,8 @@ importers:
         specifier: workspace:*
         version: link:../../packages/utils
       '@vueuse/core':
-        specifier: ^10.10.0
-        version: 10.10.0(vue@3.4.27(typescript@5.4.5))
+        specifier: ^10.10.1
+        version: 10.10.1(vue@3.4.27(typescript@5.4.5))
       ant-design-vue:
         specifier: ^4.2.3
         version: 4.2.3(vue@3.4.27(typescript@5.4.5))
@@ -157,7 +157,7 @@ importers:
     devDependencies:
       vite-plugin-mock:
         specifier: ^3.0.2
-        version: 3.0.2(esbuild@0.20.2)(mockjs@1.1.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))
+        version: 3.0.2(esbuild@0.20.2)(mockjs@1.1.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))
 
   internal/lint-configs/commitlint-config:
     dependencies:
@@ -171,11 +171,11 @@ importers:
         specifier: workspace:*
         version: link:../../node-utils
       cz-git:
-        specifier: ^1.9.2
-        version: 1.9.2
+        specifier: ^1.9.3
+        version: 1.9.3
       czg:
-        specifier: ^1.9.2
-        version: 1.9.2
+        specifier: ^1.9.3
+        version: 1.9.3
 
   internal/lint-configs/eslint-config:
     dependencies:
@@ -236,7 +236,7 @@ importers:
         version: 4.0.0(@typescript-eslint/eslint-plugin@7.13.0(@typescript-eslint/parser@7.13.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)
       eslint-plugin-vitest:
         specifier: ^0.5.4
-        version: 0.5.4(@typescript-eslint/eslint-plugin@7.13.0(@typescript-eslint/parser@7.13.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)(vitest@2.0.0-beta.3(@types/node@20.14.2)(jsdom@24.1.0)(sass@1.77.4)(terser@5.31.0))
+        version: 0.5.4(@typescript-eslint/eslint-plugin@7.13.0(@typescript-eslint/parser@7.13.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)(vitest@2.0.0-beta.3(@types/node@20.14.2)(jsdom@24.1.0)(sass@1.77.5)(terser@5.31.0))
       eslint-plugin-vue:
         specifier: ^9.26.0
         version: 9.26.0(eslint@8.57.0)
@@ -262,8 +262,8 @@ importers:
         specifier: 3.3.0
         version: 3.3.0
       prettier-plugin-tailwindcss:
-        specifier: ^0.6.2
-        version: 0.6.2(prettier@3.3.0)
+        specifier: ^0.6.3
+        version: 0.6.3(prettier@3.3.0)
 
   internal/lint-configs/stylelint-config:
     dependencies:
@@ -394,7 +394,7 @@ importers:
         version: link:../../packages/types
       vite:
         specifier: 6.0.0-alpha.17
-        version: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+        version: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
 
   internal/vite-config:
     dependencies:
@@ -415,10 +415,10 @@ importers:
         version: 2.0.2
       vite-plugin-lib-inject-css:
         specifier: ^2.1.1
-        version: 2.1.1(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))
+        version: 2.1.1(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))
       vite-plugin-vue-devtools:
         specifier: ^7.2.1
-        version: 7.2.1(rollup@4.18.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))
+        version: 7.2.1(rollup@4.18.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))
     devDependencies:
       '@types/html-minifier-terser':
         specifier: ^7.0.2
@@ -428,10 +428,10 @@ importers:
         version: link:../node-utils
       '@vitejs/plugin-vue':
         specifier: ^5.0.5
-        version: 5.0.5(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))
+        version: 5.0.5(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))
       '@vitejs/plugin-vue-jsx':
         specifier: ^4.0.0
-        version: 4.0.0(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))
+        version: 4.0.0(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))
       dayjs:
         specifier: ^1.11.11
         version: 1.11.11
@@ -442,26 +442,26 @@ importers:
         specifier: ^5.12.0
         version: 5.12.0(rollup@4.18.0)
       sass:
-        specifier: ^1.77.4
-        version: 1.77.4
+        specifier: ^1.77.5
+        version: 1.77.5
       unplugin-turbo-console:
         specifier: ^1.8.6
-        version: 1.8.6(esbuild@0.20.2)(rollup@4.18.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))
+        version: 1.8.6(esbuild@0.20.2)(rollup@4.18.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))
       vite:
         specifier: 6.0.0-alpha.17
-        version: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+        version: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
       vite-plugin-compression:
         specifier: ^0.5.1
-        version: 0.5.1(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))
+        version: 0.5.1(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))
       vite-plugin-dts:
         specifier: ^3.9.1
-        version: 3.9.1(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.4.5)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))
+        version: 3.9.1(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.4.5)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))
       vite-plugin-html:
         specifier: ^3.2.2
-        version: 3.2.2(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))
+        version: 3.2.2(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))
       vite-plugin-mock:
         specifier: ^3.0.2
-        version: 3.0.2(esbuild@0.20.2)(mockjs@1.1.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))
+        version: 3.0.2(esbuild@0.20.2)(mockjs@1.1.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))
 
   packages/@core/forward/helpers:
     dependencies:
@@ -490,8 +490,8 @@ importers:
         specifier: workspace:*
         version: link:../../shared/typings
       '@vueuse/core':
-        specifier: ^10.10.0
-        version: 10.10.0(vue@3.4.27(typescript@5.4.5))
+        specifier: ^10.10.1
+        version: 10.10.1(vue@3.4.27(typescript@5.4.5))
       vue:
         specifier: 3.4.27
         version: 3.4.27(typescript@5.4.5)
@@ -585,24 +585,18 @@ importers:
 
   packages/@core/uikit/layout-ui:
     dependencies:
-      '@vben-core/design':
-        specifier: workspace:*
-        version: link:../../shared/design
       '@vben-core/iconify':
         specifier: workspace:*
         version: link:../../shared/iconify
       '@vben-core/shadcn-ui':
         specifier: workspace:*
         version: link:../shadcn-ui
-      '@vben-core/toolkit':
-        specifier: workspace:*
-        version: link:../../shared/toolkit
       '@vben-core/typings':
         specifier: workspace:*
         version: link:../../shared/typings
       '@vueuse/core':
-        specifier: ^10.10.0
-        version: 10.10.0(vue@3.4.27(typescript@5.4.5))
+        specifier: ^10.10.1
+        version: 10.10.1(vue@3.4.27(typescript@5.4.5))
       vue:
         specifier: 3.4.27
         version: 3.4.27(typescript@5.4.5)
@@ -625,8 +619,8 @@ importers:
         specifier: workspace:*
         version: link:../../shared/typings
       '@vueuse/core':
-        specifier: ^10.10.0
-        version: 10.10.0(vue@3.4.27(typescript@5.4.5))
+        specifier: ^10.10.1
+        version: 10.10.1(vue@3.4.27(typescript@5.4.5))
       vue:
         specifier: 3.4.27
         version: 3.4.27(typescript@5.4.5)
@@ -646,8 +640,8 @@ importers:
         specifier: workspace:*
         version: link:../../shared/typings
       '@vueuse/core':
-        specifier: ^10.10.0
-        version: 10.10.0(vue@3.4.27(typescript@5.4.5))
+        specifier: ^10.10.1
+        version: 10.10.1(vue@3.4.27(typescript@5.4.5))
       class-variance-authority:
         specifier: ^0.7.0
         version: 0.7.0
@@ -709,11 +703,11 @@ importers:
         specifier: workspace:*
         version: link:../../locales
       '@vueuse/core':
-        specifier: ^10.10.0
-        version: 10.10.0(vue@3.4.27(typescript@5.4.5))
+        specifier: ^10.10.1
+        version: 10.10.1(vue@3.4.27(typescript@5.4.5))
       '@vueuse/integrations':
-        specifier: ^10.10.0
-        version: 10.10.0(async-validator@4.2.5)(axios@1.7.2)(focus-trap@7.5.4)(nprogress@0.2.0)(qrcode@1.5.3)(vue@3.4.27(typescript@5.4.5))
+        specifier: ^10.10.1
+        version: 10.10.1(async-validator@4.2.5)(axios@1.7.2)(focus-trap@7.5.4)(nprogress@0.2.0)(qrcode@1.5.3)(vue@3.4.27(typescript@5.4.5))
       qrcode:
         specifier: ^1.5.3
         version: 1.5.3
@@ -733,6 +727,9 @@ importers:
 
   packages/business/layouts:
     dependencies:
+      '@vben-core/helpers':
+        specifier: workspace:*
+        version: link:../../@core/forward/helpers
       '@vben-core/iconify':
         specifier: workspace:*
         version: link:../../@core/shared/iconify
@@ -852,7 +849,7 @@ importers:
     devDependencies:
       vitepress:
         specifier: ^1.2.3
-        version: 1.2.3(@algolia/client-search@4.23.3)(@types/node@20.14.2)(async-validator@4.2.5)(axios@1.7.2)(nprogress@0.2.0)(postcss@8.4.38)(qrcode@1.5.3)(sass@1.77.4)(search-insights@2.14.0)(terser@5.31.0)(typescript@5.4.5)
+        version: 1.2.3(@algolia/client-search@4.23.3)(@types/node@20.14.2)(async-validator@4.2.5)(axios@1.7.2)(nprogress@0.2.0)(postcss@8.4.38)(qrcode@1.5.3)(sass@1.77.5)(search-insights@2.14.0)(terser@5.31.0)(typescript@5.4.5)
       vue:
         specifier: 3.4.27
         version: 3.4.27(typescript@5.4.5)
@@ -2682,11 +2679,11 @@ packages:
   '@vue/test-utils@2.4.6':
     resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==}
 
-  '@vueuse/core@10.10.0':
-    resolution: {integrity: sha512-vexJ/YXYs2S42B783rI95lMt3GzEwkxzC8Hb0Ndpd8rD+p+Lk/Za4bd797Ym7yq4jXqdSyj3JLChunF/vyYjUw==}
+  '@vueuse/core@10.10.1':
+    resolution: {integrity: sha512-8Vr8wxILdK+qfBjbngav8LVI+6UuM2TQCufRKMPz/GrpLHQ6dbY6kL5PLa9Eobq8JRrMaDyArPX9Jj18fMTPew==}
 
-  '@vueuse/integrations@10.10.0':
-    resolution: {integrity: sha512-vHGeK7X6mkdkpcm1eE9t3Cpm21pNVfZRwrjwwbrEs9XftnSgszF4831G2rei8Dt9cIYJIfFV+iyx/29muimJPQ==}
+  '@vueuse/integrations@10.10.1':
+    resolution: {integrity: sha512-b4iPz4NLk2g5u9GNgTpYqNN1pzYWPpIglHTg6eDjJwKB7OfzJP4m5kQlzn2oRH7U0OlEOCVPrdDfqneuS9YNTg==}
     peerDependencies:
       async-validator: '*'
       axios: '*'
@@ -2726,12 +2723,15 @@ packages:
       universal-cookie:
         optional: true
 
-  '@vueuse/metadata@10.10.0':
-    resolution: {integrity: sha512-UNAo2sTCAW5ge6OErPEHb5z7NEAg3XcO9Cj7OK45aZXfLLH1QkexDcZD77HBi5zvEiLOm1An+p/4b5K3Worpug==}
+  '@vueuse/metadata@10.10.1':
+    resolution: {integrity: sha512-dpEL5afVLUqbchwGiLrV6spkl4/6UOKJ3YgxFE+wWLj/LakyIZUC83bfeFgbHkRcNhsAqTQCGR74jImsLfK8pg==}
 
   '@vueuse/shared@10.10.0':
     resolution: {integrity: sha512-2aW33Ac0Uk0U+9yo3Ypg9s5KcR42cuehRWl7vnUHadQyFvCktseyxxEPBi1Eiq4D2yBGACOnqLZpx1eMc7g5Og==}
 
+  '@vueuse/shared@10.10.1':
+    resolution: {integrity: sha512-edqexI+RQpoeqDxTatqBZa+K87ganbrwpoP++Fd9828U3js5jzwcEDeyrYcUgkKZ5LLL8q7M5SOMvSpMrxBPxg==}
+
   JSONStream@1.3.5:
     resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==}
     hasBin: true
@@ -3402,12 +3402,12 @@ packages:
     resolution: {integrity: sha512-QTaY0XjjhTQOdguARF0lGKm5/mEq9PD9/VhZZegHDIBq2tQwgNpHc3dneD4mGo2iJs+fTKv5Bp0fZ+BRuY3Z0g==}
     engines: {node: '>= 0.1.90'}
 
-  cz-git@1.9.2:
-    resolution: {integrity: sha512-HqwSIZBl/Vtlq56wsHuQHhQ0CF7rABEvhvbSAaGViLeM5YucLu5o+U2XLow1Y2bwIAfAIKZHfgg9fxP7P3XU3Q==}
+  cz-git@1.9.3:
+    resolution: {integrity: sha512-v7+vYOparImLLo6J5tAsi/cz+EAK7FI/TDnemi11xgEax4k+ZVqxsmkiEc68n+MJLp1l5THhB2KTDhjpW+ellg==}
     engines: {node: '>=v12.20.0'}
 
-  czg@1.9.2:
-    resolution: {integrity: sha512-uPSKTIsAhZp1Tu7DRO7K68qPixVFyheRKlOGhuKXo2wdlpcE0hoCmTQAwsUTerKtjcFRnhRTpJ5j0bC6SOj01Q==}
+  czg@1.9.3:
+    resolution: {integrity: sha512-2RDFcHpVrG0eWGPodkcDQ9QdaF8xVf54PMP0GdXyiz3sCKvxsHvQ/AEvz9TPndZONCajHgU7uN64MQdrHLDSYA==}
     engines: {node: '>=v12.20.0'}
     hasBin: true
 
@@ -5955,8 +5955,8 @@ packages:
     resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
     engines: {node: '>=6.0.0'}
 
-  prettier-plugin-tailwindcss@0.6.2:
-    resolution: {integrity: sha512-eFefm4cg+1c2B57+H274Qm//CTWBdtQN9ansl0YTP/8TC8x3bugCTQSS/e4FC5Ctl9djhTzsbcMrZ7x2/abIow==}
+  prettier-plugin-tailwindcss@0.6.3:
+    resolution: {integrity: sha512-GeJ9bqXN4APAP0V5T2a1J/o6a50MWevEUCPWxijpdXFDQkBCoAfz4pQfv+YMXSqZ5GXLMDYio0mUOfrYL7gf4w==}
     engines: {node: '>=14.21.3'}
     peerDependencies:
       '@ianvs/prettier-plugin-sort-imports': '*'
@@ -6305,8 +6305,8 @@ packages:
   safer-buffer@2.1.2:
     resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
 
-  sass@1.77.4:
-    resolution: {integrity: sha512-vcF3Ckow6g939GMA4PeU7b2K/9FALXk2KF9J87txdHzXbUF9XRQRwSxcAs/fGaTnJeBFd7UoV22j3lzMLdM0Pw==}
+  sass@1.77.5:
+    resolution: {integrity: sha512-oDfX1mukIlxacPdQqNb6mV2tVCrnE+P3nVYioy72V5tlk56CPNcO4TCuFcaCRKKfJ1M3lH95CleRS+dVKL2qMg==}
     engines: {node: '>=14.0.0'}
     hasBin: true
 
@@ -9323,24 +9323,24 @@ snapshots:
 
   '@ungap/structured-clone@1.2.0': {}
 
-  '@vitejs/plugin-vue-jsx@4.0.0(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))':
+  '@vitejs/plugin-vue-jsx@4.0.0(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))':
     dependencies:
       '@babel/core': 7.24.6
       '@babel/plugin-transform-typescript': 7.24.6(@babel/core@7.24.6)
       '@vue/babel-plugin-jsx': 1.2.2(@babel/core@7.24.6)
-      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
       vue: 3.4.27(typescript@5.4.5)
     transitivePeerDependencies:
       - supports-color
 
-  '@vitejs/plugin-vue@5.0.5(vite@5.2.12(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))':
+  '@vitejs/plugin-vue@5.0.5(vite@5.2.12(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))':
     dependencies:
-      vite: 5.2.12(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+      vite: 5.2.12(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
       vue: 3.4.27(typescript@5.4.5)
 
-  '@vitejs/plugin-vue@5.0.5(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))':
+  '@vitejs/plugin-vue@5.0.5(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))':
     dependencies:
-      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
       vue: 3.4.27(typescript@5.4.5)
 
   '@vitest/expect@2.0.0-beta.3':
@@ -9466,14 +9466,14 @@ snapshots:
     transitivePeerDependencies:
       - vue
 
-  '@vue/devtools-core@7.2.1(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))':
+  '@vue/devtools-core@7.2.1(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))':
     dependencies:
       '@vue/devtools-kit': 7.2.1(vue@3.4.27(typescript@5.4.5))
       '@vue/devtools-shared': 7.2.1
       mitt: 3.0.1
       nanoid: 3.3.7
       pathe: 1.1.2
-      vite-hot-client: 0.2.3(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))
+      vite-hot-client: 0.2.3(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))
     transitivePeerDependencies:
       - vite
       - vue
@@ -9545,20 +9545,20 @@ snapshots:
       js-beautify: 1.15.1
       vue-component-type-helpers: 2.0.19
 
-  '@vueuse/core@10.10.0(vue@3.4.27(typescript@5.4.5))':
+  '@vueuse/core@10.10.1(vue@3.4.27(typescript@5.4.5))':
     dependencies:
       '@types/web-bluetooth': 0.0.20
-      '@vueuse/metadata': 10.10.0
-      '@vueuse/shared': 10.10.0(vue@3.4.27(typescript@5.4.5))
+      '@vueuse/metadata': 10.10.1
+      '@vueuse/shared': 10.10.1(vue@3.4.27(typescript@5.4.5))
       vue-demi: 0.14.8(vue@3.4.27(typescript@5.4.5))
     transitivePeerDependencies:
       - '@vue/composition-api'
       - vue
 
-  '@vueuse/integrations@10.10.0(async-validator@4.2.5)(axios@1.7.2)(focus-trap@7.5.4)(nprogress@0.2.0)(qrcode@1.5.3)(vue@3.4.27(typescript@5.4.5))':
+  '@vueuse/integrations@10.10.1(async-validator@4.2.5)(axios@1.7.2)(focus-trap@7.5.4)(nprogress@0.2.0)(qrcode@1.5.3)(vue@3.4.27(typescript@5.4.5))':
     dependencies:
-      '@vueuse/core': 10.10.0(vue@3.4.27(typescript@5.4.5))
-      '@vueuse/shared': 10.10.0(vue@3.4.27(typescript@5.4.5))
+      '@vueuse/core': 10.10.1(vue@3.4.27(typescript@5.4.5))
+      '@vueuse/shared': 10.10.1(vue@3.4.27(typescript@5.4.5))
       vue-demi: 0.14.8(vue@3.4.27(typescript@5.4.5))
     optionalDependencies:
       async-validator: 4.2.5
@@ -9570,7 +9570,7 @@ snapshots:
       - '@vue/composition-api'
       - vue
 
-  '@vueuse/metadata@10.10.0': {}
+  '@vueuse/metadata@10.10.1': {}
 
   '@vueuse/shared@10.10.0(vue@3.4.27(typescript@5.4.5))':
     dependencies:
@@ -9579,6 +9579,13 @@ snapshots:
       - '@vue/composition-api'
       - vue
 
+  '@vueuse/shared@10.10.1(vue@3.4.27(typescript@5.4.5))':
+    dependencies:
+      vue-demi: 0.14.8(vue@3.4.27(typescript@5.4.5))
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+      - vue
+
   JSONStream@1.3.5:
     dependencies:
       jsonparse: 1.3.1
@@ -10344,9 +10351,9 @@ snapshots:
       csv-stringify: 5.6.5
       stream-transform: 2.1.3
 
-  cz-git@1.9.2: {}
+  cz-git@1.9.3: {}
 
-  czg@1.9.2: {}
+  czg@1.9.3: {}
 
   dargs@8.1.0: {}
 
@@ -10926,13 +10933,13 @@ snapshots:
     optionalDependencies:
       '@typescript-eslint/eslint-plugin': 7.13.0(@typescript-eslint/parser@7.13.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
 
-  eslint-plugin-vitest@0.5.4(@typescript-eslint/eslint-plugin@7.13.0(@typescript-eslint/parser@7.13.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)(vitest@2.0.0-beta.3(@types/node@20.14.2)(jsdom@24.1.0)(sass@1.77.4)(terser@5.31.0)):
+  eslint-plugin-vitest@0.5.4(@typescript-eslint/eslint-plugin@7.13.0(@typescript-eslint/parser@7.13.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)(vitest@2.0.0-beta.3(@types/node@20.14.2)(jsdom@24.1.0)(sass@1.77.5)(terser@5.31.0)):
     dependencies:
       '@typescript-eslint/utils': 7.11.0(eslint@8.57.0)(typescript@5.4.5)
       eslint: 8.57.0
     optionalDependencies:
       '@typescript-eslint/eslint-plugin': 7.13.0(@typescript-eslint/parser@7.13.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
-      vitest: 2.0.0-beta.3(@types/node@20.14.2)(jsdom@24.1.0)(sass@1.77.4)(terser@5.31.0)
+      vitest: 2.0.0-beta.3(@types/node@20.14.2)(jsdom@24.1.0)(sass@1.77.5)(terser@5.31.0)
     transitivePeerDependencies:
       - supports-color
       - typescript
@@ -12216,7 +12223,7 @@ snapshots:
 
   mkdirp@1.0.4: {}
 
-  mkdist@1.5.1(sass@1.77.4)(typescript@5.4.5)(vue-tsc@2.0.21(typescript@5.4.5)):
+  mkdist@1.5.1(sass@1.77.5)(typescript@5.4.5)(vue-tsc@2.0.21(typescript@5.4.5)):
     dependencies:
       autoprefixer: 10.4.19(postcss@8.4.38)
       citty: 0.1.6
@@ -12234,7 +12241,7 @@ snapshots:
       postcss-nested: 6.0.1(postcss@8.4.38)
       semver: 7.6.2
     optionalDependencies:
-      sass: 1.77.4
+      sass: 1.77.5
       typescript: 5.4.5
       vue-tsc: 2.0.21(typescript@5.4.5)
 
@@ -13034,7 +13041,7 @@ snapshots:
     dependencies:
       fast-diff: 1.3.0
 
-  prettier-plugin-tailwindcss@0.6.2(prettier@3.3.0):
+  prettier-plugin-tailwindcss@0.6.3(prettier@3.3.0):
     dependencies:
       prettier: 3.3.0
 
@@ -13108,7 +13115,7 @@ snapshots:
       '@internationalized/date': 3.5.4
       '@internationalized/number': 3.5.3
       '@tanstack/vue-virtual': 3.5.0(vue@3.4.27(typescript@5.4.5))
-      '@vueuse/core': 10.10.0(vue@3.4.27(typescript@5.4.5))
+      '@vueuse/core': 10.10.1(vue@3.4.27(typescript@5.4.5))
       '@vueuse/shared': 10.10.0(vue@3.4.27(typescript@5.4.5))
       aria-hidden: 1.2.4
       defu: 6.1.4
@@ -13335,7 +13342,7 @@ snapshots:
 
   safer-buffer@2.1.2: {}
 
-  sass@1.77.4:
+  sass@1.77.5:
     dependencies:
       chokidar: 3.6.0
       immutable: 4.3.6
@@ -14014,7 +14021,7 @@ snapshots:
       has-symbols: 1.0.3
       which-boxed-primitive: 1.0.2
 
-  unbuild@2.0.0(sass@1.77.4)(typescript@5.4.5)(vue-tsc@2.0.21(typescript@5.4.5)):
+  unbuild@2.0.0(sass@1.77.5)(typescript@5.4.5)(vue-tsc@2.0.21(typescript@5.4.5)):
     dependencies:
       '@rollup/plugin-alias': 5.1.0(rollup@3.29.4)
       '@rollup/plugin-commonjs': 25.0.8(rollup@3.29.4)
@@ -14031,7 +14038,7 @@ snapshots:
       hookable: 5.5.3
       jiti: 1.21.0
       magic-string: 0.30.10
-      mkdist: 1.5.1(sass@1.77.4)(typescript@5.4.5)(vue-tsc@2.0.21(typescript@5.4.5))
+      mkdist: 1.5.1(sass@1.77.5)(typescript@5.4.5)(vue-tsc@2.0.21(typescript@5.4.5))
       mlly: 1.7.0
       pathe: 1.1.2
       pkg-types: 1.1.1
@@ -14087,7 +14094,7 @@ snapshots:
 
   unpipe@1.0.0: {}
 
-  unplugin-turbo-console@1.8.6(esbuild@0.20.2)(rollup@4.18.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5)):
+  unplugin-turbo-console@1.8.6(esbuild@0.20.2)(rollup@4.18.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5)):
     dependencies:
       '@rollup/pluginutils': 5.1.0(rollup@4.18.0)
       ast-kit: 0.12.2
@@ -14100,7 +14107,7 @@ snapshots:
     optionalDependencies:
       esbuild: 0.20.2
       rollup: 4.18.0
-      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
       vue: 3.4.27(typescript@5.4.5)
     transitivePeerDependencies:
       - uWebSockets.js
@@ -14165,17 +14172,17 @@ snapshots:
 
   validator@13.12.0: {}
 
-  vite-hot-client@0.2.3(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)):
+  vite-hot-client@0.2.3(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)):
     dependencies:
-      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
 
-  vite-node@2.0.0-beta.3(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0):
+  vite-node@2.0.0-beta.3(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0):
     dependencies:
       cac: 6.7.14
       debug: 4.3.5
       pathe: 1.1.2
       picocolors: 1.0.1
-      vite: 5.2.12(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+      vite: 5.2.12(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -14186,16 +14193,16 @@ snapshots:
       - supports-color
       - terser
 
-  vite-plugin-compression@0.5.1(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)):
+  vite-plugin-compression@0.5.1(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)):
     dependencies:
       chalk: 4.1.2
       debug: 4.3.5
       fs-extra: 10.1.0
-      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
     transitivePeerDependencies:
       - supports-color
 
-  vite-plugin-dts@3.9.1(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.4.5)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)):
+  vite-plugin-dts@3.9.1(@types/node@20.14.2)(rollup@4.18.0)(typescript@5.4.5)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)):
     dependencies:
       '@microsoft/api-extractor': 7.43.0(@types/node@20.14.2)
       '@rollup/pluginutils': 5.1.0(rollup@4.18.0)
@@ -14206,13 +14213,13 @@ snapshots:
       typescript: 5.4.5
       vue-tsc: 1.8.27(typescript@5.4.5)
     optionalDependencies:
-      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
     transitivePeerDependencies:
       - '@types/node'
       - rollup
       - supports-color
 
-  vite-plugin-html@3.2.2(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)):
+  vite-plugin-html@3.2.2(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)):
     dependencies:
       '@rollup/pluginutils': 4.2.1
       colorette: 2.0.20
@@ -14226,9 +14233,9 @@ snapshots:
       html-minifier-terser: 6.1.0
       node-html-parser: 5.4.2
       pathe: 0.2.0
-      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
 
-  vite-plugin-inspect@0.8.4(rollup@4.18.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)):
+  vite-plugin-inspect@0.8.4(rollup@4.18.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)):
     dependencies:
       '@antfu/utils': 0.7.8
       '@rollup/pluginutils': 5.1.0(rollup@4.18.0)
@@ -14239,19 +14246,19 @@ snapshots:
       perfect-debounce: 1.0.0
       picocolors: 1.0.1
       sirv: 2.0.4
-      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
     transitivePeerDependencies:
       - rollup
       - supports-color
 
-  vite-plugin-lib-inject-css@2.1.1(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)):
+  vite-plugin-lib-inject-css@2.1.1(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)):
     dependencies:
       '@ast-grep/napi': 0.22.4
       magic-string: 0.30.10
       picocolors: 1.0.1
-      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
 
-  vite-plugin-mock@3.0.2(esbuild@0.20.2)(mockjs@1.1.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)):
+  vite-plugin-mock@3.0.2(esbuild@0.20.2)(mockjs@1.1.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)):
     dependencies:
       bundle-require: 4.2.1(esbuild@0.20.2)
       chokidar: 3.6.0
@@ -14262,27 +14269,27 @@ snapshots:
       mockjs: 1.1.0
       path-to-regexp: 6.2.2
       picocolors: 1.0.1
-      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
     transitivePeerDependencies:
       - supports-color
 
-  vite-plugin-vue-devtools@7.2.1(rollup@4.18.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5)):
+  vite-plugin-vue-devtools@7.2.1(rollup@4.18.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5)):
     dependencies:
-      '@vue/devtools-core': 7.2.1(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))
+      '@vue/devtools-core': 7.2.1(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))
       '@vue/devtools-kit': 7.2.1(vue@3.4.27(typescript@5.4.5))
       '@vue/devtools-shared': 7.2.1
       execa: 8.0.1
       sirv: 2.0.4
-      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
-      vite-plugin-inspect: 0.8.4(rollup@4.18.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))
-      vite-plugin-vue-inspector: 5.1.2(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))
+      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
+      vite-plugin-inspect: 0.8.4(rollup@4.18.0)(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))
+      vite-plugin-vue-inspector: 5.1.2(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))
     transitivePeerDependencies:
       - '@nuxt/kit'
       - rollup
       - supports-color
       - vue
 
-  vite-plugin-vue-inspector@5.1.2(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)):
+  vite-plugin-vue-inspector@5.1.2(vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)):
     dependencies:
       '@babel/core': 7.24.6
       '@babel/plugin-proposal-decorators': 7.24.6(@babel/core@7.24.6)
@@ -14293,11 +14300,11 @@ snapshots:
       '@vue/compiler-dom': 3.4.27
       kolorist: 1.8.0
       magic-string: 0.30.10
-      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+      vite: 6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
     transitivePeerDependencies:
       - supports-color
 
-  vite@5.2.12(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0):
+  vite@5.2.12(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0):
     dependencies:
       esbuild: 0.20.2
       postcss: 8.4.38
@@ -14305,10 +14312,10 @@ snapshots:
     optionalDependencies:
       '@types/node': 20.14.2
       fsevents: 2.3.3
-      sass: 1.77.4
+      sass: 1.77.5
       terser: 5.31.0
 
-  vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0):
+  vite@6.0.0-alpha.17(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0):
     dependencies:
       esbuild: 0.20.2
       postcss: 8.4.38
@@ -14316,26 +14323,26 @@ snapshots:
     optionalDependencies:
       '@types/node': 20.14.2
       fsevents: 2.3.3
-      sass: 1.77.4
+      sass: 1.77.5
       terser: 5.31.0
 
-  vitepress@1.2.3(@algolia/client-search@4.23.3)(@types/node@20.14.2)(async-validator@4.2.5)(axios@1.7.2)(nprogress@0.2.0)(postcss@8.4.38)(qrcode@1.5.3)(sass@1.77.4)(search-insights@2.14.0)(terser@5.31.0)(typescript@5.4.5):
+  vitepress@1.2.3(@algolia/client-search@4.23.3)(@types/node@20.14.2)(async-validator@4.2.5)(axios@1.7.2)(nprogress@0.2.0)(postcss@8.4.38)(qrcode@1.5.3)(sass@1.77.5)(search-insights@2.14.0)(terser@5.31.0)(typescript@5.4.5):
     dependencies:
       '@docsearch/css': 3.6.0
       '@docsearch/js': 3.6.0(@algolia/client-search@4.23.3)(search-insights@2.14.0)
       '@shikijs/core': 1.6.2
       '@shikijs/transformers': 1.6.2
       '@types/markdown-it': 14.1.1
-      '@vitejs/plugin-vue': 5.0.5(vite@5.2.12(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))
+      '@vitejs/plugin-vue': 5.0.5(vite@5.2.12(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))
       '@vue/devtools-api': 7.2.1(vue@3.4.27(typescript@5.4.5))
       '@vue/shared': 3.4.27
-      '@vueuse/core': 10.10.0(vue@3.4.27(typescript@5.4.5))
-      '@vueuse/integrations': 10.10.0(async-validator@4.2.5)(axios@1.7.2)(focus-trap@7.5.4)(nprogress@0.2.0)(qrcode@1.5.3)(vue@3.4.27(typescript@5.4.5))
+      '@vueuse/core': 10.10.1(vue@3.4.27(typescript@5.4.5))
+      '@vueuse/integrations': 10.10.1(async-validator@4.2.5)(axios@1.7.2)(focus-trap@7.5.4)(nprogress@0.2.0)(qrcode@1.5.3)(vue@3.4.27(typescript@5.4.5))
       focus-trap: 7.5.4
       mark.js: 8.11.1
       minisearch: 6.3.0
       shiki: 1.6.2
-      vite: 5.2.12(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+      vite: 5.2.12(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
       vue: 3.4.27(typescript@5.4.5)
     optionalDependencies:
       postcss: 8.4.38
@@ -14366,7 +14373,7 @@ snapshots:
       - typescript
       - universal-cookie
 
-  vitest@2.0.0-beta.3(@types/node@20.14.2)(jsdom@24.1.0)(sass@1.77.4)(terser@5.31.0):
+  vitest@2.0.0-beta.3(@types/node@20.14.2)(jsdom@24.1.0)(sass@1.77.5)(terser@5.31.0):
     dependencies:
       '@vitest/expect': 2.0.0-beta.3
       '@vitest/runner': 2.0.0-beta.3
@@ -14382,8 +14389,8 @@ snapshots:
       std-env: 3.7.0
       tinybench: 2.8.0
       tinypool: 0.9.0
-      vite: 5.2.12(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
-      vite-node: 2.0.0-beta.3(@types/node@20.14.2)(sass@1.77.4)(terser@5.31.0)
+      vite: 5.2.12(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
+      vite-node: 2.0.0-beta.3(@types/node@20.14.2)(sass@1.77.5)(terser@5.31.0)
       why-is-node-running: 2.2.2
     optionalDependencies:
       '@types/node': 20.14.2