UmiJS PC 项目一:ProTable 高度设置

418 阅读2分钟

UmiJS PC 项目一:ProTable 高度设置
UmiJS PC 项目二:动态路由配置
UmiJS PC 项目三:TypeScript 使用

ProTable 来自 @ant-design/pro-components,是一款强大的高级表格组件。它高度可配置,能轻松定义列与样式。具备强大数据处理能力,支持动态请求。集成搜索、过滤、分页等功能,拥有插件式架构,响应式设计,适用于后台管理、数据分析等多种场景。

image.png

遇到问题

虽然 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;
}

使用

image.png

效果展示

EasyGIF-1737621590522.gif

源码地址

GitHub umi-template