react中hook封装一个table组件

目录

react中hook封装一个table组件

依赖

js 复制代码
cnpm i react-resizable --save
cnpm i ahooks
cnpm i --save-dev @types/react-resizable

CommonTable / index.tsx

js 复制代码
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>
  );
};

使用组件

js 复制代码
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;

效果

相关推荐
如若1236 分钟前
对文件内的文件名生成目录,方便查阅
java·前端·python
滚雪球~1 小时前
npm error code ETIMEDOUT
前端·npm·node.js
沙漏无语1 小时前
npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
supermapsupport1 小时前
iClient3D for Cesium在Vue中快速实现场景卷帘
前端·vue.js·3d·cesium·supermap
brrdg_sefg1 小时前
WEB 漏洞 - 文件包含漏洞深度解析
前端·网络·安全
胡西风_foxww1 小时前
【es6复习笔记】rest参数(7)
前端·笔记·es6·参数·rest
m0_748254881 小时前
vue+elementui实现下拉表格多选+搜索+分页+回显+全选2.0
前端·vue.js·elementui
星就前端叭2 小时前
【开源】一款基于Vue3 + WebRTC + Node + SRS + FFmpeg搭建的直播间项目
前端·后端·开源·webrtc
m0_748234522 小时前
前端Vue3字体优化三部曲(webFont、font-spider、spa-font-spider-webpack-plugin)
前端·webpack·node.js
Web阿成2 小时前
3.学习webpack配置 尝试打包ts文件
前端·学习·webpack·typescript