Commit 6717fe654e88e6a939a16c523832870388ec1886

Authored by Lan
Committed by GitHub
1 parent ee7c31db

fix: Improve content height calculation (#1136)

* feat(useContentHeight): 为useContentHeight 添加 向上递归 移除差值 的功能。

* feat(useContentHeight): 为useContentHeight 添加 向上递归 移除差值 的功能。 pagewrapper添加 upwardSpace以支持向上递归功能。
src/components/Page/src/PageWrapper.vue
... ... @@ -61,6 +61,7 @@
61 61 contentFullHeight: propTypes.bool,
62 62 contentClass: propTypes.string,
63 63 fixedHeight: propTypes.bool,
  64 + upwardSpace: propTypes.oneOfType([propTypes.number, propTypes.string]).def(0),
64 65 },
65 66 setup(props, { slots, attrs }) {
66 67 const wrapperRef = ref(null);
... ... @@ -78,11 +79,13 @@
78 79 return props.contentFullHeight;
79 80 });
80 81  
  82 + const getUpwardSpace = computed(() => props.upwardSpace);
81 83 const { redoHeight, setCompensation, contentHeight } = useContentHeight(
82 84 getIsContentFullHeight,
83 85 wrapperRef,
84 86 [headerRef, footerRef],
85 87 [contentRef],
  88 + getUpwardSpace,
86 89 );
87 90 setCompensation({ useLayoutFooter: true, elements: [footerRef] });
88 91  
... ...
src/hooks/web/useContentHeight.ts
1   -import { ComputedRef, nextTick, Ref, ref, unref, watch } from 'vue';
  1 +import { ComputedRef, isRef, nextTick, Ref, ref, unref, watch } from 'vue';
2 2 import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
3 3 import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
4 4 import { useLayoutHeight } from '/@/layouts/default/content/useContentViewHeight';
5 5 import { getViewportOffset } from '/@/utils/domUtils';
  6 +import { isNumber, isString } from '/@/utils/is';
