基于 antd design 组件库的动态表头封装

效果:

通过生成树形菜单控制表头动态显示

1)功能函数文件

javascript 复制代码
import { TreeDataNode } from "antd";
import { ColumnsDataType } from "./type";

/**
 * 过滤表格头数据到 tree 组件 并且过滤掉不需要在 tree 组件需要展示的节点
 * @param  { Array<ColumnsDataType> }   columnsInfo  表头原始数据
 * @param  { string[] }                 nodesOnTheTreeAreNotDisplayed  不需要展示的节点 key
 * @param  { string[] }                 needDisabledItem 需要禁用掉的元素
 * @return { TreeDataNode[] }
 */
export const filterTableColumnsToTree = (
  columnsInfo: Array<ColumnsDataType>,
  nodesOnTheTreeAreNotDisplayed: string[],
  needDisabledItem: string[]
): TreeDataNode[] => {
  return columnsInfo.reduce((acc, item) => {
    if (!nodesOnTheTreeAreNotDisplayed.includes(item.key)) {
      let newItem: TreeDataNode = { key: item.key, title: item.title };

      if (needDisabledItem.includes(String(newItem.key))) {
        newItem.disabled = true;
      }

      if (item.children && item.children.length > 0) {
        const filteredChildren = filterTableColumnsToTree(
          item.children,
          nodesOnTheTreeAreNotDisplayed,
          needDisabledItem
        );
        if (filteredChildren.length > 0) {
          newItem.children = filteredChildren;
        }
      }
      acc.push(newItem);
    }
    return acc;
  }, [] as TreeDataNode[]);
};

/**
 * 判断树节点是否已经被选中,添加 hidden 属性为真显示对应表头
 * @param  { React.Key[] }         currentCheckTreeKeys 当前被选中的树节点 key
 * @param  { ColumnsDataType[] }   originalTableColumns 原先的表头数据
 * @param  { React.Key[] }         nodesOnTheTreeAreNotDisplayed 不需要在 tree 组件中进行操作的节点数据
 * @return { ColumnsDataType[] }   重新添加过 hidden 属性的表头数据
 */
export const filterTheHeadDataAccordingToTheTreeNode = (
  currentCheckTreeKeys: React.Key[],
  originalTableColumns: ColumnsDataType[],
  nodesOnTheTreeAreNotDisplayed: React.Key[]
): ColumnsDataType[] => {
  const updatedColumns = originalTableColumns.map((category) => {
    // 如果节点没有子节点,则根据当前节点的选中状态来显示或隐藏对应的表头
    if (!category.children || category.children.length === 0) {
      category.hidden = !currentCheckTreeKeys.includes(category.key);
    } else {
      // 检查是否为需要默认显示的节点,如果是,则将其 hidden 属性设置为 false
      if (nodesOnTheTreeAreNotDisplayed.includes(category.key)) {
        category.hidden = false;
      }
      // 如果该节点有子节点,递归处理子节点
      category.children = processChildren(
        category.children,
        currentCheckTreeKeys
      );
      // 通过子节点的 hidden 状态决定父节点的 hidden 状态
      category.hidden = category.children.every((child) => child.hidden);
    }
    return category;
  });

  return updatedColumns;
};

/**
 * 递归处理所有层级的 children,并根据需要添加父级及其所有子级到表头
 * @param   { ColumnsDataType[] }     children 表头子节点数组
 * @param   { React.Key[] }           currentCheckTreeKeys 当前被选中的树节点的 key 数组
 * @return  { ColumnsDataType[] }     处理后的表头子节点数组
 */
const processChildren = (
  children: ColumnsDataType[],
  currentCheckTreeKeys: React.Key[]
): ColumnsDataType[] => {
  return children.map((child) => {
    // 如果子节点还有自己的 children,递归处理
    if (child.children && child.children.length > 0) {
      child.children = processChildren(child.children, currentCheckTreeKeys);
      // 通过子节点的 hidden 状态决定父节点的 hidden 状态
      child.hidden = child.children.every((ch) => ch.hidden);
    } else {
      // 没有更深层次的 children,直接根据当前节点的选中状态更新 hidden
      child.hidden = !currentCheckTreeKeys.includes(child.key);
    }
    return child;
  });
};

