Browse Source

feat(other): The menu supports jumping to external links and fixing some known problems

vben 11 months ago
parent
commit
c31d21be50
43 changed files with 498 additions and 236 deletions
  1. 3 2
      apps/antd-view/package.json
  2. 1 2
      apps/antd-view/src/layout.vue
  3. 3 2
      apps/antd-view/src/router/guard/access.ts
  4. 1 1
      apps/antd-view/src/router/guard/index.ts
  5. 1 1
      apps/antd-view/src/router/index.ts
  6. 2 24
      apps/antd-view/src/router/routes/index.ts
  7. 50 0
      apps/antd-view/src/router/routes/modules/vben.ts
  8. 3 3
      internal/lint-configs/eslint-config/package.json
  9. 1 1
      internal/tailwind-config/package.json
  10. 1 1
      internal/vite-config/package.json
  11. 2 2
      package.json
  12. 4 0
      packages/@vben-core/shared/typings/global.d.ts
  13. 1 6
      packages/@vben-core/uikit/menu-ui/src/components/menu-item.vue
  14. 5 2
      packages/@vben-core/uikit/menu-ui/src/components/menu.vue
  15. 1 1
      packages/@vben-core/uikit/menu-ui/src/components/sub-menu-content.vue
  16. 1 1
      packages/@vben-core/uikit/menu-ui/src/styles/index.scss
  17. 2 2
      packages/@vben-core/uikit/shadcn-ui/src/components/breadcrumb/breadcrumb-background.vue
  18. 21 15
      packages/@vben-core/uikit/shadcn-ui/src/components/breadcrumb/breadcrumb.vue
  19. 1 1
      packages/@vben-core/uikit/tabs-ui/src/components/tabs.vue
  20. 15 16
      packages/@vben-core/uikit/tabs-ui/src/styles/tabs.scss
  21. 15 3
      packages/business/layouts/src/basic/layout.vue
  22. 6 4
      packages/business/layouts/src/basic/menu/extra-menu.vue
  23. 5 4
      packages/business/layouts/src/basic/menu/use-extra-menu.ts
  24. 5 5
      packages/business/layouts/src/basic/menu/use-mixed-menu.ts
  25. 19 0
      packages/business/layouts/src/basic/menu/use-navigation.ts
  26. 21 1
      packages/business/layouts/src/basic/tabs/use-tabs.ts
  27. 2 1
      packages/business/layouts/src/basic/widgets/breadcrumb.vue
  28. 7 0
      packages/constants/build.config.ts
  29. 45 0
      packages/constants/package.json
  30. 1 0
      packages/constants/src/index.ts
  31. 1 0
      packages/constants/src/vben.ts
  32. 5 0
      packages/constants/tsconfig.json
  33. 1 0
      packages/locales/build.config.ts
  34. 5 0
      packages/locales/package.json
  35. 8 0
      packages/locales/src/helper.ts
  36. 4 0
      packages/locales/src/langs/en-US.yaml
  37. 4 0
      packages/locales/src/langs/zh-CN.yaml
  38. 7 0
      packages/utils/build.config.ts
  39. 48 0
      packages/utils/package.json
  40. 1 0
      packages/utils/src/index.ts
  41. 5 0
      packages/utils/tsconfig.json
  42. 156 135
      pnpm-lock.yaml
  43. 8 0
      vben-admin.code-workspace

+ 3 - 2
apps/antd-view/package.json

@@ -26,17 +26,18 @@
   "dependencies": {
     "@vben-core/design": "workspace:*",
     "@vben-core/design-tokens": "workspace:*",
-    "@vben-core/toolkit": "workspace:*",
     "@vben-core/typings": "workspace:*",
     "@vben/common-ui": "workspace:*",
+    "@vben/constants": "workspace:*",
     "@vben/hooks": "workspace:*",
     "@vben/icons": "workspace:*",
     "@vben/layouts": "workspace:*",
     "@vben/locales": "workspace:*",
     "@vben/preference": "workspace:*",
     "@vben/stores": "workspace:*",
+    "@vben/utils": "workspace:*",
     "ant-design-vue": "^4.2.1",
-    "axios": "^1.6.8",
+    "axios": "^1.7.1",
     "dayjs": "^1.11.11",
     "vue": "^3.4.27",
     "vue-router": "^4.3.2"

+ 1 - 2
apps/antd-view/src/layout.vue

@@ -1,8 +1,6 @@
 <script lang="ts" setup>
 import type { NotificationItem } from '@vben/common-ui';
 
-import { openWindow } from '@vben-core/toolkit';
-
 import { Notification, UserDropdown } from '@vben/common-ui';
 import {
   IcRoundCreditScore,
@@ -14,6 +12,7 @@ import { BasicLayout } from '@vben/layouts';
 import { $t } from '@vben/locales';
 import { preference } from '@vben/preference';
 import { useAccessStore } from '@vben/stores';
+import { openWindow } from '@vben/utils';
 import { computed, ref } from 'vue';
 import { useRouter } from 'vue-router';
 

+ 3 - 2
apps/antd-view/src/router/guard/access.ts

@@ -1,9 +1,9 @@
 import type { ExRouteRecordRaw, MenuRecordRaw } from '@vben-core/typings';
 
-import { filterTree, mapTree, traverseTreeValues } from '@vben-core/toolkit';
 import type { RouteRecordRaw, Router } from 'vue-router';
 
 import { useAccessStore } from '@vben/stores';
+import { filterTree, mapTree, traverseTreeValues } from '@vben/utils';
 
 import { dynamicRoutes } from '../routes';
 
@@ -120,6 +120,7 @@ async function generatorMenus(
       hideChildrenInMenu = false,
       icon,
       orderNo,
+      target,
       title = '',
     } = meta || {};
 
@@ -138,7 +139,7 @@ async function generatorMenus(
       });
     }
     // 隐藏子菜单
