Explorar o código

refactor(project): remove the use of core internal bem syntax

vben hai 8 meses
pai
achega
ebf73b2df9
Modificáronse 37 ficheiros con 731 adicións e 174 borrados
  1. 1 0
      apps/web-antd/package.json
  2. 2 2
      apps/web-antd/src/views/dashboard/analytics/index.vue
  3. 19 19
      packages/@core/forward/stores/src/modules/tabbar.ts
  4. 2 2
      packages/@core/shared/hooks/package.json
  5. 7 0
      packages/@core/shared/hooks/src/index.ts
  6. 0 0
      packages/@core/shared/hooks/src/use-namespace.ts
  7. 5 2
      packages/@core/shared/hooks/src/use-sortable.test.ts
  8. 0 1
      packages/@core/shared/toolkit/src/index.ts
  9. 2 2
      packages/@core/shared/typings/src/basic.d.ts
  10. 1 1
      packages/@core/shared/typings/src/tabs.ts
  11. 33 0
      packages/@core/ui-kit/menu-ui/build.config.ts
  12. 2 2
      packages/@core/ui-kit/menu-ui/package.json
  13. 1 1
      packages/@core/ui-kit/menu-ui/src/components/menu-item.vue
  14. 511 1
      packages/@core/ui-kit/menu-ui/src/components/menu.vue
  15. 28 32
      packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue
  16. 1 1
      packages/@core/ui-kit/menu-ui/src/components/sub-menu-content.vue
  17. 1 1
      packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue
  18. 0 2
      packages/@core/ui-kit/menu-ui/src/index.ts
  19. 1 1
      packages/@core/ui-kit/menu-ui/src/menu.vue
  20. 49 52
      packages/@core/ui-kit/menu-ui/src/styles/index.scss
  21. 0 3
      packages/@core/ui-kit/menu-ui/vite.config.mts
  22. 1 7
      packages/@core/ui-kit/shadcn-ui/src/index.ts
  23. 0 1
      packages/@core/ui-kit/tabs-ui/package.json
  24. 1 0
      packages/@core/ui-kit/tabs-ui/src/components/index.ts
  25. 9 6
      packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue
  26. 0 5
      packages/@core/ui-kit/tabs-ui/src/components/tabs/index.vue
  27. 11 0
      packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue
  28. 3 4
      packages/@core/ui-kit/tabs-ui/src/tabs-view.vue
  29. 11 3
      packages/@core/ui-kit/tabs-ui/src/types.ts
  30. 1 0
      packages/effects/common-ui/package.json
  31. 1 1
      packages/effects/common-ui/src/authentication/login-expired-modal.vue
  32. 2 2
      packages/effects/common-ui/src/dashboard/analysis/analysis-charts-tabs.vue
  33. 3 3
      packages/effects/layouts/src/basic/tabbar/use-tabs.ts
  34. 2 7
      packages/effects/layouts/src/widgets/theme-toggle/theme-button.vue
  35. 3 0
      packages/hooks/package.json
  36. 1 1
      packages/hooks/src/index.ts
  37. 16 9
      pnpm-lock.yaml

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

@@ -35,6 +35,7 @@
     "@vben/chart-ui": "workspace:*",
     "@vben/common-ui": "workspace:*",
     "@vben/constants": "workspace:*",
+    "@vben/hooks": "workspace:*",
     "@vben/icons": "workspace:*",
     "@vben/layouts": "workspace:*",
     "@vben/styles": "workspace:*",

+ 2 - 2
apps/web-antd/src/views/dashboard/analytics/index.vue

@@ -1,6 +1,6 @@
 <script lang="ts" setup>
 import type { AnalysisOverviewItem } from '@vben/common-ui';
-import type { TabsOption } from '@vben/types';
+import type { TabOption } from '@vben/types';
 
 import {
   AnalysisChartCard,
@@ -53,7 +53,7 @@ const overviewItems: AnalysisOverviewItem[] = [
   },
 ];
 
