diff --git a/src/components/Modal/src/BasicModal.vue b/src/components/Modal/src/BasicModal.vue index 4e1a3996d93..b01ef5db3cb 100644 --- a/src/components/Modal/src/BasicModal.vue +++ b/src/components/Modal/src/BasicModal.vue @@ -84,6 +84,7 @@ 'ok', 'register', 'update:open', + 'fullscreen', ]); const attrs = useAttrs(); @@ -119,7 +120,11 @@ }; }); - const { handleFullScreen, getWrapClassName, fullScreenRef } = useFullScreen({ + const { + handleFullScreen: handleFullScreenInner, + getWrapClassName, + fullScreenRef, + } = useFullScreen({ modalWrapperRef, extHeightRef, wrapClassName: toRef(getMergeProps.value, 'wrapClassName'), @@ -229,4 +234,10 @@ e.stopPropagation(); handleFullScreen(e); } + + // 事件传递 + function handleFullScreen(e) { + handleFullScreenInner(e); + emit('fullscreen'); + } diff --git a/src/components/Table/src/hooks/useTableScroll.ts b/src/components/Table/src/hooks/useTableScroll.ts index d0f6ee6c338..6d54c5d702c 100644 --- a/src/components/Table/src/hooks/useTableScroll.ts +++ b/src/components/Table/src/hooks/useTableScroll.ts @@ -74,6 +74,12 @@ export function useTableScroll( let footerEl: HTMLElement | null; let bodyEl: HTMLElement | null; + /** + * table wrapper padding 的高度 + * @description 来自于 .vben-basic-table .ant-table-wrapper + */ + const tableWrapperPadding = 6; + function handleScrollBar(bodyEl: HTMLElement, tableEl: Element) { const hasScrollBarY = bodyEl.scrollHeight > bodyEl.clientHeight; const hasScrollBarX = bodyEl.scrollWidth > bodyEl.clientWidth; @@ -93,21 +99,33 @@ export function useTableScroll( } } + /** + * 计算分页器高度 + * @param tableEl table element + * @returns number + */ function caclPaginationHeight(tableEl: Element): number { const { pagination } = unref(propsRef); - // Pager height - let paginationHeight = 2; + + let paginationHeight = 0; if (!isBoolean(pagination)) { - paginationEl = tableEl.querySelector('.ant-pagination') as HTMLElement; + // 从 Dom 获取 + if (!paginationEl) { + paginationEl = tableEl.querySelector('.ant-pagination') as HTMLElement; + } if (paginationEl) { + // 分页 margin-top + const paginationElMarginTop = parseInt(getComputedStyle(paginationEl).marginTop); + // 分页高度 const offsetHeight = paginationEl.offsetHeight; - paginationHeight += offsetHeight || 0; + paginationHeight = offsetHeight + paginationElMarginTop; } else { - // TODO First fix 24 - paginationHeight += 24; + // 找不到分页组件,缺省给予默认分页 margin-top + 高度 + paginationHeight = 10 + 24; } } else { - paginationHeight = -8; + // 不显示分页,pagination 为 false 的时候 + paginationHeight = 0; } return paginationHeight; } @@ -134,43 +152,105 @@ export function useTableScroll( return headerHeight; } + /** + * 计算从表头一直到body底部的总高度 + * @param tableEl table element + * @param headEl table 页头 element + * @returns number + */ function calcBottomAndPaddingHeight(tableEl: Element, headEl: Element) { - const { pagination, isCanResizeParent, useSearchForm } = unref(propsRef); - // Table height from bottom height-custom offset - let paddingHeight = 30; + const { isCanResizeParent } = unref(propsRef); let bottomIncludeBody = 0; if (unref(wrapRef) && isCanResizeParent) { - const tablePadding = 12; - const formMargin = 16; - let paginationMargin = 10; + // 继承父元素高度 const wrapHeight = unref(wrapRef)?.offsetHeight ?? 0; let formHeight = unref(formRef)?.$el.offsetHeight ?? 0; if (formHeight) { - formHeight += formMargin; - } - if (isBoolean(pagination) && !pagination) { - paginationMargin = 0; + // 来自于 .vben-basic-table-form-container .ant-form 以及 .vben-basic-table-form-container + formHeight += 16 + 16 * 2; } - if (isBoolean(useSearchForm) && !useSearchForm) { - paddingHeight = 0; - } - - const headerCellHeight = - (tableEl.querySelector('.ant-table-title') as HTMLElement)?.offsetHeight ?? 0; - console.log(wrapHeight - formHeight - headerCellHeight - tablePadding - paginationMargin); - bottomIncludeBody = - wrapHeight - formHeight - headerCellHeight - tablePadding - paginationMargin; + bottomIncludeBody = wrapHeight - tableWrapperPadding - formHeight; } else { - // Table height from bottom + // 缺省 wrapRef 情况下 bottomIncludeBody = getViewportOffset(headEl).bottomIncludeBody; } - return { - paddingHeight, - bottomIncludeBody, - }; + return bottomIncludeBody; + } + + /** + * 计算 table 在 modal 内 modal 所占用的高度 + * @param tableEl table element + * @returns number + */ + function calcModalHeight(tableEl: Element) { + // 找一下 table 是否在 modal 内,获得 modal、wrap、footer,并考虑 fullscreen 的情况 + let modalEl: Nullable = null; + let modalWrapEl: Nullable = null; + let modalFooterEl: Nullable = null; + let modalElIterator: HTMLElement = tableEl.parentElement!; + let modalIsFullscreen = false; + while (modalElIterator !== document.body) { + if (modalElIterator.classList.contains('ant-modal')) { + modalEl = modalElIterator; + modalWrapEl = modalEl.parentElement; + modalFooterEl = modalElIterator.querySelector('.ant-modal-content>.ant-modal-footer'); + modalIsFullscreen = modalWrapEl?.classList.contains('fullscreen-modal') ?? false; + break; + } + modalElIterator = modalElIterator.parentElement!; + } + + if (modalEl) { + // table 在 modal 内 + + // modal top + const { top: modalTop = 0 } = modalEl ? getViewportOffset(modalEl) : {}; + + // 来自于 .ant-modal,非全屏为 24,全屏为 0 + const modalBottom = modalIsFullscreen ? 0 : 24; + + // modal footer 高度 + const modalFooterHeight = modalFooterEl?.offsetHeight ?? 0; + + // modal footer 边距,来自于 .ant-modal .ant-modal-footer + const modalFooterMarginTop = modalFooterEl + ? modalIsFullscreen + ? 0 + : parseInt(getComputedStyle(modalFooterEl).marginTop) + : 0; + + // 来自于 .ant-modal .ant-modal-body > .scrollbar + const modalScrollBarHeight = 14; + + return ( + (modalTop > modalBottom ? modalTop : modalBottom) + + modalFooterHeight + + modalFooterMarginTop + + modalScrollBarHeight + ); + } + + // table 不住 modal 内 + return 0; + } + + /** + * 根据样式返回一些间距高度 + * @returns number + */ + function getMarginPaddingHeight() { + const { isCanResizeParent } = unref(propsRef); + + if (unref(wrapRef) && isCanResizeParent) { + // 继承父元素高度 + return tableWrapperPadding; + } + return ( + tableWrapperPadding + 16 // 来自于 .vben-basic-table-form-container 或是 .p-4 + ); } async function calcTableHeight() { @@ -204,18 +284,29 @@ export function useTableScroll( const paginationHeight = caclPaginationHeight(tableEl); const footerHeight = caclFooterHeight(tableEl); const headerHeight = calcHeaderHeight(headEl); - const { paddingHeight, bottomIncludeBody } = calcBottomAndPaddingHeight(tableEl, headEl); + const bottomIncludeBody = calcBottomAndPaddingHeight(tableEl, headEl); + + const modalHeight = calcModalHeight(tableEl); - let height = + const marginPaddingHeight = getMarginPaddingHeight(); + + // Math.floor 宁愿小1px,也不溢出 + let height = Math.floor( bottomIncludeBody - - (resizeHeightOffset || 0) - - paddingHeight - - paginationHeight - - footerHeight - - headerHeight - - (getShowFooter.value ? layoutFooterHeight : 0) - - // 取高度ceil值 - 1; + (resizeHeightOffset || 0) - + paginationHeight - + footerHeight - + headerHeight - + // 弹窗(如果有)相关高度 + modalHeight - + // 页面 footer 高度(非弹窗的时候) + (getShowFooter.value && modalHeight <= 0 ? layoutFooterHeight : 0) - + // 样式间距高度 + marginPaddingHeight - + // 预留非整数高度溢出(如实际高度为100.5,offsetHeight 的值为101) + 1, + ); + height = (height > maxHeight! ? (maxHeight as number) : height) ?? height; setHeight(height); diff --git a/src/utils/domUtils.ts b/src/utils/domUtils.ts index bc80613bf93..3b3178fe5f3 100644 --- a/src/utils/domUtils.ts +++ b/src/utils/domUtils.ts @@ -2,11 +2,29 @@ import type { FunctionArgs } from '@vueuse/core'; import { upperFirst } from 'lodash-es'; export interface ViewportOffsetResult { + /** + * 元素左边距离 body 左边的距离(和 getBoundingClientRect 的 left 一样) + */ left: number; + /** + * 元素顶边距离 body 顶边的距离(和 getBoundingClientRect 的 top 一样) + */ top: number; + /** + * 元素右边距离 body 右边的距离 + */ right: number; + /** + * 元素底边距离 body 底边的距离 + */ bottom: number; + /** + * 内容宽度 + 计算后的 right + */ rightIncludeBody: number; + /** + * 内容高度 + 计算后的 bottom + */ bottomIncludeBody: number; } diff --git a/src/views/demo/comp/modal/Modal5.vue b/src/views/demo/comp/modal/Modal5.vue new file mode 100644 index 00000000000..f6cf1f14f8b --- /dev/null +++ b/src/views/demo/comp/modal/Modal5.vue @@ -0,0 +1,46 @@ + + diff --git a/src/views/demo/comp/modal/index.vue b/src/views/demo/comp/modal/index.vue index b938425f728..f63635c622b 100644 --- a/src/views/demo/comp/modal/index.vue +++ b/src/views/demo/comp/modal/index.vue @@ -11,8 +11,12 @@ 打开弹窗 + - 打开弹窗 + + 打开弹窗 + 打开弹窗(BasicTable) + 打开弹窗并传递数据 @@ -42,6 +46,7 @@ +