基于 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
};

时小记,终有成。

相关推荐
用户0595401744619 分钟前
把AI对话记忆存储测试从手工改成Playwright+pytest,覆盖率从20%提到96%,回归时间缩短90%
前端·css
kyriewen26 分钟前
别再这样写TypeScript了——Code Review中最常见的8个反模式
前端·javascript·typescript
名字还没想好☜32 分钟前
Next.js ‘use client‘ 到底加在哪:Server/Client Components 边界与常见报错
开发语言·前端·javascript·react·next.js
午安~婉1 小时前
GitHub Token/ GitHub Stats统计显示图异常
前端·github·vercel·github token
码云之上1 小时前
AI Agent 工程化总览篇:从 Prompt 到 Harness
前端·人工智能
2501_926978331 小时前
以说明书 DNA 为模板——完整 AGI 的结构图景
前端·人工智能·经验分享·笔记·ai写作
IT_陈寒1 小时前
Vite静态资源引用这个坑我踩得有点疼
前端·人工智能·后端
程序员黑豆2 小时前
鸿蒙应用开发:AttributeModifier 使用教程
前端·harmonyos
codeniu2 小时前
TRAE Work 实战 | 从"有个想法"到线上Demo,我全程只靠对话就完成了
前端·trae
妙码生花2 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(五十):增加管理员角色组管理
前端·后端·ai编程