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 | 42 | import { propTypes } from '/@/utils/propTypes'; |
43 | 43 | import { omit } from 'lodash-es'; |
44 | 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 | 47 | export default defineComponent({ |
50 | 48 | name: 'PageWrapper', |
... | ... | @@ -64,25 +62,23 @@ |
64 | 62 | fixedHeight: propTypes.bool, |
65 | 63 | }, |
66 | 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 | 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 | 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 | 83 | const getClass = computed(() => { |
88 | 84 | return [ |
... | ... | @@ -125,7 +121,7 @@ |
125 | 121 | }); |
126 | 122 | |
127 | 123 | watch( |
128 | - () => [getShowFooter.value, footerHeightRef.value], | |
124 | + () => [getShowFooter.value], | |
129 | 125 | () => { |
130 | 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 | 2 | import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated'; |
4 | 3 | import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn'; |
4 | +import { useLayoutHeight } from '/@/layouts/default/content/useContentViewHeight'; | |
5 | 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 | 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 | 30 | offsetHeightRef: Ref<number> = ref(0) |
15 | 31 | ) { |
16 | 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 | 43 | nextTick(() => { |
20 | 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 | 49 | let subtractHeight = 0; |
26 | 50 | const ZERO_PX = '0px'; |
27 | 51 | let marginBottom = ZERO_PX; |
... | ... | @@ -40,41 +64,61 @@ export function useContentHeight( |
40 | 64 | subtractHeight += contentMarginTop; |
41 | 65 | } |
42 | 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 | 78 | return; |
49 | 79 | } |
50 | 80 | // Add a delay to get the correct height |
51 | 81 | await nextTick(); |
52 | 82 | |
53 | - const wrapperEl = unref(wrapperRef); | |
83 | + const wrapperEl = getEl(unref(anchorRef)); | |
54 | 84 | if (!wrapperEl) { |
55 | 85 | return; |
56 | 86 | } |
57 | 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 | 101 | let height = |
64 | 102 | bottomIncludeBody - |
65 | 103 | unref(layoutFooterHeightRef) - |
66 | 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 | 120 | contentHeight.value = height; |
77 | - }; | |
121 | + } | |
78 | 122 | |
79 | 123 | onMountedOrActivated(() => { |
80 | 124 | nextTick(() => { |
... | ... | @@ -88,6 +132,16 @@ export function useContentHeight( |
88 | 132 | 50, |
89 | 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 | } | ... | ... |