当前位置: 首页 > article >正文

react中hook封装一个table组件 与 useColumns组件

目录

  • 1:react中hook封装一个table组件
    • 依赖
    • CommonTable / index.tsx
    • 使用组件
    • 效果
  • 2:useColumns组件
    • useColumns.tsx
    • 使用

1:react中hook封装一个table组件

依赖

cnpm i react-resizable --save
cnpm i ahooks
cnpm i --save-dev @types/react-resizable

CommonTable / index.tsx

import React, { useEffect, useMemo, useRef, useState, useCallback } from 'react';
import { createUseStyles } from 'react-jss';
import { Resizable } from 'react-resizable';
import { ColumnType } from 'antd/lib/table';
import { Table, Button } from 'antd';
import type { ButtonProps, TableProps } from 'antd';
import { useSize } from 'ahooks';

export interface ICommonTableProps<RecordType> extends TableProps<RecordType> {
  onCreate?: () => void;
  onEdit?: () => void;
  deleteBtn?: {
    props?: ButtonProps;
    onDelete?: () => void;
  };
  isDrag?: boolean; // 控制table是否展示 可拖拽的表头
}
const useCommonTableStyles = createUseStyles({
  wrapper: {
    background: '#fff',
    marginTop: '12px',
    padding: '12px 12px'
  },
  header: {
    display: 'flex',
    marginTop: '8px',
    marginBottom: '20px'
  },
  tablActions: {
    display: 'flex',
    flex: 1,
    justifyContent: 'flex-end',
    alignItems: 'center'
  },
  headerBtn: {
    marginLeft: '16px'
  },
  resizableHandle : {
    position: 'absolute',
    right: '-5px',
    bottom: 0,
    zIndex: 1,
    width: '10px',
    height: '100%',
    cursor: 'col-resize'
  }
});

// 表头拖拽组件
const ResizableTitle = (props: any ) => {
const { onResize, width, ...restProps } = props
  const classes = useCommonTableStyles();
  if (!width) { return (<th {...restProps} />) };
  return (
    <Resizable
      width={parseInt(width)}
      height={0}
      handle={
        <span className={classes.resizableHandle} onClick={e => { e.stopPropagation() }} />
      }
      onResize={onResize}
      draggableOpts={{ enableUserSelectHack: false }}
    >
      <th {...restProps} style={{ ...restProps?.style, userSelect: 'none' }} />
    </Resizable>
  );
};

export const CommonTable = <RecordType extends Record<string, any> = any>(
  props: ICommonTableProps<RecordType>
) => {
  const { onCreate, onEdit, deleteBtn, isDrag = true } = props;
  const classes = useCommonTableStyles();
  const wrapperRef = useRef<HTMLDivElement>(null);
  const bodyRef = useRef(document.body);
  const size = useSize(bodyRef);
  const [scroll, setScroll] = useState<TableProps<any>['scroll']>({ x: 'max-content' });

  const [rescolumns, setResColumns] = useState<ColumnType<RecordType>[]>(props.columns || []);

  const handleResize = (index: number): ((_: any, Resize: { size: { width: any } }) => void) => {

    return (_: any, Resize: { size: { width: any; }; }) => {
      const temp = [...rescolumns];
      temp[index] = { ...temp[index], width: Resize.size.width };
      setResColumns(temp);
    };
  };

  // 把 columns 用 map 重组为支持可拖拽的cell
  const columnsMap: any[] = useMemo(() => {
    return (
      rescolumns?.map((column:any,index:any) => ({
        ...column,
        onHeaderCell: (col: { width: any; }) => ({ width: col.width, onResize: handleResize(index) }),
        title: column.title,
      })) || []
    );
  }, [rescolumns]);

  useEffect(() => {
    if (wrapperRef.current) {
      const { top } = wrapperRef.current?.getBoundingClientRect();
      setScroll({
        x: 'max-content',
        y: innerHeight - top - 210
      });
    }
  }, [wrapperRef, size]);

  return (
    <div className={classes.wrapper} ref={wrapperRef}>
      <div className={classes.header}>
        <div className={classes.tablActions}>
          {onCreate && (
            <Button className={classes.headerBtn} type='primary' onClick={onCreate}>
              新增
            </Button>
          )}
          {onEdit && (
            <Button className={classes.headerBtn} type='default'>
              编辑
            </Button>
          )}
          {deleteBtn && (
            <Button
              {...deleteBtn.props}
              className={classes.headerBtn}
              type='default'
              danger
              onClick={deleteBtn.onDelete}
            >
              删除
            </Button>
          )}
        </div>
      </div>
      <Table 
        scroll={scroll} 
        {...props} 
        components={isDrag ? { header: { cell: ResizableTitle } } : undefined}
        columns={columnsMap}
      />
    </div>
  );
};