2)index.tsx 文件代码

javascript 复制代码
import { SettingOutlined } from "@ant-design/icons";
import { Popover, Tree } from "antd";
import { useEffect, useState } from "react";
import {
  filterTableColumnsToTree,
  filterTheHeadDataAccordingToTheTreeNode,
} from "./function";
import { TreeComponentType } from "./type";

const DynamicTableHeader = ({
  treeDataInfo,
  updataColumns,
  defaultSelected,
  needDisabledItem,
  nodesOnTheTreeAreNotDisplayed,
  getNewColumns,
}: TreeComponentType) => {
  //

  // 当前选中的所有 key
  const [checkValue, setCheckValue] = useState<React.Key[]>(
    defaultSelected ?? []
  );

  /**
   * tree 组件勾选复选框事件
   * @param  { React.Key[] }   value 获取到勾选的 key
   * @return { void }
   */
  const treeCheck = (value: React.Key[]): void => {
    setCheckValue(value);
  };

  /**
   * tree 组件点击文字选择
   * @param  { React.Key[] }   selectedKeysValue 点击文字获取到对应的 key
   * @return { void }
   */
  const treeSelect = (selectedKeysValue: React.Key[]): void => {
    let copyCheckedKeys = [...checkValue];
    if (!checkValue.includes(selectedKeysValue[0])) {
      copyCheckedKeys.push(selectedKeysValue[0]);
      setCheckValue(copyCheckedKeys);
    } else {
      copyCheckedKeys.splice(copyCheckedKeys.indexOf(selectedKeysValue[0]), 1);
      setCheckValue(copyCheckedKeys);
    }
  };

  useEffect(() => {
    let newColumns = filterTheHeadDataAccordingToTheTreeNode(
      checkValue,
      treeDataInfo,
      nodesOnTheTreeAreNotDisplayed ?? []
    );
    getNewColumns(newColumns);
  }, [updataColumns, checkValue]);

  useEffect(() => {
    setCheckValue(defaultSelected);
  }, [updataColumns]);

  const treeComponent = (
    <Tree
      checkable
      selectable={false}
      checkedKeys={checkValue}
      defaultExpandedKeys={defaultSelected}
      onCheck={treeCheck as any}
      onSelect={treeSelect}
      treeData={filterTableColumnsToTree(
        treeDataInfo,
        nodesOnTheTreeAreNotDisplayed ?? [],
        needDisabledItem
      )}
    />
  );

  return (
    <Popover
      content={
        <div
          style={{
            width: 250,
            height: 250,
            overflow: "scroll",
            marginTop: "20px",
          }}
        >
          {treeComponent}
        </div>
      }
      title="........"
    >
      <SettingOutlined style={{ fontSize: "16px", color: "#3A5E99" }} />
    </Popover>
  );
};

export default DynamicTableHeader;

3)TS 类型文件

javascript 复制代码
export type ColumnsDataType = {
  key: string;
  title: string;
  dataIndex: string;
  hidden: boolean;
  align?: string;
  fixed?: string;
  ellipsis?: {
    showTitle?: boolean;
  };
  width?: number;
  height?: number;
  children?: ColumnsDataType[];
  render?: (params1: string, record: any) => void;
  renderFormItem?: any;
};

export type TreeComponentType = {
  needDisabledItem: string[];
  updataColumns: number; // 需要操作表格内部数据单并非弹框处理 【 恢复表头到未操作之前 】
  defaultSelected: React.Key[]; // 需要默认选中的树节点
  treeDataInfo: TableColumnsType<ColumnsDataType>; // 表格头的原始数据信息
  nodesOnTheTreeAreNotDisplayed?: string[]; // 不需要在树形组件中显示的节点
  getNewColumns: (params: ColumnsDataType[]) => void; // 获取新生成的表头 columns
};

时小记,终有成。

相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅7 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment7 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
爱敲代码的小鱼7 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax