Commit 9de6ac11197d9bb5c873531c51d393f16926250d
Committed by
GitHub
1 parent
95c16a5d
refactor(contentHeight): 重构,将PageWrapper的useContentHeight抽象为公共hook,满足自定义扩展组件时的自动…
…高度计算需求。 同时优化高度计算算法。 (#826)
Showing
3 changed files
with
95 additions
and
58 deletions
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 | } |