使用组件

import { createUseStyles } from 'react-jss';
import type { TableRowSelection } from 'antd/lib/table/interface';
import type { ColumnsType } from 'antd/lib/table';
import { useEffect, useMemo, useState, useRef } from 'react';
import { CommonTable } from '../components/CommonTable/index';

const useStyles = createUseStyles({
  table: {
    background: '#fff',
    padding: '16px',
    marginTop: '16px',
    width: '100%',
  },
  textBtn: {
    color: '#0068FF',
    cursor: 'pointer',
    userSelect: 'none',
  },
});
const TablePage = () => {
  const [tableData, setTableData] = useState<any>([]);
  const [currentPage, setCurrentPage] = useState<number>(1);
  const [currentSize, setCurrentSize] = useState<number>(20);
  const classes = useStyles();
  const [tableLoading, setTableLoading] = useState(false);
  const [tableDataTotal, setTableDataTotal] = useState(0);
  const [selectedRow, setSelectedRow] = useState([] as any); //控制表格是否已选

  useEffect(() => {
    const resTable = [
      { id: 1, type: 1, status: '草稿' },
      { id: 2, type: 0, status: '已完成' },
      { id: 3, type: 1, status: '草稿' },
    ];
    setTableData(resTable);
  }, []);

  const rowSelection: TableRowSelection<any> = {
    onChange: (selectedRowKeys, selectedRows) => {
      setSelectedRow(selectedRowKeys);
    },
  };
  // 分页
  const handlePageChange = (page: number, size: number) => {
    setCurrentPage(page);
    setCurrentSize(size);
    // getList({ page, size, param: queryData }); // 获取table数据
  };
  const tableColumns: ColumnsType<any> = useMemo(() => {
    return [
      {
        title: '操作',
        dataIndex: 'code',
        fixed: 'left',
        width: '100px',
        render: (text, record) => (
          <div
            className={classes.textBtn}
            onClick={() => {
              console.log('onClick', text,"record",record);
            }}
          >
            {record['status'] === '草稿' ? '编辑' : '查看'}
          </div>
        ),
      },
      {
        title: '序号',
        dataIndex: 'id',
        width: '60px',
        render: (_, __, index) => index + 1 + (currentPage - 1) * currentSize,
      },
      {
        title: '来源',
        dataIndex: 'type',
        // width: '130px', // 最后一个宽度不传
        render: (_, __, index) => (_ === 1 ? '系统' : '手工'),
      },
    ];
  }, [classes.textBtn, currentPage, currentSize]);

  return (
    <>
      <CommonTable
        rowKey={'id'}
        className={classes.table}
        columns={tableColumns}
        scroll={{
          x: 'max-content',
        }}
        pagination={{
          showTotal: () => `${tableDataTotal} 条记录`,
          onChange: (page, size) => handlePageChange(page, size),
          hideOnSinglePage: false,
          showQuickJumper: true,
          showSizeChanger: true,
          current: currentPage,
          pageSize: currentSize,
          total: tableDataTotal,
        }}
        dataSource={tableData}
        loading={tableLoading}
        rowSelection={rowSelection}
      />
      <CommonTable
        rowKey={'id'}
        isDrag={false}
        className={classes.table}
        columns={tableColumns}
        scroll={{
          x: 'max-content',
        }}
        pagination={{
          showTotal: () => `${tableDataTotal} 条记录`,
          onChange: (page, size) => handlePageChange(page, size),
          hideOnSinglePage: false,
          showQuickJumper: true,
          showSizeChanger: true,
          current: currentPage,
          pageSize: currentSize,
          total: tableDataTotal,
        }}
        dataSource={tableData}
        loading={tableLoading}
        rowSelection={rowSelection}
      />
    </>
  );
};
export default TablePage;

效果

在这里插入图片描述

2:useColumns组件

useColumns.tsx

import React, { useMemo, useCallback } from 'react';

interface Args {
  handleEdit: (r: any) => void;
  handleSeeDetail: (r: any) => void;
}

export const useColumns = ({ handleEdit, handleSeeDetail }: Args) => {
  const handleEvent = useCallback(
    (v: string, record: any, e) => {
      e.stopPropagation();
      if (v === '编辑') {
        handleEdit(record);
      } else {
        handleSeeDetail(record);
      }
    },
    [handleSeeDetail, handleEdit],
  );

  // 渲染查看 || 编辑
  const showPop = useCallback(
    (record: any) => {
      if (record.status === '1') {
        return (
          <span
            onClick={(e) => handleEvent('编辑', record, e)}
            className="check-btn"
          >
            编辑
          </span>
        );
      } else {
        return (
          <span
            onClick={(e) => handleEvent('查看', record, e)}
            className="check-btn"
          >
            查看
          </span>
        );
      }
    },
    [handleEvent],
  );

  const columnsData: any = useMemo(() => {
    const columns = [
      {
        title: '操作',
        width: '100px',
        fixed: 'left',
        render: (_: any, record: any) => showPop(record),
      },
      {
        title: '序号',
        width: '60px',
        render: (_: any, record: any, index: number) => `${index + 1}`,
      },
      {
        title: '名称',
        dataIndex: 'name',
        width: '130px',
      },
      {
        title: '年龄',
        dataIndex: 'age',
      },
    ];
    return columns;
  }, [showPop]);


  return columnsData;
};

export default useColumns;

使用

import { createUseStyles } from 'react-jss';
import type { TableRowSelection } from 'antd/lib/table/interface';
import type { ColumnsType } from 'antd/lib/table';
import { useEffect, useMemo, useState, useRef } from 'react';
import { CommonTable } from '../components/CommonTable/index';
import useColumns from "./useColumns"

const useStyles = createUseStyles({
  table: {
    background: '#fff',
    padding: '16px',
    marginTop: '16px',
    width: '100%',
  },
  textBtn: {
    color: '#0068FF',
    cursor: 'pointer',
    userSelect: 'none',
  },
});
const TablePage = () => {
  const testData = [
    { name: '测试1',age: 10 },
    { name: '测试1',age: 20 },
    { name: '测试1',age: 30 },
  ]
  const handleSeeDetail = (row:any) => {
    console.log('查看', row);
    
  }
  const handleEdit = (row:any) => {
    console.log('编辑', row);
  }
  return (
    <>
      <CommonTable
        rowKey={'id'}
        className={classes.table}
        dataSource={testData}
        scroll={{
          x: 'max-content',
        }}
        columns={useColumns({ handleSeeDetail, handleEdit })}
      />
    </>
  );
};
export default TablePage;


http://www.kler.cn/a/235041.html

相关文章:

  • 3.使用SD卡挂载petalinux根文件系统
  • 复习打卡大数据篇——Hadoop HDFS 02
  • 昇思25天学习打卡营第33天|共赴算力时代
  • javalock(六)CyclicBarrier
  • Linux-ubuntu之主频和时钟配置
  • FastAPI介绍和简单示例代码(ChatGPT-4o生成)
  • Nginx报错合集(502 Bad Gateway,504 Gateway nginx/1.18.0 (Ubuntu) 等等报错)
  • 第三节 zookeeper基础应用与实战2
  • springboot181基于springboot的乐享田园系统
  • 【AIGC风格prompt深度指南】掌握绘画风格关键词,实现艺术模仿的革新实践
  • AutoSAR(基础入门篇)7.3-使用DEV配置SWCRTE
  • Visual Studio使用Git忽略不想上传到远程仓库的文件
  • 假期刷题打卡--Day26
  • 品牌如何营造生活感氛围?媒介盒子分享
  • 2024-02-11 Unity 编辑器开发之编辑器拓展2 —— 自定义窗口
  • 【JavaEE Spring 项目】博客系统
  • django密码管理器(创建项目)
  • 使用C++从零开始,自己写一个MiniWeb
  • Python在金融大数据分析中的AI应用实战
  • JavaScript学习之旅10------掌握jQuery:实用应用案例深度解析
  • 【深度学习 目标检测】R-CNN系列算法全面概述(一文搞懂R-CNN、Fast R-CNN、Faster R-CNN的来龙去脉)
  • tab 切换类交互功能实现
  • C语言每日一题(55)另一颗树的子树
  • 《UE5_C++多人TPS完整教程》学习笔记5 ——《P6 在线子系统(Online Subsystem)》
  • [算法学习] 唯一分解定理
  • 批量提取word文件中文本框内容的三种方法