perf: optimization of tabbar display (#4169)
* perf: optimization of tabbar display * fix: ci error * chore: typo * chore: typopull/48/MERGE
							parent
							
								
									8987067b5a
								
							
						
					
					
						commit
						0faf7810b6
					
				|  | @ -40,11 +40,11 @@ | ||||||
|     "@vben/styles": "workspace:*", |     "@vben/styles": "workspace:*", | ||||||
|     "@vben/types": "workspace:*", |     "@vben/types": "workspace:*", | ||||||
|     "@vben/utils": "workspace:*", |     "@vben/utils": "workspace:*", | ||||||
|     "@vueuse/core": "^10.11.1", |     "@vueuse/core": "^11.0.0", | ||||||
|     "ant-design-vue": "^4.2.3", |     "ant-design-vue": "^4.2.3", | ||||||
|     "dayjs": "^1.11.12", |     "dayjs": "^1.11.12", | ||||||
|     "pinia": "2.2.2", |     "pinia": "2.2.2", | ||||||
|     "vue": "^3.4.38", |     "vue": "^3.4.37", | ||||||
|     "vue-router": "^4.4.3" |     "vue-router": "^4.4.3" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -40,11 +40,11 @@ | ||||||
|     "@vben/styles": "workspace:*", |     "@vben/styles": "workspace:*", | ||||||
|     "@vben/types": "workspace:*", |     "@vben/types": "workspace:*", | ||||||
|     "@vben/utils": "workspace:*", |     "@vben/utils": "workspace:*", | ||||||
|     "@vueuse/core": "^10.11.1", |     "@vueuse/core": "^11.0.0", | ||||||
|     "dayjs": "^1.11.12", |     "dayjs": "^1.11.12", | ||||||
|     "element-plus": "^2.8.0", |     "element-plus": "^2.8.0", | ||||||
|     "pinia": "2.2.2", |     "pinia": "2.2.2", | ||||||
|     "vue": "^3.4.38", |     "vue": "^3.4.37", | ||||||
|     "vue-router": "^4.4.3" |     "vue-router": "^4.4.3" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|  |  | ||||||
|  | @ -40,10 +40,10 @@ | ||||||
|     "@vben/styles": "workspace:*", |     "@vben/styles": "workspace:*", | ||||||
|     "@vben/types": "workspace:*", |     "@vben/types": "workspace:*", | ||||||
|     "@vben/utils": "workspace:*", |     "@vben/utils": "workspace:*", | ||||||
|     "@vueuse/core": "^10.11.1", |     "@vueuse/core": "^11.0.0", | ||||||
|     "naive-ui": "^2.39.0", |     "naive-ui": "^2.39.0", | ||||||
|     "pinia": "2.2.2", |     "pinia": "2.2.2", | ||||||
|     "vue": "^3.4.38", |     "vue": "^3.4.37", | ||||||
|     "vue-router": "^4.4.3" |     "vue-router": "^4.4.3" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -14,6 +14,6 @@ | ||||||
|     "@nolebase/vitepress-plugin-git-changelog": "^2.4.0", |     "@nolebase/vitepress-plugin-git-changelog": "^2.4.0", | ||||||
|     "@vite-pwa/vitepress": "^0.5.0", |     "@vite-pwa/vitepress": "^0.5.0", | ||||||
|     "vitepress": "^1.3.2", |     "vitepress": "^1.3.2", | ||||||
|     "vue": "^3.4.38" |     "vue": "^3.4.37" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -42,7 +42,8 @@ export async function typescript(): Promise<Linter.Config[]> { | ||||||
|           }, |           }, | ||||||
|         ], |         ], | ||||||
| 
 | 
 | ||||||
|         '@typescript-eslint/consistent-type-definitions': ['warn', 'interface'], |         // '@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
 | ||||||
|  |         '@typescript-eslint/consistent-type-definitions': 'off', | ||||||
|         '@typescript-eslint/explicit-function-return-type': 'off', |         '@typescript-eslint/explicit-function-return-type': 'off', | ||||||
|         '@typescript-eslint/explicit-module-boundary-types': 'off', |         '@typescript-eslint/explicit-module-boundary-types': 'off', | ||||||
|         '@typescript-eslint/no-empty-function': [ |         '@typescript-eslint/no-empty-function': [ | ||||||
|  |  | ||||||
|  | @ -94,7 +94,7 @@ | ||||||
|     "node": ">=20", |     "node": ">=20", | ||||||
|     "pnpm": ">=9" |     "pnpm": ">=9" | ||||||
|   }, |   }, | ||||||
|   "packageManager": "pnpm@9.7.0", |   "packageManager": "pnpm@9.7.1", | ||||||
|   "pnpm": { |   "pnpm": { | ||||||
|     "peerDependencyRules": { |     "peerDependencyRules": { | ||||||
|       "allowedVersions": { |       "allowedVersions": { | ||||||
|  |  | ||||||
|  | @ -35,7 +35,7 @@ | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@iconify/vue": "^4.1.2", |     "@iconify/vue": "^4.1.2", | ||||||
|     "lucide-vue-next": "^0.427.0", |     "lucide-vue-next": "^0.428.0", | ||||||
|     "vue": "^3.4.38" |     "vue": "^3.4.37" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -56,7 +56,7 @@ | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@ctrl/tinycolor": "^4.1.0", |     "@ctrl/tinycolor": "^4.1.0", | ||||||
|     "@vue/shared": "^3.4.38", |     "@vue/shared": "^3.4.37", | ||||||
|     "clsx": "^2.1.1", |     "clsx": "^2.1.1", | ||||||
|     "defu": "^6.1.4", |     "defu": "^6.1.4", | ||||||
|     "lodash.clonedeep": "^4.5.0", |     "lodash.clonedeep": "^4.5.0", | ||||||
|  |  | ||||||
|  | @ -38,7 +38,7 @@ | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "vue": "^3.4.38", |     "vue": "^3.4.37", | ||||||
|     "vue-router": "^4.4.3" |     "vue-router": "^4.4.3" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -107,20 +107,23 @@ type MergeAll< | ||||||
|   ? MergeAll<Rest, Merge<R, F>> |   ? MergeAll<Rest, Merge<R, F>> | ||||||
|   : R; |   : R; | ||||||
| 
 | 
 | ||||||
| export { | type EmitType = (name: Name, ...args: any[]) => void; | ||||||
|   type AnyFunction, | 
 | ||||||
|   type AnyNormalFunction, | export type { | ||||||
|   type AnyPromiseFunction, |   AnyFunction, | ||||||
|   type DeepPartial, |   AnyNormalFunction, | ||||||
|   type DeepReadonly, |   AnyPromiseFunction, | ||||||
|   type IntervalHandle, |   DeepPartial, | ||||||
|   type MaybeComputedRef, |   DeepReadonly, | ||||||
|   type MaybeReadonlyRef, |   EmitType, | ||||||
|   type Merge, |   IntervalHandle, | ||||||
|   type MergeAll, |   MaybeComputedRef, | ||||||
|   type NonNullable, |   MaybeReadonlyRef, | ||||||
|   type Nullable, |   Merge, | ||||||
|   type ReadonlyRecordable, |   MergeAll, | ||||||
|   type Recordable, |   NonNullable, | ||||||
|   type TimeoutHandle, |   Nullable, | ||||||
|  |   ReadonlyRecordable, | ||||||
|  |   Recordable, | ||||||
|  |   TimeoutHandle, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -36,10 +36,10 @@ | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@vben-core/shared": "workspace:*", |     "@vben-core/shared": "workspace:*", | ||||||
|     "@vueuse/core": "^10.11.1", |     "@vueuse/core": "^11.0.0", | ||||||
|     "radix-vue": "^1.9.4", |     "radix-vue": "^1.9.4", | ||||||
|     "sortablejs": "^1.15.2", |     "sortablejs": "^1.15.2", | ||||||
|     "vue": "^3.4.38" |     "vue": "^3.4.37" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@types/sortablejs": "^1.15.8" |     "@types/sortablejs": "^1.15.8" | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| import type { CSSProperties } from 'vue'; | import type { CSSProperties } from 'vue'; | ||||||
| import { computed, nextTick, onMounted, ref } from 'vue'; | import { computed, onMounted, onUnmounted, ref } from 'vue'; | ||||||
| 
 | 
 | ||||||
| import { | import { | ||||||
|   CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT, |   CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT, | ||||||
|  | @ -14,6 +14,7 @@ import { useCssVar, useDebounceFn } from '@vueuse/core'; | ||||||
|  * @zh_CN content style |  * @zh_CN content style | ||||||
|  */ |  */ | ||||||
| function useContentStyle() { | function useContentStyle() { | ||||||
|  |   let resizeObserver: null | ResizeObserver = null; | ||||||
|   const contentElement = ref<HTMLDivElement | null>(null); |   const contentElement = ref<HTMLDivElement | null>(null); | ||||||
|   const visibleDomRect = ref<null | VisibleDomRect>(null); |   const visibleDomRect = ref<null | VisibleDomRect>(null); | ||||||
|   const contentHeight = useCssVar(CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT); |   const contentHeight = useCssVar(CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT); | ||||||
|  | @ -41,12 +42,15 @@ function useContentStyle() { | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|   onMounted(() => { |   onMounted(() => { | ||||||
|     nextTick(() => { |     if (contentElement.value && !resizeObserver) { | ||||||
|       if (contentElement.value) { |       resizeObserver = new ResizeObserver(debouncedCalcHeight); | ||||||
|         const observer = new ResizeObserver(debouncedCalcHeight); |       resizeObserver.observe(contentElement.value); | ||||||
|         observer.observe(contentElement.value); |  | ||||||
|     } |     } | ||||||
|   }); |   }); | ||||||
|  | 
 | ||||||
|  |   onUnmounted(() => { | ||||||
|  |     resizeObserver?.disconnect(); | ||||||
|  |     resizeObserver = null; | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   return { contentElement, overlayStyle, visibleDomRect }; |   return { contentElement, overlayStyle, visibleDomRect }; | ||||||
|  |  | ||||||
|  | @ -39,7 +39,7 @@ describe('useSortable', () => { | ||||||
|     expect(Sortable.default.create).toHaveBeenCalledWith( |     expect(Sortable.default.create).toHaveBeenCalledWith( | ||||||
|       mockElement, |       mockElement, | ||||||
|       expect.objectContaining({ |       expect.objectContaining({ | ||||||
|         animation: 100, |         animation: 300, | ||||||
|         delay: 400, |         delay: 400, | ||||||
|         delayOnTouchOnly: true, |         delayOnTouchOnly: true, | ||||||
|         ...customOptions, |         ...customOptions, | ||||||
|  |  | ||||||
|  | @ -18,7 +18,7 @@ function useSortable<T extends HTMLElement>( | ||||||
|     // Sortable?.default?.mount?.(AutoScroll);
 |     // Sortable?.default?.mount?.(AutoScroll);
 | ||||||
| 
 | 
 | ||||||
|     const sortable = Sortable?.default?.create?.(sortableContainer, { |     const sortable = Sortable?.default?.create?.(sortableContainer, { | ||||||
|       animation: 100, |       animation: 300, | ||||||
|       delay: 400, |       delay: 400, | ||||||
|       delayOnTouchOnly: true, |       delayOnTouchOnly: true, | ||||||
|       ...options, |       ...options, | ||||||
|  |  | ||||||
|  | @ -31,7 +31,7 @@ | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@vben-core/shared": "workspace:*", |     "@vben-core/shared": "workspace:*", | ||||||
|     "@vben-core/typings": "workspace:*", |     "@vben-core/typings": "workspace:*", | ||||||
|     "@vueuse/core": "^10.11.1", |     "@vueuse/core": "^11.0.0", | ||||||
|     "vue": "^3.4.38" |     "vue": "^3.4.37" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -41,7 +41,7 @@ | ||||||
|     "@vben-core/icons": "workspace:*", |     "@vben-core/icons": "workspace:*", | ||||||
|     "@vben-core/shadcn-ui": "workspace:*", |     "@vben-core/shadcn-ui": "workspace:*", | ||||||
|     "@vben-core/typings": "workspace:*", |     "@vben-core/typings": "workspace:*", | ||||||
|     "@vueuse/core": "^10.11.1", |     "@vueuse/core": "^11.0.0", | ||||||
|     "vue": "^3.4.38" |     "vue": "^3.4.37" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -42,7 +42,7 @@ | ||||||
|     "@vben-core/shadcn-ui": "workspace:*", |     "@vben-core/shadcn-ui": "workspace:*", | ||||||
|     "@vben-core/shared": "workspace:*", |     "@vben-core/shared": "workspace:*", | ||||||
|     "@vben-core/typings": "workspace:*", |     "@vben-core/typings": "workspace:*", | ||||||
|     "@vueuse/core": "^10.11.1", |     "@vueuse/core": "^11.0.0", | ||||||
|     "vue": "^3.4.38" |     "vue": "^3.4.37" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -46,10 +46,10 @@ | ||||||
|     "@vben-core/icons": "workspace:*", |     "@vben-core/icons": "workspace:*", | ||||||
|     "@vben-core/shared": "workspace:*", |     "@vben-core/shared": "workspace:*", | ||||||
|     "@vben-core/typings": "workspace:*", |     "@vben-core/typings": "workspace:*", | ||||||
|     "@vueuse/core": "^10.11.1", |     "@vueuse/core": "^11.0.0", | ||||||
|     "class-variance-authority": "^0.7.0", |     "class-variance-authority": "^0.7.0", | ||||||
|     "lucide-vue-next": "^0.427.0", |     "lucide-vue-next": "^0.428.0", | ||||||
|     "radix-vue": "^1.9.4", |     "radix-vue": "^1.9.4", | ||||||
|     "vue": "^3.4.38" |     "vue": "^3.4.37" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { ref } from 'vue'; | import { computed, ref } from 'vue'; | ||||||
| 
 | 
 | ||||||
| import { cn } from '@vben-core/shared'; | import { cn } from '@vben-core/shared'; | ||||||
| 
 | 
 | ||||||
|  | @ -11,6 +11,10 @@ interface Props { | ||||||
|   scrollBarClass?: any; |   scrollBarClass?: any; | ||||||
|   shadow?: boolean; |   shadow?: boolean; | ||||||
|   shadowBorder?: boolean; |   shadowBorder?: boolean; | ||||||
|  |   shadowBottom?: boolean; | ||||||
|  |   shadowLeft?: boolean; | ||||||
|  |   shadowRight?: boolean; | ||||||
|  |   shadowTop?: boolean; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const props = withDefaults(defineProps<Props>(), { | const props = withDefaults(defineProps<Props>(), { | ||||||
|  | @ -18,29 +22,66 @@ const props = withDefaults(defineProps<Props>(), { | ||||||
|   horizontal: false, |   horizontal: false, | ||||||
|   shadow: false, |   shadow: false, | ||||||
|   shadowBorder: false, |   shadowBorder: false, | ||||||
|  |   shadowBottom: true, | ||||||
|  |   shadowLeft: false, | ||||||
|  |   shadowRight: false, | ||||||
|  |   shadowTop: true, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | const emit = defineEmits<{ | ||||||
|  |   scrollAt: [{ bottom: boolean; left: boolean; right: boolean; top: boolean }]; | ||||||
|  | }>(); | ||||||
|  | 
 | ||||||
| const isAtTop = ref(true); | const isAtTop = ref(true); | ||||||
|  | const isAtRight = ref(false); | ||||||
| const isAtBottom = ref(false); | const isAtBottom = ref(false); | ||||||
|  | const isAtLeft = ref(true); | ||||||
|  | 
 | ||||||
|  | const showShadowTop = computed(() => props.shadow && props.shadowTop); | ||||||
|  | const showShadowBottom = computed(() => props.shadow && props.shadowBottom); | ||||||
|  | const showShadowLeft = computed(() => props.shadow && props.shadowLeft); | ||||||
|  | const showShadowRight = computed(() => props.shadow && props.shadowRight); | ||||||
|  | 
 | ||||||
|  | const computedShadowClasses = computed(() => ({ | ||||||
|  |   'shadow-both': | ||||||
|  |     !isAtLeft.value && | ||||||
|  |     !isAtRight.value && | ||||||
|  |     showShadowLeft.value && | ||||||
|  |     showShadowRight.value, | ||||||
|  |   'shadow-left': !isAtLeft.value && showShadowLeft.value, | ||||||
|  |   'shadow-right': !isAtRight.value && showShadowRight.value, | ||||||
|  | })); | ||||||
| 
 | 
 | ||||||
| function handleScroll(event: Event) { | function handleScroll(event: Event) { | ||||||
|   const target = event.target as HTMLElement; |   const target = event.target as HTMLElement; | ||||||
|   const scrollTop = target?.scrollTop ?? 0; |   const scrollTop = target?.scrollTop ?? 0; | ||||||
|  |   const scrollLeft = target?.scrollLeft ?? 0; | ||||||
|   const offsetHeight = target?.offsetHeight ?? 0; |   const offsetHeight = target?.offsetHeight ?? 0; | ||||||
|  |   const offsetWidth = target?.offsetWidth ?? 0; | ||||||
|   const scrollHeight = target?.scrollHeight ?? 0; |   const scrollHeight = target?.scrollHeight ?? 0; | ||||||
|  |   const scrollWidth = target?.scrollWidth ?? 0; | ||||||
|   isAtTop.value = scrollTop <= 0; |   isAtTop.value = scrollTop <= 0; | ||||||
|  |   isAtLeft.value = scrollLeft <= 0; | ||||||
|   isAtBottom.value = scrollTop + offsetHeight >= scrollHeight; |   isAtBottom.value = scrollTop + offsetHeight >= scrollHeight; | ||||||
|  |   isAtRight.value = scrollLeft + offsetWidth >= scrollWidth; | ||||||
|  | 
 | ||||||
|  |   emit('scrollAt', { | ||||||
|  |     bottom: isAtBottom.value, | ||||||
|  |     left: isAtLeft.value, | ||||||
|  |     right: isAtRight.value, | ||||||
|  |     top: isAtTop.value, | ||||||
|  |   }); | ||||||
| } | } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|   <ScrollArea |   <ScrollArea | ||||||
|     :class="[cn(props.class)]" |     :class="[cn(props.class), computedShadowClasses]" | ||||||
|     :on-scroll="handleScroll" |     :on-scroll="handleScroll" | ||||||
|     class="relative" |     class="vben-scrollbar relative" | ||||||
|   > |   > | ||||||
|     <div |     <div | ||||||
|       v-if="shadow" |       v-if="showShadowTop" | ||||||
|       :class="{ |       :class="{ | ||||||
|         'opacity-100': !isAtTop, |         'opacity-100': !isAtTop, | ||||||
|         'border-border border-t': shadowBorder && !isAtTop, |         'border-border border-t': shadowBorder && !isAtTop, | ||||||
|  | @ -49,7 +90,7 @@ function handleScroll(event: Event) { | ||||||
|     ></div> |     ></div> | ||||||
|     <slot></slot> |     <slot></slot> | ||||||
|     <div |     <div | ||||||
|       v-if="shadow" |       v-if="showShadowBottom" | ||||||
|       :class="{ |       :class="{ | ||||||
|         'opacity-100': !isAtTop && !isAtBottom, |         'opacity-100': !isAtTop && !isAtBottom, | ||||||
|         'border-border border-b': shadowBorder && !isAtTop && !isAtBottom, |         'border-border border-b': shadowBorder && !isAtTop && !isAtBottom, | ||||||
|  | @ -65,6 +106,31 @@ function handleScroll(event: Event) { | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <style scoped> | <style scoped> | ||||||
|  | .vben-scrollbar { | ||||||
|  |   &:not(.shadow-both).shadow-left { | ||||||
|  |     mask-image: linear-gradient(90deg, transparent, #000 16px); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &:not(.shadow-both).shadow-right { | ||||||
|  |     mask-image: linear-gradient( | ||||||
|  |       90deg, | ||||||
|  |       #000 0%, | ||||||
|  |       #000 calc(100% - 16px), | ||||||
|  |       transparent | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &.shadow-both { | ||||||
|  |     mask-image: linear-gradient( | ||||||
|  |       90deg, | ||||||
|  |       transparent, | ||||||
|  |       #000 16px, | ||||||
|  |       #000 calc(100% - 16px), | ||||||
|  |       transparent 100% | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .scrollbar-top-shadow { | .scrollbar-top-shadow { | ||||||
|   background: linear-gradient( |   background: linear-gradient( | ||||||
|     to bottom, |     to bottom, | ||||||
|  |  | ||||||
|  | @ -41,6 +41,7 @@ | ||||||
|     "@vben-core/icons": "workspace:*", |     "@vben-core/icons": "workspace:*", | ||||||
|     "@vben-core/shadcn-ui": "workspace:*", |     "@vben-core/shadcn-ui": "workspace:*", | ||||||
|     "@vben-core/typings": "workspace:*", |     "@vben-core/typings": "workspace:*", | ||||||
|     "vue": "^3.4.38" |     "@vueuse/core": "^11.0.0", | ||||||
|  |     "vue": "^3.4.37" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,10 +3,10 @@ import type { TabDefinition } from '@vben-core/typings'; | ||||||
| 
 | 
 | ||||||
| import type { TabConfig, TabsProps } from '../../types'; | import type { TabConfig, TabsProps } from '../../types'; | ||||||
| 
 | 
 | ||||||
| import { computed, ref, watch } from 'vue'; | import { computed, ref } from 'vue'; | ||||||
| 
 | 
 | ||||||
| import { MdiPin, X } from '@vben-core/icons'; | import { MdiPin, X } from '@vben-core/icons'; | ||||||
| import { VbenContextMenu, VbenIcon, VbenScrollbar } from '@vben-core/shadcn-ui'; | import { VbenContextMenu, VbenIcon } from '@vben-core/shadcn-ui'; | ||||||
| 
 | 
 | ||||||
| interface Props extends TabsProps {} | interface Props extends TabsProps {} | ||||||
| 
 | 
 | ||||||
|  | @ -20,17 +20,17 @@ const props = withDefaults(defineProps<Props>(), { | ||||||
|   contentClass: 'vben-tabs-content', |   contentClass: 'vben-tabs-content', | ||||||
|   contextMenus: () => [], |   contextMenus: () => [], | ||||||
|   gap: 7, |   gap: 7, | ||||||
|   maxWidth: 150, |  | ||||||
|   minWidth: 80, |  | ||||||
|   tabs: () => [], |   tabs: () => [], | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const emit = defineEmits<{ close: [string]; unpin: [TabDefinition] }>(); | const emit = defineEmits<{ | ||||||
|  |   close: [string]; | ||||||
|  |   unpin: [TabDefinition]; | ||||||
|  | }>(); | ||||||
| const active = defineModel<string>('active'); | const active = defineModel<string>('active'); | ||||||
| 
 | 
 | ||||||
| const contentRef = ref(); | const contentRef = ref(); | ||||||
| const tabRef = ref(); | const tabRef = ref(); | ||||||
| const tabWidth = ref<number>(props.maxWidth); |  | ||||||
| 
 | 
 | ||||||
| const style = computed(() => { | const style = computed(() => { | ||||||
|   const { gap } = props; |   const { gap } = props; | ||||||
|  | @ -53,51 +53,25 @@ const tabsView = computed((): TabConfig[] => { | ||||||
|     }; |     }; | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
| 
 |  | ||||||
| watch(active, () => { |  | ||||||
|   scrollIntoView(); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| function scrollIntoView() { |  | ||||||
|   setTimeout(() => { |  | ||||||
|     const element = document.querySelector(`.tabs-chrome__item.is-active`); |  | ||||||
| 
 |  | ||||||
|     if (element) { |  | ||||||
|       element.scrollIntoView({ behavior: 'smooth', block: 'start' }); |  | ||||||
|     } |  | ||||||
|   }); |  | ||||||
| } |  | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|   <div :style="style" class="tabs-chrome size-full flex-1 overflow-hidden pt-1"> |  | ||||||
|     <VbenScrollbar |  | ||||||
|       id="tabs-scrollbar" |  | ||||||
|       class="tabs-chrome__scrollbar h-full" |  | ||||||
|       horizontal |  | ||||||
|       scroll-bar-class="z-10 hidden" |  | ||||||
|     > |  | ||||||
|       <!-- footer -> 4px --> |  | ||||||
|   <div |   <div | ||||||
|     ref="contentRef" |     ref="contentRef" | ||||||
|     :class="contentClass" |     :class="contentClass" | ||||||
|         class="relative !flex h-full w-max" |     :style="style" | ||||||
|  |     class="tabs-chrome !flex h-full w-max pr-6" | ||||||
|   > |   > | ||||||
|     <TransitionGroup name="slide-left"> |     <TransitionGroup name="slide-left"> | ||||||
|       <div |       <div | ||||||
|         v-for="(tab, i) in tabsView" |         v-for="(tab, i) in tabsView" | ||||||
|         :key="tab.key" |         :key="tab.key" | ||||||
|         ref="tabRef" |         ref="tabRef" | ||||||
|             :class="[ |         :class="[{ 'is-active': tab.key === active, dragable: !tab.affixTab }]" | ||||||
|               { 'is-active': tab.key === active, dragable: !tab.affixTab }, |  | ||||||
|             ]" |  | ||||||
|         :data-active-tab="active" |         :data-active-tab="active" | ||||||
|         :data-index="i" |         :data-index="i" | ||||||
|             :style="{ |         class="tabs-chrome__item draggable group relative -mr-3 flex h-full select-none items-center" | ||||||
|               width: `${tabWidth}px`, |         data-tab-item="true" | ||||||
|               left: `${(tabWidth - gap * 2) * i}px`, |  | ||||||
|             }" |  | ||||||
|             class="tabs-chrome__item group absolute flex h-full select-none items-center transition-all" |  | ||||||
|         @click="active = tab.key" |         @click="active = tab.key" | ||||||
|       > |       > | ||||||
|         <VbenContextMenu |         <VbenContextMenu | ||||||
|  | @ -106,18 +80,18 @@ function scrollIntoView() { | ||||||
|           :modal="false" |           :modal="false" | ||||||
|           item-class="pr-6" |           item-class="pr-6" | ||||||
|         > |         > | ||||||
|               <div class="size-full"> |           <div class="relative size-full px-1"> | ||||||
|             <!-- divider --> |             <!-- divider --> | ||||||
|             <div |             <div | ||||||
|               v-if="i !== 0 && tab.key !== active" |               v-if="i !== 0 && tab.key !== active" | ||||||
|                   class="tabs-chrome__divider bg-foreground/60 absolute left-[var(--gap)] top-1/2 z-0 h-4 w-[1px] translate-y-[-50%] transition-all" |               class="tabs-chrome__divider bg-foreground/50 absolute left-[var(--gap)] top-1/2 z-0 h-4 w-[1px] translate-y-[-50%] transition-all" | ||||||
|             ></div> |             ></div> | ||||||
|             <!-- background --> |             <!-- background --> | ||||||
|             <div |             <div | ||||||
|                   class="tabs-chrome__background absolute z-[1] size-full px-[calc(var(--gap)-1px)] py-0 transition-opacity duration-150" |               class="tabs-chrome__background absolute z-[-1] size-full px-[calc(var(--gap)-1px)] py-0 transition-opacity duration-150" | ||||||
|             > |             > | ||||||
|               <div |               <div | ||||||
|                     class="tabs-chrome__background-content group-[.is-active]:bg-primary/15 dark:group-[.is-active]:bg-accent h-full rounded-tl-[var(--gap)] rounded-tr-[var(--gap)] duration-150" |                 class="tabs-chrome__background-content group-[.is-active]:bg-heavy dark:group-[.is-active]:bg-accent h-full rounded-tl-[var(--gap)] rounded-tr-[var(--gap)] duration-150" | ||||||
|               ></div> |               ></div> | ||||||
|               <svg |               <svg | ||||||
|                 class="tabs-chrome__background-before group-[.is-active]:fill-primary/15 dark:group-[.is-active]:fill-accent absolute bottom-0 left-[-1px] fill-transparent transition-all duration-150" |                 class="tabs-chrome__background-before group-[.is-active]:fill-primary/15 dark:group-[.is-active]:fill-accent absolute bottom-0 left-[-1px] fill-transparent transition-all duration-150" | ||||||
|  | @ -137,37 +111,33 @@ function scrollIntoView() { | ||||||
| 
 | 
 | ||||||
|             <!-- extra --> |             <!-- extra --> | ||||||
|             <div |             <div | ||||||
|                   class="tabs-chrome__extra absolute right-[calc(var(--gap)*1.5)] top-1/2 z-[3] size-4 translate-y-[-50%]" |               class="tabs-chrome__extra absolute right-[var(--gap)] top-1/2 z-[3] size-4 translate-y-[-50%]" | ||||||
|             > |             > | ||||||
|               <!-- close-icon --> |               <!-- close-icon --> | ||||||
|               <X |               <X | ||||||
|                     v-show=" |                 v-show="!tab.affixTab && tabsView.length > 1 && tab.closable" | ||||||
|                       !tab.affixTab && tabsView.length > 1 && tab.closable |                 class="hover:bg-accent stroke-accent-foreground/80 hover:stroke-accent-foreground text-accent-foreground/80 group-[.is-active]:text-accent-foreground mt-[2px] size-3 cursor-pointer rounded-full transition-all" | ||||||
|                     " |  | ||||||
|                     class="hover:bg-accent stroke-accent-foreground/80 hover:stroke-accent-foreground dark:group-[.is-active]:text-accent-foreground group-[.is-active]:text-primary mt-[2px] size-3 cursor-pointer rounded-full transition-all" |  | ||||||
|                 @click.stop="() => emit('close', tab.key)" |                 @click.stop="() => emit('close', tab.key)" | ||||||
|               /> |               /> | ||||||
|               <MdiPin |               <MdiPin | ||||||
|                 v-show="tab.affixTab && tabsView.length > 1 && tab.closable" |                 v-show="tab.affixTab && tabsView.length > 1 && tab.closable" | ||||||
|                     class="hover:bg-accent hover:stroke-accent-foreground group-[.is-active]:text-primary dark:group-[.is-active]:text-accent-foreground mt-[2px] size-3.5 cursor-pointer rounded-full transition-all" |                 class="hover:text-accent-foreground text-accent-foreground/80 group-[.is-active]:text-accent-foreground mt-[2px] size-3.5 cursor-pointer rounded-full transition-all" | ||||||
|                 @click.stop="() => emit('unpin', tab)" |                 @click.stop="() => emit('unpin', tab)" | ||||||
|               /> |               /> | ||||||
|             </div> |             </div> | ||||||
| 
 | 
 | ||||||
|             <!-- tab-item-main --> |             <!-- tab-item-main --> | ||||||
|             <div |             <div | ||||||
|                   class="tabs-chrome__item-main group-[.is-active]:text-primary dark:group-[.is-active]:text-accent-foreground text-accent-foreground absolute left-0 right-0 z-[2] mx-[calc(var(--gap)*2)] my-0 flex h-full items-center overflow-hidden rounded-tl-[5px] rounded-tr-[5px] pr-4 duration-150 group-hover:pr-3" |               class="tabs-chrome__item-main group-[.is-active]:text-accent-foreground dark:group-[.is-active]:text-accent-foreground text-accent-foreground z-[2] mx-[calc(var(--gap)*2)] my-0 flex h-full items-center overflow-hidden rounded-tl-[5px] rounded-tr-[5px] pl-2 pr-4 duration-150" | ||||||
|             > |             > | ||||||
|               <VbenIcon |               <VbenIcon | ||||||
|                 v-if="showIcon" |                 v-if="showIcon" | ||||||
|                 :icon="tab.icon" |                 :icon="tab.icon" | ||||||
|                     class="ml-[var(--gap)] flex size-4 items-center overflow-hidden" |                 class="mr-1 flex size-4 items-center overflow-hidden" | ||||||
|                 fallback |                 fallback | ||||||
|               /> |               /> | ||||||
| 
 | 
 | ||||||
|                   <span |               <span class="flex-1 overflow-hidden whitespace-nowrap text-sm"> | ||||||
|                     class="tabs-chrome__label ml-[var(--gap)] flex-1 overflow-hidden whitespace-nowrap text-sm" |  | ||||||
|                   > |  | ||||||
|                 {{ tab.title }} |                 {{ tab.title }} | ||||||
|               </span> |               </span> | ||||||
|             </div> |             </div> | ||||||
|  | @ -176,25 +146,25 @@ function scrollIntoView() { | ||||||
|       </div> |       </div> | ||||||
|     </TransitionGroup> |     </TransitionGroup> | ||||||
|   </div> |   </div> | ||||||
|       <!-- footer --> |  | ||||||
|       <!-- <div class="bg-background h-1"></div> --> |  | ||||||
|     </VbenScrollbar> |  | ||||||
|   </div> |  | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <style scoped> | <style scoped> | ||||||
| .tabs-chrome { | .tabs-chrome { | ||||||
|   .dragging { |   /* .dragging { */ | ||||||
|     .tabs-chrome__item-main { | 
 | ||||||
|  |   /* .tabs-chrome__item-main { | ||||||
|       @apply pr-0; |       @apply pr-0; | ||||||
|     } |     } */ | ||||||
| 
 | 
 | ||||||
|     .tabs-chrome__extra { |   /* .tabs-chrome__extra { | ||||||
|       @apply hidden; |       @apply hidden; | ||||||
|     } |     } */ | ||||||
|   } | 
 | ||||||
|  |   /* } */ | ||||||
|  | 
 | ||||||
|  |   &__item:not(.dragging) { | ||||||
|  |     @apply cursor-pointer; | ||||||
| 
 | 
 | ||||||
|   &__item { |  | ||||||
|     &:hover:not(.is-active) { |     &:hover:not(.is-active) { | ||||||
|       & + .tabs-chrome__item { |       & + .tabs-chrome__item { | ||||||
|         .tabs-chrome__divider { |         .tabs-chrome__divider { | ||||||
|  | @ -207,13 +177,10 @@ function scrollIntoView() { | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       .tabs-chrome__background { |       .tabs-chrome__background { | ||||||
|         &-content { |         @apply pb-[2px]; | ||||||
|           @apply bg-accent mx-1 rounded-md pb-2; |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         &-before, |         &-content { | ||||||
|         &-after { |           @apply bg-accent-hover mx-[2px] rounded-md; | ||||||
|           @apply fill-primary/0; |  | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  | @ -226,30 +193,7 @@ function scrollIntoView() { | ||||||
|           @apply opacity-0 !important; |           @apply opacity-0 !important; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
| 
 |  | ||||||
|       .tabs-chrome__background { |  | ||||||
|         @apply opacity-100; |  | ||||||
| 
 |  | ||||||
|         /* &-content { |  | ||||||
|           @apply bg-accent; |  | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|         &-before, |  | ||||||
|         &-after { |  | ||||||
|           @apply fill-heavy; |  | ||||||
|         } */ |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   &__scrollbar, |  | ||||||
|   &__label { |  | ||||||
|     mask-image: linear-gradient( |  | ||||||
|       90deg, |  | ||||||
|       #000 0%, |  | ||||||
|       #000 calc(100% - 16px), |  | ||||||
|       transparent |  | ||||||
|     ); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
| </style> | </style> | ||||||
|  |  | ||||||
|  | @ -3,10 +3,10 @@ import type { TabDefinition } from '@vben-core/typings'; | ||||||
| 
 | 
 | ||||||
| import type { TabConfig, TabsProps } from '../../types'; | import type { TabConfig, TabsProps } from '../../types'; | ||||||
| 
 | 
 | ||||||
| import { computed, watch } from 'vue'; | import { computed } from 'vue'; | ||||||
| 
 | 
 | ||||||
| import { MdiPin, X } from '@vben-core/icons'; | import { MdiPin, X } from '@vben-core/icons'; | ||||||
| import { VbenContextMenu, VbenIcon, VbenScrollbar } from '@vben-core/shadcn-ui'; | import { VbenContextMenu, VbenIcon } from '@vben-core/shadcn-ui'; | ||||||
| 
 | 
 | ||||||
| interface Props extends TabsProps {} | interface Props extends TabsProps {} | ||||||
| 
 | 
 | ||||||
|  | @ -21,7 +21,10 @@ const props = withDefaults(defineProps<Props>(), { | ||||||
|   tabs: () => [], |   tabs: () => [], | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const emit = defineEmits<{ close: [string]; unpin: [TabDefinition] }>(); | const emit = defineEmits<{ | ||||||
|  |   close: [string]; | ||||||
|  |   unpin: [TabDefinition]; | ||||||
|  | }>(); | ||||||
| const active = defineModel<string>('active'); | const active = defineModel<string>('active'); | ||||||
| 
 | 
 | ||||||
| const typeWithClass = computed(() => { | const typeWithClass = computed(() => { | ||||||
|  | @ -55,33 +58,12 @@ const tabsView = computed((): TabConfig[] => { | ||||||
|     }; |     }; | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
| 
 |  | ||||||
| watch(active, () => { |  | ||||||
|   scrollIntoView(); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| function scrollIntoView() { |  | ||||||
|   setTimeout(() => { |  | ||||||
|     const element = document.querySelector(`.tabs-chrome__item.is-active`); |  | ||||||
| 
 |  | ||||||
|     if (element) { |  | ||||||
|       element.scrollIntoView({ behavior: 'smooth', block: 'start' }); |  | ||||||
|     } |  | ||||||
|   }); |  | ||||||
| } |  | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|   <div class="size-full flex-1 overflow-hidden"> |  | ||||||
|     <VbenScrollbar |  | ||||||
|       id="tabs-scrollbar" |  | ||||||
|       class="tabs-scrollbar h-full" |  | ||||||
|       horizontal |  | ||||||
|       scroll-bar-class="z-10 hidden" |  | ||||||
|     > |  | ||||||
|   <div |   <div | ||||||
|     :class="contentClass" |     :class="contentClass" | ||||||
|         class="relative !flex h-full w-max items-center" |     class="relative !flex h-full w-max items-center pr-6" | ||||||
|   > |   > | ||||||
|     <TransitionGroup name="slide-left"> |     <TransitionGroup name="slide-left"> | ||||||
|       <div |       <div | ||||||
|  | @ -95,7 +77,8 @@ function scrollIntoView() { | ||||||
|           typeWithClass.content, |           typeWithClass.content, | ||||||
|         ]" |         ]" | ||||||
|         :data-index="i" |         :data-index="i" | ||||||
|             class="tabs-chrome__item [&:not(.is-active)]:hover:bg-accent group relative flex cursor-pointer select-none transition-all duration-300" |         class="tab-item [&:not(.is-active)]:hover:bg-accent group relative flex cursor-pointer select-none" | ||||||
|  |         data-tab-item="true" | ||||||
|         @click="active = tab.key" |         @click="active = tab.key" | ||||||
|       > |       > | ||||||
|         <VbenContextMenu |         <VbenContextMenu | ||||||
|  | @ -111,9 +94,7 @@ function scrollIntoView() { | ||||||
|             > |             > | ||||||
|               <!-- close-icon --> |               <!-- close-icon --> | ||||||
|               <X |               <X | ||||||
|                     v-show=" |                 v-show="!tab.affixTab && tabsView.length > 1 && tab.closable" | ||||||
|                       !tab.affixTab && tabsView.length > 1 && tab.closable |  | ||||||
|                     " |  | ||||||
|                 class="hover:bg-accent stroke-accent-foreground/80 hover:stroke-accent-foreground dark:group-[.is-active]:text-accent-foreground group-[.is-active]:text-primary size-3 cursor-pointer rounded-full transition-all" |                 class="hover:bg-accent stroke-accent-foreground/80 hover:stroke-accent-foreground dark:group-[.is-active]:text-accent-foreground group-[.is-active]:text-primary size-3 cursor-pointer rounded-full transition-all" | ||||||
|                 @click.stop="() => emit('close', tab.key)" |                 @click.stop="() => emit('close', tab.key)" | ||||||
|               /> |               /> | ||||||
|  | @ -135,9 +116,7 @@ function scrollIntoView() { | ||||||
|                 fallback |                 fallback | ||||||
|               /> |               /> | ||||||
| 
 | 
 | ||||||
|                   <span |               <span class="flex-1 overflow-hidden whitespace-nowrap text-sm"> | ||||||
|                     class="flex-1 overflow-hidden whitespace-nowrap text-sm" |  | ||||||
|                   > |  | ||||||
|                 {{ tab.title }} |                 {{ tab.title }} | ||||||
|               </span> |               </span> | ||||||
|             </div> |             </div> | ||||||
|  | @ -146,17 +125,4 @@ function scrollIntoView() { | ||||||
|       </div> |       </div> | ||||||
|     </TransitionGroup> |     </TransitionGroup> | ||||||
|   </div> |   </div> | ||||||
|     </VbenScrollbar> |  | ||||||
|   </div> |  | ||||||
| </template> | </template> | ||||||
| 
 |  | ||||||
| <style scoped> |  | ||||||
| .tabs-scrollbar { |  | ||||||
|   mask-image: linear-gradient( |  | ||||||
|     90deg, |  | ||||||
|     #000 0%, |  | ||||||
|     #000 calc(100% - 16px), |  | ||||||
|     transparent |  | ||||||
|   ); |  | ||||||
| } |  | ||||||
| </style> |  | ||||||
|  |  | ||||||
|  | @ -1,15 +1,12 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import type { Sortable } from '@vben-core/composables'; | import type { TabsEmits, TabsProps } from './types'; | ||||||
| import type { TabDefinition } from '@vben-core/typings'; |  | ||||||
| 
 | 
 | ||||||
| import type { TabsProps } from './types'; | import { useForwardPropsEmits } from '@vben-core/composables'; | ||||||
| 
 |  | ||||||
| import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue'; |  | ||||||
| 
 |  | ||||||
| import { useForwardPropsEmits, useSortable } from '@vben-core/composables'; |  | ||||||
| import { ChevronLeft, ChevronRight } from '@vben-core/icons'; | import { ChevronLeft, ChevronRight } from '@vben-core/icons'; | ||||||
|  | import { VbenScrollbar } from '@vben-core/shadcn-ui'; | ||||||
| 
 | 
 | ||||||
| import { Tabs, TabsChrome } from './components'; | import { Tabs, TabsChrome } from './components'; | ||||||
|  | import { useTabsDrag } from './use-tabs-drag'; | ||||||
| import { useTabsViewScroll } from './use-tabs-view-scroll'; | import { useTabsViewScroll } from './use-tabs-view-scroll'; | ||||||
| 
 | 
 | ||||||
| interface Props extends TabsProps {} | interface Props extends TabsProps {} | ||||||
|  | @ -24,136 +21,69 @@ const props = withDefaults(defineProps<Props>(), { | ||||||
|   styleType: 'chrome', |   styleType: 'chrome', | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const emit = defineEmits<{ | const emit = defineEmits<TabsEmits>(); | ||||||
|   close: [string]; |  | ||||||
|   sortTabs: [number, number]; |  | ||||||
|   unpin: [TabDefinition]; |  | ||||||
| }>(); |  | ||||||
| 
 | 
 | ||||||
| const forward = useForwardPropsEmits(props, emit); | const forward = useForwardPropsEmits(props, emit); | ||||||
| 
 | 
 | ||||||
| const { initScrollbar, scrollDirection } = useTabsViewScroll(); | const { | ||||||
|  |   handleScrollAt, | ||||||
|  |   scrollbarRef, | ||||||
|  |   scrollDirection, | ||||||
|  |   scrollIsAtLeft, | ||||||
|  |   scrollIsAtRight, | ||||||
|  |   showScrollButton, | ||||||
|  | } = useTabsViewScroll(props); | ||||||
| 
 | 
 | ||||||
| const sortableInstance = ref<null | Sortable>(null); | useTabsDrag(props, emit); | ||||||
| 
 |  | ||||||
| // 可能会找到拖拽的子元素,这里需要确保拖拽的dom时tab元素 |  | ||||||
| function findParentElement(element: HTMLElement) { |  | ||||||
|   const parentCls = 'group'; |  | ||||||
|   return element.classList.contains(parentCls) |  | ||||||
|     ? element |  | ||||||
|     : element.closest(`.${parentCls}`); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| async function initTabsSortable() { |  | ||||||
|   await nextTick(); |  | ||||||
|   const { contentClass } = props; |  | ||||||
| 
 |  | ||||||
|   const el = document.querySelectorAll(`.${contentClass}`)?.[0] as HTMLElement; |  | ||||||
| 
 |  | ||||||
|   const resetElState = () => { |  | ||||||
|     el.style.cursor = 'default'; |  | ||||||
|     el.classList.remove('dragging'); |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   const { initializeSortable } = useSortable(el, { |  | ||||||
|     filter: (_evt, target: HTMLElement) => { |  | ||||||
|       const parent = findParentElement(target); |  | ||||||
|       const dragable = parent?.classList.contains('dragable'); |  | ||||||
|       return !dragable || !props.dragable; |  | ||||||
|     }, |  | ||||||
|     onEnd(evt) { |  | ||||||
|       const { newIndex, oldIndex } = evt; |  | ||||||
|       // const fromElement = evt.item; |  | ||||||
|       const { srcElement } = (evt as any).originalEvent; |  | ||||||
| 
 |  | ||||||
|       if (!srcElement) { |  | ||||||
|         resetElState(); |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       const srcParent = findParentElement(srcElement); |  | ||||||
| 
 |  | ||||||
|       if (!srcParent) { |  | ||||||
|         resetElState(); |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       if (!srcParent.classList.contains('dragable')) { |  | ||||||
|         resetElState(); |  | ||||||
| 
 |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       if ( |  | ||||||
|         oldIndex !== undefined && |  | ||||||
|         newIndex !== undefined && |  | ||||||
|         !Number.isNaN(oldIndex) && |  | ||||||
|         !Number.isNaN(newIndex) && |  | ||||||
|         oldIndex !== newIndex |  | ||||||
|       ) { |  | ||||||
|         emit('sortTabs', oldIndex, newIndex); |  | ||||||
|       } |  | ||||||
|       resetElState(); |  | ||||||
|     }, |  | ||||||
|     onMove(evt) { |  | ||||||
|       const parent = findParentElement(evt.related); |  | ||||||
|       return parent?.classList.contains('dragable') && props.dragable; |  | ||||||
|     }, |  | ||||||
|     onStart: () => { |  | ||||||
|       el.style.cursor = 'grabbing'; |  | ||||||
|       el.classList.add('dragging'); |  | ||||||
|     }, |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   sortableInstance.value = await initializeSortable(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| async function init() { |  | ||||||
|   await nextTick(); |  | ||||||
|   initTabsSortable(); |  | ||||||
|   initScrollbar(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| onMounted(() => { |  | ||||||
|   init(); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| watch( |  | ||||||
|   () => props.styleType, |  | ||||||
|   () => { |  | ||||||
|     sortableInstance.value?.destroy(); |  | ||||||
|     init(); |  | ||||||
|   }, |  | ||||||
| ); |  | ||||||
| 
 |  | ||||||
| onUnmounted(() => { |  | ||||||
|   sortableInstance.value?.destroy(); |  | ||||||
| }); |  | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|   <div |   <div class="flex h-full flex-1 overflow-hidden"> | ||||||
|     :class="{ |  | ||||||
|       'overflow-hidden': styleType !== 'chrome', |  | ||||||
|     }" |  | ||||||
|     class="flex h-full flex-1" |  | ||||||
|   > |  | ||||||
|     <!-- 左侧滚动按钮 --> |     <!-- 左侧滚动按钮 --> | ||||||
|     <span |     <span | ||||||
|       class="hover:bg-muted text-muted-foreground cursor-pointer border-r px-2" |       v-show="showScrollButton" | ||||||
|  |       :class="{ | ||||||
|  |         'hover:bg-muted text-muted-foreground cursor-pointer': !scrollIsAtLeft, | ||||||
|  |         'pointer-events-none opacity-30': scrollIsAtLeft, | ||||||
|  |       }" | ||||||
|  |       class="border-r px-2" | ||||||
|       @click="scrollDirection('left')" |       @click="scrollDirection('left')" | ||||||
|     > |     > | ||||||
|       <ChevronLeft class="size-4 h-full" /> |       <ChevronLeft class="size-4 h-full" /> | ||||||
|     </span> |     </span> | ||||||
| 
 | 
 | ||||||
|  |     <div | ||||||
|  |       :class="{ | ||||||
|  |         'pt-[3px]': styleType === 'chrome', | ||||||
|  |       }" | ||||||
|  |       class="size-full flex-1 overflow-hidden" | ||||||
|  |     > | ||||||
|  |       <VbenScrollbar | ||||||
|  |         ref="scrollbarRef" | ||||||
|  |         class="h-full" | ||||||
|  |         horizontal | ||||||
|  |         scroll-bar-class="z-10 hidden" | ||||||
|  |         shadow | ||||||
|  |         shadow-left | ||||||
|  |         shadow-right | ||||||
|  |         @scroll-at="handleScrollAt" | ||||||
|  |       > | ||||||
|         <TabsChrome |         <TabsChrome | ||||||
|           v-if="styleType === 'chrome'" |           v-if="styleType === 'chrome'" | ||||||
|           v-bind="{ ...forward, ...$attrs, ...$props }" |           v-bind="{ ...forward, ...$attrs, ...$props }" | ||||||
|         /> |         /> | ||||||
|  | 
 | ||||||
|         <Tabs v-else v-bind="{ ...forward, ...$attrs, ...$props }" /> |         <Tabs v-else v-bind="{ ...forward, ...$attrs, ...$props }" /> | ||||||
|  |       </VbenScrollbar> | ||||||
|  |     </div> | ||||||
| 
 | 
 | ||||||
|     <!-- 左侧滚动按钮 --> |     <!-- 左侧滚动按钮 --> | ||||||
|     <span |     <span | ||||||
|  |       v-show="showScrollButton" | ||||||
|  |       :class="{ | ||||||
|  |         'hover:bg-muted text-muted-foreground cursor-pointer': !scrollIsAtRight, | ||||||
|  |         'pointer-events-none opacity-30': scrollIsAtRight, | ||||||
|  |       }" | ||||||
|       class="hover:bg-muted text-muted-foreground cursor-pointer border-l px-2" |       class="hover:bg-muted text-muted-foreground cursor-pointer border-l px-2" | ||||||
|       @click="scrollDirection('right')" |       @click="scrollDirection('right')" | ||||||
|     > |     > | ||||||
|  |  | ||||||
|  | @ -1,7 +1,14 @@ | ||||||
| import type { IContextMenuItem } from '@vben-core/shadcn-ui'; | import type { IContextMenuItem } from '@vben-core/shadcn-ui'; | ||||||
| import type { TabDefinition, TabsStyleType } from '@vben-core/typings'; | import type { TabDefinition, TabsStyleType } from '@vben-core/typings'; | ||||||
| 
 | 
 | ||||||
| interface TabsProps { | export type TabsEmits = { | ||||||
|  |   close: [string]; | ||||||
|  |   sortTabs: [number, number]; | ||||||
|  |   unpin: [TabDefinition]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export interface TabsProps { | ||||||
|  |   active?: string; | ||||||
|   /** |   /** | ||||||
|    * @zh_CN content class |    * @zh_CN content class | ||||||
|    * @default tabs-chrome |    * @default tabs-chrome | ||||||
|  | @ -48,12 +55,10 @@ interface TabsProps { | ||||||
|   tabs?: TabDefinition[]; |   tabs?: TabDefinition[]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface TabConfig extends TabDefinition { | export interface TabConfig extends TabDefinition { | ||||||
|   affixTab: boolean; |   affixTab: boolean; | ||||||
|   closable: boolean; |   closable: boolean; | ||||||
|   icon: string; |   icon: string; | ||||||
|   key: string; |   key: string; | ||||||
|   title: string; |   title: string; | ||||||
| } | } | ||||||
| 
 |  | ||||||
| export type { TabConfig, TabsProps }; |  | ||||||
|  |  | ||||||
|  | @ -0,0 +1,110 @@ | ||||||
|  | import type { EmitType } from '@vben-core/typings'; | ||||||
|  | 
 | ||||||
|  | import type { TabsProps } from './types'; | ||||||
|  | 
 | ||||||
|  | import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { type Sortable, useSortable } from '@vben-core/composables'; | ||||||
|  | 
 | ||||||
|  | // 可能会找到拖拽的子元素,这里需要确保拖拽的dom时tab元素
 | ||||||
|  | function findParentElement(element: HTMLElement) { | ||||||
|  |   const parentCls = 'group'; | ||||||
|  |   return element.classList.contains(parentCls) | ||||||
|  |     ? element | ||||||
|  |     : element.closest(`.${parentCls}`); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function useTabsDrag(props: TabsProps, emit: EmitType) { | ||||||
|  |   const sortableInstance = ref<null | Sortable>(null); | ||||||
|  | 
 | ||||||
|  |   async function initTabsSortable() { | ||||||
|  |     await nextTick(); | ||||||
|  | 
 | ||||||
|  |     const el = document.querySelectorAll( | ||||||
|  |       `.${props.contentClass}`, | ||||||
|  |     )?.[0] as HTMLElement; | ||||||
|  | 
 | ||||||
|  |     if (!el) { | ||||||
|  |       console.warn('Element not found for sortable initialization'); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const resetElState = async () => { | ||||||
|  |       el.style.cursor = 'default'; | ||||||
|  |       el.classList.remove('dragging'); | ||||||
|  |       el.querySelector('.draggable')?.classList.remove('dragging'); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const { initializeSortable } = useSortable(el, { | ||||||
|  |       filter: (_evt, target: HTMLElement) => { | ||||||
|  |         const parent = findParentElement(target); | ||||||
|  |         const dragable = parent?.classList.contains('dragable'); | ||||||
|  |         return !dragable || !props.dragable; | ||||||
|  |       }, | ||||||
|  |       onEnd(evt) { | ||||||
|  |         const { newIndex, oldIndex } = evt; | ||||||
|  |         // const fromElement = evt.item;
 | ||||||
|  |         const { srcElement } = (evt as any).originalEvent; | ||||||
|  | 
 | ||||||
|  |         if (!srcElement) { | ||||||
|  |           resetElState(); | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const srcParent = findParentElement(srcElement); | ||||||
|  | 
 | ||||||
|  |         if (!srcParent) { | ||||||
|  |           resetElState(); | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!srcParent.classList.contains('dragable')) { | ||||||
|  |           resetElState(); | ||||||
|  | 
 | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if ( | ||||||
|  |           oldIndex !== undefined && | ||||||
|  |           newIndex !== undefined && | ||||||
|  |           !Number.isNaN(oldIndex) && | ||||||
|  |           !Number.isNaN(newIndex) && | ||||||
|  |           oldIndex !== newIndex | ||||||
|  |         ) { | ||||||
|  |           emit('sortTabs', oldIndex, newIndex); | ||||||
|  |         } | ||||||
|  |         resetElState(); | ||||||
|  |       }, | ||||||
|  |       onMove(evt) { | ||||||
|  |         const parent = findParentElement(evt.related); | ||||||
|  |         return parent?.classList.contains('dragable') && props.dragable; | ||||||
|  |       }, | ||||||
|  |       onStart: () => { | ||||||
|  |         el.style.cursor = 'grabbing'; | ||||||
|  |         el.querySelector('.draggable')?.classList.add('dragging'); | ||||||
|  |         // el.classList.add('dragging');
 | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     sortableInstance.value = await initializeSortable(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async function init() { | ||||||
|  |     await nextTick(); | ||||||
|  |     initTabsSortable(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   onMounted(init); | ||||||
|  | 
 | ||||||
|  |   watch( | ||||||
|  |     () => props.styleType, | ||||||
|  |     () => { | ||||||
|  |       sortableInstance.value?.destroy(); | ||||||
|  |       init(); | ||||||
|  |     }, | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   onUnmounted(() => { | ||||||
|  |     sortableInstance.value?.destroy(); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | @ -1,15 +1,28 @@ | ||||||
| import { nextTick, ref } from 'vue'; | import type { TabsProps } from './types'; | ||||||
| 
 | 
 | ||||||
| type El = Element | null | undefined; | import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue'; | ||||||
| 
 | 
 | ||||||
| export function useTabsViewScroll(scrollDistance: number = 150) { | import { VbenScrollbar } from '@vben-core/shadcn-ui'; | ||||||
|   const scrollbarEl = ref<El>(null); | 
 | ||||||
|   const scrollViewportEl = ref<El>(null); | import { useDebounceFn } from '@vueuse/core'; | ||||||
|  | 
 | ||||||
|  | type DomElement = Element | null | undefined; | ||||||
|  | 
 | ||||||
|  | export function useTabsViewScroll(props: TabsProps) { | ||||||
|  |   let resizeObserver: null | ResizeObserver = null; | ||||||
|  |   let mutationObserver: MutationObserver | null = null; | ||||||
|  |   let tabItemCount = 0; | ||||||
|  |   const scrollbarRef = ref<InstanceType<typeof VbenScrollbar> | null>(null); | ||||||
|  |   const scrollViewportEl = ref<DomElement>(null); | ||||||
|  |   const showScrollButton = ref(false); | ||||||
|  |   const scrollIsAtLeft = ref(true); | ||||||
|  |   const scrollIsAtRight = ref(false); | ||||||
| 
 | 
 | ||||||
|   function getScrollClientWidth() { |   function getScrollClientWidth() { | ||||||
|     if (!scrollbarEl.value || !scrollViewportEl.value) return {}; |     const scrollbarEl = scrollbarRef.value?.$el; | ||||||
|  |     if (!scrollbarEl || !scrollViewportEl.value) return {}; | ||||||
| 
 | 
 | ||||||
|     const scrollbarWidth = scrollbarEl.value.clientWidth; |     const scrollbarWidth = scrollbarEl.clientWidth; | ||||||
|     const scrollViewWidth = scrollViewportEl.value.clientWidth; |     const scrollViewWidth = scrollViewportEl.value.clientWidth; | ||||||
| 
 | 
 | ||||||
|     return { |     return { | ||||||
|  | @ -20,7 +33,7 @@ export function useTabsViewScroll(scrollDistance: number = 150) { | ||||||
| 
 | 
 | ||||||
|   function scrollDirection( |   function scrollDirection( | ||||||
|     direction: 'left' | 'right', |     direction: 'left' | 'right', | ||||||
|     distance: number = scrollDistance, |     distance: number = 150, | ||||||
|   ) { |   ) { | ||||||
|     const { scrollbarWidth, scrollViewWidth } = getScrollClientWidth(); |     const { scrollbarWidth, scrollViewWidth } = getScrollClientWidth(); | ||||||
| 
 | 
 | ||||||
|  | @ -39,21 +52,142 @@ export function useTabsViewScroll(scrollDistance: number = 150) { | ||||||
| 
 | 
 | ||||||
|   async function initScrollbar() { |   async function initScrollbar() { | ||||||
|     await nextTick(); |     await nextTick(); | ||||||
|     const barEl = document.querySelector('#tabs-scrollbar'); |  | ||||||
| 
 | 
 | ||||||
|     const viewportEl = barEl?.querySelector( |     const scrollbarEl = scrollbarRef.value?.$el; | ||||||
|  |     if (!scrollbarEl) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const viewportEl = scrollbarEl?.querySelector( | ||||||
|       'div[data-radix-scroll-area-viewport]', |       'div[data-radix-scroll-area-viewport]', | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     scrollbarEl.value = barEl; |  | ||||||
|     scrollViewportEl.value = viewportEl; |     scrollViewportEl.value = viewportEl; | ||||||
|  |     calcShowScrollbarButton(); | ||||||
| 
 | 
 | ||||||
|     const activeItem = viewportEl?.querySelector('.is-active'); |     await nextTick(); | ||||||
|     activeItem?.scrollIntoView({ behavior: 'smooth', block: 'start' }); |     scrollToActiveIntoView(); | ||||||
|  | 
 | ||||||
|  |     // 监听大小变化
 | ||||||
|  |     resizeObserver?.disconnect(); | ||||||
|  |     resizeObserver = new ResizeObserver( | ||||||
|  |       useDebounceFn((_entries: ResizeObserverEntry[]) => { | ||||||
|  |         calcShowScrollbarButton(); | ||||||
|  |       }, 100), | ||||||
|  |     ); | ||||||
|  |     resizeObserver.observe(viewportEl); | ||||||
|  | 
 | ||||||
|  |     tabItemCount = props.tabs?.length || 0; | ||||||
|  |     mutationObserver?.disconnect(); | ||||||
|  |     // 使用 MutationObserver 仅监听子节点数量变化
 | ||||||
|  |     mutationObserver = new MutationObserver(() => { | ||||||
|  |       const count = viewportEl.querySelectorAll( | ||||||
|  |         `div[data-tab-item="true"]`, | ||||||
|  |       ).length; | ||||||
|  | 
 | ||||||
|  |       if (count > tabItemCount) { | ||||||
|  |         scrollToActiveIntoView(); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|  |       if (count !== tabItemCount) { | ||||||
|  |         calcShowScrollbarButton(); | ||||||
|  |         tabItemCount = count; | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // 配置为仅监听子节点的添加和移除
 | ||||||
|  |     mutationObserver.observe(viewportEl, { | ||||||
|  |       attributes: false, | ||||||
|  |       childList: true, | ||||||
|  |       subtree: true, | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async function scrollToActiveIntoView() { | ||||||
|  |     if (!scrollViewportEl.value) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     await nextTick(); | ||||||
|  |     const viewportEl = scrollViewportEl.value; | ||||||
|  |     const { scrollbarWidth } = getScrollClientWidth(); | ||||||
|  |     const { scrollWidth } = viewportEl; | ||||||
|  | 
 | ||||||
|  |     if (scrollbarWidth >= scrollWidth) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     requestAnimationFrame(() => { | ||||||
|  |       const activeItem = viewportEl?.querySelector('.is-active'); | ||||||
|  |       activeItem?.scrollIntoView({ behavior: 'smooth', inline: 'start' }); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * 计算tabs 宽度,用于判断是否显示左右滚动按钮 | ||||||
|  |    */ | ||||||
|  |   async function calcShowScrollbarButton() { | ||||||
|  |     if (!scrollViewportEl.value) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const { scrollbarWidth } = getScrollClientWidth(); | ||||||
|  | 
 | ||||||
|  |     showScrollButton.value = | ||||||
|  |       scrollViewportEl.value.scrollWidth > scrollbarWidth; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const handleScrollAt = useDebounceFn(({ left, right }) => { | ||||||
|  |     scrollIsAtLeft.value = left; | ||||||
|  |     scrollIsAtRight.value = right; | ||||||
|  |   }, 100); | ||||||
|  | 
 | ||||||
|  |   watch( | ||||||
|  |     () => props.active, | ||||||
|  |     async () => { | ||||||
|  |       // 200为了等待 tab 切换动画完成
 | ||||||
|  |       // setTimeout(() => {
 | ||||||
|  |       scrollToActiveIntoView(); | ||||||
|  |       // }, 300);
 | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       flush: 'post', | ||||||
|  |     }, | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   // watch(
 | ||||||
|  |   //   () => props.tabs?.length,
 | ||||||
|  |   //   async () => {
 | ||||||
|  |   //     await nextTick();
 | ||||||
|  |   //     calcShowScrollbarButton();
 | ||||||
|  |   //   },
 | ||||||
|  |   //   {
 | ||||||
|  |   //     flush: 'post',
 | ||||||
|  |   //   },
 | ||||||
|  |   // );
 | ||||||
|  | 
 | ||||||
|  |   watch( | ||||||
|  |     () => props.styleType, | ||||||
|  |     () => { | ||||||
|  |       initScrollbar(); | ||||||
|  |     }, | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   onMounted(initScrollbar); | ||||||
|  | 
 | ||||||
|  |   onUnmounted(() => { | ||||||
|  |     resizeObserver?.disconnect(); | ||||||
|  |     mutationObserver?.disconnect(); | ||||||
|  |     resizeObserver = null; | ||||||
|  |     mutationObserver = null; | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|   return { |   return { | ||||||
|  |     handleScrollAt, | ||||||
|     initScrollbar, |     initScrollbar, | ||||||
|  |     scrollbarRef, | ||||||
|     scrollDirection, |     scrollDirection, | ||||||
|  |     scrollIsAtLeft, | ||||||
|  |     scrollIsAtRight, | ||||||
|  |     showScrollButton, | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -24,6 +24,6 @@ | ||||||
|     "@vben/stores": "workspace:*", |     "@vben/stores": "workspace:*", | ||||||
|     "@vben/types": "workspace:*", |     "@vben/types": "workspace:*", | ||||||
|     "@vben/utils": "workspace:*", |     "@vben/utils": "workspace:*", | ||||||
|     "vue": "^3.4.38" |     "vue": "^3.4.37" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -21,8 +21,8 @@ | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@vben/preferences": "workspace:*", |     "@vben/preferences": "workspace:*", | ||||||
|     "@vueuse/core": "^10.11.1", |     "@vueuse/core": "^11.0.0", | ||||||
|     "echarts": "^5.5.1", |     "echarts": "^5.5.1", | ||||||
|     "vue": "^3.4.38" |     "vue": "^3.4.37" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -26,9 +26,9 @@ | ||||||
|     "@vben/icons": "workspace:*", |     "@vben/icons": "workspace:*", | ||||||
|     "@vben/locales": "workspace:*", |     "@vben/locales": "workspace:*", | ||||||
|     "@vben/types": "workspace:*", |     "@vben/types": "workspace:*", | ||||||
|     "@vueuse/integrations": "^10.11.1", |     "@vueuse/integrations": "^11.0.0", | ||||||
|     "qrcode": "^1.5.4", |     "qrcode": "^1.5.4", | ||||||
|     "vue": "^3.4.38", |     "vue": "^3.4.37", | ||||||
|     "vue-router": "^4.4.3" |     "vue-router": "^4.4.3" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|  |  | ||||||
|  | @ -25,7 +25,7 @@ | ||||||
|     "@vben/stores": "workspace:*", |     "@vben/stores": "workspace:*", | ||||||
|     "@vben/types": "workspace:*", |     "@vben/types": "workspace:*", | ||||||
|     "@vben/utils": "workspace:*", |     "@vben/utils": "workspace:*", | ||||||
|     "vue": "^3.4.38", |     "vue": "^3.4.37", | ||||||
|     "vue-router": "^4.4.3", |     "vue-router": "^4.4.3", | ||||||
|     "watermark-js-plus": "^1.5.3" |     "watermark-js-plus": "^1.5.3" | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -32,8 +32,8 @@ | ||||||
|     "@vben/stores": "workspace:*", |     "@vben/stores": "workspace:*", | ||||||
|     "@vben/types": "workspace:*", |     "@vben/types": "workspace:*", | ||||||
|     "@vben/utils": "workspace:*", |     "@vben/utils": "workspace:*", | ||||||
|     "@vueuse/core": "^10.11.1", |     "@vueuse/core": "^11.0.0", | ||||||
|     "vue": "^3.4.38", |     "vue": "^3.4.37", | ||||||
|     "vue-router": "^4.4.3" |     "vue-router": "^4.4.3" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -21,7 +21,7 @@ | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@intlify/core-base": "^9.13.1", |     "@intlify/core-base": "^9.13.1", | ||||||
|     "vue": "^3.4.38", |     "vue": "^3.4.37", | ||||||
|     "vue-i18n": "^9.13.1" |     "vue-i18n": "^9.13.1" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -24,7 +24,7 @@ | ||||||
|     "@vben-core/typings": "workspace:*", |     "@vben-core/typings": "workspace:*", | ||||||
|     "pinia": "2.2.2", |     "pinia": "2.2.2", | ||||||
|     "pinia-plugin-persistedstate": "^3.2.1", |     "pinia-plugin-persistedstate": "^3.2.1", | ||||||
|     "vue": "^3.4.38", |     "vue": "^3.4.37", | ||||||
|     "vue-router": "^4.4.3" |     "vue-router": "^4.4.3" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -124,10 +124,21 @@ export const useTabbarStore = defineStore('core-tabbar', { | ||||||
|       } else { |       } else { | ||||||
|         // 页面已经存在,不重复添加选项卡,只更新选项卡参数
 |         // 页面已经存在,不重复添加选项卡,只更新选项卡参数
 | ||||||
|         const currentTab = toRaw(this.tabs)[tabIndex]; |         const currentTab = toRaw(this.tabs)[tabIndex]; | ||||||
|         const mergedTab = { ...currentTab, ...tab }; |         const mergedTab = { | ||||||
|         if (currentTab && Reflect.has(currentTab.meta, 'affixTab')) { |           ...currentTab, | ||||||
|           mergedTab.meta.affixTab = currentTab.meta.affixTab; |           ...tab, | ||||||
|  |           meta: { ...currentTab?.meta, ...tab.meta }, | ||||||
|  |         }; | ||||||
|  |         if (currentTab) { | ||||||
|  |           const curMeta = currentTab.meta; | ||||||
|  |           if (Reflect.has(curMeta, 'affixTab')) { | ||||||
|  |             mergedTab.meta.affixTab = curMeta.affixTab; | ||||||
|           } |           } | ||||||
|  |           if (Reflect.has(curMeta, 'newTabTitle')) { | ||||||
|  |             mergedTab.meta.newTabTitle = curMeta.newTabTitle; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         this.tabs.splice(tabIndex, 1, mergedTab); |         this.tabs.splice(tabIndex, 1, mergedTab); | ||||||
|       } |       } | ||||||
|       this.updateCacheTab(); |       this.updateCacheTab(); | ||||||
|  |  | ||||||
|  | @ -21,7 +21,7 @@ | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@vben-core/typings": "workspace:*", |     "@vben-core/typings": "workspace:*", | ||||||
|     "vue": "^3.4.38", |     "vue": "^3.4.37", | ||||||
|     "vue-router": "^4.4.3" |     "vue-router": "^4.4.3" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -40,11 +40,11 @@ | ||||||
|     "@vben/styles": "workspace:*", |     "@vben/styles": "workspace:*", | ||||||
|     "@vben/types": "workspace:*", |     "@vben/types": "workspace:*", | ||||||
|     "@vben/utils": "workspace:*", |     "@vben/utils": "workspace:*", | ||||||
|     "@vueuse/core": "^10.11.1", |     "@vueuse/core": "^11.0.0", | ||||||
|     "ant-design-vue": "^4.2.3", |     "ant-design-vue": "^4.2.3", | ||||||
|     "dayjs": "^1.11.12", |     "dayjs": "^1.11.12", | ||||||
|     "pinia": "2.2.2", |     "pinia": "2.2.2", | ||||||
|     "vue": "^3.4.38", |     "vue": "^3.4.37", | ||||||
|     "vue-router": "^4.4.3" |     "vue-router": "^4.4.3" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -161,7 +161,7 @@ const routes: RouteRecordRaw[] = [ | ||||||
|                   import( |                   import( | ||||||
|                     '#/views/demos/features/hide-menu-children/children.vue' |                     '#/views/demos/features/hide-menu-children/children.vue' | ||||||
|                   ), |                   ), | ||||||
|                 meta: { title: 'HideChildrenInMenuChildrenDemo' }, |                 meta: { title: $t('page.demos.features.hideChildrenInMenu') }, | ||||||
|               }, |               }, | ||||||
|             ], |             ], | ||||||
|           }, |           }, | ||||||
|  |  | ||||||
							
								
								
									
										115
									
								
								pnpm-lock.yaml
								
								
								
								
							
							
						
						
									
										115
									
								
								pnpm-lock.yaml
								
								
								
								
							|  | @ -165,8 +165,8 @@ importers: | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../../packages/utils |         version: link:../../packages/utils | ||||||
|       '@vueuse/core': |       '@vueuse/core': | ||||||
|         specifier: ^10.11.1 |         specifier: ^11.0.0 | ||||||
|         version: 10.11.1(vue@3.4.38(typescript@5.5.4)) |         version: 11.0.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|       ant-design-vue: |       ant-design-vue: | ||||||
|         specifier: ^4.2.3 |         specifier: ^4.2.3 | ||||||
|         version: 4.2.3(vue@3.4.38(typescript@5.5.4)) |         version: 4.2.3(vue@3.4.38(typescript@5.5.4)) | ||||||
|  | @ -228,8 +228,8 @@ importers: | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../../packages/utils |         version: link:../../packages/utils | ||||||
|       '@vueuse/core': |       '@vueuse/core': | ||||||
|         specifier: ^10.11.1 |         specifier: ^11.0.0 | ||||||
|         version: 10.11.1(vue@3.4.38(typescript@5.5.4)) |         version: 11.0.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|       dayjs: |       dayjs: | ||||||
|         specifier: ^1.11.12 |         specifier: ^1.11.12 | ||||||
|         version: 1.11.12 |         version: 1.11.12 | ||||||
|  | @ -295,8 +295,8 @@ importers: | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../../packages/utils |         version: link:../../packages/utils | ||||||
|       '@vueuse/core': |       '@vueuse/core': | ||||||
|         specifier: ^10.11.1 |         specifier: ^11.0.0 | ||||||
|         version: 10.11.1(vue@3.4.38(typescript@5.5.4)) |         version: 11.0.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|       naive-ui: |       naive-ui: | ||||||
|         specifier: ^2.39.0 |         specifier: ^2.39.0 | ||||||
|         version: 2.39.0(vue@3.4.38(typescript@5.5.4)) |         version: 2.39.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|  | @ -660,8 +660,8 @@ importers: | ||||||
|         specifier: ^4.1.2 |         specifier: ^4.1.2 | ||||||
|         version: 4.1.2(vue@3.4.38(typescript@5.5.4)) |         version: 4.1.2(vue@3.4.38(typescript@5.5.4)) | ||||||
|       lucide-vue-next: |       lucide-vue-next: | ||||||
|         specifier: ^0.427.0 |         specifier: ^0.428.0 | ||||||
|         version: 0.427.0(vue@3.4.38(typescript@5.5.4)) |         version: 0.428.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|       vue: |       vue: | ||||||
|         specifier: 3.4.38 |         specifier: 3.4.38 | ||||||
|         version: 3.4.38(typescript@5.5.4) |         version: 3.4.38(typescript@5.5.4) | ||||||
|  | @ -672,7 +672,7 @@ importers: | ||||||
|         specifier: 4.1.0 |         specifier: 4.1.0 | ||||||
|         version: 4.1.0 |         version: 4.1.0 | ||||||
|       '@vue/shared': |       '@vue/shared': | ||||||
|         specifier: ^3.4.38 |         specifier: ^3.4.37 | ||||||
|         version: 3.4.38 |         version: 3.4.38 | ||||||
|       clsx: |       clsx: | ||||||
|         specifier: 2.1.1 |         specifier: 2.1.1 | ||||||
|  | @ -715,8 +715,8 @@ importers: | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../base/shared |         version: link:../base/shared | ||||||
|       '@vueuse/core': |       '@vueuse/core': | ||||||
|         specifier: ^10.11.1 |         specifier: ^11.0.0 | ||||||
|         version: 10.11.1(vue@3.4.38(typescript@5.5.4)) |         version: 11.0.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|       radix-vue: |       radix-vue: | ||||||
|         specifier: ^1.9.4 |         specifier: ^1.9.4 | ||||||
|         version: 1.9.4(vue@3.4.38(typescript@5.5.4)) |         version: 1.9.4(vue@3.4.38(typescript@5.5.4)) | ||||||
|  | @ -740,8 +740,8 @@ importers: | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../base/typings |         version: link:../base/typings | ||||||
|       '@vueuse/core': |       '@vueuse/core': | ||||||
|         specifier: ^10.11.1 |         specifier: ^11.0.0 | ||||||
|         version: 10.11.1(vue@3.4.38(typescript@5.5.4)) |         version: 11.0.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|       vue: |       vue: | ||||||
|         specifier: 3.4.38 |         specifier: 3.4.38 | ||||||
|         version: 3.4.38(typescript@5.5.4) |         version: 3.4.38(typescript@5.5.4) | ||||||
|  | @ -761,8 +761,8 @@ importers: | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../../base/typings |         version: link:../../base/typings | ||||||
|       '@vueuse/core': |       '@vueuse/core': | ||||||
|         specifier: ^10.11.1 |         specifier: ^11.0.0 | ||||||
|         version: 10.11.1(vue@3.4.38(typescript@5.5.4)) |         version: 11.0.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|       vue: |       vue: | ||||||
|         specifier: 3.4.38 |         specifier: 3.4.38 | ||||||
|         version: 3.4.38(typescript@5.5.4) |         version: 3.4.38(typescript@5.5.4) | ||||||
|  | @ -785,8 +785,8 @@ importers: | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../../base/typings |         version: link:../../base/typings | ||||||
|       '@vueuse/core': |       '@vueuse/core': | ||||||
|         specifier: ^10.11.1 |         specifier: ^11.0.0 | ||||||
|         version: 10.11.1(vue@3.4.38(typescript@5.5.4)) |         version: 11.0.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|       vue: |       vue: | ||||||
|         specifier: 3.4.38 |         specifier: 3.4.38 | ||||||
|         version: 3.4.38(typescript@5.5.4) |         version: 3.4.38(typescript@5.5.4) | ||||||
|  | @ -806,14 +806,14 @@ importers: | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../../base/typings |         version: link:../../base/typings | ||||||
|       '@vueuse/core': |       '@vueuse/core': | ||||||
|         specifier: ^10.11.1 |         specifier: ^11.0.0 | ||||||
|         version: 10.11.1(vue@3.4.38(typescript@5.5.4)) |         version: 11.0.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|       class-variance-authority: |       class-variance-authority: | ||||||
|         specifier: ^0.7.0 |         specifier: ^0.7.0 | ||||||
|         version: 0.7.0 |         version: 0.7.0 | ||||||
|       lucide-vue-next: |       lucide-vue-next: | ||||||
|         specifier: ^0.427.0 |         specifier: ^0.428.0 | ||||||
|         version: 0.427.0(vue@3.4.38(typescript@5.5.4)) |         version: 0.428.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|       radix-vue: |       radix-vue: | ||||||
|         specifier: ^1.9.4 |         specifier: ^1.9.4 | ||||||
|         version: 1.9.4(vue@3.4.38(typescript@5.5.4)) |         version: 1.9.4(vue@3.4.38(typescript@5.5.4)) | ||||||
|  | @ -835,6 +835,9 @@ importers: | ||||||
|       '@vben-core/typings': |       '@vben-core/typings': | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../../base/typings |         version: link:../../base/typings | ||||||
|  |       '@vueuse/core': | ||||||
|  |         specifier: ^11.0.0 | ||||||
|  |         version: 11.0.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|       vue: |       vue: | ||||||
|         specifier: 3.4.38 |         specifier: 3.4.38 | ||||||
|         version: 3.4.38(typescript@5.5.4) |         version: 3.4.38(typescript@5.5.4) | ||||||
|  | @ -869,8 +872,8 @@ importers: | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../../preferences |         version: link:../../preferences | ||||||
|       '@vueuse/core': |       '@vueuse/core': | ||||||
|         specifier: ^10.11.1 |         specifier: ^11.0.0 | ||||||
|         version: 10.11.1(vue@3.4.38(typescript@5.5.4)) |         version: 11.0.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|       echarts: |       echarts: | ||||||
|         specifier: ^5.5.1 |         specifier: ^5.5.1 | ||||||
|         version: 5.5.1 |         version: 5.5.1 | ||||||
|  | @ -899,8 +902,8 @@ importers: | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../../types |         version: link:../../types | ||||||
|       '@vueuse/integrations': |       '@vueuse/integrations': | ||||||
|         specifier: ^10.11.1 |         specifier: ^11.0.0 | ||||||
|         version: 10.11.1(async-validator@4.2.5)(axios@1.7.4)(focus-trap@7.5.4)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.2)(vue@3.4.38(typescript@5.5.4)) |         version: 11.0.0(async-validator@4.2.5)(axios@1.7.4)(focus-trap@7.5.4)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.2)(vue@3.4.38(typescript@5.5.4)) | ||||||
|       qrcode: |       qrcode: | ||||||
|         specifier: ^1.5.4 |         specifier: ^1.5.4 | ||||||
|         version: 1.5.4 |         version: 1.5.4 | ||||||
|  | @ -981,8 +984,8 @@ importers: | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../../utils |         version: link:../../utils | ||||||
|       '@vueuse/core': |       '@vueuse/core': | ||||||
|         specifier: ^10.11.1 |         specifier: ^11.0.0 | ||||||
|         version: 10.11.1(vue@3.4.38(typescript@5.5.4)) |         version: 11.0.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|       vue: |       vue: | ||||||
|         specifier: 3.4.38 |         specifier: 3.4.38 | ||||||
|         version: 3.4.38(typescript@5.5.4) |         version: 3.4.38(typescript@5.5.4) | ||||||
|  | @ -1129,8 +1132,8 @@ importers: | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../packages/utils |         version: link:../packages/utils | ||||||
|       '@vueuse/core': |       '@vueuse/core': | ||||||
|         specifier: ^10.11.1 |         specifier: ^11.0.0 | ||||||
|         version: 10.11.1(vue@3.4.38(typescript@5.5.4)) |         version: 11.0.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|       ant-design-vue: |       ant-design-vue: | ||||||
|         specifier: ^4.2.3 |         specifier: ^4.2.3 | ||||||
|         version: 4.2.3(vue@3.4.38(typescript@5.5.4)) |         version: 4.2.3(vue@3.4.38(typescript@5.5.4)) | ||||||
|  | @ -4179,6 +4182,9 @@ packages: | ||||||
|   '@vueuse/core@10.11.1': |   '@vueuse/core@10.11.1': | ||||||
|     resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==} |     resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==} | ||||||
| 
 | 
 | ||||||
|  |   '@vueuse/core@11.0.0': | ||||||
|  |     resolution: {integrity: sha512-shibzNGjmRjZucEm97B8V0NO5J3vPHMCE/mltxQ3vHezbDoFQBMtK11XsfwfPionxSbo+buqPmsCljtYuXIBpw==} | ||||||
|  | 
 | ||||||
|   '@vueuse/core@9.13.0': |   '@vueuse/core@9.13.0': | ||||||
|     resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==} |     resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==} | ||||||
| 
 | 
 | ||||||
|  | @ -4223,21 +4229,21 @@ packages: | ||||||
|       universal-cookie: |       universal-cookie: | ||||||
|         optional: true |         optional: true | ||||||
| 
 | 
 | ||||||
|   '@vueuse/integrations@10.11.1': |   '@vueuse/integrations@11.0.0': | ||||||
|     resolution: {integrity: sha512-Y5hCGBguN+vuVYTZmdd/IMXLOdfS60zAmDmFYc4BKBcMUPZH1n4tdyDECCPjXm0bNT3ZRUy1xzTLGaUje8Xyaw==} |     resolution: {integrity: sha512-B95nBX4B2q2ZETBDldrKARM/fYXBHfwdo44UbHBq4bUTi25lrlc8MwAZGqEoRvdV4ND9T6O1Rb9e4kaCJFXnqw==} | ||||||
|     peerDependencies: |     peerDependencies: | ||||||
|       async-validator: ^4 |       async-validator: ^4 | ||||||
|       axios: ^1 |       axios: ^1 | ||||||
|       change-case: ^4 |       change-case: ^5 | ||||||
|       drauu: ^0.3 |       drauu: ^0.4 | ||||||
|       focus-trap: ^7 |       focus-trap: ^7 | ||||||
|       fuse.js: ^6 |       fuse.js: ^7 | ||||||
|       idb-keyval: ^6 |       idb-keyval: ^6 | ||||||
|       jwt-decode: ^3 |       jwt-decode: ^4 | ||||||
|       nprogress: ^0.2 |       nprogress: ^0.2 | ||||||
|       qrcode: ^1.5 |       qrcode: ^1.5 | ||||||
|       sortablejs: ^1 |       sortablejs: ^1 | ||||||
|       universal-cookie: ^6 |       universal-cookie: ^7 | ||||||
|     peerDependenciesMeta: |     peerDependenciesMeta: | ||||||
|       async-validator: |       async-validator: | ||||||
|         optional: true |         optional: true | ||||||
|  | @ -4270,6 +4276,9 @@ packages: | ||||||
|   '@vueuse/metadata@10.11.1': |   '@vueuse/metadata@10.11.1': | ||||||
|     resolution: {integrity: sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==} |     resolution: {integrity: sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==} | ||||||
| 
 | 
 | ||||||
|  |   '@vueuse/metadata@11.0.0': | ||||||
|  |     resolution: {integrity: sha512-0TKsAVT0iUOAPWyc9N79xWYfovJVPATiOPVKByG6jmAYdDiwvMVm9xXJ5hp4I8nZDxpCcYlLq/Rg9w1Z/jrGcg==} | ||||||
|  | 
 | ||||||
|   '@vueuse/metadata@9.13.0': |   '@vueuse/metadata@9.13.0': | ||||||
|     resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==} |     resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==} | ||||||
| 
 | 
 | ||||||
|  | @ -4279,6 +4288,9 @@ packages: | ||||||
|   '@vueuse/shared@10.11.1': |   '@vueuse/shared@10.11.1': | ||||||
|     resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==} |     resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==} | ||||||
| 
 | 
 | ||||||
|  |   '@vueuse/shared@11.0.0': | ||||||
|  |     resolution: {integrity: sha512-i4ZmOrIEjSsL94uAEt3hz88UCz93fMyP/fba9S+vypX90fKg3uYX9cThqvWc9aXxuTzR0UGhOKOTQd//Goh1nQ==} | ||||||
|  | 
 | ||||||
|   '@vueuse/shared@9.13.0': |   '@vueuse/shared@9.13.0': | ||||||
|     resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==} |     resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==} | ||||||
| 
 | 
 | ||||||
|  | @ -6916,8 +6928,8 @@ packages: | ||||||
|     resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} |     resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} | ||||||
|     engines: {node: '>=10'} |     engines: {node: '>=10'} | ||||||
| 
 | 
 | ||||||
|   lucide-vue-next@0.427.0: |   lucide-vue-next@0.428.0: | ||||||
|     resolution: {integrity: sha512-zI1FhbfQ3Wl0SgPKnOWhTDC6yAC5TTjSC9FSZ61ULg3U36e+GVK+RT1qfkU9Q5BjeBuwmsHWKsXKptKMjUAwFA==} |     resolution: {integrity: sha512-of9GJGus9VKGIUOp3yQ0uQtNv+8MRLaso8H4OiDzI6+T7TeMRXTzqVOLhnyg9fdXUnYuwE9Xm1zD1nfQ7oFPmg==} | ||||||
|     peerDependencies: |     peerDependencies: | ||||||
|       vue: 3.4.38 |       vue: 3.4.38 | ||||||
| 
 | 
 | ||||||
|  | @ -13196,6 +13208,16 @@ snapshots: | ||||||
|       - '@vue/composition-api' |       - '@vue/composition-api' | ||||||
|       - vue |       - vue | ||||||
| 
 | 
 | ||||||
|  |   '@vueuse/core@11.0.0(vue@3.4.38(typescript@5.5.4))': | ||||||
|  |     dependencies: | ||||||
|  |       '@types/web-bluetooth': 0.0.20 | ||||||
|  |       '@vueuse/metadata': 11.0.0 | ||||||
|  |       '@vueuse/shared': 11.0.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|  |       vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4)) | ||||||
|  |     transitivePeerDependencies: | ||||||
|  |       - '@vue/composition-api' | ||||||
|  |       - vue | ||||||
|  | 
 | ||||||
|   '@vueuse/core@9.13.0(vue@3.4.38(typescript@5.5.4))': |   '@vueuse/core@9.13.0(vue@3.4.38(typescript@5.5.4))': | ||||||
|     dependencies: |     dependencies: | ||||||
|       '@types/web-bluetooth': 0.0.16 |       '@types/web-bluetooth': 0.0.16 | ||||||
|  | @ -13222,10 +13244,10 @@ snapshots: | ||||||
|       - '@vue/composition-api' |       - '@vue/composition-api' | ||||||
|       - vue |       - vue | ||||||
| 
 | 
 | ||||||
|   '@vueuse/integrations@10.11.1(async-validator@4.2.5)(axios@1.7.4)(focus-trap@7.5.4)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.2)(vue@3.4.38(typescript@5.5.4))': |   '@vueuse/integrations@11.0.0(async-validator@4.2.5)(axios@1.7.4)(focus-trap@7.5.4)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.2)(vue@3.4.38(typescript@5.5.4))': | ||||||
|     dependencies: |     dependencies: | ||||||
|       '@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.5.4)) |       '@vueuse/core': 11.0.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|       '@vueuse/shared': 10.11.1(vue@3.4.38(typescript@5.5.4)) |       '@vueuse/shared': 11.0.0(vue@3.4.38(typescript@5.5.4)) | ||||||
|       vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4)) |       vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4)) | ||||||
|     optionalDependencies: |     optionalDependencies: | ||||||
|       async-validator: 4.2.5 |       async-validator: 4.2.5 | ||||||
|  | @ -13242,6 +13264,8 @@ snapshots: | ||||||
| 
 | 
 | ||||||
|   '@vueuse/metadata@10.11.1': {} |   '@vueuse/metadata@10.11.1': {} | ||||||
| 
 | 
 | ||||||
|  |   '@vueuse/metadata@11.0.0': {} | ||||||
|  | 
 | ||||||
|   '@vueuse/metadata@9.13.0': {} |   '@vueuse/metadata@9.13.0': {} | ||||||
| 
 | 
 | ||||||
|   '@vueuse/shared@10.11.0(vue@3.4.38(typescript@5.5.4))': |   '@vueuse/shared@10.11.0(vue@3.4.38(typescript@5.5.4))': | ||||||
|  | @ -13258,6 +13282,13 @@ snapshots: | ||||||
|       - '@vue/composition-api' |       - '@vue/composition-api' | ||||||
|       - vue |       - vue | ||||||
| 
 | 
 | ||||||
|  |   '@vueuse/shared@11.0.0(vue@3.4.38(typescript@5.5.4))': | ||||||
|  |     dependencies: | ||||||
|  |       vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4)) | ||||||
|  |     transitivePeerDependencies: | ||||||
|  |       - '@vue/composition-api' | ||||||
|  |       - vue | ||||||
|  | 
 | ||||||
|   '@vueuse/shared@9.13.0(vue@3.4.38(typescript@5.5.4))': |   '@vueuse/shared@9.13.0(vue@3.4.38(typescript@5.5.4))': | ||||||
|     dependencies: |     dependencies: | ||||||
|       vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4)) |       vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4)) | ||||||
|  | @ -16250,7 +16281,7 @@ snapshots: | ||||||
|     dependencies: |     dependencies: | ||||||
|       yallist: 4.0.0 |       yallist: 4.0.0 | ||||||
| 
 | 
 | ||||||
|   lucide-vue-next@0.427.0(vue@3.4.38(typescript@5.5.4)): |   lucide-vue-next@0.428.0(vue@3.4.38(typescript@5.5.4)): | ||||||
|     dependencies: |     dependencies: | ||||||
|       vue: 3.4.38(typescript@5.5.4) |       vue: 3.4.38(typescript@5.5.4) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Vben
						Vben