Files
JeecgBoot/jeecgboot-vue3/src/components/Table/src/hooks/useTableScroll.ts

236 lines
8.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { BasicTableProps, TableRowSelection, BasicColumn } from '../types/table';
import type { Ref, ComputedRef, Slots } from 'vue';
import { computed, unref, ref, nextTick, watch } from 'vue';
import { getViewportOffset } from '/@/utils/domUtils';
import { isBoolean } from '/@/utils/is';
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
import { useModalContext } from '/@/components/Modal';
import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
import { useDebounceFn } from '@vueuse/core';
import componentSetting from '/@/settings/componentSetting';
export function useTableScroll(
propsRef: ComputedRef<BasicTableProps>,
tableElRef: Ref<ComponentRef>,
columnsRef: ComputedRef<BasicColumn[]>,
rowSelectionRef: ComputedRef<TableRowSelection<any> | null>,
getDataSourceRef: ComputedRef<Recordable[]>,
slots: Slots,
getPaginationInfo: ComputedRef<any>
) {
const tableHeightRef: Ref<Nullable<number>> = ref(null);
const modalFn = useModalContext();
// Greater than animation time 280
const debounceRedoHeight = useDebounceFn(redoHeight, 100);
const getCanResize = computed(() => {
const { canResize, scroll } = unref(propsRef);
return canResize && !(scroll || {}).y;
});
watch(
() => [unref(getCanResize), unref(getDataSourceRef)?.length],
() => {
debounceRedoHeight();
},
{
flush: 'post',
}
);
function redoHeight() {
nextTick(() => {
calcTableHeight();
});
}
function setHeight(heigh: number) {
tableHeightRef.value = heigh;
// Solve the problem of modal adaptive height calculation when the form is placed in the modal
modalFn?.redoModalHeight?.();
}
// No need to repeat queries
let paginationEl: HTMLElement | null;
let footerEl: HTMLElement | null;
let bodyEl: HTMLElement | null;
async function calcTableHeight() {
const { resizeHeightOffset, pagination, maxHeight, minHeight } = unref(propsRef);
const tableData = unref(getDataSourceRef);
const table = unref(tableElRef);
if (!table) return;
const tableEl: Element = table.$el;
if (!tableEl) return;
if (!bodyEl) {
// 代码逻辑说明: issues/355 前端-jeecgboot-vue3 3.4.4版本,BasicTable高度自适应功能失效,设置BasicTable组件maxHeight失效; 原因已找到,请看详情
bodyEl = tableEl.querySelector('.ant-table-tbody');
if (!bodyEl) return;
}
const hasScrollBarY = bodyEl.scrollHeight > bodyEl.clientHeight;
const hasScrollBarX = bodyEl.scrollWidth > bodyEl.clientWidth;
if (hasScrollBarY) {
tableEl.classList.contains('hide-scrollbar-y') && tableEl.classList.remove('hide-scrollbar-y');
} else {
!tableEl.classList.contains('hide-scrollbar-y') && tableEl.classList.add('hide-scrollbar-y');
}
if (hasScrollBarX) {
tableEl.classList.contains('hide-scrollbar-x') && tableEl.classList.remove('hide-scrollbar-x');
} else {
!tableEl.classList.contains('hide-scrollbar-x') && tableEl.classList.add('hide-scrollbar-x');
}
bodyEl!.style.height = 'unset';
if (!unref(getCanResize) || ( !tableData || tableData.length === 0)) return;
await nextTick();
//Add a delay to get the correct bottomIncludeBody paginationHeight footerHeight headerHeight
const headEl = tableEl.querySelector('.ant-table-thead');
if (!headEl) return;
// Table height from bottom
const { bottomIncludeBody } = getViewportOffset(headEl);
// Table height from bottom height-custom offset
const paddingHeight = 32;
// Pager height
let paginationHeight = 2;
// 【issues/9217】当配置了pagination: true时,BasicTable组件自适应高度异常
if (pagination !== false) {
paginationEl = tableEl.querySelector('.ant-pagination') as HTMLElement;
if (paginationEl) {
const offsetHeight = paginationEl.offsetHeight;
paginationHeight += offsetHeight || 0;
} else {
// TODO First fix 24
paginationHeight += 24;
}
} else {
paginationHeight = -8;
}
let footerHeight = 0;
// 代码逻辑说明: 【issues/1137】BasicTable自适应高度计算没有减去尾部高度
footerEl = tableEl.querySelector('.ant-table-footer');
if (footerEl) {
const offsetHeight = footerEl.offsetHeight;
footerHeight = offsetHeight || 0;
}
let headerHeight = 0;
if (headEl) {
headerHeight = (headEl as HTMLElement).offsetHeight;
}
let height = bottomIncludeBody - (resizeHeightOffset || 0) - paddingHeight - paginationHeight - footerHeight - headerHeight;
// update-begin--author:liaozhiyang---date:20240603---for【TV360X-861】列表查询区域不可往上滚动
// 10+6(外层边距padding:10 + 内层padding-bottom:6)
height -= 16;
// 代码逻辑说明: 【issues/8880】BasicTable组件在modal中适应高度
try {
// 当BasicTable在BasicModal容器中时,扣减容器底部高度
const modalFooter = tableEl.closest('.ant-modal-root')?.querySelector('.ant-modal-footer');
if (modalFooter) {
const { bottomIncludeBody: modalFooterHeight } = getViewportOffset(modalFooter);
height = height - modalFooterHeight;
}
} catch (e) {}
height = (height < minHeight! ? (minHeight as number) : height) ?? height;
height = (height > maxHeight! ? (maxHeight as number) : height) ?? height;
setHeight(height);
bodyEl!.style.height = `${height}px`;
// update-begin--author:liaozhiyang---date:20240609---for【issues/8374】分页始终显示在底部
nextTick(() => {
if (maxHeight === undefined) {
if (unref(getPaginationInfo) && unref(getDataSourceRef).length) {
const pageSize = unref(getPaginationInfo)?.pageSize;
const current = unref(getPaginationInfo)?.current;
const total = unref(getPaginationInfo)?.total;
const tableBody = tableEl.querySelector('.ant-table-body') as HTMLElement;
const tr = tableEl.querySelector('.ant-table-tbody')?.children ?? [];
const lastrEl = tr[tr.length - 1] as HTMLElement;
const trHeight = lastrEl.offsetHeight;
const dataHeight = trHeight * pageSize;
if (tableBody && lastrEl) {
// table是否隐藏隐藏的table不能吸底
const isTableBodyHide = tableBody.offsetHeight == 0 && tableBody.offsetWidth == 0;
if (isTableBodyHide) {
return;
}
if (current === 1 && pageSize > unref(getDataSourceRef).length && total <= pageSize) {
tableBody.style.height = `${height}px`;
} else {
tableBody.style.height = `${dataHeight < height ? dataHeight : height}px`;
}
}
}
}
});
// update-end--author:liaozhiyang---date:20240609---for【issues/8374】分页始终显示在底部
}
useWindowSizeFn(calcTableHeight, 280);
onMountedOrActivated(() => {
calcTableHeight();
nextTick(() => {
debounceRedoHeight();
});
});
const getScrollX = computed(() => {
let width = 0;
// if (unref(rowSelectionRef)) {
// width += 60;
// }
// 代码逻辑说明: 【issues/5411】BasicTable 配置maxColumnWidth 未生效
const { maxColumnWidth } = unref(propsRef);
// TODO props ?? 0;
const NORMAL_WIDTH = maxColumnWidth ?? 150;
// date-begin--author:liaozhiyang---date:20250716---for【QQYUN-13122】有数十个字段时只展示2个字段其余字段为ifShow:false会有滚动条
const columns = unref(columnsRef).filter((item) => !(item.defaultHidden == true || item.ifShow == false))
// date-end--author:liaozhiyang---date:20250716---for【QQYUN-13122】有数十个字段时只展示2个字段其余字段为ifShow:false会有滚动条
columns.forEach((item) => {
width += Number.parseInt(item.width as string) || 0;
});
const unsetWidthColumns = columns.filter((item) => !Reflect.has(item, 'width'));
const len = unsetWidthColumns.length;
if (len !== 0) {
width += len * NORMAL_WIDTH;
}
// 代码逻辑说明: 【TV360X-116】内嵌风格字段较多时表格错位
if (slots.expandedRowRender) {
width += propsRef.value.expandColumnWidth;
}
const table = unref(tableElRef);
const tableWidth = table?.$el?.offsetWidth ?? 0;
return tableWidth > width ? '100%' : width;
});
const getScrollRef = computed(() => {
const tableHeight = unref(tableHeightRef);
const { canResize, scroll } = unref(propsRef);
const { table } = componentSetting;
return {
x: unref(getScrollX),
y: canResize ? tableHeight : null,
// 代码逻辑说明: 【issues/1188】BasicTable加上scrollToFirstRowOnChange类型定义
scrollToFirstRowOnChange: table.scrollToFirstRowOnChange,
...scroll,
};
});
return { getScrollRef, redoHeight };
}