2024-08-11 16:29:39 +08:00
|
|
|
<script setup lang="ts">
|
2025-01-01 11:39:49 +08:00
|
|
|
import type { StyleValue } from 'vue';
|
|
|
|
|
2024-12-19 20:37:42 +08:00
|
|
|
import type { PageProps } from './types';
|
|
|
|
|
2025-01-01 11:39:49 +08:00
|
|
|
import { computed, nextTick, onMounted, ref, useTemplateRef } from 'vue';
|
2024-12-04 21:40:41 +08:00
|
|
|
|
2024-12-07 23:26:47 +08:00
|
|
|
import { CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT } from '@vben-core/shared/constants';
|
2024-12-04 21:40:41 +08:00
|
|
|
import { cn } from '@vben-core/shared/utils';
|
2024-10-04 23:05:28 +08:00
|
|
|
|
2024-08-11 16:29:39 +08:00
|
|
|
defineOptions({
|
|
|
|
name: 'Page',
|
|
|
|
});
|
|
|
|
|
2025-04-29 18:15:12 +08:00
|
|
|
const { autoContentHeight = false, heightOffset = 0 } =
|
|
|
|
defineProps<PageProps>();
|
2024-10-04 23:05:28 +08:00
|
|
|
|
|
|
|
const headerHeight = ref(0);
|
|
|
|
const footerHeight = ref(0);
|
|
|
|
const shouldAutoHeight = ref(false);
|
|
|
|
|
|
|
|
const headerRef = useTemplateRef<HTMLDivElement>('headerRef');
|
|
|
|
const footerRef = useTemplateRef<HTMLDivElement>('footerRef');
|
|
|
|
|
2024-12-10 17:37:06 +08:00
|
|
|
const contentStyle = computed<StyleValue>(() => {
|
2024-10-04 23:05:28 +08:00
|
|
|
if (autoContentHeight) {
|
|
|
|
return {
|
2025-04-29 18:15:12 +08:00
|
|
|
height: `calc(var(${CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT}) - ${headerHeight.value}px - ${typeof heightOffset === 'number' ? `${heightOffset}px` : heightOffset})`,
|
2024-12-10 17:37:06 +08:00
|
|
|
overflowY: shouldAutoHeight.value ? 'auto' : 'unset',
|
2024-10-04 23:05:28 +08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
});
|
|
|
|
|
|
|
|
async function calcContentHeight() {
|
|
|
|
if (!autoContentHeight) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
await nextTick();
|
|
|
|
headerHeight.value = headerRef.value?.offsetHeight || 0;
|
|
|
|
footerHeight.value = footerRef.value?.offsetHeight || 0;
|
|
|
|
setTimeout(() => {
|
|
|
|
shouldAutoHeight.value = true;
|
|
|
|
}, 30);
|
|
|
|
}
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
calcContentHeight();
|
2024-08-11 16:29:39 +08:00
|
|
|
});
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
2024-10-04 23:05:28 +08:00
|
|
|
<div class="relative">
|
2024-08-11 21:01:22 +08:00
|
|
|
<div
|
2024-10-04 23:05:28 +08:00
|
|
|
v-if="
|
|
|
|
description ||
|
|
|
|
$slots.description ||
|
|
|
|
title ||
|
|
|
|
$slots.title ||
|
|
|
|
$slots.extra
|
|
|
|
"
|
|
|
|
ref="headerRef"
|
2024-12-04 21:40:41 +08:00
|
|
|
:class="
|
|
|
|
cn(
|
2024-12-07 23:26:47 +08:00
|
|
|
'bg-card border-border relative flex items-end border-b px-6 py-4',
|
2024-12-04 21:40:41 +08:00
|
|
|
headerClass,
|
|
|
|
)
|
|
|
|
"
|
2024-08-11 20:05:52 +08:00
|
|
|
>
|
2024-12-07 23:26:47 +08:00
|
|
|
<div class="flex-auto">
|
|
|
|
<slot name="title">
|
|
|
|
<div v-if="title" class="mb-2 flex text-lg font-semibold">
|
|
|
|
{{ title }}
|
|
|
|
</div>
|
|
|
|
</slot>
|
2024-08-12 21:05:01 +08:00
|
|
|
|
2024-12-07 23:26:47 +08:00
|
|
|
<slot name="description">
|
|
|
|
<p v-if="description" class="text-muted-foreground">
|
|
|
|
{{ description }}
|
|
|
|
</p>
|
|
|
|
</slot>
|
|
|
|
</div>
|
2024-10-04 23:05:28 +08:00
|
|
|
|
2024-12-07 23:26:47 +08:00
|
|
|
<div v-if="$slots.extra">
|
2024-10-04 23:05:28 +08:00
|
|
|
<slot name="extra"></slot>
|
|
|
|
</div>
|
2024-08-11 21:01:22 +08:00
|
|
|
</div>
|
|
|
|
|
2024-12-18 22:53:05 +08:00
|
|
|
<div :class="cn('h-full p-4', contentClass)" :style="contentStyle">
|
2024-08-11 16:29:39 +08:00
|
|
|
<slot></slot>
|
|
|
|
</div>
|
2024-08-11 21:01:22 +08:00
|
|
|
|
|
|
|
<div
|
2024-10-04 23:05:28 +08:00
|
|
|
v-if="$slots.footer"
|
|
|
|
ref="footerRef"
|
2024-12-04 21:40:41 +08:00
|
|
|
:class="
|
|
|
|
cn(
|
|
|
|
'bg-card align-center absolute bottom-0 left-0 right-0 flex px-6 py-4',
|
2024-12-07 23:26:47 +08:00
|
|
|
footerClass,
|
2024-12-04 21:40:41 +08:00
|
|
|
)
|
|
|
|
"
|
2024-08-11 21:01:22 +08:00
|
|
|
>
|
|
|
|
<slot name="footer"></slot>
|
|
|
|
</div>
|
2024-08-11 16:29:39 +08:00
|
|
|
</div>
|
|
|
|
</template>
|