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

react中hook封装一个table组件

目录

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

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;

效果

在这里插入图片描述


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

相关文章:

  • Easyui ComboBox 数据加载完成之后过滤数据
  • 聊聊Flink:Flink的分区机制
  • python makedirs() 详解
  • 基于SpringBoot+RabbitMQ完成应⽤通信
  • spring boot整合https协议
  • 检测敏感词功能
  • Java项目管理01-Maven基础
  • Mac安装nvm装完项目内node找不到
  • ChatGPT 变懒最新解释!或和系统Prompt太长有关
  • 自然语言学习nlp 六
  • 【flink状态管理(三)】StateBackend的整体设计、StateBackend创建说明
  • 从REPR设计模式看 .NET的新生代类库FastEndpoints的威力
  • 故障诊断 | 一文解决,TCN时间卷积神经网络模型的故障诊断(Matlab)
  • 设计模式理解:单例模式+工厂模式+建设者模式+原型模式
  • 怎么在Springboot启动的时候就启动一个永不停止的线程
  • 20240208作业
  • LeetCode-第28题-找出字符串中第一个匹配项的下标
  • Rust语言入门(第3篇)
  • Go Context -- 管理请求的上下文信息
  • vue3 之 商城项目—一级分类
  • 删除和清空Hive外部表数据
  • 上位机建立TCP/IP连接:Matlab实现
  • 发文新思路!双流卷积!CWT-DSCNN-MSA基于时序特征、cwt小波时频图的双流卷积融合注意力机制的故障识别程序!直接运行!
  • armbian ddns
  • 海纳思NAS盒子设置网卡静态IP
  • 基于BatchNorm的模型剪枝【详解+代码】