feat: add swap component (#4149)

This commit is contained in:
Vben
2024-08-14 20:37:21 +08:00
committed by GitHub
parent b28740042b
commit 83fcdec37c
27 changed files with 260 additions and 146 deletions

View File

@@ -84,7 +84,7 @@ function handleToggleMenu() {
</div>
<VbenIconButton
v-if="showToggleBtn || isMobile"
class="my-0 ml-2 mr-1 rounded"
class="my-0 ml-2 mr-1 rounded-md"
@click="handleToggleMenu"
>
<Menu class="size-4" />

View File

@@ -24,9 +24,8 @@ interface Props {
domVisible?: boolean;
/**
* 扩展区域宽度
* @default 180
*/
extraWidth?: number;
extraWidth: number;
/**
* 固定扩展区域
* @default false
@@ -69,13 +68,12 @@ interface Props {
/**
* 主题
*/
theme?: string;
theme: string;
/**
* 宽度
* @default 180
*/
width?: number;
width: number;
/**
* zIndex
* @default 0
@@ -87,7 +85,6 @@ const props = withDefaults(defineProps<Props>(), {
collapseHeight: 42,
collapseWidth: 48,
domVisible: true,
extraWidth: 180,
fixedExtra: false,
isSidebarMixed: false,
marginTop: 0,
@@ -95,8 +92,6 @@ const props = withDefaults(defineProps<Props>(), {
paddingTop: 0,
show: true,
showCollapseButton: true,
theme: 'dark',
width: 180,
zIndex: 0,
});
@@ -181,10 +176,8 @@ const extraContentStyle = computed((): CSSProperties => {
});
const collapseStyle = computed((): CSSProperties => {
const { collapseHeight } = props;
return {
height: `${collapseHeight}px`,
height: `${props.collapseHeight}px`,
};
});

View File

@@ -0,0 +1,41 @@
import type { LayoutType } from '@vben-core/typings';
import type { VbenLayoutProps } from '../vben-layout';
import { computed } from 'vue';
export function useLayout(props: VbenLayoutProps) {
const currentLayout = computed(() =>
props.isMobile ? 'sidebar-nav' : (props.layout as LayoutType),
);
/**
* 是否全屏显示content不需要侧边、底部、顶部、tab区域
*/
const isFullContent = computed(() => currentLayout.value === 'full-content');
/**
* 是否侧边混合模式
*/
const isSidebarMixedNav = computed(
() => currentLayout.value === 'sidebar-mixed-nav',
);
/**
* 是否为头部导航模式
*/
const isHeaderNav = computed(() => currentLayout.value === 'header-nav');
/**
* 是否为混合导航模式
*/
const isMixedNav = computed(() => currentLayout.value === 'mixed-nav');
return {
currentLayout,
isFullContent,
isHeaderNav,
isMixedNav,
isSidebarMixedNav,
};
}

View File

@@ -62,12 +62,6 @@ interface VbenLayoutProps {
* @default 48
*/
headerHeight?: number;
/**
* header高度增加高度
* 在顶部存在导航时额外加高header高度
* @default 10
*/
headerHeightOffset?: number;
/**
* 顶栏是否隐藏
* @default false
@@ -133,11 +127,7 @@ interface VbenLayoutProps {
* @default 80
*/
sidebarMixedWidth?: number;
/**
* 侧边栏是否半深色
* @default false
*/
sidebarSemiDark?: boolean;
/**
* 侧边栏
* @default dark

View File

@@ -13,6 +13,7 @@ import {
LayoutSidebar,
LayoutTabbar,
} from './components';
import { useLayout } from './hooks/use-layout';
interface Props extends VbenLayoutProps {}
@@ -32,7 +33,6 @@ const props = withDefaults(defineProps<Props>(), {
footerFixed: true,
footerHeight: 32,
headerHeight: 50,
headerHeightOffset: 10,
headerHidden: false,
headerMode: 'fixed',
headerToggleSidebarButton: true,
@@ -43,7 +43,6 @@ const props = withDefaults(defineProps<Props>(), {
sidebarExtraCollapsedWidth: 60,
sidebarHidden: false,
sidebarMixedWidth: 80,
sidebarSemiDark: true,
sidebarTheme: 'dark',
sidebarWidth: 180,
sideCollapseWidth: 60,
@@ -73,57 +72,23 @@ const {
const { y: mouseY } = useMouse({ target: contentRef, type: 'client' });
const realLayout = computed(() =>
props.isMobile ? 'sidebar-nav' : props.layout,
);
/**
* 是否全屏显示content不需要侧边、底部、顶部、tab区域
*/
const fullContent = computed(() => realLayout.value === 'full-content');
/**
* 是否侧边混合模式
*/
const isSidebarMixedNav = computed(
() => realLayout.value === 'sidebar-mixed-nav',
);
/**
* 是否为头部导航模式
*/
const isHeaderNav = computed(() => realLayout.value === 'header-nav');
/**
* 是否为混合导航模式
*/
const isMixedNav = computed(() => realLayout.value === 'mixed-nav');
const {
currentLayout,
isFullContent,
isHeaderNav,
isMixedNav,
isSidebarMixedNav,
} = useLayout(props);
/**
* 顶栏是否自动隐藏
*/
const isHeaderAutoMode = computed(() => props.headerMode === 'auto');
/**
* header区域高度
*/
const getHeaderHeight = computed(() => {
const { headerHeight, headerHeightOffset } = props;
// if (!headerVisible) {
// return 0;
// }
// 顶部存在导航时增加10
const offset = isMixedNav.value || isHeaderNav.value ? headerHeightOffset : 0;
return headerHeight + offset;
});
const headerWrapperHeight = computed(() => {
let height = 0;
if (props.headerVisible && !props.headerHidden) {
height += getHeaderHeight.value;
height += props.headerHeight;
}
if (props.tabbarEnable) {
height += props.tabbarHeight;
@@ -151,8 +116,8 @@ const sidebarEnableState = computed(() => {
* 侧边区域离顶部高度
*/
const sidebarMarginTop = computed(() => {
const { isMobile } = props;
return isMixedNav.value && !isMobile ? getHeaderHeight.value : 0;
const { headerHeight, isMobile } = props;
return isMixedNav.value && !isMobile ? headerHeight : 0;
});
/**
@@ -195,30 +160,13 @@ const sidebarExtraWidth = computed(() => {
/**
* 是否侧边栏模式,包含混合侧边
*/
const isSideMode = computed(() =>
['mixed-nav', 'sidebar-mixed-nav', 'sidebar-nav'].includes(realLayout.value),
const isSideMode = computed(
() =>
currentLayout.value === 'mixed-nav' ||
currentLayout.value === 'sidebar-mixed-nav' ||
currentLayout.value === 'sidebar-nav',
);
const showSidebar = computed(() => {
// if (isMixedNav.value && !props.sideHidden) {
// return false;
// }
return isSideMode.value && sidebarEnable.value;
});
const sidebarFace = computed(() => {
const { sidebarSemiDark, sidebarTheme } = props;
const isDark = sidebarTheme === 'dark' || sidebarSemiDark;
return {
theme: isDark ? 'dark' : 'light',
};
});
/**
* 遮罩可见性
*/
const maskVisible = computed(() => !sidebarCollapse.value && props.isMobile);
/**
* header fixed值
*/
@@ -232,13 +180,25 @@ const headerFixed = computed(() => {
);
});
const showSidebar = computed(() => {
// if (isMixedNav.value && !props.sideHidden) {
// return false;
// }
return isSideMode.value && sidebarEnable.value;
});
/**
* 遮罩可见性
*/
const maskVisible = computed(() => !sidebarCollapse.value && props.isMobile);
const mainStyle = computed(() => {
let width = '100%';
let sidebarAndExtraWidth = 'unset';
if (
headerFixed.value &&
realLayout.value !== 'header-nav' &&
realLayout.value !== 'mixed-nav' &&
currentLayout.value !== 'header-nav' &&
currentLayout.value !== 'mixed-nav' &&
showSidebar.value &&
!props.isMobile
) {
@@ -253,7 +213,7 @@ const mainStyle = computed(() => {
? getSideCollapseWidth.value
: props.sidebarMixedWidth;
const sideWidth = sidebarExtraCollapse.value
? getSideCollapseWidth.value
? props.sidebarExtraCollapsedWidth
: props.sidebarWidth;
// 100% - 侧边菜单混合宽度 - 菜单宽度
@@ -312,7 +272,7 @@ const contentStyle = computed((): CSSProperties => {
return {
marginTop:
fixed &&
!fullContent.value &&
!isFullContent.value &&
!headerIsHidden.value &&
(!isHeaderAutoMode.value || scrollY.value < headerWrapperHeight.value)
? `${headerWrapperHeight.value}px`
@@ -330,11 +290,11 @@ const headerZIndex = computed(() => {
const headerWrapperStyle = computed((): CSSProperties => {
const fixed = headerFixed.value;
return {
height: fullContent.value ? '0' : `${headerWrapperHeight.value}px`,
height: isFullContent.value ? '0' : `${headerWrapperHeight.value}px`,
left: isMixedNav.value ? 0 : mainStyle.value.sidebarAndExtraWidth,
position: fixed ? 'fixed' : 'static',
top:
headerIsHidden.value || fullContent.value
headerIsHidden.value || isFullContent.value
? `-${headerWrapperHeight.value}px`
: 0,
width: mainStyle.value.width,
@@ -403,7 +363,10 @@ watch(
watch(
[() => props.headerMode, () => mouseY.value],
() => {
if (!isHeaderAutoMode.value || isMixedNav.value || fullContent.value) {
if (!isHeaderAutoMode.value || isMixedNav.value || isFullContent.value) {
if (props.headerMode !== 'auto-scroll') {
headerIsHidden.value = false;
}
return;
}
headerIsHidden.value = true;
@@ -439,7 +402,7 @@ watch(
if (
props.headerMode !== 'auto-scroll' ||
isMixedNav.value ||
fullContent.value
isFullContent.value
) {
return;
}
@@ -465,8 +428,6 @@ function handleOpenMenu() {
<template>
<div class="relative flex min-h-full w-full">
<slot name="preferences"></slot>
<slot name="floating-groups"></slot>
<LayoutSidebar
v-if="sidebarEnableState"
v-model:collapse="sidebarCollapse"
@@ -478,12 +439,12 @@ function handleOpenMenu() {
:dom-visible="!isMobile"
:extra-width="sidebarExtraWidth"
:fixed-extra="sidebarExpandOnHover"
:header-height="isMixedNav ? 0 : getHeaderHeight"
:header-height="isMixedNav ? 0 : headerHeight"
:is-sidebar-mixed="isSidebarMixedNav"
:margin-top="sidebarMarginTop"
:mixed-width="sidebarMixedWidth"
:show="showSidebar"
:theme="sidebarFace.theme"
:theme="sidebarTheme"
:width="getSidebarWidth"
:z-index="sidebarZIndex"
@leave="() => emit('sideMouseLeave')"
@@ -518,10 +479,10 @@ function handleOpenMenu() {
<LayoutHeader
v-if="headerVisible"
:full-width="!isSideMode"
:height="getHeaderHeight"
:height="headerHeight"
:is-mixed-nav="isMixedNav"
:is-mobile="isMobile"
:show="!fullContent && !headerHidden"
:show="!isFullContent && !headerHidden"
:show-toggle-btn="showHeaderToggleButton"
:sidebar-width="sidebarWidth"
:width="mainStyle.width"
@@ -563,7 +524,7 @@ function handleOpenMenu() {
v-if="footerEnable"
:fixed="footerFixed"
:height="footerHeight"
:show="!fullContent"
:show="!isFullContent"
:width="footerWidth"
:z-index="zIndex"
>