mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-26 16:46:19 +08:00
fix: fix a series of known problems,fixed #54
This commit is contained in:
@@ -9,50 +9,20 @@ import { useContentHeightListener } from '@vben-core/hooks';
|
||||
interface Props {
|
||||
/**
|
||||
* 内容区域定宽
|
||||
* @default 'wide'
|
||||
*/
|
||||
contentCompact?: ContentCompactType;
|
||||
contentCompact: ContentCompactType;
|
||||
/**
|
||||
* 定宽布局宽度
|
||||
* @default 1200
|
||||
*/
|
||||
contentCompactWidth?: number;
|
||||
/**
|
||||
* padding
|
||||
* @default 16
|
||||
*/
|
||||
padding?: number;
|
||||
/**
|
||||
* paddingBottom
|
||||
* @default 16
|
||||
*/
|
||||
paddingBottom?: number;
|
||||
/**
|
||||
* paddingLeft
|
||||
* @default 16
|
||||
*/
|
||||
paddingLeft?: number;
|
||||
/**
|
||||
* paddingRight
|
||||
* @default 16
|
||||
*/
|
||||
paddingRight?: number;
|
||||
/**
|
||||
* paddingTop
|
||||
* @default 16
|
||||
*/
|
||||
paddingTop?: number;
|
||||
contentCompactWidth: number;
|
||||
padding: number;
|
||||
paddingBottom: number;
|
||||
paddingLeft: number;
|
||||
paddingRight: number;
|
||||
paddingTop: number;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
contentCompact: 'wide',
|
||||
contentCompactWidth: 1200,
|
||||
padding: 16,
|
||||
paddingBottom: 16,
|
||||
paddingLeft: 16,
|
||||
paddingRight: 16,
|
||||
paddingTop: 16,
|
||||
});
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
|
||||
const { contentElement } = useContentHeightListener();
|
||||
|
||||
@@ -83,7 +53,7 @@ const style = computed((): CSSProperties => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main ref="contentElement" :style="style" class="bg-background-content">
|
||||
<main ref="contentElement" :style="style" class="bg-background-deep">
|
||||
<slot></slot>
|
||||
</main>
|
||||
</template>
|
||||
|
@@ -4,38 +4,21 @@ import { computed } from 'vue';
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
* 是否固定在顶部
|
||||
* @default true
|
||||
* 是否固定在底部
|
||||
*/
|
||||
fixed?: boolean;
|
||||
/**
|
||||
* 高度
|
||||
* @default 32
|
||||
*/
|
||||
height?: number;
|
||||
height: number;
|
||||
/**
|
||||
* 是否显示
|
||||
* @default true
|
||||
*/
|
||||
show?: boolean;
|
||||
/**
|
||||
* 高度
|
||||
* @default 100%
|
||||
*/
|
||||
width?: string;
|
||||
/**
|
||||
* zIndex
|
||||
* @default 0
|
||||
*/
|
||||
zIndex?: number;
|
||||
width: string;
|
||||
zIndex: number;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
fixed: true,
|
||||
height: 32,
|
||||
show: true,
|
||||
width: '100%',
|
||||
zIndex: 0,
|
||||
});
|
||||
|
||||
const style = computed((): CSSProperties => {
|
||||
@@ -53,7 +36,7 @@ const style = computed((): CSSProperties => {
|
||||
<template>
|
||||
<footer
|
||||
:style="style"
|
||||
class="bg-background-content bottom-0 w-full transition-all duration-200"
|
||||
class="bg-background-deep bottom-0 w-full transition-all duration-200"
|
||||
>
|
||||
<slot></slot>
|
||||
</footer>
|
||||
|
@@ -8,61 +8,45 @@ import { VbenIconButton } from '@vben-core/shadcn-ui';
|
||||
interface Props {
|
||||
/**
|
||||
* 横屏
|
||||
* @default false
|
||||
*/
|
||||
fullWidth?: boolean;
|
||||
fullWidth: boolean;
|
||||
/**
|
||||
* 高度
|
||||
* @default 60
|
||||
*/
|
||||
height?: number;
|
||||
height: number;
|
||||
/**
|
||||
* 是否混合导航
|
||||
* @default false
|
||||
*/
|
||||
isMixedNav?: boolean;
|
||||
isMixedNav: boolean;
|
||||
/**
|
||||
* 是否移动端
|
||||
* @default false
|
||||
*/
|
||||
isMobile?: boolean;
|
||||
isMobile: boolean;
|
||||
/**
|
||||
* 是否显示
|
||||
* @default true
|
||||
*/
|
||||
show?: boolean;
|
||||
show: boolean;
|
||||
/**
|
||||
* 是否显示关闭菜单按钮
|
||||
* @default true
|
||||
*/
|
||||
showToggleBtn?: boolean;
|
||||
showToggleBtn: boolean;
|
||||
|
||||
/**
|
||||
* 侧边菜单宽度
|
||||
* @default 0
|
||||
*/
|
||||
sidebarWidth?: number;
|
||||
sidebarWidth: number;
|
||||
/**
|
||||
* 宽度
|
||||
* @default 100%
|
||||
*/
|
||||
width?: string;
|
||||
width: string;
|
||||
/**
|
||||
* zIndex
|
||||
* @default 0
|
||||
*/
|
||||
zIndex?: number;
|
||||
zIndex: number;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
height: 60,
|
||||
isMixedNav: false,
|
||||
show: true,
|
||||
showToggleBtn: false,
|
||||
sidebarWidth: 0,
|
||||
width: '100%',
|
||||
zIndex: 0,
|
||||
});
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
|
||||
const emit = defineEmits<{ openMenu: []; toggleSidebar: [] }>();
|
||||
|
||||
@@ -73,7 +57,6 @@ const style = computed((): CSSProperties => {
|
||||
const right = !show || !fullWidth ? undefined : 0;
|
||||
|
||||
return {
|
||||
// ...(props.isMixedNav ? { left: 0, position: `fixed` } : {}),
|
||||
height: `${height}px`,
|
||||
marginTop: show ? 0 : `-${height}px`,
|
||||
right,
|
||||
@@ -87,11 +70,7 @@ const logoStyle = computed((): CSSProperties => {
|
||||
});
|
||||
|
||||
function handleToggleMenu() {
|
||||
if (props.isMobile) {
|
||||
emit('openMenu');
|
||||
} else {
|
||||
emit('toggleSidebar');
|
||||
}
|
||||
props.isMobile ? emit('openMenu') : emit('toggleSidebar');
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@@ -9,7 +9,7 @@ import { SidebarCollapseButton, SidebarFixedButton } from './widgets';
|
||||
interface Props {
|
||||
/**
|
||||
* 折叠区域高度
|
||||
* @default 32
|
||||
* @default 42
|
||||
*/
|
||||
collapseHeight?: number;
|
||||
/**
|
||||
@@ -41,6 +41,11 @@ interface Props {
|
||||
* @default false
|
||||
*/
|
||||
isSidebarMixed?: boolean;
|
||||
/**
|
||||
* 顶部margin
|
||||
* @default 60
|
||||
*/
|
||||
marginTop?: number;
|
||||
/**
|
||||
* 混合菜单宽度
|
||||
* @default 80
|
||||
@@ -85,8 +90,9 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
extraWidth: 180,
|
||||
fixedExtra: false,
|
||||
isSidebarMixed: false,
|
||||
mixedWidth: 80,
|
||||
paddingTop: 60,
|
||||
marginTop: 0,
|
||||
mixedWidth: 70,
|
||||
paddingTop: 0,
|
||||
show: true,
|
||||
showCollapseButton: true,
|
||||
theme: 'dark',
|
||||
@@ -108,11 +114,13 @@ const asideRef = shallowRef<HTMLDivElement | null>();
|
||||
const hiddenSideStyle = computed((): CSSProperties => calcMenuWidthStyle(true));
|
||||
|
||||
const style = computed((): CSSProperties => {
|
||||
const { isSidebarMixed, paddingTop, zIndex } = props;
|
||||
const { isSidebarMixed, marginTop, paddingTop, zIndex } = props;
|
||||
|
||||
return {
|
||||
'--scroll-shadow': 'var(--sidebar)',
|
||||
...calcMenuWidthStyle(false),
|
||||
height: `calc(100% - ${marginTop}px)`,
|
||||
marginTop: `${marginTop}px`,
|
||||
paddingTop: `${paddingTop}px`,
|
||||
zIndex,
|
||||
...(isSidebarMixed && extraVisible.value ? { transition: 'none' } : {}),
|
||||
@@ -222,6 +230,7 @@ function handleMouseleave() {
|
||||
if (expandOnHover.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
expandOnHovering.value = false;
|
||||
collapse.value = true;
|
||||
extraVisible.value = false;
|
||||
@@ -233,7 +242,7 @@ function handleMouseleave() {
|
||||
v-if="domVisible"
|
||||
:class="theme"
|
||||
:style="hiddenSideStyle"
|
||||
class="h-full transition-all duration-200"
|
||||
class="h-full transition-all duration-150"
|
||||
></div>
|
||||
<aside
|
||||
:class="[
|
||||
@@ -244,7 +253,7 @@ function handleMouseleave() {
|
||||
},
|
||||
]"
|
||||
:style="style"
|
||||
class="border-border fixed left-0 top-0 h-full border-r transition-all duration-200"
|
||||
class="border-border fixed left-0 top-0 h-full border-r transition-all duration-150"
|
||||
@mouseenter="handleMouseenter"
|
||||
@mouseleave="handleMouseleave"
|
||||
>
|
||||
@@ -255,7 +264,7 @@ function handleMouseleave() {
|
||||
<div v-if="slots.logo" :style="headerStyle">
|
||||
<slot name="logo"></slot>
|
||||
</div>
|
||||
<VbenScrollbar :style="contentStyle" shadow>
|
||||
<VbenScrollbar :style="contentStyle" shadow shadow-border>
|
||||
<slot></slot>
|
||||
</VbenScrollbar>
|
||||
|
||||
@@ -267,8 +276,11 @@ function handleMouseleave() {
|
||||
<div
|
||||
v-if="isSidebarMixed"
|
||||
ref="asideRef"
|
||||
:class="{
|
||||
'border-r': extraVisible,
|
||||
}"
|
||||
:style="extraStyle"
|
||||
class="border-border bg-sidebar fixed top-0 h-full overflow-hidden border-x transition-all duration-200"
|
||||
class="border-border bg-sidebar fixed top-0 h-full overflow-hidden transition-all duration-200"
|
||||
>
|
||||
<SidebarCollapseButton
|
||||
v-if="isSidebarMixed && expandOnHover"
|
||||
@@ -286,6 +298,7 @@ function handleMouseleave() {
|
||||
:style="extraContentStyle"
|
||||
class="border-border border-t py-2"
|
||||
shadow
|
||||
shadow-border
|
||||
>
|
||||
<slot name="extra"></slot>
|
||||
</VbenScrollbar>
|
||||
|
@@ -5,15 +5,11 @@ import { computed } from 'vue';
|
||||
interface Props {
|
||||
/**
|
||||
* 高度
|
||||
* @default 30
|
||||
*/
|
||||
height?: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
fixed: true,
|
||||
height: 38,
|
||||
});
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
|
||||
const style = computed((): CSSProperties => {
|
||||
const { height } = props;
|
||||
|
@@ -123,6 +123,11 @@ interface VbenLayoutProps {
|
||||
* @default true
|
||||
*/
|
||||
sidebarEnable?: boolean;
|
||||
/**
|
||||
* 侧边菜单折叠额外宽度
|
||||
* @default 48
|
||||
*/
|
||||
sidebarExtraCollapsedWidth?: number;
|
||||
/**
|
||||
* 侧边栏是否隐藏
|
||||
* @default false
|
||||
|
@@ -21,6 +21,7 @@ defineOptions({
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
contentCompact: 'wide',
|
||||
contentCompactWidth: 1200,
|
||||
contentPadding: 0,
|
||||
contentPaddingBottom: 0,
|
||||
contentPaddingLeft: 0,
|
||||
@@ -39,6 +40,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
layout: 'sidebar-nav',
|
||||
sideCollapseWidth: 60,
|
||||
sidebarCollapseShowTitle: false,
|
||||
sidebarExtraCollapsedWidth: 60,
|
||||
sidebarHidden: false,
|
||||
sidebarMixedWidth: 80,
|
||||
sidebarSemiDark: true,
|
||||
@@ -62,16 +64,16 @@ const {
|
||||
isScrolling,
|
||||
y: scrollY,
|
||||
} = useScroll(document);
|
||||
|
||||
const { y: mouseY } = useMouse({ type: 'client' });
|
||||
|
||||
// side是否处于hover状态展开菜单中
|
||||
const sidebarExpandOnHovering = ref(false);
|
||||
// const sideHidden = ref(false);
|
||||
const headerIsHidden = ref(false);
|
||||
|
||||
const realLayout = computed(() => {
|
||||
return props.isMobile ? 'sidebar-nav' : props.layout;
|
||||
});
|
||||
const realLayout = computed(() =>
|
||||
props.isMobile ? 'sidebar-nav' : props.layout,
|
||||
);
|
||||
|
||||
/**
|
||||
* 是否全屏显示content,不需要侧边、底部、顶部、tab区域
|
||||
@@ -98,7 +100,7 @@ const isMixedNav = computed(() => realLayout.value === 'mixed-nav');
|
||||
/**
|
||||
* 顶栏是否自动隐藏
|
||||
*/
|
||||
const isHeaderAuto = computed(() => props.headerMode === 'auto');
|
||||
const isHeaderAutoMode = computed(() => props.headerMode === 'auto');
|
||||
|
||||
/**
|
||||
* header区域高度
|
||||
@@ -146,7 +148,7 @@ const sidebarEnableState = computed(() => {
|
||||
/**
|
||||
* 侧边区域离顶部高度
|
||||
*/
|
||||
const sidePaddingTop = computed(() => {
|
||||
const sidebarMarginTop = computed(() => {
|
||||
const { isMobile } = props;
|
||||
return isMixedNav.value && !isMobile ? getHeaderHeight.value : 0;
|
||||
});
|
||||
@@ -182,10 +184,10 @@ const getSidebarWidth = computed(() => {
|
||||
/**
|
||||
* 获取扩展区域宽度
|
||||
*/
|
||||
const getExtraWidth = computed(() => {
|
||||
const { sidebarWidth } = props;
|
||||
const sidebarExtraWidth = computed(() => {
|
||||
const { sidebarExtraCollapsedWidth, sidebarWidth } = props;
|
||||
|
||||
return sidebarExtraCollapse.value ? getSideCollapseWidth.value : sidebarWidth;
|
||||
return sidebarExtraCollapse.value ? sidebarExtraCollapsedWidth : sidebarWidth;
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -269,19 +271,29 @@ const mainStyle = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
// 计算 tabbar 的样式
|
||||
const tabbarStyle = computed((): CSSProperties => {
|
||||
let width = '';
|
||||
let marginLeft = 0;
|
||||
|
||||
// 如果不是混合导航,tabbar 的宽度为 100%
|
||||
if (!isMixedNav.value) {
|
||||
width = '100%';
|
||||
} else if (sidebarEnable.value) {
|
||||
// 鼠标在侧边栏上时,且侧边栏展开时的宽度
|
||||
const onHoveringWidth = sidebarExpandOnHover.value
|
||||
? props.sidebarWidth
|
||||
: getSideCollapseWidth.value;
|
||||
|
||||
// 设置 marginLeft,根据侧边栏是否折叠来决定
|
||||
marginLeft = sidebarCollapse.value
|
||||
? getSideCollapseWidth.value
|
||||
: props.sidebarWidth;
|
||||
: onHoveringWidth;
|
||||
|
||||
width = `calc(100% - ${getSidebarWidth.value}px)`;
|
||||
// 设置 tabbar 的宽度,计算方式为 100% 减去侧边栏的宽度
|
||||
width = `calc(100% - ${sidebarCollapse.value ? getSidebarWidth.value : onHoveringWidth}px)`;
|
||||
} else {
|
||||
// 默认情况下,tabbar 的宽度为 100%
|
||||
width = '100%';
|
||||
}
|
||||
|
||||
@@ -300,7 +312,7 @@ const contentStyle = computed((): CSSProperties => {
|
||||
fixed &&
|
||||
!fullContent.value &&
|
||||
!headerIsHidden.value &&
|
||||
(!isHeaderAuto.value || scrollY.value < headerWrapperHeight.value)
|
||||
(!isHeaderAutoMode.value || scrollY.value < headerWrapperHeight.value)
|
||||
? `${headerWrapperHeight.value}px`
|
||||
: 0,
|
||||
paddingBottom: `${footerEnable && footerFixed ? footerHeight : 0}px`,
|
||||
@@ -333,7 +345,12 @@ const headerWrapperStyle = computed((): CSSProperties => {
|
||||
*/
|
||||
const sidebarZIndex = computed(() => {
|
||||
const { isMobile, zIndex } = props;
|
||||
const offset = isMobile || isSideMode.value ? 1 : -1;
|
||||
let offset = isMobile || isSideMode.value ? 1 : -1;
|
||||
|
||||
if (isMixedNav.value) {
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
return zIndex + offset;
|
||||
});
|
||||
|
||||
@@ -366,7 +383,12 @@ const showHeaderLogo = computed(() => {
|
||||
watch(
|
||||
() => props.isMobile,
|
||||
(val) => {
|
||||
sidebarCollapse.value = val;
|
||||
if (val) {
|
||||
sidebarCollapse.value = true;
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -379,7 +401,7 @@ watch(
|
||||
watch(
|
||||
[() => props.headerMode, () => mouseY.value],
|
||||
() => {
|
||||
if (!isHeaderAuto.value || isMixedNav.value || fullContent.value) {
|
||||
if (!isHeaderAutoMode.value || isMixedNav.value || fullContent.value) {
|
||||
return;
|
||||
}
|
||||
headerIsHidden.value = true;
|
||||
@@ -434,10 +456,6 @@ function handleClickMask() {
|
||||
sidebarCollapse.value = true;
|
||||
}
|
||||
|
||||
function handleToggleSidebar() {
|
||||
emit('toggleSidebar');
|
||||
}
|
||||
|
||||
function handleOpenMenu() {
|
||||
sidebarCollapse.value = false;
|
||||
}
|
||||
@@ -456,12 +474,12 @@ function handleOpenMenu() {
|
||||
v-model:extra-visible="sidebarExtraVisible"
|
||||
:collapse-width="getSideCollapseWidth"
|
||||
:dom-visible="!isMobile"
|
||||
:extra-width="getExtraWidth"
|
||||
:extra-width="sidebarExtraWidth"
|
||||
:fixed-extra="sidebarExpandOnHover"
|
||||
:header-height="isMixedNav ? 0 : getHeaderHeight"
|
||||
:is-sidebar-mixed="isSidebarMixedNav"
|
||||
:margin-top="sidebarMarginTop"
|
||||
:mixed-width="sidebarMixedWidth"
|
||||
:padding-top="sidePaddingTop"
|
||||
:show="showSidebar"
|
||||
:theme="sidebarFace.theme"
|
||||
:width="getSidebarWidth"
|
||||
@@ -506,7 +524,7 @@ function handleOpenMenu() {
|
||||
:width="mainStyle.width"
|
||||
:z-index="headerZIndex"
|
||||
@open-menu="handleOpenMenu"
|
||||
@toggle-sidebar="handleToggleSidebar"
|
||||
@toggle-sidebar="() => emit('toggleSidebar')"
|
||||
>
|
||||
<template v-if="showHeaderLogo" #logo>
|
||||
<slot name="logo"></slot>
|
||||
|
Reference in New Issue
Block a user