Merge branch 'main' into refactor/router-redirect
This commit is contained in:
@@ -68,6 +68,8 @@ const defaultPreferences: Preferences = {
|
|||||||
enable: true,
|
enable: true,
|
||||||
fit: 'contain',
|
fit: 'contain',
|
||||||
source: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
|
source: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
|
||||||
|
showText: true,
|
||||||
|
logoMode: 'icon',
|
||||||
},
|
},
|
||||||
navigation: {
|
navigation: {
|
||||||
accordion: true,
|
accordion: true,
|
||||||
|
|||||||
@@ -228,6 +228,12 @@ interface LogoPreferences {
|
|||||||
enable: boolean;
|
enable: boolean;
|
||||||
/** logo图片适应方式 */
|
/** logo图片适应方式 */
|
||||||
fit: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
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地址 */
|
/** logo地址 */
|
||||||
source: string;
|
source: string;
|
||||||
/** 暗色主题logo地址 (可选,若不设置则使用 source) */
|
/** 暗色主题logo地址 (可选,若不设置则使用 source) */
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ interface Props {
|
|||||||
* 是否移动端
|
* 是否移动端
|
||||||
*/
|
*/
|
||||||
isMobile: boolean;
|
isMobile: boolean;
|
||||||
|
/**
|
||||||
|
* logo是否显示
|
||||||
|
*/
|
||||||
|
logoVisible?: boolean;
|
||||||
/**
|
/**
|
||||||
* 是否显示
|
* 是否显示
|
||||||
*/
|
*/
|
||||||
@@ -38,7 +42,9 @@ interface Props {
|
|||||||
zIndex: number;
|
zIndex: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {});
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
logoVisible: true,
|
||||||
|
});
|
||||||
|
|
||||||
const slots = useSlots();
|
const slots = useSlots();
|
||||||
|
|
||||||
@@ -54,6 +60,12 @@ const style = computed((): CSSProperties => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const logoStyle = computed((): CSSProperties => {
|
const logoStyle = computed((): CSSProperties => {
|
||||||
|
if (!props.logoVisible) {
|
||||||
|
return {
|
||||||
|
minWidth: '12px',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
minWidth: `${props.isMobile ? 40 : props.sidebarWidth}px`,
|
minWidth: `${props.isMobile ? 40 : props.sidebarWidth}px`,
|
||||||
};
|
};
|
||||||
@@ -66,7 +78,7 @@ const logoStyle = computed((): CSSProperties => {
|
|||||||
:style="style"
|
: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"
|
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>
|
<slot name="logo"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ interface Props {
|
|||||||
* @default true
|
* @default true
|
||||||
*/
|
*/
|
||||||
domVisible?: boolean;
|
domVisible?: boolean;
|
||||||
|
/**
|
||||||
|
* 扩展区域extra-title的高度
|
||||||
|
*/
|
||||||
|
extraTitleHeight?: number;
|
||||||
/**
|
/**
|
||||||
* 扩展区域宽度
|
* 扩展区域宽度
|
||||||
*/
|
*/
|
||||||
@@ -97,6 +101,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
collapseHeight: 42,
|
collapseHeight: 42,
|
||||||
collapseWidth: 48,
|
collapseWidth: 48,
|
||||||
domVisible: true,
|
domVisible: true,
|
||||||
|
extraTitleHeight: undefined,
|
||||||
fixedExtra: false,
|
fixedExtra: false,
|
||||||
isSidebarMixed: false,
|
isSidebarMixed: false,
|
||||||
marginTop: 0,
|
marginTop: 0,
|
||||||
@@ -149,10 +154,10 @@ const extraStyle = computed((): CSSProperties => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const extraTitleStyle = computed((): CSSProperties => {
|
const extraTitleStyle = computed((): CSSProperties => {
|
||||||
const { headerHeight } = props;
|
const { extraTitleHeight, headerHeight } = props;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
height: `${headerHeight - 1}px`,
|
height: `${extraTitleHeight ?? headerHeight - 1}px`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -185,9 +190,10 @@ const headerStyle = computed((): CSSProperties => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const extraContentStyle = computed((): CSSProperties => {
|
const extraContentStyle = computed((): CSSProperties => {
|
||||||
const { collapseHeight, headerHeight } = props;
|
const { collapseHeight, extraTitleHeight, headerHeight } = props;
|
||||||
|
const titleHeight = extraTitleHeight ?? headerHeight;
|
||||||
return {
|
return {
|
||||||
height: `calc(100% - ${headerHeight + collapseHeight}px)`,
|
height: `calc(100% - ${titleHeight + collapseHeight}px)`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -126,6 +126,10 @@ interface VbenLayoutProps {
|
|||||||
* @default 48
|
* @default 48
|
||||||
*/
|
*/
|
||||||
sidebarExtraCollapsedWidth?: number;
|
sidebarExtraCollapsedWidth?: number;
|
||||||
|
/**
|
||||||
|
* 扩展区域extra-title的高度
|
||||||
|
*/
|
||||||
|
sidebarExtraTitleHeight?: number;
|
||||||
/**
|
/**
|
||||||
* 侧边菜单折叠按钮是否固定
|
* 侧边菜单折叠按钮是否固定
|
||||||
* @default true
|
* @default true
|
||||||
@@ -136,6 +140,10 @@ interface VbenLayoutProps {
|
|||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
sidebarHidden?: boolean;
|
sidebarHidden?: boolean;
|
||||||
|
/**
|
||||||
|
* 侧边栏 Logo 区域是否显示
|
||||||
|
*/
|
||||||
|
sidebarLogoVisible: boolean;
|
||||||
/**
|
/**
|
||||||
* 混合侧边栏宽度
|
* 混合侧边栏宽度
|
||||||
* @default 80
|
* @default 80
|
||||||
|
|||||||
@@ -368,6 +368,17 @@ const maskStyle = computed((): CSSProperties => {
|
|||||||
return { zIndex: props.zIndex };
|
return { zIndex: props.zIndex };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 侧边栏 Logo 区域是否显示
|
||||||
|
*/
|
||||||
|
const sidebarHeaderHeight = computed(() => {
|
||||||
|
if (isMixedNav.value || !props.sidebarLogoVisible) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return props.headerHeight;
|
||||||
|
});
|
||||||
|
|
||||||
const showHeaderToggleButton = computed(() => {
|
const showHeaderToggleButton = computed(() => {
|
||||||
return (
|
return (
|
||||||
props.isMobile ||
|
props.isMobile ||
|
||||||
@@ -510,7 +521,10 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
|
|||||||
:dom-visible="!isMobile"
|
:dom-visible="!isMobile"
|
||||||
:extra-width="sidebarExtraWidth"
|
:extra-width="sidebarExtraWidth"
|
||||||
:fixed-extra="sidebarExpandOnHover"
|
:fixed-extra="sidebarExpandOnHover"
|
||||||
:header-height="isMixedNav ? 0 : headerHeight"
|
:header-height="sidebarHeaderHeight"
|
||||||
|
:extra-title-height="
|
||||||
|
isSidebarMixedNav || isHeaderMixedNav ? sidebarExtraTitleHeight : 0
|
||||||
|
"
|
||||||
:is-sidebar-mixed="isSidebarMixedNav || isHeaderMixedNav"
|
:is-sidebar-mixed="isSidebarMixedNav || isHeaderMixedNav"
|
||||||
:margin-top="sidebarMarginTop"
|
:margin-top="sidebarMarginTop"
|
||||||
:mixed-width="sidebarMixedWidth"
|
:mixed-width="sidebarMixedWidth"
|
||||||
@@ -522,7 +536,7 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
|
|||||||
@leave="() => emit('sideMouseLeave')"
|
@leave="() => emit('sideMouseLeave')"
|
||||||
@update:width="(val) => emit('update:sidebarWidth', val)"
|
@update:width="(val) => emit('update:sidebarWidth', val)"
|
||||||
>
|
>
|
||||||
<template v-if="isSideMode && !isMixedNav" #logo>
|
<template v-if="isSideMode && !isMixedNav && sidebarLogoVisible" #logo>
|
||||||
<slot name="logo"></slot>
|
<slot name="logo"></slot>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -565,6 +579,7 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
|
|||||||
:theme="headerTheme"
|
:theme="headerTheme"
|
||||||
:width="mainStyle.width"
|
:width="mainStyle.width"
|
||||||
:z-index="headerZIndex"
|
:z-index="headerZIndex"
|
||||||
|
:logo-visible="sidebarLogoVisible"
|
||||||
>
|
>
|
||||||
<template v-if="showHeaderLogo" #logo>
|
<template v-if="showHeaderLogo" #logo>
|
||||||
<slot name="logo"></slot>
|
<slot name="logo"></slot>
|
||||||
|
|||||||
@@ -5,21 +5,33 @@ import { VbenAvatar } from '../avatar';
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/**
|
/**
|
||||||
* @zh_CN 是否收起文本
|
* @zh_CN 是否收起文本;布局状态,侧边栏收起时隐藏文字。
|
||||||
*/
|
*/
|
||||||
collapsed?: boolean;
|
collapsed?: boolean;
|
||||||
/**
|
/**
|
||||||
* @zh_CN Logo 图片适应方式
|
* @zh_CN Logo 图片适应方式
|
||||||
*/
|
*/
|
||||||
fit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
fit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
||||||
|
/**
|
||||||
|
* @zh_CN logo高度, 只在 logoMode=full时失效
|
||||||
|
*/
|
||||||
|
fullLogoHeight?: number | string;
|
||||||
/**
|
/**
|
||||||
* @zh_CN Logo 跳转地址
|
* @zh_CN Logo 跳转地址
|
||||||
*/
|
*/
|
||||||
href?: string;
|
href?: string;
|
||||||
|
/**
|
||||||
|
* @zh_CN logo 展示类型,icon 图标模式, full 铺满logo区域
|
||||||
|
*/
|
||||||
|
logoMode?: 'full' | 'icon';
|
||||||
/**
|
/**
|
||||||
* @zh_CN Logo 图片大小
|
* @zh_CN Logo 图片大小
|
||||||
*/
|
*/
|
||||||
logoSize?: number;
|
logoSize?: number;
|
||||||
|
/**
|
||||||
|
* @zh_CN Logo 是否展示文本
|
||||||
|
*/
|
||||||
|
showText?: boolean;
|
||||||
/**
|
/**
|
||||||
* @zh_CN Logo 图标
|
* @zh_CN Logo 图标
|
||||||
*/
|
*/
|
||||||
@@ -28,10 +40,12 @@ interface Props {
|
|||||||
* @zh_CN 暗色主题 Logo 图标 (可选,若不设置则使用 src)
|
* @zh_CN 暗色主题 Logo 图标 (可选,若不设置则使用 src)
|
||||||
*/
|
*/
|
||||||
srcDark?: string;
|
srcDark?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @zh_CN Logo 文本
|
* @zh_CN Logo 文本
|
||||||
*/
|
*/
|
||||||
text: string;
|
text: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @zh_CN Logo 主题
|
* @zh_CN Logo 主题
|
||||||
*/
|
*/
|
||||||
@@ -45,11 +59,14 @@ defineOptions({
|
|||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
href: 'javascript:void 0',
|
href: 'javascript:void 0',
|
||||||
|
logoMode: 'icon',
|
||||||
logoSize: 32,
|
logoSize: 32,
|
||||||
|
fullLogoHeight: 42,
|
||||||
src: '',
|
src: '',
|
||||||
srcDark: '',
|
srcDark: '',
|
||||||
theme: 'light',
|
theme: 'light',
|
||||||
fit: 'cover',
|
fit: 'cover',
|
||||||
|
showText: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,24 +80,67 @@ const logoSrc = computed(() => {
|
|||||||
// 否则使用默认的 src
|
// 否则使用默认的 src
|
||||||
return props.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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="theme" class="flex h-full items-center text-lg">
|
<div :class="theme" class="flex h-full items-center text-lg">
|
||||||
<a
|
<a
|
||||||
:class="$attrs.class"
|
:class="[
|
||||||
|
$attrs.class,
|
||||||
|
shouldShowText
|
||||||
|
? 'gap-2 px-3 justify-start'
|
||||||
|
: 'w-full p-0 justify-center',
|
||||||
|
]"
|
||||||
:href="href"
|
: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
|
<VbenAvatar
|
||||||
v-if="logoSrc"
|
v-else-if="logoSrc"
|
||||||
:alt="text"
|
:alt="text"
|
||||||
:src="logoSrc"
|
:src="logoSrc"
|
||||||
:size="logoSize"
|
:size="logoSize"
|
||||||
:fit="fit"
|
:fit="fit"
|
||||||
class="relative rounded-none bg-transparent"
|
class="relative rounded-none bg-transparent"
|
||||||
/>
|
/>
|
||||||
<template v-if="!collapsed">
|
<template v-if="shouldShowText">
|
||||||
<slot name="text">
|
<slot name="text">
|
||||||
<span class="text-foreground truncate font-semibold text-nowrap">
|
<span class="text-foreground truncate font-semibold text-nowrap">
|
||||||
{{ text }}
|
{{ text }}
|
||||||
|
|||||||
@@ -118,6 +118,15 @@ const logoTheme = computed(() => {
|
|||||||
return showLogoInHeader ? headerTheme.value : sidebarTheme.value;
|
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 {
|
const {
|
||||||
handleMenuSelect,
|
handleMenuSelect,
|
||||||
handleMenuOpen,
|
handleMenuOpen,
|
||||||
@@ -253,12 +262,14 @@ const headerSlots = computed(() => {
|
|||||||
:sidebar-expand-on-hover="preferences.sidebar.expandOnHover"
|
:sidebar-expand-on-hover="preferences.sidebar.expandOnHover"
|
||||||
:sidebar-extra-collapse="preferences.sidebar.extraCollapse"
|
:sidebar-extra-collapse="preferences.sidebar.extraCollapse"
|
||||||
:sidebar-extra-collapsed-width="preferences.sidebar.extraCollapsedWidth"
|
:sidebar-extra-collapsed-width="preferences.sidebar.extraCollapsedWidth"
|
||||||
|
:sidebar-extra-title-height="sidebarExtraTitleHeight"
|
||||||
:sidebar-hidden="preferences.sidebar.hidden"
|
:sidebar-hidden="preferences.sidebar.hidden"
|
||||||
:sidebar-mixed-width="preferences.sidebar.mixedWidth"
|
:sidebar-mixed-width="preferences.sidebar.mixedWidth"
|
||||||
:sidebar-theme="sidebarTheme"
|
:sidebar-theme="sidebarTheme"
|
||||||
:sidebar-theme-sub="sidebarThemeSub"
|
:sidebar-theme-sub="sidebarThemeSub"
|
||||||
:sidebar-width="preferences.sidebar.width"
|
:sidebar-width="preferences.sidebar.width"
|
||||||
:side-collapse-width="preferences.sidebar.collapseWidth"
|
:side-collapse-width="preferences.sidebar.collapseWidth"
|
||||||
|
:sidebar-logo-visible="preferences.logo.enable"
|
||||||
:tabbar-enable="preferences.tabbar.enable"
|
:tabbar-enable="preferences.tabbar.enable"
|
||||||
:tabbar-height="preferences.tabbar.height"
|
:tabbar-height="preferences.tabbar.height"
|
||||||
:z-index="preferences.app.zIndex"
|
:z-index="preferences.app.zIndex"
|
||||||
@@ -292,6 +303,9 @@ const headerSlots = computed(() => {
|
|||||||
:src="preferences.logo.source"
|
:src="preferences.logo.source"
|
||||||
:src-dark="preferences.logo.sourceDark"
|
:src-dark="preferences.logo.sourceDark"
|
||||||
:text="preferences.app.name"
|
:text="preferences.app.name"
|
||||||
|
:show-text="preferences.logo.showText"
|
||||||
|
:logo-mode="preferences.logo.logoMode"
|
||||||
|
:full-logo-height="preferences.logo.fullLogoHeight"
|
||||||
:theme="logoTheme"
|
:theme="logoTheme"
|
||||||
@click="clickLogo"
|
@click="clickLogo"
|
||||||
>
|
>
|
||||||
@@ -382,6 +396,7 @@ const headerSlots = computed(() => {
|
|||||||
<VbenLogo
|
<VbenLogo
|
||||||
v-if="preferences.logo.enable"
|
v-if="preferences.logo.enable"
|
||||||
:fit="preferences.logo.fit"
|
:fit="preferences.logo.fit"
|
||||||
|
:show-text="preferences.logo.showText"
|
||||||
:text="preferences.app.name"
|
:text="preferences.app.name"
|
||||||
:theme="sidebarThemeSub"
|
:theme="sidebarThemeSub"
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user