6 7  
7 8 export interface CompensationHeight {
8 9 // 使用 layout Footer 高度作为判断补偿高度的条件
... ... @@ -11,6 +12,8 @@ export interface CompensationHeight {
11 12 elements?: Ref[];
12 13 }
13 14  
  15 +type Upward = number | string | null | undefined;
  16 +
14 17 /**
15 18 * 动态计算内容高度,根据锚点dom最下坐标到屏幕最下坐标,根据传入dom的高度、padding、margin等值进行动态计算
16 19 * 最终获取合适的内容高度
... ... @@ -20,6 +23,7 @@ export interface CompensationHeight {
20 23 * @param subtractHeightRefs 待减去高度的组件列表 Ref<ElRef | ComponentRef>
21 24 * @param substractSpaceRefs 待减去空闲空间(margins/paddings)的组件列表 Ref<ElRef | ComponentRef>
22 25 * @param offsetHeightRef 计算偏移的响应式高度,计算高度时将直接减去此值
  26 + * @param upwardSpace 向上递归减去空闲空间的 层级 或 直到指定class为止 数值为2代表向上递归两次|数值为ant-layout表示向上递归直到碰见.ant-layout为止
23 27 * @returns 响应式高度
24 28 */
25 29 export function useContentHeight(
... ... @@ -27,6 +31,7 @@ export function useContentHeight(
27 31 anchorRef: Ref,
28 32 subtractHeightRefs: Ref[],
29 33 substractSpaceRefs: Ref[],
  34 + upwardSpace: Ref<Upward> | ComputedRef<Upward> | Upward = 0,
30 35 offsetHeightRef: Ref<number> = ref(0),
31 36 ) {
32 37 const contentHeight: Ref<Nullable<number>> = ref(null);
... ... @@ -45,23 +50,33 @@ export function useContentHeight(
45 50 });
46 51 }
47 52  
48   - function calcSubtractSpace(element: HTMLDivElement | null | undefined): number {
  53 + function calcSubtractSpace(
  54 + element: Element | null | undefined,
  55 + direction: 'all' | 'top' | 'bottom' = 'all',
  56 + ): number {
  57 + function numberPx(px: string) {
  58 + return Number(px.replace(/[^\d]/g, ''));
  59 + }
49 60 let subtractHeight = 0;
50 61 const ZERO_PX = '0px';
51   - let marginBottom = ZERO_PX;
52   - let marginTop = ZERO_PX;
53 62 if (element) {
54 63 const cssStyle = getComputedStyle(element);
55   - marginBottom = cssStyle?.marginBottom ?? ZERO_PX;
56   - marginTop = cssStyle?.marginTop ?? ZERO_PX;
57   - }
58   - if (marginBottom) {
59   - const contentMarginBottom = Number(marginBottom.replace(/[^\d]/g, ''));
60   - subtractHeight += contentMarginBottom;
61   - }
62   - if (marginTop) {
63   - const contentMarginTop = Number(marginTop.replace(/[^\d]/g, ''));
64   - subtractHeight += contentMarginTop;
  64 + const marginTop = numberPx(cssStyle?.marginTop ?? ZERO_PX);
  65 + const marginBottom = numberPx(cssStyle?.marginBottom ?? ZERO_PX);
  66 + const paddingTop = numberPx(cssStyle?.paddingTop ?? ZERO_PX);
  67 + const paddingBottom = numberPx(cssStyle?.paddingBottom ?? ZERO_PX);
  68 + if (direction === 'all') {
  69 + subtractHeight += marginTop;
  70 + subtractHeight += marginBottom;
  71 + subtractHeight += paddingTop;
  72 + subtractHeight += paddingBottom;
  73 + } else if (direction === 'top') {
  74 + subtractHeight += marginTop;
  75 + subtractHeight += paddingTop;
  76 + } else {
  77 + subtractHeight += marginBottom;
  78 + subtractHeight += paddingBottom;
  79 + }
65 80 }
66 81 return subtractHeight;
67 82 }
... ... @@ -80,11 +95,11 @@ export function useContentHeight(
80 95 // Add a delay to get the correct height
81 96 await nextTick();
82 97  
83   - const wrapperEl = getEl(unref(anchorRef));
84   - if (!wrapperEl) {
  98 + const anchorEl = getEl(unref(anchorRef));
  99 + if (!anchorEl) {
85 100 return;
86 101 }
87   - const { bottomIncludeBody } = getViewportOffset(wrapperEl);
  102 + const { bottomIncludeBody } = getViewportOffset(anchorEl);
88 103  
89 104 // substract elements height
90 105 let substractHeight = 0;
... ... @@ -93,17 +108,46 @@ export function useContentHeight(
93 108 });
94 109  
95 110 // subtract margins / paddings
96   - let substractSpaceHeight = 0;
  111 + let substractSpaceHeight = calcSubtractSpace(anchorEl) ?? 0;
97 112 substractSpaceRefs.forEach((item) => {
98 113 substractSpaceHeight += calcSubtractSpace(getEl(unref(item)));
99 114 });
100 115  
  116 + // upwardSpace
  117 + let upwardSpaceHeight = 0;
  118 + function upward(element: Element | null, upwardLvlOrClass: number | string | null | undefined) {
  119 + if (element && upwardLvlOrClass) {
  120 + const parent = element.parentElement;
  121 + if (parent) {
  122 + if (isString(upwardLvlOrClass)) {
  123 + if (!parent.classList.contains(upwardLvlOrClass)) {
  124 + upwardSpaceHeight += calcSubtractSpace(parent, 'bottom');
  125 + upward(parent, upwardLvlOrClass);
  126 + } else {
  127 + upwardSpaceHeight += calcSubtractSpace(parent, 'bottom');
  128 + }
  129 + } else if (isNumber(upwardLvlOrClass)) {
  130 + if (upwardLvlOrClass > 0) {
  131 + upwardSpaceHeight += calcSubtractSpace(parent, 'bottom');
  132 + upward(parent, --upwardLvlOrClass);
  133 + }
  134 + }
  135 + }
  136 + }
  137 + }
  138 + if (isRef(upwardSpace)) {
  139 + upward(anchorEl, unref(upwardSpace));
  140 + } else {
  141 + upward(anchorEl, upwardSpace);
  142 + }
  143 +
101 144 let height =
102 145 bottomIncludeBody -
103 146 unref(layoutFooterHeightRef) -
104 147 unref(offsetHeightRef) -
105 148 substractHeight -
106   - substractSpaceHeight;
  149 + substractSpaceHeight -
  150 + upwardSpaceHeight;
107 151  
108 152 // compensation height
109 153 const calcCompensationHeight = () => {
... ...