Commit 4d8e39857ea59fff99e69832b4a8cabf3a424c24
Committed by
GitHub
1 parent
c4b22a22
perf(pagewrapper): 优化PageWrapper的高度自适应表现使用getViewportOffset替代useContentViewHeight (#792)
Co-authored-by: NorthLan <lan6995@gmail.com>
Showing
4 changed files
with
135 additions
and
90 deletions
src/components/Page/src/PageWrapper.vue
1 | 1 | <template> |
2 | - <div :class="getClass"> | |
2 | + <div :class="getClass" ref="wrapperRef"> | |
3 | 3 | <PageHeader |
4 | 4 | :ghost="ghost" |
5 | 5 | :title="title" |
... | ... | @@ -18,7 +18,7 @@ |
18 | 18 | </template> |
19 | 19 | </PageHeader> |
20 | 20 | |
21 | - <div class="overflow-hidden" :class="getContentClass" :style="getContentStyle"> | |
21 | + <div class="overflow-hidden" :class="getContentClass" :style="getContentStyle" ref="contentRef"> | |
22 | 22 | <slot></slot> |
23 | 23 | </div> |
24 | 24 | |
... | ... | @@ -35,16 +35,16 @@ |
35 | 35 | <script lang="ts"> |
36 | 36 | import type { CSSProperties, PropType } from 'vue'; |
37 | 37 | |
38 | - import { defineComponent, computed, watch, nextTick, ref, unref } from 'vue'; | |
38 | + import { defineComponent, computed, watch, ref, unref } from 'vue'; | |
39 | 39 | import PageFooter from './PageFooter.vue'; |
40 | - import { usePageContext } from '/@/hooks/component/usePageContext'; | |
41 | 40 | |
42 | 41 | import { useDesign } from '/@/hooks/web/useDesign'; |
43 | 42 | import { propTypes } from '/@/utils/propTypes'; |
44 | 43 | import { omit } from 'lodash-es'; |
45 | 44 | import { PageHeader } from 'ant-design-vue'; |
46 | - import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated'; | |
47 | 45 | import { useLayoutHeight } from '/@/layouts/default/content/useContentViewHeight'; |
46 | + import { useContentHeight } from './useContentHeight'; | |
47 | + import { WrapperProps } from './types'; | |
48 | 48 | |
49 | 49 | export default defineComponent({ |
50 | 50 | name: 'PageWrapper', |
... | ... | @@ -64,13 +64,26 @@ |
64 | 64 | fixedHeight: propTypes.bool, |
65 | 65 | }, |
66 | 66 | setup(props, { slots }) { |
67 | + const wrapperRef = ref<ElRef>(null); | |
67 | 68 | const headerRef = ref<ComponentRef>(null); |
69 | + const contentRef = ref<ElRef>(null); | |
68 | 70 | const footerRef = ref<ComponentRef>(null); |
69 | - const footerHeight = ref(0); | |
70 | - const { prefixCls, prefixVar } = useDesign('page-wrapper'); | |
71 | - const { contentHeight, setPageHeight, pageHeight } = usePageContext(); | |
71 | + const { prefixCls } = useDesign('page-wrapper'); | |
72 | 72 | const { footerHeightRef } = useLayoutHeight(); |
73 | 73 | |
74 | + const getProps = computed(() => { | |
75 | + return props as WrapperProps; | |
76 | + }); | |
77 | + | |
78 | + const { redoHeight, contentHeight } = useContentHeight( | |
79 | + getProps, | |
80 | + wrapperRef, | |
81 | + headerRef, | |
82 | + contentRef, | |
83 | + footerRef, | |
84 | + footerHeightRef | |
85 | + ); | |
86 | + | |
74 | 87 | const getClass = computed(() => { |
75 | 88 | return [ |
76 | 89 | prefixCls, |
... | ... | @@ -91,7 +104,8 @@ |
91 | 104 | if (!contentFullHeight) { |
92 | 105 | return { ...contentStyle }; |
93 | 106 | } |
94 | - const height = `${unref(pageHeight)}px`; | |
107 | + | |
108 | + const height = `${unref(contentHeight)}px`; | |
95 | 109 | return { |
96 | 110 | ...contentStyle, |
97 | 111 | minHeight: height, |
... | ... | @@ -111,9 +125,9 @@ |
111 | 125 | }); |
112 | 126 | |
113 | 127 | watch( |
114 | - () => [contentHeight?.value, getShowFooter.value, footerHeightRef.value], | |
128 | + () => [getShowFooter.value, footerHeightRef.value], | |
115 | 129 | () => { |
116 | - calcContentHeight(); | |
130 | + redoHeight(); | |
117 | 131 | }, |
118 | 132 | { |
119 | 133 | flush: 'post', |
... | ... | @@ -121,91 +135,16 @@ |
121 | 135 | } |
122 | 136 | ); |
123 | 137 | |
124 | - onMountedOrActivated(() => { | |
125 | - nextTick(() => { | |
126 | - calcContentHeight(); | |
127 | - }); | |
128 | - }); | |
129 | - | |
130 | - function calcContentHeight() { | |
131 | - if (!props.contentFullHeight) { | |
132 | - return; | |
133 | - } | |
134 | - //fix:in contentHeight mode: delay getting footer and header dom element to get the correct height | |
135 | - const footer = unref(footerRef); | |
136 | - const header = unref(headerRef); | |
137 | - footerHeight.value = 0; | |
138 | - const footerEl = footer?.$el; | |
139 | - | |
140 | - if (footerEl) { | |
141 | - footerHeight.value += footerEl?.offsetHeight ?? 0; | |
142 | - } | |
143 | - let headerHeight = 0; | |
144 | - const headerEl = header?.$el; | |
145 | - if (headerEl) { | |
146 | - headerHeight += headerEl?.offsetHeight ?? 0; | |
147 | - } | |
148 | - // fix:subtract content's marginTop and marginBottom value | |
149 | - let subtractHeight = 0; | |
150 | - const ZERO_PX = '0px'; | |
151 | - let marginBottom = ZERO_PX; | |
152 | - let marginTop = ZERO_PX; | |
153 | - const classElments = document.querySelectorAll(`.${prefixVar}-page-wrapper-content`); | |
154 | - if (classElments && classElments.length > 0) { | |
155 | - const contentEl = classElments[0]; | |
156 | - const cssStyle = getComputedStyle(contentEl); | |
157 | - marginBottom = cssStyle?.marginBottom ?? ZERO_PX; | |
158 | - marginTop = cssStyle?.marginTop ?? ZERO_PX; | |
159 | - } | |
160 | - if (marginBottom) { | |
161 | - const contentMarginBottom = Number(marginBottom.replace(/[^\d]/g, '')); | |
162 | - subtractHeight += contentMarginBottom; | |
163 | - } | |
164 | - if (marginTop) { | |
165 | - const contentMarginTop = Number(marginTop.replace(/[^\d]/g, '')); | |
166 | - subtractHeight += contentMarginTop; | |
167 | - } | |
168 | - | |
169 | - // fix: wrapper marginTop and marginBottom value | |
170 | - let wrapperSubtractHeight = 0; | |
171 | - let wrapperMarginBottom = ZERO_PX; | |
172 | - let wrapperMarginTop = ZERO_PX; | |
173 | - const wrapperClassElments = document.querySelectorAll(`.${prefixVar}-page-wrapper`); | |
174 | - if (wrapperClassElments && wrapperClassElments.length > 0) { | |
175 | - const contentEl = wrapperClassElments[0]; | |
176 | - const cssStyle = getComputedStyle(contentEl); | |
177 | - wrapperMarginBottom = cssStyle?.marginBottom ?? ZERO_PX; | |
178 | - wrapperMarginTop = cssStyle?.marginTop ?? ZERO_PX; | |
179 | - } | |
180 | - if (wrapperMarginBottom) { | |
181 | - const contentMarginBottom = Number(wrapperMarginBottom.replace(/[^\d]/g, '')); | |
182 | - wrapperSubtractHeight += contentMarginBottom; | |
183 | - } | |
184 | - if (wrapperMarginTop) { | |
185 | - const contentMarginTop = Number(wrapperMarginTop.replace(/[^\d]/g, '')); | |
186 | - wrapperSubtractHeight += contentMarginTop; | |
187 | - } | |
188 | - let height = | |
189 | - unref(contentHeight) - | |
190 | - unref(footerHeight) - | |
191 | - headerHeight - | |
192 | - subtractHeight - | |
193 | - wrapperSubtractHeight; | |
194 | - if (unref(getShowFooter)) { | |
195 | - height -= unref(footerHeightRef); | |
196 | - } | |
197 | - setPageHeight?.(height); | |
198 | - } | |
199 | - | |
200 | 138 | return { |
201 | 139 | getContentStyle, |
202 | - footerRef, | |
140 | + wrapperRef, | |
203 | 141 | headerRef, |
142 | + contentRef, | |
143 | + footerRef, | |
204 | 144 | getClass, |
205 | 145 | getHeaderSlots, |
206 | 146 | prefixCls, |
207 | 147 | getShowFooter, |
208 | - pageHeight, | |
209 | 148 | omit, |
210 | 149 | getContentClass, |
211 | 150 | }; | ... | ... |
src/components/Page/src/types.ts
0 → 100644
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
0 → 100644
1 | +import { ComputedRef, nextTick, Ref, ref, unref } from 'vue'; | |
2 | +import { WrapperProps } from './types'; | |
3 | +import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated'; | |
4 | +import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn'; | |
5 | +import { getViewportOffset } from '/@/utils/domUtils'; | |
6 | + | |
7 | +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), | |
14 | + offsetHeightRef: Ref<number> = ref(0) | |
15 | +) { | |
16 | + const contentHeight: Ref<Nullable<number>> = ref(null); | |
17 | + | |
18 | + const redoHeight = () => { | |
19 | + nextTick(() => { | |
20 | + calcContentHeight(); | |
21 | + }); | |
22 | + }; | |
23 | + | |
24 | + const subtractMargin = (element: HTMLElement | null | undefined): number => { | |
25 | + let subtractHeight = 0; | |
26 | + const ZERO_PX = '0px'; | |
27 | + let marginBottom = ZERO_PX; | |
28 | + let marginTop = ZERO_PX; | |
29 | + if (element) { | |
30 | + const cssStyle = getComputedStyle(element); | |
31 | + marginBottom = cssStyle?.marginBottom ?? ZERO_PX; | |
32 | + marginTop = cssStyle?.marginTop ?? ZERO_PX; | |
33 | + } | |
34 | + if (marginBottom) { | |
35 | + const contentMarginBottom = Number(marginBottom.replace(/[^\d]/g, '')); | |
36 | + subtractHeight += contentMarginBottom; | |
37 | + } | |
38 | + if (marginTop) { | |
39 | + const contentMarginTop = Number(marginTop.replace(/[^\d]/g, '')); | |
40 | + subtractHeight += contentMarginTop; | |
41 | + } | |
42 | + return subtractHeight; | |
43 | + }; | |
44 | + | |
45 | + const calcContentHeight = async () => { | |
46 | + const { contentFullHeight } = unref(propsRef); | |
47 | + if (!contentFullHeight) { | |
48 | + return; | |
49 | + } | |
50 | + // Add a delay to get the correct height | |
51 | + await nextTick(); | |
52 | + | |
53 | + const wrapperEl = unref(wrapperRef); | |
54 | + if (!wrapperEl) { | |
55 | + return; | |
56 | + } | |
57 | + const { bottomIncludeBody } = getViewportOffset(wrapperEl); | |
58 | + const headerHeight = unref(headerRef)?.$el.offsetHeight ?? 0; | |
59 | + const footerHeight = unref(footerRef)?.$el.offsetHeight ?? 0; | |
60 | + | |
61 | + // content's subtract | |
62 | + const substractHeight = subtractMargin(unref(contentRef)); | |
63 | + let height = | |
64 | + bottomIncludeBody - | |
65 | + unref(layoutFooterHeightRef) - | |
66 | + unref(offsetHeightRef) - | |
67 | + headerHeight - | |
68 | + footerHeight - | |
69 | + substractHeight; | |
70 | + | |
71 | + // fix: compensation height both layout's footer and page's footer was shown | |
72 | + if (unref(layoutFooterHeightRef) > 0 && footerHeight > 0) { | |
73 | + height += footerHeight; | |
74 | + } | |
75 | + | |
76 | + contentHeight.value = height; | |
77 | + }; | |
78 | + | |
79 | + onMountedOrActivated(() => { | |
80 | + nextTick(() => { | |
81 | + calcContentHeight(); | |
82 | + }); | |
83 | + }); | |
84 | + useWindowSizeFn( | |
85 | + () => { | |
86 | + calcContentHeight(); | |
87 | + }, | |
88 | + 50, | |
89 | + { immediate: true } | |
90 | + ); | |
91 | + | |
92 | + return { redoHeight, contentHeight }; | |
93 | +} | ... | ... |
src/layouts/default/content/useContentViewHeight.ts
... | ... | @@ -19,7 +19,7 @@ export function useContentViewHeight() { |
19 | 19 | const contentHeight = ref(window.innerHeight); |
20 | 20 | const pageHeight = ref(window.innerHeight); |
21 | 21 | const getViewHeight = computed(() => { |
22 | - return unref(contentHeight) - unref(headerHeightRef) || 0; | |
22 | + return unref(contentHeight) - unref(headerHeightRef) - unref(footerHeightRef) || 0; | |
23 | 23 | }); |
24 | 24 | |
25 | 25 | useWindowSizeFn( | ... | ... |