-    const resultPath = hideChildrenInMenu ? redirect : path;
+    const resultPath = hideChildrenInMenu ? redirect : target || path;
     return {
       badge,
       badgeType,

+ 1 - 1
apps/antd-view/src/router/guard/index.ts

@@ -1,7 +1,7 @@
-import { startProgress, stopProgress } from '@vben-core/toolkit';
 import type { Router } from 'vue-router';
 
 import { preference } from '@vben/preference';
+import { startProgress, stopProgress } from '@vben/utils';
 
 import { configAccessGuard } from './access';
 

+ 1 - 1
apps/antd-view/src/router/index.ts

@@ -1,6 +1,6 @@
-import { traverseTreeValues } from '@vben-core/toolkit';
 import type { RouteRecordName, RouteRecordRaw } from 'vue-router';
 
+import { traverseTreeValues } from '@vben/utils';
 import { createRouter, createWebHashHistory } from 'vue-router';
 
 import { createRouteGuard } from './guard';

+ 2 - 24
apps/antd-view/src/router/routes/index.ts

@@ -4,6 +4,7 @@ import { builtinRoutes } from './builtin';
 import { Layout } from './layout';
 import { nestedRoutes } from './modules/nested';
 import { outsideRoutes } from './modules/outside';
+import { vbenRoutes } from './modules/vben';
 
 /** 动态路由 */
 const dynamicRoutes: RouteRecordRaw[] = [
@@ -31,30 +32,7 @@ const dynamicRoutes: RouteRecordRaw[] = [
   },
   ...nestedRoutes,
   ...outsideRoutes,
-  // 关于
-  {
-    component: Layout,
-    meta: {
-      hideChildrenInMenu: true,
-      icon: 'https://cdn.jsdelivr.net/gh/vbenjs/vben-cdn-static@0.1.2/vben-admin/admin-logo.png',
-      keepAlive: false,
-      title: '关于',
-    },
-    name: 'AboutLayout',
-    path: '/about',
-    redirect: '/about/index',
-    children: [
-      {
-        name: 'About',
-        path: 'index',
-        component: () => import('@/views/about/index.vue'),
-        meta: {
-          keepAlive: false,
-          title: '关于',
-        },
-      },
-    ],
-  },
+  ...vbenRoutes,
 ];
 
 /** 排除在主框架外的路由,这些路由没有菜单和顶部及其他框架内容 */

+ 50 - 0
apps/antd-view/src/router/routes/modules/vben.ts

@@ -0,0 +1,50 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+import { VBEN_GITHUB_URL } from '@vben/constants';
+import { $t } from '@vben/locales/helper';
+import { preference } from '@vben/preference';
+
+import { IFrameView, Layout } from '@/router/routes/layout';
+
+export const vbenRoutes: RouteRecordRaw[] = [
+  {
+    component: Layout,
+    meta: {
+      icon: preference.logo,
+      title: 'Vben Admin',
+    },
+    name: 'AboutLayout',
+    path: '/vben-admin',
+    redirect: '/vben-admin/about',
+    children: [
+      {
+        name: 'About',
+        path: 'about',
+        component: () => import('@/views/about/index.vue'),
+        meta: {
+          icon: 'mdi:creative-commons',
+          title: $t('page.about'),
+        },
+      },
+      {
+        name: 'AboutDocument',
+        path: 'document',
+        component: () => import('@/views/about/index.vue'),
+        meta: {
+          icon: 'mdi:flame-circle',
+          title: $t('page.document'),
+        },
+      },
+      {
+        name: 'Github',
+        path: 'github',
+        component: IFrameView,
+        meta: {
+          icon: 'mdi:github',
+          target: VBEN_GITHUB_URL,
+          title: 'Github',
+        },
+      },
+    ],
+  },
+];

+ 3 - 3
internal/lint-configs/eslint-config/package.json

@@ -37,8 +37,8 @@
   "devDependencies": {
     "@eslint/js": "^9.3.0",
     "@types/eslint": "^8.56.10",
-    "@typescript-eslint/eslint-plugin": "^7.9.0",
-    "@typescript-eslint/parser": "^7.9.0",
+    "@typescript-eslint/eslint-plugin": "^7.10.0",
+    "@typescript-eslint/parser": "^7.10.0",
     "eslint": "^8.57.0",
     "eslint-config-prettier": "^9.1.0",
     "eslint-plugin-eslint-comments": "^3.2.0",
@@ -54,7 +54,7 @@
     "eslint-plugin-unused-imports": "^3.2.0",
     "eslint-plugin-vitest": "^0.5.4",
     "eslint-plugin-vue": "^9.26.0",
-    "globals": "^15.2.0",
+    "globals": "^15.3.0",
     "jsonc-eslint-parser": "^2.4.0",
     "vue-eslint-parser": "^9.4.2"
   }

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

@@ -47,7 +47,7 @@
     "./*": "./*"
   },
   "dependencies": {
-    "@iconify/json": "^2.2.211",
+    "@iconify/json": "^2.2.212",
     "@iconify/tailwind": "^1.1.1",
     "@tailwindcss/forms": "^0.5.7",
     "autoprefixer": "^10.4.19",

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

@@ -38,7 +38,7 @@
     "html-minifier-terser": "^7.2.0",
     "resolve.exports": "^2.0.2",
     "vite-plugin-lib-inject-css": "^2.1.1",
-    "vite-plugin-vue-devtools": "^7.2.0"
+    "vite-plugin-vue-devtools": "^7.2.1"
   },
   "devDependencies": {
     "@types/html-minifier-terser": "^7.0.2",

+ 2 - 2
package.json

@@ -48,7 +48,7 @@
     "test": "vitest"
   },
   "devDependencies": {
-    "@changesets/cli": "^2.27.2",
+    "@changesets/cli": "^2.27.3",
     "@ls-lint/ls-lint": "^2.2.3",
     "@types/jsdom": "^21.1.6",
     "@types/node": "^20.12.12",
@@ -79,7 +79,7 @@
     "node": ">=18.7.0",
     "pnpm": ">=8.5.0"
   },