-const chartTabs: TabsOption[] = [
+const chartTabs: TabOption[] = [
   {
     label: '流量趋势',
     value: 'trends',

+ 19 - 19
packages/@core/forward/stores/src/modules/tabbar.ts

@@ -1,4 +1,4 @@
-import type { TabItem } from '@vben-core/typings';
+import type { TabDefinition } from '@vben-core/typings';
 import type { RouteRecordNormalized, Router } from 'vue-router';
 
 import { toRaw } from 'vue';
@@ -27,7 +27,7 @@ interface TabsState {
   /**
    * @zh_CN 当前打开的标签页列表
    */
-  tabs: TabItem[];
+  tabs: TabDefinition[];
 }
 
 /**
@@ -49,7 +49,7 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
      * @zh_CN 关闭标签页
      * @param tab
      */
-    _close(tab: TabItem) {
+    _close(tab: TabDefinition) {
       const { fullPath } = tab;
       if (isAffixTab(tab)) {
         return;
@@ -72,7 +72,7 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
      * @zh_CN 跳转到标签页
      * @param tab
      */
-    async _goToTab(tab: TabItem, router: Router) {
+    async _goToTab(tab: TabDefinition, router: Router) {
       const { params, path, query } = tab;
       const toParams = {
         params: params || {},
@@ -85,7 +85,7 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
      * @zh_CN 添加标签页
      * @param routeTab
      */
-    addTab(routeTab: TabItem) {
+    addTab(routeTab: TabDefinition) {
       const tab = cloneTab(routeTab);
       if (!isTabShown(tab)) {
         return;
@@ -116,7 +116,7 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
      * @zh_CN 关闭左侧标签页
      * @param tab
      */
-    async closeLeftTabs(tab: TabItem) {
+    async closeLeftTabs(tab: TabDefinition) {
       const index = this.tabs.findIndex(
         (item) => getTabPath(item) === getTabPath(tab),
       );
@@ -139,7 +139,7 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
      * @zh_CN 关闭其他标签页
      * @param tab
      */
-    async closeOtherTabs(tab: TabItem) {
+    async closeOtherTabs(tab: TabDefinition) {
       const closePaths = this.tabs.map((item) => getTabPath(item));
 
       const paths: string[] = [];
@@ -162,7 +162,7 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
      * @zh_CN 关闭右侧标签页
      * @param tab
      */
-    async closeRightTabs(tab: TabItem) {
+    async closeRightTabs(tab: TabDefinition) {
       const index = this.tabs.findIndex(
         (item) => getTabPath(item) === getTabPath(tab),
       );
@@ -185,7 +185,7 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
      * @param tab
      * @param router
      */
-    async closeTab(tab: TabItem, router: Router) {
+    async closeTab(tab: TabDefinition, router: Router) {
       const { currentRoute } = router;
 
       // 关闭不是激活选项卡
@@ -230,7 +230,7 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
      * @zh_CN 固定标签页
      * @param tab
      */
-    async pinTab(tab: TabItem) {
+    async pinTab(tab: TabDefinition) {
       const index = this.tabs.findIndex(
         (item) => getTabPath(item) === getTabPath(tab),
       );
@@ -282,7 +282,7 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
      * @zh_CN 取消固定标签页
      * @param tab
      */
-    async unpinTab(tab: TabItem) {
+    async unpinTab(tab: TabDefinition) {
       const index = this.tabs.findIndex(
         (item) => getTabPath(item) === getTabPath(tab),
       );
@@ -318,7 +318,7 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
     },
   },
   getters: {
-    affixTabs(): TabItem[] {
+    affixTabs(): TabDefinition[] {
       return this.tabs.filter((tab) => isAffixTab(tab));
     },
     getCachedTabs(): string[] {
@@ -327,7 +327,7 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
     getExcludeCachedTabs(): string[] {
       return [...this.excludeCachedTabs];
     },
-    getTabs(): TabItem[] {
+    getTabs(): TabDefinition[] {
       const affixTabs = this.tabs.filter((tab) => isAffixTab(tab));
       const normalTabs = this.tabs.filter((tab) => !isAffixTab(tab));
       return [...affixTabs, ...normalTabs].filter(Boolean);
@@ -359,7 +359,7 @@ if (hot) {
  * @zh_CN 克隆路由,防止路由被修改
  * @param route
  */
-function cloneTab(route: TabItem): TabItem {
+function cloneTab(route: TabDefinition): TabDefinition {
   if (!route) {
     return route;
   }
@@ -380,7 +380,7 @@ function cloneTab(route: TabItem): TabItem {
  * @zh_CN 是否是固定标签页
  * @param tab
  */
-function isAffixTab(tab: TabItem) {
+function isAffixTab(tab: TabDefinition) {
   return tab?.meta?.affixTab ?? false;
 }
 
@@ -388,7 +388,7 @@ function isAffixTab(tab: TabItem) {
  * @zh_CN 是否显示标签
  * @param tab
  */
-function isTabShown(tab: TabItem) {
+function isTabShown(tab: TabDefinition) {
   return !tab.meta.hideInTab;
 }
 
@@ -396,8 +396,8 @@ function isTabShown(tab: TabItem) {
  * @zh_CN 获取标签页路径
  * @param tab
  */
-function getTabPath(tab: RouteRecordNormalized | TabItem) {
-  return decodeURIComponent((tab as TabItem).fullPath || tab.path);
+function getTabPath(tab: RouteRecordNormalized | TabDefinition) {
+  return decodeURIComponent((tab as TabDefinition).fullPath || tab.path);
 }
 
 function routeToTab(route: RouteRecordNormalized) {
@@ -405,7 +405,7 @@ function routeToTab(route: RouteRecordNormalized) {
     meta: route.meta,
     name: route.name,
     path: route.path,
-  } as TabItem;
+  } as TabDefinition;
 }
 
 export { useCoreTabbarStore };

+ 2 - 2
packages/@core/shared/hooks/package.json

@@ -36,8 +36,8 @@
     }
   },
   "dependencies": {
-    "sortablejs": "^1.15.2",
-    "vue": "^3.4.31"
+    "radix-vue": "^1.9.1",
+    "sortablejs": "^1.15.2"
   },
   "devDependencies": {
     "@types/sortablejs": "^1.15.8"

+ 7 - 0
packages/@core/shared/hooks/src/index.ts

@@ -1 +1,8 @@
+export * from './use-namespace';
 export * from './use-sortable';
+export {
+  useEmitAsProps,
+  useForwardExpose,
+  useForwardProps,
+  useForwardPropsEmits,
+} from 'radix-vue';

+ 0 - 0
packages/@core/shared/toolkit/src/namespace.ts → packages/@core/shared/hooks/src/use-namespace.ts


+ 5 - 2
packages/@core/shared/hooks/src/use-sortable.test.ts

@@ -6,7 +6,7 @@ import { useSortable } from './use-sortable';
 
 describe('useSortable', () => {
   beforeEach(() => {
-    vi.mock('sortablejs', () => ({
+    vi.mock('sortablejs/modular/sortable.complete.esm.js', () => ({
       default: {
         create: vi.fn(),
       },
@@ -29,7 +29,10 @@ describe('useSortable', () => {
     await initializeSortable();
 
     // Import sortablejs to access the mocked create function
-    const Sortable = await import('sortablejs');
+    const Sortable = await import(
+      // @ts-expect-error - This is a dynamic import
+      'sortablejs/modular/sortable.complete.esm.js'
+    );
 
     // Verify that Sortable.create was called with the correct parameters
     expect(Sortable.default.create).toHaveBeenCalledTimes(1);

+ 0 - 1
packages/@core/shared/toolkit/src/index.ts

@@ -6,7 +6,6 @@ export * from './dom';
 export * from './inference';
 export * from './letter';
 export * from './merge';
-export * from './namespace';
 export * from './nprogress';
 export * from './tree';
 export * from './unique';

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

@@ -5,7 +5,7 @@ interface BasicOption {
 
 interface SelectOption extends BasicOption {}
 
-interface TabsOption extends BasicOption {}
+interface TabOption extends BasicOption {}
 
 interface BasicUserInfo {
   /**
@@ -30,4 +30,4 @@ interface BasicUserInfo {
   username: string;
 }
 
-export type { BasicOption, BasicUserInfo, SelectOption, TabsOption };
+export type { BasicOption, BasicUserInfo, SelectOption, TabOption };

+ 1 - 1
packages/@core/shared/typings/src/tabs.ts

@@ -1,3 +1,3 @@
 import type { RouteLocationNormalized } from 'vue-router';
 
-export type TabItem = RouteLocationNormalized;
+export type TabDefinition = RouteLocationNormalized;

+ 33 - 0
packages/@core/ui-kit/menu-ui/build.config.ts

@@ -0,0 +1,33 @@
+import { defineBuildConfig } from 'unbuild';
+
+export default defineBuildConfig({
+  clean: true,
+  declaration: true,
+  entries: [
+    {
+      builder: 'mkdist',
+      input: './src',
+      pattern: ['**/*'],
+    },
+    {
+      builder: 'mkdist',
+      input: './src',
+      loaders: ['vue'],
+      pattern: ['**/*.vue'],
+    },
+    // {
+    //   builder: 'mkdist',
+    //   format: 'cjs',
+    //   input: './src',
+    //   loaders: ['js'],
+    //   pattern: ['**/*.ts'],
+    // },
+    {
+      builder: 'mkdist',
+      format: 'esm',
+      input: './src',
+      loaders: ['js'],
+      pattern: ['**/*.ts'],
+    },
+  ],
+});

+ 2 - 2
packages/@core/ui-kit/menu-ui/package.json

@@ -11,7 +11,7 @@
   "license": "MIT",
   "type": "module",
   "scripts": {
-    "build": "pnpm vite build",
+    "build": "pnpm unbuild",
     "prepublishOnly": "npm run build"
   },
   "files": [
@@ -37,7 +37,7 @@
     }
   },
   "dependencies": {
-    "@vben-core/design": "workspace:*",
+    "@vben-core/hooks": "workspace:*",
     "@vben-core/iconify": "workspace:*",
     "@vben-core/shadcn-ui": "workspace:*",
     "@vben-core/toolkit": "workspace:*",

+ 1 - 1
packages/@core/ui-kit/menu-ui/src/components/menu-item.vue

@@ -3,8 +3,8 @@ import type { MenuItemProps, MenuItemRegistered } from '../interface';
 
 import { computed, onBeforeUnmount, onMounted, reactive, useSlots } from 'vue';
 
+import { useNamespace } from '@vben-core/hooks';
 import { VbenIcon, VbenMenuBadge, VbenTooltip } from '@vben-core/shadcn-ui';
-import { useNamespace } from '@vben-core/toolkit';
 
 import { useMenu, useMenuContext, useSubMenuContext } from '../hooks';
 

+ 511 - 1
packages/@core/ui-kit/menu-ui/src/components/menu.vue

@@ -18,8 +18,9 @@ import {
   watchEffect,
 } from 'vue';
 
+import { useNamespace } from '@vben-core/hooks';
 import { IcRoundMoreHoriz } from '@vben-core/iconify';
-import { isHttpUrl, useNamespace } from '@vben-core/toolkit';
+import { isHttpUrl } from '@vben-core/toolkit';
 
 import { UseResizeObserverReturn, useResizeObserver } from '@vueuse/core';
 
@@ -348,3 +349,512 @@ function removeMenuItem(item: MenuItemRegistered) {
     </template>
   </ul>
 </template>
+
+<style lang="scss">
+$namespace: vben;
+
+@mixin menu-item-active {
+  color: var(--menu-item-active-color);
+  text-decoration: none;
+  cursor: pointer;
+  background: var(--menu-item-active-background-color);
+}
+
+@mixin menu-item {
+  position: relative;
+  display: flex;
+  // gap: 12px;
+  align-items: center;
+  height: var(--menu-item-height);
+  padding: var(--menu-item-padding-y) var(--menu-item-padding-x);
+  margin: 0 var(--menu-item-margin-x) var(--menu-item-margin-y)
+    var(--menu-item-margin-x);
+  font-size: var(--menu-font-size);
+  color: var(--menu-item-color);
+  text-decoration: none;
+  white-space: nowrap;
+  list-style: none;
+  cursor: pointer;
+  background: var(--menu-item-background-color);
+  border: none;
+  border-radius: var(--menu-item-radius);
+  transition:
+    background 0.15s ease,
+    color 0.15s ease,
+    padding 0.15s ease,
+    border-color 0.15s ease;
+
+  &.is-disabled {
+    cursor: not-allowed;
+    background: none !important;
+    opacity: 0.25;
+  }
+
+  .#{$namespace}-menu__icon {
+    transition: transform 0.25s;
+  }
+
+  &:hover {
+    .#{$namespace}-menu__icon {
+      transform: scale(1.3);
+    }
+  }
+
+  &:hover,
+  &:focus {
+    outline: none;
+  }
+
+  * {
+    vertical-align: bottom;
+  }
+}
+
+@mixin menu-title {
+  max-width: var(--menu-title-width);
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  opacity: 1;
+}
+
+.#{$namespace}-menu__popup-container,
+.#{$namespace}-menu {
+  --menu-title-width: 140px;
+  --menu-item-icon-width: 20px;
+  --menu-item-height: 38px;
+  --menu-item-padding-y: 26px;
+  --menu-item-padding-x: 12px;
+  --menu-item-popup-padding-y: 22px;
+  --menu-item-popup-padding-x: 12px;
+  --menu-item-margin-y: 4px;
+  --menu-item-margin-x: 0px;
+  --menu-item-collapse-padding-y: 25px;
+  --menu-item-collapse-padding-x: 0px;
+  --menu-item-collapse-margin-y: 4px;
+  --menu-item-collapse-margin-x: 0px;
+  --menu-item-radius: 0px;
+  --menu-item-indent: 16px;
+  --menu-font-size: 14px;
+  --menu-dark-background: 0deg 0% 100% / 10%;
+  --menu-light-background: 192deg 1% 93%;
+
+  &.is-dark {
+    --menu-background-color: hsl(var(--menu-dark));
+    // --menu-submenu-opened-background-color: hsl(var(--menu-opened-dark));
+    --menu-item-background-color: var(--menu-background-color);
+    --menu-item-color: hsl(var(--dark-foreground) / 80%);
+    --menu-item-hover-color: hsl(var(--primary-foreground));
+    --menu-item-hover-background-color: hsl(var(--menu-dark-background));
+    --menu-item-active-color: hsl(var(--primary-foreground));
+    --menu-item-active-background-color: hsl(var(--primary));
+    --menu-submenu-hover-color: hsl(var(--dark-foreground));
+    --menu-submenu-hover-background-color: hsl(var(--menu-dark-background));
+    --menu-submenu-active-color: hsl(var(--dark-foreground));
+    --menu-submenu-active-background-color: transparent;
+    --menu-submenu-background-color: var(--menu-background-color);
+  }
+
+  &.is-light {
+    --menu-background-color: hsl(var(--menu));
+    // --menu-submenu-opened-background-color: hsl(var(--menu-opened));
+    --menu-item-background-color: var(--menu-background-color);
+    --menu-item-color: hsl(var(--foreground));
+    --menu-item-hover-color: var(--menu-item-color);
+    --menu-item-hover-background-color: hsl(var(--menu-light-background));
+    --menu-item-active-color: hsl(var(--primary-foreground));
+    --menu-item-active-background-color: hsl(var(--primary));
+    --menu-submenu-hover-color: hsl(var(--primary));
+    --menu-submenu-hover-background-color: hsl(var(--menu-light-background));
+    --menu-submenu-active-color: hsl(var(--primary));
+    --menu-submenu-active-background-color: transparent;
+    --menu-submenu-background-color: var(--menu-background-color);
+  }
+
+  &.is-rounded {
+    --menu-item-margin-x: 8px;
+    --menu-item-collapse-margin-x: 6px;
+    --menu-item-radius: 6px;
+  }
+
+  &.is-horizontal:not(.is-rounded) {
+    --menu-item-height: 60px;
+    --menu-item-radius: 0px;
+  }
+
+  &.is-horizontal.is-rounded {
+    --menu-item-height: 40px;
+    --menu-item-radius: 6px;
+    --menu-item-padding-x: 12px;
+  }
+
+  // .vben-menu__popup,
+  &.is-horizontal {
+    --menu-item-padding-y: 0px;
+    --menu-item-padding-x: 10px;
+    --menu-item-margin-y: 0px;
+    --menu-item-margin-x: 1px;
+    --menu-background-color: transparent;
+
+    &.is-dark {
+      --menu-item-hover-color: var(--foreground);
+      --menu-item-hover-background-color: hsl(var(--menu-dark-background));
+      --menu-item-active-color: hsl(var(--foreground));
+      --menu-item-active-background-color: hsl(var(--menu-dark-background));
+      --menu-submenu-active-color: hsl(var(--foreground));
+      --menu-submenu-active-background-color: hsl(var(--menu-dark-background));
+      --menu-submenu-hover-color: hsl(var(--foreground));
+      --menu-submenu-hover-background-color: hsl(var(--menu-dark-background));
+    }
+
+    &.is-light {
+      --menu-item-active-color: hsl(var(--foreground));
+      --menu-item-active-background-color: hsl(var(--menu-light-background));
+      --menu-item-hover-background-color: hsl(var(--menu-light-background));
+      --menu-item-hover-color: hsl(var(--primary));
+      --menu-submenu-hover-color: hsl(var(--primary));
+      --menu-submenu-hover-background-color: hsl(var(--menu-light-background));
+      --menu-submenu-active-color: hsl(var(--foreground));
+      --menu-submenu-active-background-color: hsl(var(--menu-light-background));
+    }
+  }
+}
+
+.#{$namespace}-menu {
+  position: relative;
+  box-sizing: border-box;
+  padding-left: 0;
+  margin: 0;
+  list-style: none;
+  background: hsl(var(--menu-background-color));
+
+  // 垂直菜单
+  &.is-vertical {
+    &:not(.#{$namespace}-menu.is-collapse) {
+      & .#{$namespace}-menu-item,
+      & .#{$namespace}-sub-menu-content,
+      & .#{$namespace}-menu-item-group__title {
+        padding-left: calc(
+          var(--menu-item-indent) + var(--menu-level) * var(--menu-item-indent)
+        );
+        white-space: nowrap;
+      }
+
+      & > .#{$namespace}-sub-menu {
+        // .#{$namespace}-menu {
+        //   background: var(--menu-submenu-opened-background-color);
+
+        //   .#{$namespace}-sub-menu,
+        //   .#{$namespace}-menu-item:not(.is-active),
+        //   .#{$namespace}-sub-menu-content:not(.is-active) {
+        //     background: var(--menu-submenu-opened-background-color);
+        //   }
+        // }
+        & > .#{$namespace}-menu {
+          & > .#{$namespace}-menu-item {
+            padding-left: calc(
+              0px + var(--menu-item-indent) + var(--menu-level) *
+                var(--menu-item-indent)
+            );
+          }
+        }
+
+        & > .#{$namespace}-sub-menu-content {
+          padding-left: calc(var(--menu-item-indent) - 8px);
+        }
+      }
+      & > .#{$namespace}-menu-item {
+        padding-left: calc(var(--menu-item-indent) - 8px);
+      }
+    }
+  }
+
+  &.is-horizontal {
+    display: flex;
+    flex-wrap: nowrap;
+    max-width: 100%;
+    height: var(--height-horizontal-height);
+    border-right: none;
+
+    .#{$namespace}-menu-item {
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
+      height: var(--menu-item-height);
+      padding-right: calc(var(--menu-item-padding-x) + 6px);
+      margin: 0;
+      margin-right: 2px;
+      // border-bottom: 2px solid transparent;
+      border-radius: var(--menu-item-radius);
+    }
+
+    & > .#{$namespace}-sub-menu {
+      height: var(--menu-item-height);
+      margin-right: 2px;
+
+      &:focus,
+      &:hover {
+        outline: none;
+      }
+
+      & .#{$namespace}-sub-menu-content {
+        height: 100%;
+        padding-right: 40px;
+        // border-bottom: 2px solid transparent;
+        border-radius: var(--menu-item-radius);
+      }
+    }
+
+    & .#{$namespace}-menu-item:not(.is-disabled):hover,
+    & .#{$namespace}-menu-item:not(.is-disabled):focus {
+      outline: none;
+    }
+
+    & > .#{$namespace}-menu-item.is-active {
+      color: var(--menu-item-active-color);
+    }
+
+    // &.is-light {
+    //   & > .#{$namespace}-sub-menu {
+    //     &.is-active {
+    //       border-bottom: 2px solid var(--menu-item-active-color);
+    //     }
+    //     &:not(.is-active) .#{$namespace}-sub-menu-content {
+    //       &:hover {
+    //         border-bottom: 2px solid var(--menu-item-active-color);
+    //       }
+    //     }
+    //   }
+    //   & > .#{$namespace}-menu-item.is-active {
+    //     border-bottom: 2px solid var(--menu-item-active-color);
+    //   }
+
+    //   & .#{$namespace}-menu-item:not(.is-disabled):hover,
+    //   & .#{$namespace}-menu-item:not(.is-disabled):focus {
+    //     border-bottom: 2px solid var(--menu-item-active-color);
+    //   }
+    // }
+  }
+  // 折叠菜单
+
+  &.is-collapse {
+    .#{$namespace}-menu__icon {
+      margin-right: 0;
+    }
+    .#{$namespace}-sub-menu__icon-arrow {
+      display: none;
+    }
+
+    .#{$namespace}-sub-menu-content,
+    .#{$namespace}-menu-item {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      padding: var(--menu-item-collapse-padding-y)
+        var(--menu-item-collapse-padding-x);
+      margin: var(--menu-item-collapse-margin-y)
+        var(--menu-item-collapse-margin-x);
+      transition: all 0.3s;
+
+      &.is-active {
+        background: var(--menu-item-active-background-color) !important;
+        border-radius: var(--menu-item-radius);
+      }
+    }
+
+    &.is-light {
+      .#{$namespace}-sub-menu-content,
+      .#{$namespace}-menu-item {
+        &.is-active {
+          color: hsl(var(--primary-foreground)) !important;
+          background: var(--menu-item-active-background-color) !important;
+        }
+      }
+    }
+
+    &.is-rounded {
+      .#{$namespace}-sub-menu-content,
+      .#{$namespace}-menu-item {
+        &.is-collapse-show-title {
+          // padding: 32px 0 !important;
+          margin: 4px 8px !important;
+        }
+      }
+    }
+  }
+
+  &__popup-container {
+    max-width: 240px;
+    height: unset;
+    padding: 0;
+    background: var(--menu-background-color);
+  }
+
+  &__popup {
+    padding: 4px 0;
+    border-radius: var(--menu-item-radius);
+
+    .#{$namespace}-sub-menu-content,
+    .#{$namespace}-menu-item {
+      padding: var(--menu-item-popup-padding-y) var(--menu-item-popup-padding-x);
+    }
+  }
+
+  &__icon {
+    flex-shrink: 0;
+    // width: var(--menu-item-icon-width);
+    max-height: var(--menu-item-icon-width);
+    margin-right: 12px;
+    font-size: 20px;
+    text-align: center;
+    vertical-align: middle;
+  }
+}
+
+.#{$namespace}-menu-item {
+  fill: var(--menu-item-color);
+  stroke: var(--menu-item-color);
+
+  @include menu-item;
+
+  &.is-active {
+    fill: var(--menu-item-active-color);
+    stroke: var(--menu-item-active-color);
+
+    @include menu-item-active;
+  }
+
+  &__content {
+    display: inline-flex;
+    align-items: center;
+    width: 100%;
+    height: var(--menu-item-height);
+  }
+
+  &.is-collapse-show-title {
+    padding: 32px 0 !important;
+    // margin: 4px 8px !important;
+    .#{$namespace}-menu-tooltip__trigger {
+      flex-direction: column;
+    }
+    .#{$namespace}-menu__icon {
+      display: block;
+      font-size: 20px !important;
+      transition: all 0.25s ease;
+    }
+
+    .#{$namespace}-menu__name {
+      display: inline-flex;
+      margin-top: 8px;
+      margin-bottom: 0;
+      font-size: 12px;
+      font-weight: 400;
+      line-height: normal;
+      transition: all 0.25s ease;
+    }
+  }
+
+  &:not(.is-active):hover {
+    color: var(--menu-item-hover-color);
+    text-decoration: none;
+    cursor: pointer;
+    background: var(--menu-item-hover-background-color) !important;
+  }
+
+  .#{$namespace}-menu-tooltip__trigger {
+    position: absolute;
+    top: 0;
+    left: 0;
+    box-sizing: border-box;
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    width: 100%;
+    height: 100%;
+    padding: 0 var(--menu-item-padding-x);
+    font-size: var(--menu-font-size);
+    line-height: var(--menu-item-height);
+  }
+}
+
+.#{$namespace}-sub-menu {
+  padding-left: 0;
+  margin: 0;
+  list-style: none;
+  background: var(--menu-submenu-background-color);
+  fill: var(--menu-item-color);
+  stroke: var(--menu-item-color);
+
+  &.is-active {
+    div[data-state='open'] > .#{$namespace}-sub-menu-content,
+    > .#{$namespace}-sub-menu-content {
+      color: var(--menu-submenu-active-color);
+      text-decoration: none;
+      cursor: pointer;
+      background: var(--menu-submenu-active-background-color);
+      fill: var(--menu-submenu-active-color);
+      stroke: var(--menu-submenu-active-color);
+    }
+  }
+}
+
+.#{$namespace}-sub-menu-content {
+  height: var(--menu-item-height);
+
+  @include menu-item;
+
+  &__icon-arrow {
+    position: absolute;
+    top: 50%;
+    right: 6px;
+    width: inherit;
+    margin-top: -8px;
+    margin-right: 0;
+    font-size: 16px;
+    font-weight: normal;
+    opacity: 1;
+    transition: transform 0.25s ease;
+  }
+
+  &__title {
+    @include menu-title;
+  }
+
+  &.is-collapse-show-title {
+    flex-direction: column;
+    padding: 32px 0 !important;
+    // margin: 4px 8px !important;
+    .#{$namespace}-menu__icon {
+      display: block;
+      font-size: 20px !important;
+      transition: all 0.25s ease;
+    }
+    .#{$namespace}-sub-menu-content__title {
+      display: inline-flex;
+      flex-shrink: 0;
+      margin-top: 8px;
+      margin-bottom: 0;
+      font-size: 12px;
+      font-weight: 400;
+      line-height: normal;
+      transition: all 0.25s ease;
+    }
+  }
+
+  &.is-more {
+    padding-right: 12px !important;
+  }
+
+  // &:not(.is-active):hover {
+  &:hover {
+    color: var(--menu-submenu-hover-color);
+    text-decoration: none;
+    cursor: pointer;
+    background: var(--menu-submenu-hover-background-color) !important;
+
+    svg {
+      fill: var(--menu-submenu-hover-color);
+    }
+  }
+}
+</style>

+ 28 - 32
packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue

@@ -3,8 +3,8 @@ import type { MenuRecordRaw } from '@vben-core/typings';
 
 import type { NormalMenuProps } from './normal-menu';
 
+import { useNamespace } from '@vben-core/hooks';
 import { VbenIcon } from '@vben-core/shadcn-ui';
-import { useNamespace } from '@vben-core/toolkit';
 
 interface Props extends NormalMenuProps {}
 
@@ -58,9 +58,9 @@ function handleMouseenter(menu: MenuRecordRaw) {
   </ul>
 </template>
 <style lang="scss" scoped>
-@import '@vben-core/design/bem';
+$namespace: vben;
 
-@include b('normal-menu') {
+.#{$namespace}-normal-menu {
   --menu-item-margin-y: 4px;
   --menu-item-margin-x: 0px;
   --menu-item-padding-y: 11px;
@@ -70,18 +70,37 @@ function handleMouseenter(menu: MenuRecordRaw) {
 
   height: calc(100% - 4px);
 
-  @include is('rounded') {
+  &.is-rounded {
     --menu-item-radius: 6px;
     --menu-item-margin-x: 8px;
   }
 
-  @include is('dark') {
+  &.is-dark {
     .#{$namespace}-normal-menu__item {
       color: hsl(var(--dark-foreground) / 80%);
+
+      &:not(.is-active):hover {
+        color: hsl(var(--primary-foreground));
+        background-color: hsl(var(--menu-dark-background));
+      }
+    }
+  }
+
+  &.is-collapse {
+    .#{$namespace}-normal-menu__name {
+      width: 0;
+      height: 0;
+      margin-top: 0;
+      overflow: hidden;
+      opacity: 0;
+    }
+
+    .#{$namespace}-normal-menu__icon {
+      font-size: 20px;
     }
   }
 
-  @include e('item') {
+  &__item {
     position: relative;
     display: flex;
     flex-direction: column;
@@ -100,7 +119,7 @@ function handleMouseenter(menu: MenuRecordRaw) {
       padding 0.15s ease,
       border-color 0.15s ease;
 
-    @include is('active') {
+    &.is-active {
       font-weight: 700;
       color: hsl(var(--primary-foreground));
       background-color: hsl(var(--primary));
@@ -126,36 +145,13 @@ function handleMouseenter(menu: MenuRecordRaw) {
     }
   }
 
-  @include is('dark') {
-    .#{$namespace}-normal-menu__item {
-      &:not(.is-active):hover {
-        color: hsl(var(--primary-foreground));
-        background-color: hsl(var(--menu-dark-background));
-      }
-    }
-  }
-
-  @include is('collapse') {
-    .#{$namespace}-normal-menu__name {
-      width: 0;
-      height: 0;
-      margin-top: 0;
-      overflow: hidden;
-      opacity: 0;
-    }
-
-    .#{$namespace}-normal-menu__icon {
-      font-size: 20px;
-    }
-  }
-
-  @include e('icon') {
+  &__icon {
     max-height: 20px;
     font-size: 20px;
     transition: all 0.25s ease;
   }
 
-  @include e('name') {
+  &__name {
     margin-top: 8px;
     margin-bottom: 0;
     font-size: 12px;

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

@@ -3,12 +3,12 @@ import type { MenuItemProps } from '../interface';
 
 import { computed } from 'vue';
 
+import { useNamespace } from '@vben-core/hooks';
 import {
   IcRoundChevronRight,
   IcRoundKeyboardArrowDown,
 } from '@vben-core/iconify';
 import { VbenIcon } from '@vben-core/shadcn-ui';
-import { useNamespace } from '@vben-core/toolkit';
 
 import { useMenuContext } from '../hooks';
 

+ 1 - 1
packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue

@@ -9,8 +9,8 @@ import type {
 
 import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue';
 
+import { useNamespace } from '@vben-core/hooks';
 import { VbenHoverCard } from '@vben-core/shadcn-ui';
-import { useNamespace } from '@vben-core/toolkit';
 
 import {
   createSubMenuContext,

+ 0 - 2
packages/@core/ui-kit/menu-ui/src/index.ts

@@ -1,5 +1,3 @@
-import './styles/index.scss';
-
 export * from './components/normal-menu';
 export type * from './interface';
 export { default as Menu } from './menu.vue';

+ 1 - 1
packages/@core/ui-kit/menu-ui/src/menu.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 import type { MenuRecordRaw } from '@vben-core/typings';
 
-import { useForwardProps } from '@vben-core/shadcn-ui';
+import { useForwardProps } from '@vben-core/hooks';
 
 import { Menu } from './components';
 import { MenuProps } from './interface';

+ 49 - 52
packages/@core/ui-kit/menu-ui/src/styles/index.scss

@@ -1,4 +1,4 @@
-@import '@vben-core/design/bem';
+$namespace: vben;
 
 .#{$namespace}-menu__popup-container,
 .#{$namespace}-menu {
@@ -133,7 +133,7 @@
     padding 0.15s ease,
     border-color 0.15s ease;
 
-  @include is(disabled) {
+  &.is-disabled {
     cursor: not-allowed;
     background: none !important;
     opacity: 0.25;
@@ -167,7 +167,7 @@
   opacity: 1;
 }
 
-@include b('menu') {
+.#{$namespace}-menu {
   position: relative;
   box-sizing: border-box;
   padding-left: 0;
@@ -176,50 +176,47 @@
   background: hsl(var(--menu-background-color));
 
   // 垂直菜单
-  @include is('vertical') {
-    & {
-      &:not(.#{$namespace}-menu.is-collapse) {
-        & .#{$namespace}-menu-item,
-        & .#{$namespace}-sub-menu-content,
-        & .#{$namespace}-menu-item-group__title {
-          padding-left: calc(
-            var(--menu-item-indent) + var(--menu-level) *
-              var(--menu-item-indent)
-          );
-          white-space: nowrap;
-        }
-
-        & > .#{$namespace}-sub-menu {
-          // .#{$namespace}-menu {
-          //   background: var(--menu-submenu-opened-background-color);
-
-          //   .#{$namespace}-sub-menu,
-          //   .#{$namespace}-menu-item:not(.is-active),
-          //   .#{$namespace}-sub-menu-content:not(.is-active) {
-          //     background: var(--menu-submenu-opened-background-color);
-          //   }
-          // }
-          & > .#{$namespace}-menu {
-            & > .#{$namespace}-menu-item {
-              padding-left: calc(
-                0px + var(--menu-item-indent) + var(--menu-level) *
-                  var(--menu-item-indent)
-              );
-            }
-          }
+  &.is-vertical {
+    &:not(.#{$namespace}-menu.is-collapse) {
+      & .#{$namespace}-menu-item,
+      & .#{$namespace}-sub-menu-content,
+      & .#{$namespace}-menu-item-group__title {
+        padding-left: calc(
+          var(--menu-item-indent) + var(--menu-level) * var(--menu-item-indent)
+        );
+        white-space: nowrap;
+      }
 
-          & > .#{$namespace}-sub-menu-content {
-            padding-left: calc(var(--menu-item-indent) - 8px);
+      & > .#{$namespace}-sub-menu {
+        // .#{$namespace}-menu {
+        //   background: var(--menu-submenu-opened-background-color);
+
+        //   .#{$namespace}-sub-menu,
+        //   .#{$namespace}-menu-item:not(.is-active),
+        //   .#{$namespace}-sub-menu-content:not(.is-active) {
+        //     background: var(--menu-submenu-opened-background-color);
+        //   }
+        // }
+        & > .#{$namespace}-menu {
+          & > .#{$namespace}-menu-item {
+            padding-left: calc(
+              0px + var(--menu-item-indent) + var(--menu-level) *
+                var(--menu-item-indent)
+            );
           }
         }
-        & > .#{$namespace}-menu-item {
+
+        & > .#{$namespace}-sub-menu-content {
           padding-left: calc(var(--menu-item-indent) - 8px);
         }
       }
+      & > .#{$namespace}-menu-item {
+        padding-left: calc(var(--menu-item-indent) - 8px);
+      }
     }
   }
 
-  @include is('horizontal') {
+  &.is-horizontal {
     display: flex;
     flex-wrap: nowrap;
     max-width: 100%;
@@ -287,7 +284,7 @@
   }
   // 折叠菜单
 
-  @include is('collapse') {
+  &.is-collapse {
     .#{$namespace}-menu__icon {
       margin-right: 0;
     }
@@ -333,14 +330,14 @@
     }
   }
 
-  @include e('popup-container') {
+  &__popup-container {
     max-width: 240px;
     height: unset;
     padding: 0;
     background: var(--menu-background-color);
   }
 
-  @include e('popup') {
+  &__popup {
     padding: 4px 0;
     border-radius: var(--menu-item-radius);
 
@@ -350,7 +347,7 @@
     }
   }
 
-  @include e('icon') {
+  &__icon {
     flex-shrink: 0;
     // width: var(--menu-item-icon-width);
     max-height: var(--menu-item-icon-width);
@@ -361,27 +358,27 @@
   }
 }
 
-@include b('menu-item') {
+.#{$namespace}-menu-item {
   fill: var(--menu-item-color);
   stroke: var(--menu-item-color);
 
   @include menu-item;
 
-  @include is(active) {
+  &.is-active {
     fill: var(--menu-item-active-color);
     stroke: var(--menu-item-active-color);
 
     @include menu-item-active;
   }
 
-  @include e('content') {
+  &__content {
     display: inline-flex;
     align-items: center;
     width: 100%;
     height: var(--menu-item-height);
   }
 
-  @include is('collapse-show-title') {
+  &.is-collapse-show-title {
     padding: 32px 0 !important;
     // margin: 4px 8px !important;
     .#{$namespace}-menu-tooltip__trigger {
@@ -427,7 +424,7 @@
   }
 }
 
-@include b('sub-menu') {
+.#{$namespace}-sub-menu {
   padding-left: 0;
   margin: 0;
   list-style: none;
@@ -435,7 +432,7 @@
   fill: var(--menu-item-color);
   stroke: var(--menu-item-color);
 
-  @include is('active') {
+  &.is-active {
     div[data-state='open'] > .#{$namespace}-sub-menu-content,
     > .#{$namespace}-sub-menu-content {
       color: var(--menu-submenu-active-color);
@@ -448,12 +445,12 @@
   }
 }
 
-@include b('sub-menu-content') {
+.#{$namespace}-sub-menu-content {
   height: var(--menu-item-height);
 
   @include menu-item;
 
-  @include e('icon-arrow') {
+  &__icon-arrow {
     position: absolute;
     top: 50%;
     right: 6px;
@@ -466,11 +463,11 @@
     transition: transform 0.25s ease;
   }
 
-  @include e('title') {
+  &__title {
     @include menu-title;
   }
 
-  @include is('collapse-show-title') {
+  &.is-collapse-show-title {
     flex-direction: column;
     padding: 32px 0 !important;
     // margin: 4px 8px !important;

+ 0 - 3
packages/@core/ui-kit/menu-ui/vite.config.mts

@@ -1,3 +0,0 @@
-import { defineConfig } from '@vben/vite-config';
-
-export default defineConfig();

+ 1 - 7
packages/@core/ui-kit/shadcn-ui/src/index.ts

@@ -1,10 +1,4 @@
 import './styles/index.css';
 
 export * from './components';
-export {
-  VisuallyHidden,
-  useEmitAsProps,
-  useForwardExpose,
-  useForwardProps,
-  useForwardPropsEmits,
-} from 'radix-vue';
+export { VisuallyHidden } from 'radix-vue';

+ 0 - 1
packages/@core/ui-kit/tabs-ui/package.json

@@ -37,7 +37,6 @@
     }
   },
   "dependencies": {
-    "@vben-core/design": "workspace:*",
     "@vben-core/hooks": "workspace:*",
     "@vben-core/iconify": "workspace:*",
     "@vben-core/shadcn-ui": "workspace:*",

+ 1 - 0
packages/@core/ui-kit/tabs-ui/src/components/index.ts

@@ -1 +1,2 @@
+export { default as Tabs } from './tabs/tabs.vue';
 export { default as TabsChrome } from './tabs-chrome/tabs.vue';

+ 9 - 6
packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
-import type { TabItem } from '@vben-core/typings';
+import type { TabDefinition } from '@vben-core/typings';
 
-import type { TabsProps } from '../../types';
+import type { TabConfig, TabsProps } from '../../types';
 
 import { computed, nextTick, onMounted, ref, watch } from 'vue';
 
@@ -25,7 +25,7 @@ const props = withDefaults(defineProps<Props>(), {
   tabs: () => [],
 });
 
-const emit = defineEmits<{ close: [string]; unpin: [TabItem] }>();
+const emit = defineEmits<{ close: [string]; unpin: [TabDefinition] }>();
 const active = defineModel<string>('active');
 
 const contentRef = ref();
@@ -56,12 +56,14 @@ const layout = () => {
   tabWidth.value = width;
 };
 
-const tabsView = computed(() => {
+const tabsView = computed((): TabConfig[] => {
   return props.tabs.map((tab) => {
     return {
       ...tab,
       affixTab: !!tab.meta?.affixTab,
-      closable: tab.meta?.tabClosable ?? true,
+      closable: Reflect.has(tab.meta, 'tabClosable')
+        ? !!tab.meta.tabClosable
+        : true,
       icon: tab.meta.icon as string,
       key: tab.fullPath || tab.path,
       title: (tab.meta?.title || tab.name) as string,
@@ -85,7 +87,8 @@ onMounted(() => {
 function handleClose(key: string) {
   emit('close', key);
 }
-function handleUnpinTab(tab: TabItem) {
+
+function handleUnpinTab(tab: TabConfig) {
   emit('unpin', tab);
 }
 </script>

+ 0 - 5
packages/@core/ui-kit/tabs-ui/src/components/tabs/index.vue

@@ -1,5 +0,0 @@
-<script lang="ts" setup></script>
-
-<template>
-  <div></div>
-</template>

+ 11 - 0
packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue

@@ -0,0 +1,11 @@
+<script lang="ts" setup>
+import { VbenScrollbar } from '@vben-core/shadcn-ui';
+</script>
+
+<template>
+  <div class="bg-accent size-full">
+    <VbenScrollbar>
+      <slot></slot>
+    </VbenScrollbar>
+  </div>
+</template>

+ 3 - 4
packages/@core/ui-kit/tabs-ui/src/tabs-view.vue

@@ -1,10 +1,9 @@
 <script setup lang="ts">
-import type { TabItem } from '@vben-core/typings';
+import type { TabDefinition } from '@vben-core/typings';
 
 import { nextTick, onMounted } from 'vue';
 
-import { useSortable } from '@vben-core/hooks';
-import { useForwardPropsEmits } from '@vben-core/shadcn-ui';
+import { useForwardPropsEmits, useSortable } from '@vben-core/hooks';
 
 import { TabsChrome } from './components';
 import { TabsProps } from './types';
@@ -23,7 +22,7 @@ const props = withDefaults(defineProps<Props>(), {
 const emit = defineEmits<{
   close: [string];
   sortTabs: [number, number];
-  unpin: [TabItem];
+  unpin: [TabDefinition];
 }>();
 
 const forward = useForwardPropsEmits(props, emit);

+ 11 - 3
packages/@core/ui-kit/tabs-ui/src/types.ts

@@ -1,5 +1,5 @@
 import type { IContextMenuItem } from '@vben-core/shadcn-ui';
-import type { TabItem } from '@vben-core/typings';
+import type { TabDefinition } from '@vben-core/typings';
 
 interface TabsProps {
   /**
@@ -41,7 +41,15 @@ interface TabsProps {
   /**
    * @zh_CN 选项卡数据
    */
-  tabs?: TabItem[];
+  tabs?: TabDefinition[];
 }
 
-export type { TabsProps };
+interface TabConfig extends TabDefinition {
+  affixTab: boolean;
+  closable: boolean;
+  icon: string;
+  key: string;
+  title: string;
+}
+
+export type { TabConfig, TabsProps };

+ 1 - 0
packages/effects/common-ui/package.json

@@ -37,6 +37,7 @@
     }
   },
   "dependencies": {
+    "@vben-core/hooks": "workspace:*",
     "@vben-core/iconify": "workspace:*",
     "@vben-core/locales": "workspace:*",
     "@vben-core/shadcn-ui": "workspace:*",

+ 1 - 1
packages/effects/common-ui/src/authentication/login-expired-modal.vue

@@ -1,11 +1,11 @@
 <script setup lang="ts">
+import { useForwardPropsEmits } from '@vben-core/hooks';
 import {
   Dialog,
   DialogContent,
   DialogDescription,
   DialogTitle,
   VisuallyHidden,
-  useForwardPropsEmits,
 } from '@vben-core/shadcn-ui';
 
 import AuthenticationLogin from './login.vue';

+ 2 - 2
packages/effects/common-ui/src/dashboard/analysis/analysis-charts-tabs.vue

@@ -1,12 +1,12 @@
 <script setup lang="ts">
-import type { TabsOption } from '@vben/types';
+import type { TabOption } from '@vben/types';
 
 import { computed } from 'vue';
 
 import { Tabs, TabsContent, TabsList, TabsTrigger } from '@vben-core/shadcn-ui';
 
 interface Props {
-  tabs: TabsOption[];
+  tabs: TabOption[];
 }
 
 defineOptions({

+ 3 - 3
packages/effects/layouts/src/basic/tabbar/use-tabs.ts

@@ -1,5 +1,5 @@
 import type { IContextMenuItem } from '@vben-core/tabs-ui';
-import type { TabItem } from '@vben-core/typings';
+import type { TabDefinition } from '@vben-core/typings';
 import type {
   RouteLocationNormalized,
   RouteLocationNormalizedGeneric,
@@ -105,7 +105,7 @@ function useTabs() {
     { immediate: true },
   );
 
-  const createContextMenus = (tab: TabItem) => {
+  const createContextMenus = (tab: TabDefinition) => {
     const tabs = coreTabbarStore.getTabs;
     const affixTabs = coreTabbarStore.affixTabs;
     const index = tabs.findIndex((item) => item.path === tab.path);
@@ -228,7 +228,7 @@ function useTabs() {
   /**
    * 取消固定标签页
    */
-  const handleUnpinTab = async (tab: TabItem) => {
+  const handleUnpinTab = async (tab: TabDefinition) => {
     await coreTabbarStore.unpinTab(tab);
   };
 

+ 2 - 7
packages/effects/layouts/src/widgets/theme-toggle/theme-button.vue

@@ -2,7 +2,6 @@
 import { computed, nextTick } from 'vue';
 
 import { VbenButton } from '@vben-core/shadcn-ui';
-import { useNamespace } from '@vben-core/toolkit';
 
 interface Props {
   /**
@@ -21,8 +20,6 @@ const props = withDefaults(defineProps<Props>(), {
 
 const isDark = defineModel<boolean>();
 
-const { b, e, is } = useNamespace('theme-toggle');
-
 const theme = computed(() => {
   return isDark.value ? 'light' : 'dark';
 });
@@ -86,7 +83,7 @@ function toggleTheme(event: MouseEvent) {
 <template>
   <VbenButton
     :aria-label="theme"
-    :class="[b(), is(theme), `is-${theme}`]"
+    :class="[`is-${theme}`]"
     aria-live="polite"
     class="theme-toggle cursor-pointer border-none bg-none"
     v-bind="bindProps"
@@ -95,7 +92,6 @@ function toggleTheme(event: MouseEvent) {
     <svg aria-hidden="true" height="24" viewBox="0 0 24 24" width="24">
       <mask
         id="theme-toggle-moon"
-        :class="e('moon')"
         class="theme-toggle__moon"
         fill="hsl(var(--foreground)/80%)"
         stroke="none"
@@ -105,14 +101,13 @@ function toggleTheme(event: MouseEvent) {
       </mask>
       <circle
         id="sun"
-        :class="e('sun')"
         class="theme-toggle__sun"
         cx="12"
         cy="12"
         mask="url(#theme-toggle-moon)"
         r="11"
       />
-      <g :class="e('sun-beams')" class="theme-toggle__sun-beams">
+      <g class="theme-toggle__sun-beams">
         <line x1="12" x2="12" y1="1" y2="3" />
         <line x1="12" x2="12" y1="21" y2="23" />
         <line x1="4.22" x2="5.64" y1="4.22" y2="5.64" />

+ 3 - 0
packages/hooks/package.json

@@ -36,5 +36,8 @@
         "default": "./dist/index.mjs"
       }
     }
+  },
+  "dependencies": {
+    "@vben-core/hooks": "workspace:*"
   }
 }

+ 1 - 1
packages/hooks/src/index.ts

@@ -1 +1 @@
-export {};
+export * from '@vben-core/hooks';

+ 16 - 9
pnpm-lock.yaml

@@ -210,6 +210,9 @@ importers:
       '@vben/constants':
         specifier: workspace:*
         version: link:../../packages/constants
+      '@vben/hooks':
+        specifier: workspace:*
+        version: link:../../packages/hooks
       '@vben/icons':
         specifier: workspace:*
         version: link:../../packages/icons
@@ -655,12 +658,12 @@ importers:
 
   packages/@core/shared/hooks:
     dependencies:
+      radix-vue:
+        specifier: ^1.9.1
+        version: 1.9.1(vue@3.4.31(typescript@5.5.3))
       sortablejs:
         specifier: ^1.15.2
         version: 1.15.2
-      vue:
-        specifier: ^3.4.31
-        version: 3.4.31(typescript@5.5.3)
     devDependencies:
       '@types/sortablejs':
         specifier: ^1.15.8
@@ -741,9 +744,9 @@ importers:
 
   packages/@core/ui-kit/menu-ui:
     dependencies:
-      '@vben-core/design':
+      '@vben-core/hooks':
         specifier: workspace:*
-        version: link:../../shared/design
+        version: link:../../shared/hooks
       '@vben-core/iconify':
         specifier: workspace:*
         version: link:../../shared/iconify
@@ -795,9 +798,6 @@ importers:
 
   packages/@core/ui-kit/tabs-ui:
     dependencies:
-      '@vben-core/design':
-        specifier: workspace:*
-        version: link:../../shared/design
       '@vben-core/hooks':
         specifier: workspace:*
         version: link:../../shared/hooks
@@ -858,6 +858,9 @@ importers:
 
   packages/effects/common-ui:
     dependencies:
+      '@vben-core/hooks':
+        specifier: workspace:*
+        version: link:../../@core/shared/hooks
       '@vben-core/iconify':
         specifier: workspace:*
         version: link:../../@core/shared/iconify
@@ -935,7 +938,11 @@ importers:
         specifier: ^4.4.0
         version: 4.4.0(vue@3.4.31(typescript@5.5.3))
 
-  packages/hooks: {}
+  packages/hooks:
+    dependencies:
+      '@vben-core/hooks':
+        specifier: workspace:*
+        version: link:../@core/shared/hooks
 
   packages/icons:
     dependencies: