Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin
This commit is contained in:
@@ -4,7 +4,7 @@ import type { ContentCompactType } from '@vben-core/typings';
|
||||
import type { CSSProperties } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { useContentHeightListener } from '@vben-core/composables';
|
||||
import { useContentStyle } from '@vben-core/composables';
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
@@ -24,7 +24,7 @@ interface Props {
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
|
||||
const { contentElement } = useContentHeightListener();
|
||||
const { contentElement, overlayStyle } = useContentStyle();
|
||||
|
||||
const style = computed((): CSSProperties => {
|
||||
const {
|
||||
@@ -53,7 +53,9 @@ const style = computed((): CSSProperties => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main ref="contentElement" :style="style" class="bg-background-deep">
|
||||
<main ref="contentElement" :style="style" class="bg-background-deep relative">
|
||||
<!-- <BlurShadow :style="shadowStyle" /> -->
|
||||
<slot :overlay-style="overlayStyle" name="overlay"></slot>
|
||||
<slot></slot>
|
||||
</main>
|
||||
</template>
|
||||
|
@@ -519,6 +519,10 @@ function handleOpenMenu() {
|
||||
class="transition-[margin-top] duration-200"
|
||||
>
|
||||
<slot name="content"></slot>
|
||||
|
||||
<template #overlay="{ overlayStyle }">
|
||||
<slot :overlay-style="overlayStyle" name="content-overlay"></slot>
|
||||
</template>
|
||||
</LayoutContent>
|
||||
|
||||
<LayoutFooter
|
||||
|
@@ -72,9 +72,10 @@ function scrollIntoView() {
|
||||
<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"
|
||||
scroll-bar-class="z-10 hidden"
|
||||
>
|
||||
<!-- footer -> 4px -->
|
||||
<div
|
||||
|
@@ -72,11 +72,12 @@ function scrollIntoView() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-full flex-1 overflow-hidden">
|
||||
<div class="size-full flex-1 overflow-hidden">
|
||||
<VbenScrollbar
|
||||
id="tabs-scrollbar"
|
||||
class="tabs-scrollbar h-full"
|
||||
horizontal
|
||||
scroll-bar-class="z-10"
|
||||
scroll-bar-class="z-10 hidden"
|
||||
>
|
||||
<div
|
||||
:class="contentClass"
|
||||
|
@@ -7,8 +7,10 @@ import type { TabsProps } from './types';
|
||||
import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||
|
||||
import { useForwardPropsEmits, useSortable } from '@vben-core/composables';
|
||||
import { ChevronLeft, ChevronRight } from '@vben-core/icons';
|
||||
|
||||
import { Tabs, TabsChrome } from './components';
|
||||
import { useTabsViewScroll } from './use-tabs-view-scroll';
|
||||
|
||||
interface Props extends TabsProps {}
|
||||
|
||||
@@ -30,6 +32,8 @@ const emit = defineEmits<{
|
||||
|
||||
const forward = useForwardPropsEmits(props, emit);
|
||||
|
||||
const { initScrollbar, scrollDirection } = useTabsViewScroll();
|
||||
|
||||
const sortableInstance = ref<null | Sortable>(null);
|
||||
|
||||
// 可能会找到拖拽的子元素,这里需要确保拖拽的dom时tab元素
|
||||
@@ -104,13 +108,21 @@ async function initTabsSortable() {
|
||||
sortableInstance.value = await initializeSortable();
|
||||
}
|
||||
|
||||
onMounted(initTabsSortable);
|
||||
async function init() {
|
||||
await nextTick();
|
||||
initTabsSortable();
|
||||
initScrollbar();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.styleType,
|
||||
() => {
|
||||
sortableInstance.value?.destroy();
|
||||
initTabsSortable();
|
||||
init();
|
||||
},
|
||||
);
|
||||
|
||||
@@ -120,6 +132,32 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TabsChrome v-if="styleType === 'chrome'" v-bind="forward" />
|
||||
<Tabs v-else v-bind="forward" />
|
||||
<div
|
||||
:class="{
|
||||
'overflow-hidden': styleType !== 'chrome',
|
||||
}"
|
||||
class="flex h-full flex-1"
|
||||
>
|
||||
<!-- 左侧滚动按钮 -->
|
||||
<span
|
||||
class="hover:bg-muted text-muted-foreground cursor-pointer border-r px-2"
|
||||
@click="scrollDirection('left')"
|
||||
>
|
||||
<ChevronLeft class="size-4 h-full" />
|
||||
</span>
|
||||
|
||||
<TabsChrome
|
||||
v-if="styleType === 'chrome'"
|
||||
v-bind="{ ...forward, ...$attrs, ...$props }"
|
||||
/>
|
||||
<Tabs v-else v-bind="{ ...forward, ...$attrs, ...$props }" />
|
||||
|
||||
<!-- 左侧滚动按钮 -->
|
||||
<span
|
||||
class="hover:bg-muted text-muted-foreground cursor-pointer border-l px-2"
|
||||
@click="scrollDirection('right')"
|
||||
>
|
||||
<ChevronRight class="size-4 h-full" />
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
59
packages/@core/ui-kit/tabs-ui/src/use-tabs-view-scroll.ts
Normal file
59
packages/@core/ui-kit/tabs-ui/src/use-tabs-view-scroll.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { nextTick, ref } from 'vue';
|
||||
|
||||
type El = Element | null | undefined;
|
||||
|
||||
export function useTabsViewScroll(scrollDistance: number = 150) {
|
||||
const scrollbarEl = ref<El>(null);
|
||||
const scrollViewportEl = ref<El>(null);
|
||||
|
||||
function getScrollClientWidth() {
|
||||
if (!scrollbarEl.value || !scrollViewportEl.value) return {};
|
||||
|
||||
const scrollbarWidth = scrollbarEl.value.clientWidth;
|
||||
const scrollViewWidth = scrollViewportEl.value.clientWidth;
|
||||
|
||||
return {
|
||||
scrollbarWidth,
|
||||
scrollViewWidth,
|
||||
};
|
||||
}
|
||||
|
||||
function scrollDirection(
|
||||
direction: 'left' | 'right',
|
||||
distance: number = scrollDistance,
|
||||
) {
|
||||
const { scrollbarWidth, scrollViewWidth } = getScrollClientWidth();
|
||||
|
||||
if (!scrollbarWidth || !scrollViewWidth) return;
|
||||
|
||||
if (scrollbarWidth > scrollViewWidth) return;
|
||||
|
||||
scrollViewportEl.value?.scrollBy({
|
||||
behavior: 'smooth',
|
||||
left:
|
||||
direction === 'left'
|
||||
? -(scrollbarWidth - distance)
|
||||
: +(scrollbarWidth - distance),
|
||||
});
|
||||
}
|
||||
|
||||
async function initScrollbar() {
|
||||
await nextTick();
|
||||
const barEl = document.querySelector('#tabs-scrollbar');
|
||||
|
||||
const viewportEl = barEl?.querySelector(
|
||||
'div[data-radix-scroll-area-viewport]',
|
||||
);
|
||||
|
||||
scrollbarEl.value = barEl;
|
||||
scrollViewportEl.value = viewportEl;
|
||||
|
||||
const activeItem = viewportEl?.querySelector('.is-active');
|
||||
activeItem?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
|
||||
return {
|
||||
initScrollbar,
|
||||
scrollDirection,
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user