-  "packageManager": "pnpm@9.1.1",
+  "packageManager": "pnpm@9.1.2",
   "pnpm": {
     "overrides": {
       "@ctrl/tinycolor": "4.1.0",

+ 4 - 0
packages/@vben-core/shared/typings/global.d.ts

@@ -80,6 +80,10 @@ declare module 'vue-router' {
      * 用于路由->菜单排序
      */
     orderNo?: number;
+    /**
+     * 外链-跳转路径
+     */
+    target?: string;
     /**
      * 标题名称
      */

+ 1 - 6
packages/@vben-core/uikit/menu-ui/src/components/menu-item.vue

@@ -100,12 +100,7 @@ onBeforeUnmount(() => {
     </VbenTooltip>
     <div v-show="!showTooltip" :class="[e('content')]">
       <VbenMenuBadge v-bind="props" />
-      <VbenIcon
-        v-if="isTopLevelMenuItem"
-        :class="nsMenu.e('icon')"
-        :icon="icon"
-        fallback
-      />
+      <VbenIcon :class="nsMenu.e('icon')" :icon="icon" fallback />
 
       <slot></slot>
       <slot name="title"></slot>

+ 5 - 2
packages/@vben-core/uikit/menu-ui/src/components/menu.vue

@@ -1,6 +1,6 @@
 <script lang="ts" setup>
 import { IcRoundMoreHoriz } from '@vben-core/iconify';
-import { useNamespace } from '@vben-core/toolkit';
+import { isHttpUrl, useNamespace } from '@vben-core/toolkit';
 
 import { UseResizeObserverReturn, useResizeObserver } from '@vueuse/core';
 import {
@@ -241,7 +241,10 @@ function handleMenuItemClick(data: MenuItemClicked) {
   if (!path || !parentPaths) {
     return;
   }
-  activePath.value = path;
+  if (!isHttpUrl(path)) {
+    activePath.value = path;
+  }
+
   emit('select', path, parentPaths);
 }
 

+ 1 - 1
packages/@vben-core/uikit/menu-ui/src/components/sub-menu-content.vue

@@ -86,7 +86,7 @@ const iconArrowStyle = computed(() => {
     <slot></slot>
 
     <VbenIcon
-      v-if="isTopLevelMenuSubmenu && !isMenuMore"
+      v-if="!isMenuMore"
       :class="nsMenu.e('icon')"
       :icon="icon"
       fallback

+ 1 - 1
packages/@vben-core/uikit/menu-ui/src/styles/index.scss

@@ -16,7 +16,7 @@
   --menu-item-collapse-margin-y: 4px;
   --menu-item-collapse-margin-x: 0px;
   --menu-item-radius: 0px;
-  --menu-item-indent: 24px;
+  --menu-item-indent: 16px;
   --menu-font-size: 14px;
   --menu-dark-background: 0deg 0% 100% / 10%;
   --menu-light-background: 192deg 1% 93%;

+ 2 - 2
packages/@vben-core/uikit/shadcn-ui/src/components/breadcrumb/breadcrumb-background.vue

@@ -1,5 +1,5 @@
 <script lang="ts" setup>
-import { Icon } from '@vben-core/iconify';
+import { VbenIcon } from '@vben-core/shadcn-ui';
 
 import type { IBreadcrumb } from './interface';
 
@@ -32,7 +32,7 @@ function handleClick(path?: string) {
         <li>
           <a href="javascript:void 0" @click.stop="handleClick(item.path)">
             <span class="flex-center h-full">
-              <Icon
+              <VbenIcon
                 v-if="item.icon && showIcon"
                 class="mr-1 size-5 flex-shrink-0"
                 :icon="item.icon"

+ 21 - 15
packages/@vben-core/uikit/shadcn-ui/src/components/breadcrumb/breadcrumb.vue

@@ -1,5 +1,6 @@
 <script lang="ts" setup>
-import { IcRoundKeyboardArrowDown, Icon } from '@vben-core/iconify';
+import { IcRoundKeyboardArrowDown } from '@vben-core/iconify';
+import { VbenIcon } from '@vben-core/shadcn-ui';
 
 import {
   Breadcrumb,
@@ -49,7 +50,7 @@ function handleClick(path?: string) {
             <div v-if="item.items?.length ?? 0 > 0">
               <DropdownMenu>
                 <DropdownMenuTrigger class="flex items-center gap-1">
-                  <Icon
+                  <VbenIcon
                     v-if="item.icon && showIcon"
                     class="size-5"
                     :icon="item.icon"
@@ -74,21 +75,26 @@ function handleClick(path?: string) {
               href="javascript:void 0"
               @click.stop="handleClick(item.path)"
             >
-              <Icon
-                v-if="item.icon && showIcon"
-                class="size-4"
-                :class="{ 'size-5': item.isHome }"
-                :icon="item.icon"
-              />
-              {{ item.title }}
+              <div class="flex-center">
+                <VbenIcon
+                  v-if="item.icon && showIcon"
+                  class="mr-1 size-4"
+                  :class="{ 'size-5': item.isHome }"
+                  :icon="item.icon"
+                />
+                {{ item.title }}
+              </div>
             </BreadcrumbLink>
             <BreadcrumbPage v-else>
-              <Icon
-                v-if="item.icon && showIcon"
-                class="size-4"
-                :icon="item.icon"
-              />
-              {{ item.title }}
+              <div class="flex-center">
+                <VbenIcon
+                  v-if="item.icon && showIcon"
+                  class="mr-1 size-4"
+                  :class="{ 'size-5': item.isHome }"
+                  :icon="item.icon"
+                />
+                {{ item.title }}
+              </div>
             </BreadcrumbPage>
           </BreadcrumbItem>
           <BreadcrumbSeparator

+ 1 - 1
packages/@vben-core/uikit/tabs-ui/src/components/tabs.vue

@@ -29,7 +29,7 @@ const props = withDefaults(defineProps<Props>(), {
 
 const emit = defineEmits<{ close: [string]; unPushPin: [TabItem] }>();
 
-const gap = 7;
+const gap = 6;
 
 const active = defineModel<string>('active');
 const { b, e, is } = useNamespace('tabs-ui');

+ 15 - 16
packages/@vben-core/uikit/tabs-ui/src/styles/tabs.scss

@@ -2,7 +2,7 @@
 
 @include b('tabs-ui') {
   --tabs-background: hsl(var(--color-background));
-  --tabs-gap: 8px;
+  --tabs-gap: 10px;
   --tabs-divider: hsl(var(--color-border));
   --tabs-hover: hsl(var(--color-heavy));
   --tabs-active-background: hsl(var(--color-primary) / 15%);
@@ -30,7 +30,6 @@
   color: hsl(var(--color-muted-foreground));
   cursor: pointer;
   user-select: none;
-  // mask-image: linear-gradient(to right, #000 calc(100% - 0px), transparent);
 
   @include is('active') {
     z-index: 1;
@@ -49,10 +48,10 @@
       background-color: var(--tabs-active-background);
     }
 
-    // .#{$namespace}-tab-background__before,
-    // .#{$namespace}-tab-background__after {
-    //   fill: var(--tabs-active-background);
-    // }
+    .#{$namespace}-tab-background__before,
+    .#{$namespace}-tab-background__after {
+      fill: var(--tabs-active-background);
+    }
   }
 
   @include e('content') {
@@ -95,7 +94,7 @@
     height: 100%;
     font-size: 12px;
     border-radius: 50%;
-    // transition: all 0.15s ease;
+    transition: all 0.15s ease;
 
     &:hover {
       color: hsl(var(--color-foreground));
@@ -185,15 +184,15 @@
     height: 100%;
     margin: 0 7px;
 
-    &::before {
-      position: absolute;
-      top: 20%;
-      right: 100%;
-      width: 1px;
-      height: 60%;
-      content: '';
-      background-color: var(--tabs-divider);
-    }
+    // &::before {
+    //   position: absolute;
+    //   top: 20%;
+    //   right: 100%;
+    //   width: 1px;
+    //   height: 60%;
+    //   content: '';
+    //   background-color: var(--tabs-divider);
+    // }
 
     // &::after {
     //   position: absolute;

+ 15 - 3
packages/business/layouts/src/basic/layout.vue

@@ -1,8 +1,11 @@
 <script lang="ts" setup>
 import { VbenAdminLayout } from '@vben-core/layout-ui';
 import { VbenBackTop, VbenLogo } from '@vben-core/shadcn-ui';
+import { mapTree } from '@vben-core/toolkit';
+import { MenuRecordRaw } from '@vben-core/typings';
 
 import { PreferenceWidget } from '@vben/common-ui';
+import { $t } from '@vben/locales';
 import { preference, updatePreference, usePreference } from '@vben/preference';
 import { computed } from 'vue';
 
@@ -80,6 +83,15 @@ const {
   sideMenus,
   sideVisible,
 } = useMixedMenu();
+
+function wrapperMenus(menus: MenuRecordRaw[]) {
+  return mapTree(menus, (item) => {
+    return {
+      ...item,
+      name: $t(item.name),
+    };
+  });
+}
 </script>
 
 <template>
@@ -154,7 +166,7 @@ const {
             :rounded="isMenuRounded"
             mode="horizontal"
             :theme="headerMenuTheme"
-            :menus="headerMenus"
+            :menus="wrapperMenus(headerMenus)"
             :default-active="headerActive"
             @select="handleMenuSelect"
           />
@@ -175,7 +187,7 @@ const {
         :collapse-show-title="preference.sideCollapseShowTitle"
         :collapse="preference.sideCollapse"
         :theme="theme"
-        :menus="sideMenus"
+        :menus="wrapperMenus(sideMenus)"
         :default-active="sideActive"
         @select="handleMenuSelect"
       />
@@ -195,7 +207,7 @@ const {
     <template #side-extra>
       <LayoutExtraMenu
         :rounded="isMenuRounded"
-        :menus="extraMenus"
+        :menus="wrapperMenus(extraMenus)"
         :collapse="preference.sideExtraCollapse"
         :theme="theme"
       />

+ 6 - 4
packages/business/layouts/src/basic/menu/extra-menu.vue

@@ -3,7 +3,9 @@ import type { MenuRecordRaw } from '@vben-core/typings';
 
 import { Menu, MenuProps } from '@vben-core/menu-ui';
 
-import { useRoute, useRouter } from 'vue-router';
+import { useRoute } from 'vue-router';
+
+import { useNavigation } from './use-navigation';
 
 interface Props extends MenuProps {
   collspae?: boolean;
@@ -13,10 +15,10 @@ interface Props extends MenuProps {
 defineProps<Props>();
 
 const route = useRoute();
-const router = useRouter();
+const { navigation } = useNavigation();
 
-function handleSelect(key: string) {
-  router.push(key);
+async function handleSelect(key: string) {
+  await navigation(key);
 }
 </script>
 

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

@@ -3,17 +3,18 @@ import type { MenuRecordRaw } from '@vben-core/typings';
 import { preference } from '@vben/preference';
 import { useAccessStore } from '@vben/stores';
 import { computed, ref } from 'vue';
-import { useRoute, useRouter } from 'vue-router';
+import { useRoute } from 'vue-router';
 
 import { findRootMenuByPath } from './helper';
+import { useNavigation } from './use-navigation';
 
 function useExtraMenu() {
   const accessStore = useAccessStore();
+  const { navigation } = useNavigation();
 
   const menus = computed(() => accessStore.getAccessMenus);
 
   const route = useRoute();
-  const router = useRouter();
   const extraMenus = ref<MenuRecordRaw[]>([]);
   const extraVisible = ref<boolean>(false);
   const extraActiveMenu = ref('');
@@ -22,14 +23,14 @@ function useExtraMenu() {
    * 选择混合菜单事件
    * @param menu
    */
-  const handleMixedMenuSelect = (menu: MenuRecordRaw) => {
+  const handleMixedMenuSelect = async (menu: MenuRecordRaw) => {
     extraMenus.value = menu?.children ?? [];
     extraActiveMenu.value = menu.parents?.[0] ?? menu.path;
     const hasChildren = extraMenus.value.length > 0;
 
     extraVisible.value = hasChildren;
     if (!hasChildren) {
-      router.push(menu.path);
+      await navigation(menu.path);
     }
   };
 

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

@@ -3,15 +3,15 @@ import type { MenuRecordRaw } from '@vben-core/typings';
 import { preference, usePreference } from '@vben/preference';
 import { useAccessStore } from '@vben/stores';
 import { computed, onBeforeMount, ref } from 'vue';
-import { useRoute, useRouter } from 'vue-router';
+import { useRoute } from 'vue-router';
 
 import { findRootMenuByPath } from './helper';
+import { useNavigation } from './use-navigation';
 
 function useMixedMenu() {
   const accessStore = useAccessStore();
-
+  const { navigation } = useNavigation();
   const route = useRoute();
-  const router = useRouter();
   const splitSideMenus = ref<MenuRecordRaw[]>([]);
   const rootMenuPath = ref<string>('');
 
@@ -75,7 +75,7 @@ function useMixedMenu() {
    */
   const handleMenuSelect = (key: string, mode?: string) => {
     if (!isMixedNav.value || mode === 'vertical') {
-      router.push(key);
+      navigation(key);
       return;
     }
 
@@ -83,7 +83,7 @@ function useMixedMenu() {
     rootMenuPath.value = rootMenu?.path ?? '';
     splitSideMenus.value = rootMenu?.children ?? [];
     if (splitSideMenus.value.length === 0) {
-      router.push(key);
+      navigation(key);
     }
   };
 

+ 19 - 0
packages/business/layouts/src/basic/menu/use-navigation.ts

@@ -0,0 +1,19 @@
+import { isHttpUrl, openWindow } from '@vben-core/toolkit';
+
+import { useRouter } from 'vue-router';
+
+function useNavigation() {
+  const router = useRouter();
+
+  const navigation = async (path: string) => {
+    if (isHttpUrl(path)) {
+      openWindow(path, { target: '_blank' });
+    } else {
+      await router.push(path);
+    }
+  };
+
+  return { navigation };
+}
+
+export { useNavigation };

+ 21 - 1
packages/business/layouts/src/basic/tabs/use-tabs.ts

@@ -12,7 +12,12 @@ import {
   MdiPinOff,
 } from '@vben-core/iconify';
 import { filterTree } from '@vben-core/toolkit';
+import type {
+  RouteLocationNormalized,
+  RouteRecordNormalized,
+} from 'vue-router';
 
+import { $t } from '@vben/locales';
 import { storeToRefs, useAccessStore, useTabsStore } from '@vben/stores';
 import { computed, watch } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
@@ -39,6 +44,9 @@ function useTabs() {
     const affixTabs = filterTree(router.getRoutes(), (route) => {
       return !!route.meta?.affixTab;
     });
+    affixTabs.forEach((tab) => {
+      Object.assign(tab, wrapperTabLocale(tab));
+    });
     tabsStore.setAffixTabs(affixTabs);
   };
 
@@ -52,6 +60,18 @@ function useTabs() {
     await tabsStore.closeTabByKey(key, router);
   };
 
+  function wrapperTabLocale(
+    tab: RouteLocationNormalized | RouteRecordNormalized,
+  ) {
+    return {
+      ...tab,
+      meta: {
+        ...tab.meta,
+        title: $t(tab.meta.title as string),
+      },
+    };
+  }
+
   watch(
     () => accessMenus.value,
     () => {
@@ -63,7 +83,7 @@ function useTabs() {
   watch(
     () => route.path,
     () => {
-      tabsStore.addTab(route);
+      tabsStore.addTab(wrapperTabLocale(route) as RouteLocationNormalized);
     },
     { immediate: true },
   );

+ 2 - 1
packages/business/layouts/src/basic/widgets/breadcrumb.vue

@@ -4,6 +4,7 @@ import type { IBreadcrumb } from '@vben-core/shadcn-ui';
 import { VbenBackgroundBreadcrumb, VbenBreadcrumb } from '@vben-core/shadcn-ui';
 import { BreadcrumbStyle } from '@vben-core/typings';
 
+import { $t } from '@vben/locales';
 import { computed } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
 
@@ -43,7 +44,7 @@ const breadcrumbs = computed((): IBreadcrumb[] => {
     resultBreadcrumb.push({
       icon: icon as string,
       path: path || route.path,
-      title: (title || name) as string,
+      title: $t((title || name) as string),
       // items: children.map((child) => {
       //   return {
       //     icon: child?.meta?.icon as string,

+ 7 - 0
packages/constants/build.config.ts

@@ -0,0 +1,7 @@
+import { defineBuildConfig } from 'unbuild';
+
+export default defineBuildConfig({
+  clean: true,
+  declaration: true,
+  entries: ['src/index'],
+});

+ 45 - 0
packages/constants/package.json

@@ -0,0 +1,45 @@
+{
+  "name": "@vben/constants",
+  "version": "1.0.0",
+  "type": "module",
+  "license": "MIT",
+  "homepage": "https://github.com/vbenjs/vue-vben-admin",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/vbenjs/vue-vben-admin.git",
+    "directory": "packages/constants"
+  },
+  "bugs": {
+    "url": "https://github.com/vbenjs/vue-vben-admin/issues"
+  },
+  "scripts": {
+    "build": "pnpm unbuild",
+    "stub": "pnpm unbuild --stub"
+  },
+  "files": [
+    "dist"
+  ],
+  "sideEffects": [
+    "**/*.css"
+  ],
+  "main": "./dist/index.mjs",
+  "module": "./dist/index.mjs",
+  "imports": {
+    "#*": "./src/*"
+  },
+  "exports": {
+    ".": {
+      "development": "./src/index.ts",
+      "types": "./src/index.ts",
+      "default": "./dist/index.mjs"
+    }
+  },
+  "publishConfig": {
+    "exports": {
+      ".": {
+        "types": "./dist/index.d.ts",
+        "default": "./dist/index.mjs"
+      }
+    }
+  }
+}

+ 1 - 0
packages/constants/src/index.ts

@@ -0,0 +1 @@
+export * from './vben';

+ 1 - 0
packages/constants/src/vben.ts

@@ -0,0 +1 @@
+export const VBEN_GITHUB_URL = 'https://github.com/vbenjs/vue-vben-admin';

+ 5 - 0
packages/constants/tsconfig.json

@@ -0,0 +1,5 @@
+{
+  "$schema": "https://json.schemastore.org/tsconfig",
+  "extends": "@vben/tsconfig/library.json",
+  "include": ["src"]
+}

+ 1 - 0
packages/locales/build.config.ts

@@ -5,6 +5,7 @@ export default defineBuildConfig({
   declaration: true,
   entries: [
     'src/index',
+    'src/helper',
     {
       builder: 'mkdist',
       input: './src/langs',

+ 5 - 0
packages/locales/package.json

@@ -35,6 +35,11 @@
     },
     "./langs/*": {
       "default": "./dist/langs/*"
+    },
+    "./helper": {
+      "development": "./src/helper.ts",
+      "types": "./src/helper.ts",
+      "default": "./dist/helper.mjs"
     }
   },
   "publishConfig": {

+ 8 - 0
packages/locales/src/helper.ts

@@ -0,0 +1,8 @@
+/**
+ * 没有任何实际作用,只用于 IDE 对I18的提示
+ */
+function $t(key: string) {
+  return key;
+}
+
+export { $t };

+ 4 - 0
packages/locales/src/langs/en-US.yaml

@@ -149,3 +149,7 @@ authentication:
   send-code: Get Security code
   send-text: "Reacquire in {0}s"
   third-party-login: Or continue with
+
+page:
+  about: About
+  document: Document

+ 4 - 0
packages/locales/src/langs/zh-CN.yaml

@@ -148,3 +148,7 @@ authentication:
   send-code: 获取验证码
   send-text: "{0}秒后重新获取"
   third-party-login: 其他登录方式
+
+page:
+  about: 关于
+  document: 文档

+ 7 - 0
packages/utils/build.config.ts

@@ -0,0 +1,7 @@
+import { defineBuildConfig } from 'unbuild';
+
+export default defineBuildConfig({
+  clean: true,
+  declaration: true,
+  entries: ['src/index'],
+});

+ 48 - 0
packages/utils/package.json

@@ -0,0 +1,48 @@
+{
+  "name": "@vben/utils",
+  "version": "1.0.0",
+  "type": "module",
+  "license": "MIT",
+  "homepage": "https://github.com/vbenjs/vue-vben-admin",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/vbenjs/vue-vben-admin.git",
+    "directory": "packages/utils"
+  },
+  "bugs": {
+    "url": "https://github.com/vbenjs/vue-vben-admin/issues"
+  },
+  "scripts": {
+    "build": "pnpm unbuild",
+    "stub": "pnpm unbuild --stub"
+  },
+  "files": [
+    "dist"
+  ],
+  "sideEffects": [
+    "**/*.css"
+  ],
+  "main": "./dist/index.mjs",
+  "module": "./dist/index.mjs",
+  "imports": {
+    "#*": "./src/*"
+  },
+  "exports": {
+    ".": {
+      "development": "./src/index.ts",
+      "types": "./src/index.ts",
+      "default": "./dist/index.mjs"
+    }
+  },
+  "publishConfig": {
+    "exports": {
+      ".": {
+        "types": "./dist/index.d.ts",
+        "default": "./dist/index.mjs"
+      }
+    }
+  },
+  "dependencies": {
+    "@vben-core/toolkit": "workspace:*"
+  }
+}

+ 1 - 0
packages/utils/src/index.ts

@@ -0,0 +1 @@
+export * from '@vben-core/toolkit';

+ 5 - 0
packages/utils/tsconfig.json

@@ -0,0 +1,5 @@
+{
+  "$schema": "https://json.schemastore.org/tsconfig",
+  "extends": "@vben/tsconfig/library.json",
+  "include": ["src"]
+}

+ 156 - 135
pnpm-lock.yaml

@@ -13,8 +13,8 @@ importers:
   .:
     devDependencies:
       '@changesets/cli':
-        specifier: ^2.27.2
-        version: 2.27.2
+        specifier: ^2.27.3
+        version: 2.27.3
       '@ls-lint/ls-lint':
         specifier: ^2.2.3
         version: 2.2.3
@@ -99,15 +99,15 @@ importers:
       '@vben-core/design-tokens':
         specifier: workspace:*
         version: link:../../packages/@vben-core/shared/design-tokens
-      '@vben-core/toolkit':
-        specifier: workspace:*
-        version: link:../../packages/@vben-core/shared/toolkit
       '@vben-core/typings':
         specifier: workspace:*
         version: link:../../packages/@vben-core/shared/typings
       '@vben/common-ui':
         specifier: workspace:*
         version: link:../../packages/business/common-ui
+      '@vben/constants':
+        specifier: workspace:*
+        version: link:../../packages/constants
       '@vben/hooks':
         specifier: workspace:*
         version: link:../../packages/hooks
@@ -126,12 +126,15 @@ importers:
       '@vben/stores':
         specifier: workspace:*
         version: link:../../packages/stores
+      '@vben/utils':
+        specifier: workspace:*
+        version: link:../../packages/utils
       ant-design-vue:
         specifier: ^4.2.1
         version: 4.2.1(vue@3.4.27(typescript@5.4.5))
       axios:
-        specifier: ^1.6.8
-        version: 1.6.8
+        specifier: ^1.7.1
+        version: 1.7.1
       dayjs:
         specifier: ^1.11.11
         version: 1.11.11
@@ -167,8 +170,8 @@ importers:
   internal/lint-configs/eslint-config:
     dependencies:
       eslint-plugin-command:
-        specifier: ^0.2.2
-        version: 0.2.2(eslint@8.57.0)
+        specifier: ^0.2.3
+        version: 0.2.3(eslint@8.57.0)
     devDependencies:
       '@eslint/js':
         specifier: ^9.3.0
@@ -177,11 +180,11 @@ importers:
         specifier: ^8.56.10
         version: 8.56.10
       '@typescript-eslint/eslint-plugin':
-        specifier: ^7.9.0
-        version: 7.9.0(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
+        specifier: ^7.10.0
+        version: 7.10.0(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
       '@typescript-eslint/parser':
-        specifier: ^7.9.0
-        version: 7.9.0(eslint@8.57.0)(typescript@5.4.5)
+        specifier: ^7.10.0
+        version: 7.10.0(eslint@8.57.0)(typescript@5.4.5)
       eslint:
         specifier: ^8.57.0
         version: 8.57.0
@@ -193,7 +196,7 @@ importers:
         version: 3.2.0(eslint@8.57.0)
       eslint-plugin-i:
         specifier: ^2.29.1
-        version: 2.29.1(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)
+        version: 2.29.1(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)
       eslint-plugin-jsdoc:
         specifier: ^48.2.5
         version: 48.2.5(eslint@8.57.0)
@@ -220,16 +223,16 @@ importers:
         version: 53.0.0(eslint@8.57.0)
       eslint-plugin-unused-imports:
         specifier: ^3.2.0
-        version: 3.2.0(@typescript-eslint/eslint-plugin@7.9.0(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)
+        version: 3.2.0(@typescript-eslint/eslint-plugin@7.10.0(@typescript-eslint/parser@7.10.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.9.0(@typescript-eslint/parser@7.9.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@1.6.0(@types/node@20.12.12)(jsdom@24.0.0)(sass@1.77.2)(terser@5.31.0))
+        version: 0.5.4(@typescript-eslint/eslint-plugin@7.10.0(@typescript-eslint/parser@7.10.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@1.6.0(@types/node@20.12.12)(jsdom@24.0.0)(sass@1.77.2)(terser@5.31.0))
       eslint-plugin-vue:
         specifier: ^9.26.0
         version: 9.26.0(eslint@8.57.0)
       globals:
-        specifier: ^15.2.0
-        version: 15.2.0
+        specifier: ^15.3.0
+        version: 15.3.0
       jsonc-eslint-parser:
         specifier: ^2.4.0
         version: 2.4.0
@@ -328,8 +331,8 @@ importers:
   internal/tailwind-config:
     dependencies:
       '@iconify/json':
-        specifier: ^2.2.211
-        version: 2.2.211
+        specifier: ^2.2.212
+        version: 2.2.212
       '@iconify/tailwind':
         specifier: ^1.1.1
         version: 1.1.1
@@ -392,8 +395,8 @@ importers:
         specifier: ^2.1.1
         version: 2.1.1(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0))
       vite-plugin-vue-devtools:
-        specifier: ^7.2.0
-        version: 7.2.0(rollup@4.17.2)(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))
+        specifier: ^7.2.1
+        version: 7.2.1(rollup@4.17.2)(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))
     devDependencies:
       '@types/html-minifier-terser':
         specifier: ^7.0.2
@@ -616,7 +619,7 @@ importers:
         version: 10.9.0(vue@3.4.27(typescript@5.4.5))
       '@vueuse/integrations':
         specifier: ^10.9.0
-        version: 10.9.0(async-validator@4.2.5)(axios@1.6.8)(nprogress@0.2.0)(qrcode@1.5.3)(vue@3.4.27(typescript@5.4.5))
+        version: 10.9.0(async-validator@4.2.5)(axios@1.7.1)(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
@@ -677,6 +680,8 @@ importers:
         specifier: workspace:*
         version: link:../../@vben-core/shared/typings
 
+  packages/constants: {}
+
   packages/hooks:
     dependencies:
       vue:
@@ -746,6 +751,12 @@ importers:
         specifier: ^4.3.2
         version: 4.3.2(vue@3.4.27(typescript@5.4.5))
 
+  packages/utils:
+    dependencies:
+      '@vben-core/toolkit':
+        specifier: workspace:*
+        version: link:../@vben-core/shared/toolkit
+
   scripts/vsh:
     dependencies:
       '@vben/node-utils':
@@ -761,8 +772,8 @@ importers:
         specifier: ^1.4.7
         version: 1.4.7
       publint:
-        specifier: ^0.2.7
-        version: 0.2.7
+        specifier: ^0.2.8
+        version: 0.2.8
 
 packages:
 
@@ -1089,8 +1100,8 @@ packages:
   '@changesets/changelog-git@0.2.0':
     resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==}
 
-  '@changesets/cli@2.27.2':
-    resolution: {integrity: sha512-6/kADjKMOrlLwNr/Y5HAq7T9oGOA2Lq5A59AGtwQCCiXuSGp4EgszzdJFeBiF8pdz7Wn1HaLzSUBhAaNToEJqg==}
+  '@changesets/cli@2.27.3':
+    resolution: {integrity: sha512-ve/VpWApILlSs8cr0okNx5C2LKRawI9XZgvfmf58S8sar2nhx5DPJREFXYZBahs0FeTfvH0rdVl+nGe8QF45Ig==}
     hasBin: true
 
   '@changesets/config@3.0.0':
@@ -1786,8 +1797,8 @@ packages:
   '@humanwhocodes/object-schema@2.0.3':
     resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
 
-  '@iconify/json@2.2.211':
-    resolution: {integrity: sha512-bg/rNpMYwjXJrOR8R56AuMuuNxcSgf/lw/EqtRUyPP1bbiXytF5VJModXohysnsN46M/gr+ydRUjIXRa/vuotA==}
+  '@iconify/json@2.2.212':
+    resolution: {integrity: sha512-d1IXjpbSwk8V3C9D+mruRTJRPqZZpCnOWh9zyG/Zc5zJmPDLBEHNTKz+ZmeiJQ6LdzgjwLJai1WgFvt1HWVIPw==}
 
   '@iconify/tailwind@1.1.1':
     resolution: {integrity: sha512-4mmA//qjZigv7D4KlqcVSYTqfRIJzyts2/lSCAJfCL0rVMIE76+ifJnaE5jxCo1+nYGBF8FsFo0qFOs+sX4EnA==}
@@ -1886,7 +1897,6 @@ packages:
 
   '@ls-lint/ls-lint@2.2.3':
     resolution: {integrity: sha512-ekM12jNm/7O2I/hsRv9HvYkRdfrHpiV1epVuI2NP+eTIcEgdIdKkKCs9KgQydu/8R5YXTov9aHdOgplmCHLupw==}
-    cpu: [x64, arm64, s390x]
     os: [darwin, linux, win32]
     hasBin: true
 
@@ -2264,8 +2274,8 @@ packages:
   '@types/which@3.0.3':
     resolution: {integrity: sha512-2C1+XoY0huExTbs8MQv1DuS5FS86+SEjdM9F/+GS61gg5Hqbtj8ZiDSx8MfWcyei907fIPbfPGCOrNUTnVHY1g==}
 
-  '@typescript-eslint/eslint-plugin@7.9.0':
-    resolution: {integrity: sha512-6e+X0X3sFe/G/54aC3jt0txuMTURqLyekmEHViqyA2VnxhLMpvA6nqmcjIy+Cr9tLDHPssA74BP5Mx9HQIxBEA==}
+  '@typescript-eslint/eslint-plugin@7.10.0':
+    resolution: {integrity: sha512-PzCr+a/KAef5ZawX7nbyNwBDtM1HdLIT53aSA2DDlxmxMngZ43O8SIePOeX8H5S+FHXeI6t97mTt/dDdzY4Fyw==}
     engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       '@typescript-eslint/parser': ^7.0.0
@@ -2275,8 +2285,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/parser@7.9.0':
-    resolution: {integrity: sha512-qHMJfkL5qvgQB2aLvhUSXxbK7OLnDkwPzFalg458pxQgfxKDfT1ZDbHQM/I6mDIf/svlMkj21kzKuQ2ixJlatQ==}
+  '@typescript-eslint/parser@7.10.0':
+    resolution: {integrity: sha512-2EjZMA0LUW5V5tGQiaa2Gys+nKdfrn2xiTIBLR4fxmPmVSvgPcKNW+AE/ln9k0A4zDUti0J/GZXMDupQoI+e1w==}
     engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       eslint: ^8.56.0
@@ -2285,16 +2295,16 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/scope-manager@7.8.0':
-    resolution: {integrity: sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==}
+  '@typescript-eslint/scope-manager@7.10.0':
+    resolution: {integrity: sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==}
     engines: {node: ^18.18.0 || >=20.0.0}
 
-  '@typescript-eslint/scope-manager@7.9.0':
-    resolution: {integrity: sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==}
+  '@typescript-eslint/scope-manager@7.8.0':
+    resolution: {integrity: sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==}
     engines: {node: ^18.18.0 || >=20.0.0}
 
-  '@typescript-eslint/type-utils@7.9.0':
-    resolution: {integrity: sha512-6Qy8dfut0PFrFRAZsGzuLoM4hre4gjzWJB6sUvdunCYZsYemTkzZNwF1rnGea326PHPT3zn5Lmg32M/xfJfByA==}
+  '@typescript-eslint/type-utils@7.10.0':
+    resolution: {integrity: sha512-D7tS4WDkJWrVkuzgm90qYw9RdgBcrWmbbRkrLA4d7Pg3w0ttVGDsvYGV19SH8gPR5L7OtcN5J1hTtyenO9xE9g==}
     engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       eslint: ^8.56.0
@@ -2303,6 +2313,10 @@ packages:
       typescript:
         optional: true
 
+  '@typescript-eslint/types@7.10.0':
+    resolution: {integrity: sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==}
+    engines: {node: ^18.18.0 || >=20.0.0}
+
   '@typescript-eslint/types@7.8.0':
     resolution: {integrity: sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==}
     engines: {node: ^18.18.0 || >=20.0.0}
@@ -2311,8 +2325,8 @@ packages:
     resolution: {integrity: sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==}
     engines: {node: ^18.18.0 || >=20.0.0}
 
-  '@typescript-eslint/typescript-estree@7.8.0':
-    resolution: {integrity: sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==}
+  '@typescript-eslint/typescript-estree@7.10.0':
+    resolution: {integrity: sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==}
     engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       typescript: '*'
@@ -2320,8 +2334,8 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/typescript-estree@7.9.0':
-    resolution: {integrity: sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==}
+  '@typescript-eslint/typescript-estree@7.8.0':
+    resolution: {integrity: sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==}
     engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       typescript: '*'
@@ -2329,24 +2343,24 @@ packages:
       typescript:
         optional: true
 
-  '@typescript-eslint/utils@7.8.0':
-    resolution: {integrity: sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==}
+  '@typescript-eslint/utils@7.10.0':
+    resolution: {integrity: sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==}
     engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       eslint: ^8.56.0
 
-  '@typescript-eslint/utils@7.9.0':
-    resolution: {integrity: sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==}
+  '@typescript-eslint/utils@7.8.0':
+    resolution: {integrity: sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==}
     engines: {node: ^18.18.0 || >=20.0.0}
     peerDependencies:
       eslint: ^8.56.0
 
-  '@typescript-eslint/visitor-keys@7.8.0':
-    resolution: {integrity: sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==}
+  '@typescript-eslint/visitor-keys@7.10.0':
+    resolution: {integrity: sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==}
     engines: {node: ^18.18.0 || >=20.0.0}
 
-  '@typescript-eslint/visitor-keys@7.9.0':
-    resolution: {integrity: sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==}
+  '@typescript-eslint/visitor-keys@7.8.0':
+    resolution: {integrity: sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==}
     engines: {node: ^18.18.0 || >=20.0.0}
 
   '@ungap/structured-clone@1.2.0':
@@ -2430,16 +2444,16 @@ packages:
   '@vue/devtools-api@6.6.1':
     resolution: {integrity: sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==}
 
-  '@vue/devtools-core@7.2.0':
-    resolution: {integrity: sha512-cHSeu70rTtubt2DYia+VDGNTC1m84Xyuk5eNTjmOpMLECaJnWnzCv6kR84EZp7rG+MVZalJG+4ecX2GaTbU3cQ==}
+  '@vue/devtools-core@7.2.1':
+    resolution: {integrity: sha512-OyWl455UnJIVgZ6lo5WQ79WbDMoXtSRwyNKp9WzCZ0HhuQywIk4qv59KtLRe75uVmtGBde4hXNaSyRm+x9bY6g==}
 
-  '@vue/devtools-kit@7.2.0':
-    resolution: {integrity: sha512-Kx+U0QiQg/g714euYKfnCdhTcOycSlH1oyTE57D0sAmisdsRCNLfXcnnIwcFY2jdCpuz9DNbuE0VWQuYF5zAZQ==}
+  '@vue/devtools-kit@7.2.1':
+    resolution: {integrity: sha512-Wak/fin1X0Q8LLIfCAHBrdaaB+R6IdpSXsDByPHbQ3BmkCP0/cIo/oEGp9i0U2+gEqD4L3V9RDjNf1S34DTzQQ==}
     peerDependencies:
       vue: ^3.0.0
 
-  '@vue/devtools-shared@7.2.0':
-    resolution: {integrity: sha512-gVr3IjKjU7axNvclRgICgy1gq/TDnF1hhBAEox+l5mMXZiTIFVIm1zpcIPssc0HxMDgzy+lXqOVsY4DGyZ+ZeA==}
+  '@vue/devtools-shared@7.2.1':
+    resolution: {integrity: sha512-PCJF4UknJmOal68+X9XHyVeQ+idv0LFujkTOIW30+GaMJqwFVN9LkQKX4gLqn61KkGMdJTzQ1bt7EJag3TI6AA==}
 
   '@vue/language-core@1.8.27':
     resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==}
@@ -2705,8 +2719,8 @@ packages:
     resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
     engines: {node: '>= 0.4'}
 
-  axios@1.6.8:
-    resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==}
+  axios@1.7.1:
+    resolution: {integrity: sha512-+LV37nQcd1EpFalkXksWNBiA17NZ5m5/WspmHGmZmdx1qBOg/VNq/c4eRJiA9VQQHBOs+N0ZhhdU10h2TyNK7Q==}
 
   balanced-match@1.0.2:
     resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
@@ -3588,8 +3602,8 @@ packages:
       eslint-import-resolver-webpack:
         optional: true
 
-  eslint-plugin-command@0.2.2:
-    resolution: {integrity: sha512-St9OM9w9/vjltIze9yhXPHKyc+wRHw3M/ql9Kvw+9BYE5BTsFz0f7nlI7xFIkOBEHs2MyZQCQLP1bV/qAT3q2w==}
+  eslint-plugin-command@0.2.3:
+    resolution: {integrity: sha512-1bBYNfjZg60N2ZpLV5ATYSYyueIJ+zl5yKrTs0UFDdnyu07dNSZ7Xplnc+Wb6SXTdc1sIaoIrnuyhvztcltX6A==}
     peerDependencies:
       eslint: '*'
 
@@ -4049,8 +4063,8 @@ packages:
     resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
     engines: {node: '>=18'}
 
-  globals@15.2.0:
-    resolution: {integrity: sha512-FQ5YwCHZM3nCmtb5FzEWwdUc9K5d3V/w9mzcz8iGD1gC/aOTHc6PouYu0kkKipNJqHAT7m51sqzQjEjIP+cK0A==}
+  globals@15.3.0:
+    resolution: {integrity: sha512-cCdyVjIUVTtX8ZsPkq1oCsOsLmGIswqnjZYMJJTGaNApj1yHtLSymKhwH51ttirREn75z3p4k051clwg7rvNKA==}
     engines: {node: '>=18'}
 
   globalthis@1.0.4:
@@ -5238,6 +5252,9 @@ packages:
   picocolors@1.0.0:
     resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
 
+  picocolors@1.0.1:
+    resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
+
   picomatch@2.3.1:
     resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
     engines: {node: '>=8.6'}
@@ -5807,8 +5824,8 @@ packages:
   psl@1.9.0:
     resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
 
-  publint@0.2.7:
-    resolution: {integrity: sha512-tLU4ee3110BxWfAmCZggJmCUnYWgPTr0QLnx08sqpLYa8JHRiOudd+CgzdpfU5x5eOaW2WMkpmOrFshRFYK7Mw==}
+  publint@0.2.8:
+    resolution: {integrity: sha512-C5MjGJ7gpanqaDpgBN+6QhjvXcoj0/YpbucoW29oO5729CGTMzfr3wZTIYcpzB1xl9ZfEqj4KL86P2Z50pt/JA==}
     engines: {node: '>=16'}
     hasBin: true
 
@@ -6856,8 +6873,8 @@ packages:
       mockjs: '>=1.1.0'
       vite: '>=4.0.0'
 
-  vite-plugin-vue-devtools@7.2.0:
-    resolution: {integrity: sha512-bFWwx/YF9M+aXTjDo0/6DrC7+WCzLg7wAmFoQA3Gd7cv5WV4u65hHSZN8bq0zhgHqtYQZdWnp0L2z6JNCwcIGg==}
+  vite-plugin-vue-devtools@7.2.1:
+    resolution: {integrity: sha512-4k7QNZz0nSojoePQoxnE5fIzi8RU1QJHc0TEg4golv2phZxhBGfjScZD2B8X6bcrRbUQ9CaRKN0dzBs1xtzzNg==}
     engines: {node: '>=v14.21.3'}
     peerDependencies:
       vite: ^3.1.0 || ^4.0.0-0 || ^5.0.0-0
@@ -7553,7 +7570,7 @@ snapshots:
     dependencies:
       '@changesets/types': 6.0.0
 
-  '@changesets/cli@2.27.2':
+  '@changesets/cli@2.27.3':
     dependencies:
       '@babel/runtime': 7.24.5
       '@changesets/apply-release-plan': 7.0.1
@@ -8245,7 +8262,7 @@ snapshots:
 
   '@humanwhocodes/object-schema@2.0.3': {}
 
-  '@iconify/json@2.2.211':
+  '@iconify/json@2.2.212':
     dependencies:
       '@iconify/types': 2.0.0
       pathe: 1.1.2
@@ -8758,14 +8775,14 @@ snapshots:
 
   '@types/which@3.0.3': {}
 
-  '@typescript-eslint/eslint-plugin@7.9.0(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)':
+  '@typescript-eslint/eslint-plugin@7.10.0(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)':
     dependencies:
       '@eslint-community/regexpp': 4.10.0
-      '@typescript-eslint/parser': 7.9.0(eslint@8.57.0)(typescript@5.4.5)
-      '@typescript-eslint/scope-manager': 7.9.0
-      '@typescript-eslint/type-utils': 7.9.0(eslint@8.57.0)(typescript@5.4.5)
-      '@typescript-eslint/utils': 7.9.0(eslint@8.57.0)(typescript@5.4.5)
-      '@typescript-eslint/visitor-keys': 7.9.0
+      '@typescript-eslint/parser': 7.10.0(eslint@8.57.0)(typescript@5.4.5)
+      '@typescript-eslint/scope-manager': 7.10.0
+      '@typescript-eslint/type-utils': 7.10.0(eslint@8.57.0)(typescript@5.4.5)
+      '@typescript-eslint/utils': 7.10.0(eslint@8.57.0)(typescript@5.4.5)
+      '@typescript-eslint/visitor-keys': 7.10.0
       eslint: 8.57.0
       graphemer: 1.4.0
       ignore: 5.3.1
@@ -8776,12 +8793,12 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5)':
+  '@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5)':
     dependencies:
-      '@typescript-eslint/scope-manager': 7.9.0
-      '@typescript-eslint/types': 7.9.0
-      '@typescript-eslint/typescript-estree': 7.9.0(typescript@5.4.5)
-      '@typescript-eslint/visitor-keys': 7.9.0
+      '@typescript-eslint/scope-manager': 7.10.0
+      '@typescript-eslint/types': 7.10.0
+      '@typescript-eslint/typescript-estree': 7.10.0(typescript@5.4.5)
+      '@typescript-eslint/visitor-keys': 7.10.0
       debug: 4.3.4
       eslint: 8.57.0
     optionalDependencies:
@@ -8789,20 +8806,20 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  '@typescript-eslint/scope-manager@7.10.0':
+    dependencies:
+      '@typescript-eslint/types': 7.10.0
+      '@typescript-eslint/visitor-keys': 7.10.0
+
   '@typescript-eslint/scope-manager@7.8.0':
     dependencies:
       '@typescript-eslint/types': 7.8.0
       '@typescript-eslint/visitor-keys': 7.8.0
 
-  '@typescript-eslint/scope-manager@7.9.0':
-    dependencies:
-      '@typescript-eslint/types': 7.9.0
-      '@typescript-eslint/visitor-keys': 7.9.0
-
-  '@typescript-eslint/type-utils@7.9.0(eslint@8.57.0)(typescript@5.4.5)':
+  '@typescript-eslint/type-utils@7.10.0(eslint@8.57.0)(typescript@5.4.5)':
     dependencies:
-      '@typescript-eslint/typescript-estree': 7.9.0(typescript@5.4.5)
-      '@typescript-eslint/utils': 7.9.0(eslint@8.57.0)(typescript@5.4.5)
+      '@typescript-eslint/typescript-estree': 7.10.0(typescript@5.4.5)
+      '@typescript-eslint/utils': 7.10.0(eslint@8.57.0)(typescript@5.4.5)
       debug: 4.3.4
       eslint: 8.57.0
       ts-api-utils: 1.3.0(typescript@5.4.5)
@@ -8811,14 +8828,16 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  '@typescript-eslint/types@7.10.0': {}
+
   '@typescript-eslint/types@7.8.0': {}
 
   '@typescript-eslint/types@7.9.0': {}
 
-  '@typescript-eslint/typescript-estree@7.8.0(typescript@5.4.5)':
+  '@typescript-eslint/typescript-estree@7.10.0(typescript@5.4.5)':
     dependencies:
-      '@typescript-eslint/types': 7.8.0
-      '@typescript-eslint/visitor-keys': 7.8.0
+      '@typescript-eslint/types': 7.10.0
+      '@typescript-eslint/visitor-keys': 7.10.0
       debug: 4.3.4
       globby: 11.1.0
       is-glob: 4.0.3
@@ -8830,10 +8849,10 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  '@typescript-eslint/typescript-estree@7.9.0(typescript@5.4.5)':
+  '@typescript-eslint/typescript-estree@7.8.0(typescript@5.4.5)':
     dependencies:
-      '@typescript-eslint/types': 7.9.0
-      '@typescript-eslint/visitor-keys': 7.9.0
+      '@typescript-eslint/types': 7.8.0
+      '@typescript-eslint/visitor-keys': 7.8.0
       debug: 4.3.4
       globby: 11.1.0
       is-glob: 4.0.3
@@ -8845,6 +8864,17 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  '@typescript-eslint/utils@7.10.0(eslint@8.57.0)(typescript@5.4.5)':
+    dependencies:
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
+      '@typescript-eslint/scope-manager': 7.10.0
+      '@typescript-eslint/types': 7.10.0
+      '@typescript-eslint/typescript-estree': 7.10.0(typescript@5.4.5)
+      eslint: 8.57.0
+    transitivePeerDependencies:
+      - supports-color
+      - typescript
+
   '@typescript-eslint/utils@7.8.0(eslint@8.57.0)(typescript@5.4.5)':
     dependencies:
       '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
@@ -8859,27 +8889,16 @@ snapshots:
       - supports-color
       - typescript
 
-  '@typescript-eslint/utils@7.9.0(eslint@8.57.0)(typescript@5.4.5)':
+  '@typescript-eslint/visitor-keys@7.10.0':
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
-      '@typescript-eslint/scope-manager': 7.9.0
-      '@typescript-eslint/types': 7.9.0
-      '@typescript-eslint/typescript-estree': 7.9.0(typescript@5.4.5)
-      eslint: 8.57.0
-    transitivePeerDependencies:
-      - supports-color
-      - typescript
+      '@typescript-eslint/types': 7.10.0
+      eslint-visitor-keys: 3.4.3
 
   '@typescript-eslint/visitor-keys@7.8.0':
     dependencies:
       '@typescript-eslint/types': 7.8.0
       eslint-visitor-keys: 3.4.3
 
-  '@typescript-eslint/visitor-keys@7.9.0':
-    dependencies:
-      '@typescript-eslint/types': 7.9.0
-      eslint-visitor-keys: 3.4.3
-
   '@ungap/structured-clone@1.2.0': {}
 
   '@vitejs/plugin-vue-jsx@3.1.0(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))':
@@ -9013,10 +9032,10 @@ snapshots:
 
   '@vue/devtools-api@6.6.1': {}
 
-  '@vue/devtools-core@7.2.0(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))':
+  '@vue/devtools-core@7.2.1(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))':
     dependencies:
-      '@vue/devtools-kit': 7.2.0(vue@3.4.27(typescript@5.4.5))
-      '@vue/devtools-shared': 7.2.0
+      '@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
@@ -9025,16 +9044,16 @@ snapshots:
       - vite
       - vue
 
-  '@vue/devtools-kit@7.2.0(vue@3.4.27(typescript@5.4.5))':
+  '@vue/devtools-kit@7.2.1(vue@3.4.27(typescript@5.4.5))':
     dependencies:
-      '@vue/devtools-shared': 7.2.0
+      '@vue/devtools-shared': 7.2.1
       hookable: 5.5.3
       mitt: 3.0.1
       perfect-debounce: 1.0.0
       speakingurl: 14.0.1
       vue: 3.4.27(typescript@5.4.5)
 
-  '@vue/devtools-shared@7.2.0':
+  '@vue/devtools-shared@7.2.1':
     dependencies:
       rfdc: 1.3.1
 
@@ -9102,14 +9121,14 @@ snapshots:
       - '@vue/composition-api'
       - vue
 
-  '@vueuse/integrations@10.9.0(async-validator@4.2.5)(axios@1.6.8)(nprogress@0.2.0)(qrcode@1.5.3)(vue@3.4.27(typescript@5.4.5))':
+  '@vueuse/integrations@10.9.0(async-validator@4.2.5)(axios@1.7.1)(nprogress@0.2.0)(qrcode@1.5.3)(vue@3.4.27(typescript@5.4.5))':
     dependencies:
       '@vueuse/core': 10.9.0(vue@3.4.27(typescript@5.4.5))
       '@vueuse/shared': 10.9.0(vue@3.4.27(typescript@5.4.5))
       vue-demi: 0.14.7(vue@3.4.27(typescript@5.4.5))
     optionalDependencies:
       async-validator: 4.2.5
-      axios: 1.6.8
+      axios: 1.7.1
       nprogress: 0.2.0
       qrcode: 1.5.3
     transitivePeerDependencies:
@@ -9314,7 +9333,7 @@ snapshots:
     dependencies:
       possible-typed-array-names: 1.0.0
 
-  axios@1.6.8:
+  axios@1.7.1:
     dependencies:
       follow-redirects: 1.15.6
       form-data: 4.0.0
@@ -10308,17 +10327,17 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  eslint-module-utils@2.8.1(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0):
+  eslint-module-utils@2.8.1(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0):
     dependencies:
       debug: 3.2.7
     optionalDependencies:
-      '@typescript-eslint/parser': 7.9.0(eslint@8.57.0)(typescript@5.4.5)
+      '@typescript-eslint/parser': 7.10.0(eslint@8.57.0)(typescript@5.4.5)
       eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
     transitivePeerDependencies:
       - supports-color
 
-  eslint-plugin-command@0.2.2(eslint@8.57.0):
+  eslint-plugin-command@0.2.3(eslint@8.57.0):
     dependencies:
       '@es-joy/jsdoccomment': 0.43.0
       eslint: 8.57.0
@@ -10336,13 +10355,13 @@ snapshots:
       eslint: 8.57.0
       ignore: 5.3.1
 
-  eslint-plugin-i@2.29.1(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0):
+  eslint-plugin-i@2.29.1(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0):
     dependencies:
       debug: 4.3.4
       doctrine: 3.0.0
       eslint: 8.57.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0)
+      eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0)
       get-tsconfig: 4.7.4
       is-glob: 4.0.3
       minimatch: 3.1.2
@@ -10386,7 +10405,7 @@ snapshots:
       eslint: 8.57.0
       eslint-plugin-es-x: 7.6.0(eslint@8.57.0)
       get-tsconfig: 4.7.4
-      globals: 15.2.0
+      globals: 15.3.0
       ignore: 5.3.1
       minimatch: 9.0.4
       semver: 7.6.2
@@ -10448,19 +10467,19 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  eslint-plugin-unused-imports@3.2.0(@typescript-eslint/eslint-plugin@7.9.0(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0):
+  eslint-plugin-unused-imports@3.2.0(@typescript-eslint/eslint-plugin@7.10.0(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0):
     dependencies:
       eslint: 8.57.0
       eslint-rule-composer: 0.3.0
     optionalDependencies:
-      '@typescript-eslint/eslint-plugin': 7.9.0(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
+      '@typescript-eslint/eslint-plugin': 7.10.0(@typescript-eslint/parser@7.10.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.9.0(@typescript-eslint/parser@7.9.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@1.6.0(@types/node@20.12.12)(jsdom@24.0.0)(sass@1.77.2)(terser@5.31.0)):
+  eslint-plugin-vitest@0.5.4(@typescript-eslint/eslint-plugin@7.10.0(@typescript-eslint/parser@7.10.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@1.6.0(@types/node@20.12.12)(jsdom@24.0.0)(sass@1.77.2)(terser@5.31.0)):
     dependencies:
       '@typescript-eslint/utils': 7.8.0(eslint@8.57.0)(typescript@5.4.5)
       eslint: 8.57.0
     optionalDependencies:
-      '@typescript-eslint/eslint-plugin': 7.9.0(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
+      '@typescript-eslint/eslint-plugin': 7.10.0(@typescript-eslint/parser@7.10.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
       vitest: 1.6.0(@types/node@20.12.12)(jsdom@24.0.0)(sass@1.77.2)(terser@5.31.0)
     transitivePeerDependencies:
       - supports-color
@@ -10895,7 +10914,7 @@ snapshots:
 
   globals@14.0.0: {}
 
-  globals@15.2.0: {}
+  globals@15.3.0: {}
 
   globalthis@1.0.4:
     dependencies:
@@ -12055,6 +12074,8 @@ snapshots:
 
   picocolors@1.0.0: {}
 
+  picocolors@1.0.1: {}
+
   picomatch@2.3.1: {}
 
   pidtree@0.6.0: {}
@@ -12574,10 +12595,10 @@ snapshots:
 
   psl@1.9.0: {}
 
-  publint@0.2.7:
+  publint@0.2.8:
     dependencies:
       npm-packlist: 5.1.3
-      picocolors: 1.0.0
+      picocolors: 1.0.1
       sade: 1.8.1
 
   punycode@2.3.1: {}
@@ -13781,11 +13802,11 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  vite-plugin-vue-devtools@7.2.0(rollup@4.17.2)(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5)):
+  vite-plugin-vue-devtools@7.2.1(rollup@4.17.2)(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5)):
     dependencies:
-      '@vue/devtools-core': 7.2.0(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))
-      '@vue/devtools-kit': 7.2.0(vue@3.4.27(typescript@5.4.5))
-      '@vue/devtools-shared': 7.2.0
+      '@vue/devtools-core': 7.2.1(vite@5.2.11(@types/node@20.12.12)(sass@1.77.2)(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: 5.2.11(@types/node@20.12.12)(sass@1.77.2)(terser@5.31.0)

+ 8 - 0
vben-admin.code-workspace

@@ -84,6 +84,10 @@
       "name": "@vben/layouts",
       "path": "packages/business/layouts",
     },
+    {
+      "name": "@vben/constants",
+      "path": "packages/constants",
+    },
     {
       "name": "@vben/hooks",
       "path": "packages/hooks",
@@ -104,6 +108,10 @@
       "name": "@vben/stores",
       "path": "packages/stores",
     },
+    {
+      "name": "@vben/utils",
+      "path": "packages/utils",
+    },
     {
       "name": "@vben/vsh",
       "path": "scripts/vsh",