PageWrapper.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. <template>
  2. <div :class="getClass" ref="wrapperRef">
  3. <PageHeader
  4. :ghost="ghost"
  5. :title="title"
  6. v-bind="omit($attrs, 'class')"
  7. ref="headerRef"
  8. v-if="content || $slots.headerContent || title || getHeaderSlots.length"
  9. >
  10. <template #default>
  11. <template v-if="content">
  12. {{ content }}
  13. </template>
  14. <slot name="headerContent" v-else></slot>
  15. </template>
  16. <template #[item]="data" v-for="item in getHeaderSlots">
  17. <slot :name="item" v-bind="data || {}"></slot>
  18. </template>
  19. </PageHeader>
  20. <div class="overflow-hidden" :class="getContentClass" :style="getContentStyle" ref="contentRef">
  21. <slot></slot>
  22. </div>
  23. <PageFooter v-if="getShowFooter" ref="footerRef">
  24. <template #left>
  25. <slot name="leftFooter"></slot>
  26. </template>
  27. <template #right>
  28. <slot name="rightFooter"></slot>
  29. </template>
  30. </PageFooter>
  31. </div>
  32. </template>
  33. <script lang="ts">
  34. import { CSSProperties, PropType, provide } from 'vue';
  35. import { defineComponent, computed, watch, ref, unref } from 'vue';
  36. import PageFooter from './PageFooter.vue';
  37. import { useDesign } from '/@/hooks/web/useDesign';
  38. import { propTypes } from '/@/utils/propTypes';
  39. import { omit } from 'lodash-es';
  40. import { PageHeader } from 'ant-design-vue';
  41. import { useContentHeight } from '/@/hooks/web/useContentHeight';
  42. import { PageWrapperFixedHeightKey } from '../injectionKey';
  43. export default defineComponent({
  44. name: 'PageWrapper',
  45. components: { PageFooter, PageHeader },
  46. inheritAttrs: false,
  47. props: {
  48. title: propTypes.string,
  49. dense: propTypes.bool,
  50. ghost: propTypes.bool,
  51. content: propTypes.string,
  52. contentStyle: {
  53. type: Object as PropType<CSSProperties>,
  54. },
  55. contentBackground: propTypes.bool,
  56. contentFullHeight: propTypes.bool,
  57. contentClass: propTypes.string,
  58. fixedHeight: propTypes.bool,
  59. upwardSpace: propTypes.oneOfType([propTypes.number, propTypes.string]).def(0),
  60. },
  61. setup(props, { slots, attrs }) {
  62. const wrapperRef = ref(null);
  63. const headerRef = ref(null);
  64. const contentRef = ref(null);
  65. const footerRef = ref(null);
  66. const { prefixCls } = useDesign('page-wrapper');
  67. provide(
  68. PageWrapperFixedHeightKey,
  69. computed(() => props.fixedHeight)
  70. );
  71. const getIsContentFullHeight = computed(() => {
  72. return props.contentFullHeight;
  73. });
  74. const getUpwardSpace = computed(() => props.upwardSpace);
  75. const { redoHeight, setCompensation, contentHeight } = useContentHeight(
  76. getIsContentFullHeight,
  77. wrapperRef,
  78. [headerRef, footerRef],
  79. [contentRef],
  80. getUpwardSpace
  81. );
  82. setCompensation({ useLayoutFooter: true, elements: [footerRef] });
  83. const getClass = computed(() => {
  84. return [
  85. prefixCls,
  86. {
  87. [`${prefixCls}--dense`]: props.dense,
  88. },
  89. attrs.class ?? {},
  90. ];
  91. });
  92. const getShowFooter = computed(() => slots?.leftFooter || slots?.rightFooter);
  93. const getHeaderSlots = computed(() => {
  94. return Object.keys(omit(slots, 'default', 'leftFooter', 'rightFooter', 'headerContent'));
  95. });
  96. const getContentStyle = computed((): CSSProperties => {
  97. const { contentFullHeight, contentStyle, fixedHeight } = props;
  98. if (!contentFullHeight) {
  99. return { ...contentStyle };
  100. }
  101. const height = `${unref(contentHeight)}px`;
  102. return {
  103. ...contentStyle,
  104. minHeight: height,
  105. ...(fixedHeight ? { height } : {}),
  106. };
  107. });
  108. const getContentClass = computed(() => {
  109. const { contentBackground, contentClass } = props;
  110. return [
  111. `${prefixCls}-content`,
  112. contentClass,
  113. {
  114. [`${prefixCls}-content-bg`]: contentBackground,
  115. },
  116. ];
  117. });
  118. watch(
  119. () => [getShowFooter.value],
  120. () => {
  121. redoHeight();
  122. },
  123. {
  124. flush: 'post',
  125. immediate: true,
  126. }
  127. );
  128. return {
  129. getContentStyle,
  130. wrapperRef,
  131. headerRef,
  132. contentRef,
  133. footerRef,
  134. getClass,
  135. getHeaderSlots,
  136. prefixCls,
  137. getShowFooter,
  138. omit,
  139. getContentClass,
  140. };
  141. },
  142. });
  143. </script>
  144. <style lang="less">
  145. @prefix-cls: ~'@{namespace}-page-wrapper';
  146. @ventSpace: zxm;
  147. .@{prefix-cls} {
  148. position: relative;
  149. .@{prefix-cls}-content {
  150. margin: 16px;
  151. }
  152. .@{ventSpace}-page-header {
  153. &:empty {
  154. padding: 0;
  155. }
  156. }
  157. &-content-bg {
  158. background-color: @component-background;
  159. }
  160. &--dense {
  161. .@{prefix-cls}-content {
  162. margin: 0;
  163. }
  164. }
  165. }
  166. </style>