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

时小记,终有成。

相关推荐
竹林8181 天前
用 wagmi v2 + viem 监听链上事件,我踩了三天坑终于搞懂了实时日志与历史补全
javascript
Momo__1 天前
VueUse createReusableTemplate —— 单文件组件内的模板复用神器
前端·vue.js
无名氏同学1 天前
React 16-19 新特性
react.js
只一1 天前
😭从回调地狱到 async/await:一文打通 Ajax 与 JS 异步编程
javascript
程序员小富1 天前
我开源了一个开发者专属的智能 JSON 工具,得到了媳妇高度认可
前端·vue.js·后端
小小小小宇1 天前
程序员如何给 LLM 装工具以及看懂推理过程
前端
写代码的皮筏艇1 天前
React中的forwardRef
前端·react.js·面试
槑有老呆1 天前
花三个月工资请了个 AI 程序员,结果它连青岛啤酒股价都查不了
前端
风骏时光牛马1 天前
Verilog开发常见问题汇总解析
前端
子兮曰1 天前
AI Coding Method Map:一张图看懂 AI 编程的完整链路
前端·人工智能·后端