Commit 9de6ac11197d9bb5c873531c51d393f16926250d

Authored by Lan
Committed by GitHub
1 parent 95c16a5d

refactor(contentHeight): 重构,将PageWrapper的useContentHeight抽象为公共hook,满足自定义扩展组件时的自动…

…高度计算需求。 同时优化高度计算算法。 (#826)
src/components/Page/src/PageWrapper.vue
@@ -42,9 +42,7 @@ @@ -42,9 +42,7 @@
42 import { propTypes } from '/@/utils/propTypes'; 42 import { propTypes } from '/@/utils/propTypes';
43 import { omit } from 'lodash-es'; 43 import { omit } from 'lodash-es';
44 import { PageHeader } from 'ant-design-vue'; 44 import { PageHeader } from 'ant-design-vue';
45 - import { useLayoutHeight } from '/@/layouts/default/content/useContentViewHeight';  
46 - import { useContentHeight } from './useContentHeight';  
47 - import { WrapperProps } from './types'; 45 + import { useContentHeight } from '/@/hooks/web/useContentHeight';
48 46
49 export default defineComponent({ 47 export default defineComponent({
50 name: 'PageWrapper', 48 name: 'PageWrapper',
@@ -64,25 +62,23 @@ @@ -64,25 +62,23 @@
64 fixedHeight: propTypes.bool, 62 fixedHeight: propTypes.bool,
65 }, 63 },
66 setup(props, { slots }) { 64 setup(props, { slots }) {
67 - const wrapperRef = ref<ElRef>(null);  
68 - const headerRef = ref<ComponentRef>(null);  
69 - const contentRef = ref<ElRef>(null);  
70 - const footerRef = ref<ComponentRef>(null); 65 + const wrapperRef = ref(null);
  66 + const headerRef = ref(null);
  67 + const contentRef = ref(null);
  68 + const footerRef = ref(null);
71 const { prefixCls } = useDesign('page-wrapper'); 69 const { prefixCls } = useDesign('page-wrapper');
72 - const { footerHeightRef } = useLayoutHeight();  
73 70
74 - const getProps = computed(() => {  
75 - return props as WrapperProps; 71 + const getIsContentFullHeight = computed(() => {
  72 + return props.contentFullHeight;
76 }); 73 });
77 74
78 - const { redoHeight, contentHeight } = useContentHeight(  
79 - getProps, 75 + const { redoHeight, setCompensation, contentHeight } = useContentHeight(
  76 + getIsContentFullHeight,
80 wrapperRef, 77 wrapperRef,
81 - headerRef,  
82 - contentRef,  
83 - footerRef,  
84 - footerHeightRef 78 + [headerRef, footerRef],
  79 + [contentRef]
85 ); 80 );
  81 + setCompensation({ useLayoutFooter: true, elements: [footerRef] });
86 82
87 const getClass = computed(() => { 83 const getClass = computed(() => {
88 return [ 84 return [
@@ -125,7 +121,7 @@ @@ -125,7 +121,7 @@
125 }); 121 });
126 122
127 watch( 123 watch(
128 - () => [getShowFooter.value, footerHeightRef.value], 124 + () => [getShowFooter.value],
129 () => { 125 () => {
130 redoHeight(); 126 redoHeight();
131 }, 127 },
src/components/Page/src/types.ts deleted 100644 → 0
1 -import { CSSProperties } from 'vue';  
2 -  
3 -export interface WrapperProps {  
4 - title?: string;  
5 - dense: boolean;  
6 - ghost: boolean;  
7 - content: string;  
8 - contentStyle?: CSSProperties;  
9 - contentBackground: boolean;  
10 - contentFullHeight: boolean;  
11 - contentClass?: string;  
12 - fixedHeight: boolean;  
13 -}  
src/components/Page/src/useContentHeight.ts renamed to src/hooks/web/useContentHeight.ts
1 -import { ComputedRef, nextTick, Ref, ref, unref } from 'vue';  
2 -import { WrapperProps } from './types'; 1 +import { ComputedRef, nextTick, Ref, ref, unref, watch } from 'vue';
3 import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated'; 2 import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
4 import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn'; 3 import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
  4 +import { useLayoutHeight } from '/@/layouts/default/content/useContentViewHeight';
5 import { getViewportOffset } from '/@/utils/domUtils'; 5 import { getViewportOffset } from '/@/utils/domUtils';
6 6
  7 +export interface CompensationHeight {
  8 + // 使用 layout Footer 高度作为判断补偿高度的条件
  9 + useLayoutFooter: boolean;
  10 + // refs HTMLElement
  11 + elements?: Ref[];
  12 +}
  13 +
  14 +/**
  15 + * 动态计算内容高度,根据锚点dom最下坐标到屏幕最下坐标,根据传入dom的高度、padding、margin等值进行动态计算
  16 + * 最终获取合适的内容高度
  17 + *
  18 + * @param flag 用于开启计算的响应式标识
  19 + * @param anchorRef 锚点组件 Ref<ElRef | ComponentRef>
  20 + * @param subtractHeightRefs 待减去高度的组件列表 Ref<ElRef | ComponentRef>
  21 + * @param substractSpaceRefs 待减去空闲空间(margins/paddings)的组件列表 Ref<ElRef | ComponentRef>
  22 + * @param offsetHeightRef 计算偏移的响应式高度,计算高度时将直接减去此值
  23 + * @returns 响应式高度
  24 + */
7 export function useContentHeight( 25 export function useContentHeight(
8 - propsRef: ComputedRef<WrapperProps>,  
9 - wrapperRef: Ref<ElRef>,  
10 - headerRef?: Ref<ComponentRef>,  
11 - contentRef?: Ref<ElRef>,  
12 - footerRef?: Ref<ComponentRef>,  
13 - layoutFooterHeightRef: Ref<number> = ref(0), 26 + flag: ComputedRef<Boolean>,
  27 + anchorRef: Ref,
  28 + subtractHeightRefs: Ref[],
  29 + substractSpaceRefs: Ref[],
14 offsetHeightRef: Ref<number> = ref(0) 30 offsetHeightRef: Ref<number> = ref(0)
15 ) { 31 ) {
16 const contentHeight: Ref<Nullable<number>> = ref(null); 32 const contentHeight: Ref<Nullable<number>> = ref(null);
  33 + const { footerHeightRef: layoutFooterHeightRef } = useLayoutHeight();
  34 + let compensationHeight: CompensationHeight = {
  35 + useLayoutFooter: true,
  36 + };
  37 +
  38 + const setCompensation = (params: CompensationHeight) => {
  39 + compensationHeight = params;
  40 + };
17 41
18 - const redoHeight = () => { 42 + function redoHeight() {
19 nextTick(() => { 43 nextTick(() => {
20 calcContentHeight(); 44 calcContentHeight();
21 }); 45 });
22 - }; 46 + }
23 47
24 - const subtractMargin = (element: HTMLElement | null | undefined): number => { 48 + function calcSubtractSpace(element: HTMLDivElement | null | undefined): number {
25 let subtractHeight = 0; 49 let subtractHeight = 0;
26 const ZERO_PX = '0px'; 50 const ZERO_PX = '0px';
27 let marginBottom = ZERO_PX; 51 let marginBottom = ZERO_PX;
@@ -40,41 +64,61 @@ export function useContentHeight( @@ -40,41 +64,61 @@ export function useContentHeight(
40 subtractHeight += contentMarginTop; 64 subtractHeight += contentMarginTop;
41 } 65 }
42 return subtractHeight; 66 return subtractHeight;
43 - }; 67 + }
44 68
45 - const calcContentHeight = async () => {  
46 - const { contentFullHeight } = unref(propsRef);  
47 - if (!contentFullHeight) { 69 + function getEl(element: any): Nullable<HTMLDivElement> {
  70 + if (element == null) {
  71 + return null;
  72 + }
  73 + return (element instanceof HTMLDivElement ? element : element.$el) as HTMLDivElement;
  74 + }
  75 +
  76 + async function calcContentHeight() {
  77 + if (!flag.value) {
48 return; 78 return;
49 } 79 }
50 // Add a delay to get the correct height 80 // Add a delay to get the correct height
51 await nextTick(); 81 await nextTick();
52 82
53 - const wrapperEl = unref(wrapperRef); 83 + const wrapperEl = getEl(unref(anchorRef));
54 if (!wrapperEl) { 84 if (!wrapperEl) {
55 return; 85 return;
56 } 86 }
57 const { bottomIncludeBody } = getViewportOffset(wrapperEl); 87 const { bottomIncludeBody } = getViewportOffset(wrapperEl);
58 - const headerHeight = unref(headerRef)?.$el.offsetHeight ?? 0;  
59 - const footerHeight = unref(footerRef)?.$el.offsetHeight ?? 0;  
60 88
61 - // content's subtract  
62 - const substractHeight = subtractMargin(unref(contentRef)); 89 + // substract elements height
  90 + let substractHeight = 0;
  91 + subtractHeightRefs.forEach((item) => {
  92 + substractHeight += getEl(unref(item))?.offsetHeight ?? 0;
  93 + });
  94 +
  95 + // subtract margins / paddings
  96 + let substractSpaceHeight = 0;
  97 + substractSpaceRefs.forEach((item) => {
  98 + substractSpaceHeight += calcSubtractSpace(getEl(unref(item)));
  99 + });
  100 +
63 let height = 101 let height =
64 bottomIncludeBody - 102 bottomIncludeBody -
65 unref(layoutFooterHeightRef) - 103 unref(layoutFooterHeightRef) -
66 unref(offsetHeightRef) - 104 unref(offsetHeightRef) -
67 - headerHeight -  
68 - footerHeight -  
69 - substractHeight; 105 + substractHeight -
  106 + substractSpaceHeight;
70 107
71 - // fix: compensation height both layout's footer and page's footer was shown  
72 - if (unref(layoutFooterHeightRef) > 0 && footerHeight > 0) {  
73 - height += footerHeight; 108 + // compensation height
  109 + const calcCompensationHeight = () => {
  110 + compensationHeight.elements?.forEach((item) => {
  111 + height += getEl(unref(item))?.offsetHeight ?? 0;
  112 + });
  113 + };
  114 + if (compensationHeight.useLayoutFooter && unref(layoutFooterHeightRef) > 0) {
  115 + calcCompensationHeight();
  116 + } else {
  117 + calcCompensationHeight();
74 } 118 }
75 119
76 contentHeight.value = height; 120 contentHeight.value = height;
77 - }; 121 + }
78 122
79 onMountedOrActivated(() => { 123 onMountedOrActivated(() => {
80 nextTick(() => { 124 nextTick(() => {
@@ -88,6 +132,16 @@ export function useContentHeight( @@ -88,6 +132,16 @@ export function useContentHeight(
88 50, 132 50,
89 { immediate: true } 133 { immediate: true }
90 ); 134 );
  135 + watch(
  136 + () => [layoutFooterHeightRef.value],
  137 + () => {
  138 + calcContentHeight();
  139 + },
  140 + {
  141 + flush: 'post',
  142 + immediate: true,
  143 + }
  144 + );
91 145
92 - return { redoHeight, contentHeight }; 146 + return { redoHeight, setCompensation, contentHeight };
93 } 147 }