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

news/2025/2/21 7:00:31/

目录

  • 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 } = propsconst classes = useCommonTableStyles();if (!width) { return (<th {...restProps} />) };return (<Resizablewidth={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 重组为支持可拖拽的cellconst 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'dangeronClick={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) => (<divclassName={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 (<><CommonTablerowKey={'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}/><CommonTablerowKey={'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 (<spanonClick={(e) => handleEvent('编辑', record, e)}className="check-btn">编辑</span>);} else {return (<spanonClick={(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 (<><CommonTablerowKey={'id'}className={classes.table}dataSource={testData}scroll={{x: 'max-content',}}columns={useColumns({ handleSeeDetail, handleEdit })}/></>);
};
export default TablePage;

http://www.ppmy.cn/news/1350524.html

相关文章

如何下载huggingface的模型到本地

https://huggingface.co/docs/transformers/installation#fetch-models-and-tokenizers-to-use-offline 以下代码下载模型到 ./123/chatglm3-6b from transformers import AutoTokenizer, AutoModel from huggingface_hub.hf_api import HfFolderHfFolder.save_token(hf_ZYmP…

面向对象2:继承

目录 2.1继承 2.2 继承的好处 2.3 权限修饰符 2.4 单继承、Object 2.5 方法重写 2.6 子类中访问成员的特点 2.7 子类中访问构造器的特点 面向对象1&#xff1a;静态 2.1继承 向对象编程之所以能够能够被广大开发者认可&#xff0c;有一个非常重要的原因&#xff0c;是…

下个目标,突破 10w+

转眼间&#xff0c;2023 年已经过去了&#xff0c;今天是大年初四&#xff0c;还有十来天就马上除夕了&#xff0c;迈入新的一年。 回顾 2023 年&#xff0c;如果让我给自己打分&#xff0c;我算是 7.5 分吧。 在这一年了&#xff0c;工作上表现平平&#xff0c;并没有什么突…

【Java】案例:检测MySQL是否存在某数据库,没有则创建

1.代码 package hello; import java.sql.*;public class CeShi {//定义基本数据static final String JDBC_DRIVER "com.mysql.cj.jdbc.Driver";static final String DB_URL "jdbc:mysql://localhost/";static final String USER "your_username&q…

Dubbo集成Zookeeper embbed模式

为了简化应用支持服务方便的分合&#xff0c;使用Zookeeper embbed模式。集成Zookeeper比较容易&#xff0c;使用starter或自己写代码都可以。但是由于集成了Dubbo&#xff0c;每次启动时都会发现zookeeper没有启动就开始报错退出&#xff0c;但是确是已经集成了。 于是只能翻…

C++:面向对象——类的构造

1.1学会面向对象的编程思想 面向对象的英文缩写是OO&#xff0c;它是一种设计思想。 面向对象有3大特点&#xff1a;封装、继承和多态。 1.封装 封装有两个作用&#xff0c;一个是将不同的小对象封装成一个大对象&#xff1b;另外一个是把一部分内部属性和功能对外界屏蔽。…

基于JAVA开发的数字化智慧工地管理平台源码,可私有化部署、带可视化大屏

智能工地应用价值 智慧工地现场构建了基于物联网的智能化数据传感器通用的管理平台。利用计算机、人工智能、无线通信&#xff0c;全天候现场监视、施工检查、质量管理、服务&#xff0c;提高数字化管理、安全、绿色、施工等现场管理能力&#xff0c;标志着现场管理进入信息化时…

蓝桥杯每日一题之内存问题

蓝桥杯真题---内存问题 题目描述&#xff1a; 小蓝最近总喜欢计算自己的代码中定义的变量占用了多少内存空间。 为了简化问题&#xff0c;变量的类型只有以下三种&#xff1a; int&#xff1a;整型变量&#xff0c;一个 int 型变量占用 4 Byte 的内存空间。 long&#xff…