| | |
| | | <template> |
| | | <div :class="getClass" ref="wrapperRef"> |
| | | <div :class="getClass" :style="getStyle" ref="wrapperRef"> |
| | | <PageHeader |
| | | :ghost="ghost" |
| | | :title="title" |
| | | v-bind="omit($attrs, 'class')" |
| | | :style="getHeaderStyle" |
| | | ref="headerRef" |
| | | v-if="getShowHeader" |
| | | > |
| | |
| | | </PageFooter> |
| | | </div> |
| | | </template> |
| | | <script lang="ts"> |
| | | <script lang="ts" setup> |
| | | import { PageWrapperFixedHeightKey } from '@/enums/pageEnum'; |
| | | import { useContentHeight } from '@/hooks/web/useContentHeight'; |
| | | import { useDesign } from '@/hooks/web/useDesign'; |
| | | import { propTypes } from '@/utils/propTypes'; |
| | | import { PageHeader } from 'ant-design-vue'; |
| | | import { omit, debounce } from 'lodash-es'; |
| | | import { useElementSize } from '@vueuse/core'; |
| | | import { |
| | | CSSProperties, |
| | | PropType, |
| | | provide, |
| | | defineComponent, |
| | | computed, |
| | | watch, |
| | | provide, |
| | | ref, |
| | | unref, |
| | | useAttrs, |
| | | useSlots, |
| | | watch, |
| | | } from 'vue'; |
| | | |
| | | import PageFooter from './PageFooter.vue'; |
| | | |
| | | import { useDesign } from '/@/hooks/web/useDesign'; |
| | | import { propTypes } from '/@/utils/propTypes'; |
| | | import { omit } from 'lodash-es'; |
| | | import { PageHeader } from 'ant-design-vue'; |
| | | import { useContentHeight } from '/@/hooks/web/useContentHeight'; |
| | | import { PageWrapperFixedHeightKey } from '/@/enums/pageEnum'; |
| | | |
| | | export default defineComponent({ |
| | | defineOptions({ |
| | | name: 'PageWrapper', |
| | | components: { PageFooter, PageHeader }, |
| | | inheritAttrs: false, |
| | | props: { |
| | | title: propTypes.string, |
| | | dense: propTypes.bool, |
| | | ghost: propTypes.bool, |
| | | content: propTypes.string, |
| | | contentStyle: { |
| | | type: Object as PropType<CSSProperties>, |
| | | }); |
| | | |
| | | const props = defineProps({ |
| | | title: propTypes.string, |
| | | dense: propTypes.bool, |
| | | ghost: propTypes.bool, |
| | | headerSticky: propTypes.bool, |
| | | headerStyle: Object as PropType<CSSProperties>, |
| | | content: propTypes.string, |
| | | contentStyle: { |
| | | type: Object as PropType<CSSProperties>, |
| | | }, |
| | | contentBackground: propTypes.bool, |
| | | contentFullHeight: propTypes.bool.def(false), |
| | | contentClass: propTypes.string, |
| | | fixedHeight: propTypes.bool, |
| | | upwardSpace: propTypes.oneOfType([propTypes.number, propTypes.string]).def(0), |
| | | }); |
| | | |
| | | const attrs = useAttrs(); |
| | | const slots = useSlots(); |
| | | |
| | | const wrapperRef = ref(null); |
| | | const headerRef = ref(null); |
| | | const contentRef = ref(null); |
| | | const footerRef = ref(null); |
| | | |
| | | const { height } = useElementSize(wrapperRef); |
| | | |
| | | const { prefixCls } = useDesign('page-wrapper'); |
| | | |
| | | provide( |
| | | PageWrapperFixedHeightKey, |
| | | computed(() => props.fixedHeight), |
| | | ); |
| | | |
| | | const getIsContentFullHeight = computed(() => { |
| | | return props.contentFullHeight; |
| | | }); |
| | | |
| | | const getUpwardSpace = computed(() => props.upwardSpace); |
| | | const { redoHeight, setCompensation, contentHeight } = useContentHeight( |
| | | getIsContentFullHeight, |
| | | wrapperRef, |
| | | [headerRef, footerRef], |
| | | [contentRef], |
| | | getUpwardSpace, |
| | | ); |
| | | const debounceRedoHeight = debounce(redoHeight, 50); |
| | | setCompensation({ useLayoutFooter: true, elements: [footerRef] }); |
| | | |
| | | const getClass = computed(() => { |
| | | return [ |
| | | prefixCls, |
| | | { |
| | | [`${prefixCls}--dense`]: props.dense, |
| | | }, |
| | | contentBackground: propTypes.bool, |
| | | contentFullHeight: propTypes.bool, |
| | | contentClass: propTypes.string, |
| | | fixedHeight: propTypes.bool, |
| | | upwardSpace: propTypes.oneOfType([propTypes.number, propTypes.string]).def(0), |
| | | attrs.class ?? {}, |
| | | ]; |
| | | }); |
| | | |
| | | const getStyle = computed(() => { |
| | | const { contentFullHeight, fixedHeight } = props; |
| | | return { |
| | | ...(contentFullHeight && fixedHeight ? { height: '100%' } : {}), |
| | | }; |
| | | }); |
| | | |
| | | const getHeaderStyle = computed((): CSSProperties => { |
| | | const { headerSticky } = props; |
| | | if (!headerSticky) { |
| | | return {}; |
| | | } |
| | | |
| | | return { |
| | | position: 'sticky', |
| | | top: 0, |
| | | zIndex: 99, |
| | | ...props.headerStyle, |
| | | }; |
| | | }); |
| | | |
| | | const getShowHeader = computed( |
| | | () => props.content || slots?.headerContent || props.title || getHeaderSlots.value.length, |
| | | ); |
| | | |
| | | const getShowFooter = computed(() => slots?.leftFooter || slots?.rightFooter); |
| | | |
| | | const getHeaderSlots = computed(() => { |
| | | return Object.keys(omit(slots, 'default', 'leftFooter', 'rightFooter', 'headerContent')); |
| | | }); |
| | | |
| | | const getContentStyle = computed((): CSSProperties => { |
| | | const { contentFullHeight, contentStyle, fixedHeight } = props; |
| | | if (!contentFullHeight) { |
| | | return { ...contentStyle }; |
| | | } |
| | | |
| | | const height = `${unref(contentHeight)}px`; |
| | | return { |
| | | ...contentStyle, |
| | | minHeight: height, |
| | | ...(fixedHeight ? { height } : {}), |
| | | }; |
| | | }); |
| | | |
| | | const getContentClass = computed(() => { |
| | | const { contentBackground, contentClass } = props; |
| | | return [ |
| | | `${prefixCls}-content`, |
| | | contentClass, |
| | | { |
| | | [`${prefixCls}-content-bg`]: contentBackground, |
| | | }, |
| | | ]; |
| | | }); |
| | | |
| | | watch( |
| | | () => [getShowFooter.value], |
| | | () => { |
| | | redoHeight(); |
| | | }, |
| | | setup(props, { slots, attrs }) { |
| | | const wrapperRef = ref(null); |
| | | const headerRef = ref(null); |
| | | const contentRef = ref(null); |
| | | const footerRef = ref(null); |
| | | const { prefixCls } = useDesign('page-wrapper'); |
| | | |
| | | provide( |
| | | PageWrapperFixedHeightKey, |
| | | computed(() => props.fixedHeight), |
| | | ); |
| | | |
| | | const getIsContentFullHeight = computed(() => { |
| | | return props.contentFullHeight; |
| | | }); |
| | | |
| | | const getUpwardSpace = computed(() => props.upwardSpace); |
| | | const { redoHeight, setCompensation, contentHeight } = useContentHeight( |
| | | getIsContentFullHeight, |
| | | wrapperRef, |
| | | [headerRef, footerRef], |
| | | [contentRef], |
| | | getUpwardSpace, |
| | | ); |
| | | setCompensation({ useLayoutFooter: true, elements: [footerRef] }); |
| | | |
| | | const getClass = computed(() => { |
| | | return [ |
| | | prefixCls, |
| | | { |
| | | [`${prefixCls}--dense`]: props.dense, |
| | | }, |
| | | attrs.class ?? {}, |
| | | ]; |
| | | }); |
| | | |
| | | const getShowHeader = computed( |
| | | () => props.content || slots?.headerContent || props.title || getHeaderSlots.value.length, |
| | | ); |
| | | |
| | | const getShowFooter = computed(() => slots?.leftFooter || slots?.rightFooter); |
| | | |
| | | const getHeaderSlots = computed(() => { |
| | | return Object.keys(omit(slots, 'default', 'leftFooter', 'rightFooter', 'headerContent')); |
| | | }); |
| | | |
| | | const getContentStyle = computed((): CSSProperties => { |
| | | const { contentFullHeight, contentStyle, fixedHeight } = props; |
| | | if (!contentFullHeight) { |
| | | return { ...contentStyle }; |
| | | } |
| | | |
| | | const height = `${unref(contentHeight)}px`; |
| | | return { |
| | | ...contentStyle, |
| | | minHeight: height, |
| | | ...(fixedHeight ? { height } : {}), |
| | | }; |
| | | }); |
| | | |
| | | const getContentClass = computed(() => { |
| | | const { contentBackground, contentClass } = props; |
| | | return [ |
| | | `${prefixCls}-content`, |
| | | contentClass, |
| | | { |
| | | [`${prefixCls}-content-bg`]: contentBackground, |
| | | }, |
| | | ]; |
| | | }); |
| | | |
| | | watch( |
| | | () => [getShowFooter.value], |
| | | () => { |
| | | redoHeight(); |
| | | }, |
| | | { |
| | | flush: 'post', |
| | | immediate: true, |
| | | }, |
| | | ); |
| | | |
| | | return { |
| | | getContentStyle, |
| | | wrapperRef, |
| | | headerRef, |
| | | contentRef, |
| | | footerRef, |
| | | getClass, |
| | | getHeaderSlots, |
| | | prefixCls, |
| | | getShowHeader, |
| | | getShowFooter, |
| | | omit, |
| | | getContentClass, |
| | | }; |
| | | { |
| | | flush: 'post', |
| | | immediate: true, |
| | | }, |
| | | ); |
| | | |
| | | watch(height, () => { |
| | | const { contentFullHeight, fixedHeight } = props; |
| | | contentFullHeight && fixedHeight && debounceRedoHeight(); |
| | | }); |
| | | </script> |
| | | <style lang="less"> |