Merge branch 'main' into refactor/router-redirect

This commit is contained in:
Evan
2026-06-30 22:23:54 +08:00
committed by GitHub
8 changed files with 137 additions and 13 deletions

View File

@@ -68,6 +68,8 @@ const defaultPreferences: Preferences = {
enable: true,
fit: 'contain',
source: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
showText: true,
logoMode: 'icon',
},
navigation: {
accordion: true,

View File

@@ -228,6 +228,12 @@ interface LogoPreferences {
enable: boolean;
/** logo图片适应方式 */
fit: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
/** logo高度 只在 logoMode=full时生效 */
fullLogoHeight?: number | string;
/** logo 展示类型icon 图标模式, full 铺满logo区域 */
logoMode: 'full' | 'icon';
/** logo text是否展示 */
showText: boolean;
/** logo地址 */
source: string;
/** 暗色主题logo地址 (可选,若不设置则使用 source) */

View File

@@ -16,6 +16,10 @@ interface Props {
* 是否移动端
*/
isMobile: boolean;
/**
* logo是否显示
*/
logoVisible?: boolean;
/**
* 是否显示
*/
@@ -38,7 +42,9 @@ interface Props {
zIndex: number;
}
const props = withDefaults(defineProps<Props>(), {});
const props = withDefaults(defineProps<Props>(), {
logoVisible: true,
});
const slots = useSlots();
@@ -54,6 +60,12 @@ const style = computed((): CSSProperties => {
});
const logoStyle = computed((): CSSProperties => {
if (!props.logoVisible) {
return {
minWidth: '12px',
};
}
return {
minWidth: `${props.isMobile ? 40 : props.sidebarWidth}px`,
};
@@ -66,7 +78,7 @@ const logoStyle = computed((): CSSProperties => {
:style="style"
class="top-0 flex w-full flex-[0_0_auto] items-center border-b border-border bg-header pl-2 transition-[margin-top] duration-200"
>
<div v-if="slots.logo" :style="logoStyle">
<div v-if="slots.logo || (!logoVisible && !isMobile)" :style="logoStyle">
<slot name="logo"></slot>
</div>

View File

@@ -26,6 +26,10 @@ interface Props {
* @default true
*/
domVisible?: boolean;
/**
* 扩展区域extra-title的高度
*/
extraTitleHeight?: number;
/**
* 扩展区域宽度
*/
@@ -97,6 +101,7 @@ const props = withDefaults(defineProps<Props>(), {
collapseHeight: 42,
collapseWidth: 48,
domVisible: true,
extraTitleHeight: undefined,
fixedExtra: false,
isSidebarMixed: false,
marginTop: 0,
@@ -149,10 +154,10 @@ const extraStyle = computed((): CSSProperties => {
});
const extraTitleStyle = computed((): CSSProperties => {
const { headerHeight } = props;
const { extraTitleHeight, headerHeight } = props;
return {
height: `${headerHeight - 1}px`,
height: `${extraTitleHeight ?? headerHeight - 1}px`,
};
});
@@ -185,9 +190,10 @@ const headerStyle = computed((): CSSProperties => {
});
const extraContentStyle = computed((): CSSProperties => {
const { collapseHeight, headerHeight } = props;
const { collapseHeight, extraTitleHeight, headerHeight } = props;
const titleHeight = extraTitleHeight ?? headerHeight;
return {
height: `calc(100% - ${headerHeight + collapseHeight}px)`,
height: `calc(100% - ${titleHeight + collapseHeight}px)`,
};
});

View File

@@ -126,6 +126,10 @@ interface VbenLayoutProps {
* @default 48
*/
sidebarExtraCollapsedWidth?: number;
/**
* 扩展区域extra-title的高度
*/
sidebarExtraTitleHeight?: number;
/**
* 侧边菜单折叠按钮是否固定
* @default true
@@ -136,6 +140,10 @@ interface VbenLayoutProps {
* @default false
*/
sidebarHidden?: boolean;
/**
* 侧边栏 Logo 区域是否显示
*/
sidebarLogoVisible: boolean;
/**
* 混合侧边栏宽度
* @default 80

View File

@@ -368,6 +368,17 @@ const maskStyle = computed((): CSSProperties => {
return { zIndex: props.zIndex };
});
/**
* 侧边栏 Logo 区域是否显示
*/
const sidebarHeaderHeight = computed(() => {
if (isMixedNav.value || !props.sidebarLogoVisible) {
return 0;
}
return props.headerHeight;
});
const showHeaderToggleButton = computed(() => {
return (
props.isMobile ||
@@ -510,7 +521,10 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
:dom-visible="!isMobile"
:extra-width="sidebarExtraWidth"
:fixed-extra="sidebarExpandOnHover"
:header-height="isMixedNav ? 0 : headerHeight"
:header-height="sidebarHeaderHeight"
:extra-title-height="
isSidebarMixedNav || isHeaderMixedNav ? sidebarExtraTitleHeight : 0
"
:is-sidebar-mixed="isSidebarMixedNav || isHeaderMixedNav"
:margin-top="sidebarMarginTop"
:mixed-width="sidebarMixedWidth"
@@ -522,7 +536,7 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
@leave="() => emit('sideMouseLeave')"
@update:width="(val) => emit('update:sidebarWidth', val)"
>
<template v-if="isSideMode && !isMixedNav" #logo>
<template v-if="isSideMode && !isMixedNav && sidebarLogoVisible" #logo>
<slot name="logo"></slot>
</template>
@@ -565,6 +579,7 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
:theme="headerTheme"
:width="mainStyle.width"
:z-index="headerZIndex"
:logo-visible="sidebarLogoVisible"
>
<template v-if="showHeaderLogo" #logo>
<slot name="logo"></slot>

View File

@@ -5,21 +5,33 @@ import { VbenAvatar } from '../avatar';
interface Props {
/**
* @zh_CN 是否收起文本
* @zh_CN 是否收起文本;布局状态,侧边栏收起时隐藏文字。
*/
collapsed?: boolean;
/**
* @zh_CN Logo 图片适应方式
*/
fit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
/**
* @zh_CN logo高度 只在 logoMode=full时失效
*/
fullLogoHeight?: number | string;
/**
* @zh_CN Logo 跳转地址
*/
href?: string;
/**
* @zh_CN logo 展示类型icon 图标模式, full 铺满logo区域
*/
logoMode?: 'full' | 'icon';
/**
* @zh_CN Logo 图片大小
*/
logoSize?: number;
/**
* @zh_CN Logo 是否展示文本
*/
showText?: boolean;
/**
* @zh_CN Logo 图标
*/
@@ -28,10 +40,12 @@ interface Props {
* @zh_CN 暗色主题 Logo 图标 (可选,若不设置则使用 src)
*/
srcDark?: string;
/**
* @zh_CN Logo 文本
*/
text: string;
/**
* @zh_CN Logo 主题
*/
@@ -45,11 +59,14 @@ defineOptions({
const props = withDefaults(defineProps<Props>(), {
collapsed: false,
href: 'javascript:void 0',
logoMode: 'icon',
logoSize: 32,
fullLogoHeight: 42,
src: '',
srcDark: '',
theme: 'light',
fit: 'cover',
showText: true,
});
/**
@@ -63,24 +80,67 @@ const logoSrc = computed(() => {
// 否则使用默认的 src
return props.src;
});
/**
* @zh_CN 是否铺满容器显示log
*/
const shouldUseFullLogo = computed(() => {
return props.logoMode === 'full' && !props.collapsed;
});
/**
* @zh_CN 根据配置展示 logo text
*/
const shouldShowText = computed(() => {
return (
props.showText &&
!props.collapsed &&
!shouldUseFullLogo.value &&
!!props.text
);
});
/**
* @zh_CN full 模式下logo的样式
*/
const fullLogoStyle = computed(() => ({
height:
typeof props.fullLogoHeight === 'number'
? `${props.fullLogoHeight}px`
: props.fullLogoHeight,
objectFit: props.fit,
}));
</script>
<template>
<div :class="theme" class="flex h-full items-center text-lg">
<a
:class="$attrs.class"
:class="[
$attrs.class,
shouldShowText
? 'gap-2 px-3 justify-start'
: 'w-full p-0 justify-center',
]"
:href="href"
class="flex h-full items-center gap-2 overflow-hidden px-3 text-lg leading-normal transition-all duration-500"
class="flex h-full items-center overflow-hidden text-lg leading-normal transition-all duration-500"
>
<img
v-if="logoSrc && shouldUseFullLogo"
:alt="text"
:src="logoSrc"
:style="fullLogoStyle"
class="w-full"
/>
<VbenAvatar
v-if="logoSrc"
v-else-if="logoSrc"
:alt="text"
:src="logoSrc"
:size="logoSize"
:fit="fit"
class="relative rounded-none bg-transparent"
/>
<template v-if="!collapsed">
<template v-if="shouldShowText">
<slot name="text">
<span class="text-foreground truncate font-semibold text-nowrap">
{{ text }}

View File

@@ -118,6 +118,15 @@ const logoTheme = computed(() => {
return showLogoInHeader ? headerTheme.value : sidebarTheme.value;
});
/**
* layout-sidebar扩展区域插槽extra-title的高度
*/
const sidebarExtraTitleHeight = computed<number | undefined>(() => {
const showSideExtraTitle =
preferences.logo.enable && preferences.logo.showText;
return showSideExtraTitle ? undefined : 0;
});
const {
handleMenuSelect,
handleMenuOpen,
@@ -253,12 +262,14 @@ const headerSlots = computed(() => {
:sidebar-expand-on-hover="preferences.sidebar.expandOnHover"
:sidebar-extra-collapse="preferences.sidebar.extraCollapse"
:sidebar-extra-collapsed-width="preferences.sidebar.extraCollapsedWidth"
:sidebar-extra-title-height="sidebarExtraTitleHeight"
:sidebar-hidden="preferences.sidebar.hidden"
:sidebar-mixed-width="preferences.sidebar.mixedWidth"
:sidebar-theme="sidebarTheme"
:sidebar-theme-sub="sidebarThemeSub"
:sidebar-width="preferences.sidebar.width"
:side-collapse-width="preferences.sidebar.collapseWidth"
:sidebar-logo-visible="preferences.logo.enable"
:tabbar-enable="preferences.tabbar.enable"
:tabbar-height="preferences.tabbar.height"
:z-index="preferences.app.zIndex"
@@ -292,6 +303,9 @@ const headerSlots = computed(() => {
:src="preferences.logo.source"
:src-dark="preferences.logo.sourceDark"
:text="preferences.app.name"
:show-text="preferences.logo.showText"
:logo-mode="preferences.logo.logoMode"
:full-logo-height="preferences.logo.fullLogoHeight"
:theme="logoTheme"
@click="clickLogo"
>
@@ -382,6 +396,7 @@ const headerSlots = computed(() => {
<VbenLogo
v-if="preferences.logo.enable"
:fit="preferences.logo.fit"
:show-text="preferences.logo.showText"
:text="preferences.app.name"
:theme="sidebarThemeSub"
>