Commit 6717fe654e88e6a939a16c523832870388ec1886
Committed by
GitHub
1 parent
ee7c31db
fix: Improve content height calculation (#1136)
* feat(useContentHeight): 为useContentHeight 添加 向上递归 移除差值 的功能。 * feat(useContentHeight): 为useContentHeight 添加 向上递归 移除差值 的功能。 pagewrapper添加 upwardSpace以支持向上递归功能。
Showing
2 changed files
with
66 additions
and
19 deletions
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 = () => { | ... | ... |