UmiJS PC 项目一:ProTable 高度设置
UmiJS PC 项目二:动态路由配置
UmiJS PC 项目三:TypeScript 使用
ProTable 来自 @ant-design/pro-components,是一款强大的高级表格组件。它高度可配置,能轻松定义列与样式。具备强大数据处理能力,支持动态请求。集成搜索、过滤、分页等功能,拥有插件式架构,响应式设计,适用于后台管理、数据分析等多种场景。
遇到问题
虽然 ProTable 具备强大的配置功能,但在实际使用过程中,表格的高度设置问题对用户交互产生了不良影响。
其一,若不配置高度,分页栏会被挤至页面最底部,极大地降低了操作便利性。
其二,尽管 ProTable 提供了 scroll
配置项,但对于适配多尺寸屏幕以及处理筛选栏展开和收起的场景,文档与示例均未给出解决方案。若设置不合理,极易出现双滚动条、页面底部留白过多等问题。
解决方案
经过不断实践找到一种相对比较合适方案,分享出来交流学习。
自定义hook,通过监听 筛选栏、标题栏、表格区域高度变化,实现动态计算高度。
存在问题 querySelector 硬编码问题
代码实现
import { MutableRefObject, useEffect, useState } from "react";
type ParentElement = Pick<HTMLElement, "querySelector" | "querySelectorAll">;
export function useProTableSizeObserver<T>(
actionRef: MutableRefObject<T>,
options?: {
wrapId?: string;
totalHeight?: string;
bottom?: number;
}
) {
let { totalHeight, wrapId, bottom } = options ?? {};
totalHeight ??= "100vh";
bottom ??= 32;
const [searchH, setSearchH] = useState<number>(80);
const getWrapSelector = (selector: string) =>
wrapId ? `#${wrapId} .ant-pro-table ${selector}` : selector;
const querySelector = (selector: string) => {
let parentElement: ParentElement = document;
if (wrapId) {
parentElement = document.getElementById(wrapId) as ParentElement;
}
let nodeList = Array.from(
parentElement.querySelectorAll<HTMLDivElement>(getWrapSelector(selector))
);
if (nodeList.length > 0) {
return nodeList[nodeList.length - 1];
}
};
useEffect(() => {
if (!actionRef.current) return;
let observer: ResizeObserver | undefined;
let tableSearch: HTMLDivElement | undefined;
let tableHeader: HTMLDivElement | undefined;
let tableCardBody: HTMLDivElement | undefined;
setTimeout(() => {
tableSearch = querySelector(".ant-pro-table-search");
tableHeader = querySelector(".ant-table-header");
tableCardBody = querySelector(".ant-pro-card-body")!;
observer = new ResizeObserver(() => {
if (tableHeader && tableCardBody)
calcTableHeight(tableHeader, tableCardBody);
});
if (tableSearch) observer.observe(tableSearch);
if (tableHeader) observer.observe(tableHeader);
if (tableCardBody) observer.observe(tableCardBody);
}, 100);
return () => {
if (observer) {
if (tableSearch) observer.unobserve(tableSearch);
if (tableHeader) observer.unobserve(tableHeader);
if (tableCardBody) observer.unobserve(tableCardBody);
}
};
}, [actionRef]);
function calcTableHeight(
tableHeader: HTMLDivElement,
tableCardBody: HTMLDivElement
) {
let otherH = 0;
const { bottom } = tableHeader.getBoundingClientRect();
const { paddingBlockEnd } = getComputedStyle(tableCardBody, null);
otherH = bottom + parseInt(paddingBlockEnd);
const tablePagination = querySelector(".ant-table-pagination");
if (tablePagination) {
otherH += tablePagination?.offsetHeight ?? 24;
const { marginTop } = getComputedStyle(tablePagination, null);
otherH += parseInt(marginTop);
}
setSearchH(otherH);
}
return {
// 冗余高度: 4px
tableScrollY: `calc(${totalHeight} - ${bottom}px - ${searchH}px - 4px)`,
};
}
参数介绍
/** ProTable actionRef */
actionRef: React.MutableRefObject<ActionType>
options?: {
/** 一个页面存在多个table场景 */
wrapId?: string;
/** 屏幕高度:默认 100vh */
totalHeight?: string;
/** table 距离底部高度:默认 32px */
bottom?: number;
}
使用