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

react + ts + material-ui V5版本的table封装

以下是一份 material-ui V5版本的table封装


import React, { forwardRef, useImperativeHandle, useEffect, useState } from 'react';
import {Table,TableBody,TableSortLabel,TableCell,TableContainer,TableHead,TableRow,Typography,Paper,Checkbox,CircularProgress,Box,TablePagination,Grid,Button
} from '@mui/material';
import { useDispatch } from 'store';
import { useIntl } from 'react-intl';
import { openSnackbar } from 'store/slices/snackbar';
// import { cloneDeep } from 'lodash';interface IColumn {slot?: string;title?: string;prop: string;fixed?: string;minWidth?: number;sorting?: boolean; // 是否可以排序titleRender?: (row: any, injectionData?: any) => React.ReactNode;render?: (row: any, injectionData?: any) => React.ReactNode;isRowSelectable?: (row: any) => boolean; // 新增属性,用于判断某行是否可以被勾选align?: 'left' | 'center' | 'right'; // 新增属性,用于指定对齐方式
}
// interface IRequiredParameters {
//     [key: string]: any;
// }interface ItableConfig {columns: IColumn[];Api: any; //请求接口方法fetchConfig?: {};onSort?: (column: string, order: 'asc' | 'desc') => void;showSelect?: boolean; // 控制是否显示选择框initParams?: {};requestParams?: {};pagination?: {fixedColumns?: string[];sorting?: boolean;pageSize: number;pageSizeOptions?: number[];};isDisableFirstRequest?: boolean; //是否禁用第一次进入时就请求接口  为true时,需要手动调用request方法rowKey?: string;isCacheCheckOptions?: boolean; // 新增属性,控制是否缓存勾选项dataRootArrKey?: string;paginationKey?: {pageSizeKeyName: string;totalCountKeyName: string;pageIndexKeyName: string;};injectionData?: any; //从外部注入的数据  给内部通讯dataListKey?: string; // data下面的第二级别list key的名称maxHeight?: number | string;onSelectChange?: (selected: any[]) => void; // 新增属性,当选择变化时调用 selectedIds: string[],showTablePagination?: boolean; //是否展示分页组件render?: (row: any) => React.ReactNode;getCurrentStatus?(status: boolean): any; //获取当前是否是loading状态  来同步search的按钮状态requiredParametersCallBack?: (params: any) => boolean; //搜索必要参数条件onChangeList?: (data: any[], obj: any) => void; // 获取列表数据回调onGetListAfter?: (ls?: any[]) => void; // 获取列表成功后调用一下
}// type Order = 'asc' | 'desc';
export interface TableMethods {getTableList: (option?: any) => void; // 你希望父组件能调用的方法resetTableList: (option?: any) => void; // 你希望父组件能调用的方法clearSelection: () => void; // 新增的方法getCheckedRows: () => void;
}
const EnhancedTable = forwardRef<TableMethods, ItableConfig>(({columns,Api,showTablePagination = true,paginationKey = {pageSizeKeyName: 'pageSize',totalCountKeyName: 'total',pageIndexKeyName: 'page'},onSort,initParams = {},onSelectChange,showSelect = false,dataRootArrKey,rowKey = 'id',pagination = {pageSize: 10},maxHeight = 610,requestParams = [],dataListKey = 'list',isCacheCheckOptions = false,isDisableFirstRequest = false,getCurrentStatus,requiredParametersCallBack = null,onChangeList,onGetListAfter,injectionData},ref) => {// 使用 useImperativeHandle 来暴露方法给父组件useImperativeHandle(ref, () => ({getTableList(option?: any) {option ? getList(option) : getList();},resetTableList(option?: any) {// ... 实现你希望父组件能调用的方法if (page === 0 && rowsPerPage === pagination.pageSize) {option ? getList(option) : getList();} else {setRowsPerPage(pagination.pageSize);setPage(0);}},getCheckedRows() {return selected;},clearSelection // 暴露新的方法}));const [isRestPageIndex, setIsRestPageIndex] = useState(true);const getStickyStyle = (column: IColumn, index: number): React.CSSProperties => {if (column.fixed) {return {position: 'sticky',borderLeft: column.fixed === 'right' ? '1px solid rgba(224, 224, 224, 1)' : undefined,boxShadow: column.fixed === 'right' ? '-2px 0px 3px rgba(0, 0, 0, 0.2)' : undefined,right: column.fixed === 'right' ? 0 : undefined,left: column.fixed === 'left' ? 0 : undefined,backgroundColor: '#fff',zIndex: 2};}return {};};const dispatch = useDispatch();const intl = useIntl();const [data, setData] = useState<any[]>([]);const [rowsPerPage, setRowsPerPage] = useState(pagination.pageSize || 10);const [loading, setLoading] = useState(false);const [order, setOrder] = useState<'asc' | 'desc'>('asc');const [orderBy, setOrderBy] = useState<string | null>(null);const [totalCount, setTotalCount] = useState(0);const [selected, setSelected] = useState<any[]>([]);const [page, setPage] = useState(0);const defaultErrorMessage = '出了点问题请稍后再试';// const [lastRequestParams, setLastRequestParams] = useState<any>({});// 新增的方法来清除所有勾选项const clearSelection = () => {setSelected([]);if (onSelectChange) {onSelectChange([]);}};// isDisableFirstRequestconst [flagFirst, setFlagFirst] = useState(false);useEffect(() => {if (isDisableFirstRequest) {if (flagFirst) {getList();} else {setFlagFirst(true);}} else {getList();}}, [page, rowsPerPage]);useEffect(() => {getCurrentStatus && getCurrentStatus(loading);}, [loading]);const [isCacheCheckFlag, setIsCacheCheckFlag] = useState(false);const getList = async (option = {}) => {if (loading) return;var ppppageIndex = page + 1;if (isRestPageIndex) {setPage(0);ppppageIndex = 1;}try {var params: any = {[paginationKey.pageIndexKeyName]: ppppageIndex, //TablePagination ui是从0开始的[paginationKey.pageSizeKeyName]: rowsPerPage,...initParams,...requestParams,...option};if (!!requiredParametersCallBack) {var fl: boolean = requiredParametersCallBack(params); //返回true才截断if (fl) {return false;}}setLoading(true);// 如果请求参数变化,并且不是因为翻页或修改每页条数(即是一次新的搜索),则清除勾选项if (showSelect) {if (isCacheCheckOptions) {if (isCacheCheckFlag// (lastRequestParams[paginationKey.pageIndexKeyName] !== params[paginationKey.pageIndexKeyName] ||//     lastRequestParams[paginationKey.pageSizeKeyName] !== params[paginationKey.pageSizeKeyName])) {setIsCacheCheckFlag(false);}} else {clearSelection();}}const res = await Api(params, dispatch, intl);// // 更新最后一次请求参数// setLastRequestParams(cloneDeep(params));setLoading(false);setIsRestPageIndex(true);if (res.code === 0) {const datas: {[dataListKey: string]: any;} = res.data;const ls = datas[dataListKey];if (ls && Array.isArray(ls)) {setData(ls);const ss = paginationKey.totalCountKeyName as string;var num = datas[ss] as number;setTotalCount(num); //总页码onChangeList?.(ls, res);} else {setData([]);setTotalCount(0);}onGetListAfter && onGetListAfter(ls);} else {setLoading(false);setData([]);setTotalCount(0);onChangeList?.([], res);dispatch(openSnackbar({open: true,message: res.msg || defaultErrorMessage,variant: 'alert',alert: {color: 'error'},close: false,anchorOrigin: {vertical: 'top',horizontal: 'center'}}));}} catch (error) {setLoading(false);setIsRestPageIndex(true);console.log('error error error', error);}};const handleSortRequest = (column: string) => {const isAsc = orderBy === column && order === 'asc';setOrder(isAsc ? 'desc' : 'asc');setOrderBy(column);onSort?.(column, isAsc ? 'desc' : 'asc');};const handleClick = (event: React.MouseEvent<unknown>, row: any) => {const isSelectable = columns.every((column) => (column.isRowSelectable ? column.isRowSelectable(row) : true));if (!isSelectable) {// 如果行不可选,直接返回不执行任何操作return;}const selectedIndex = selected.findIndex((r) => r[rowKey] === row[rowKey]);let newSelected: any[] = [];if (selectedIndex === -1) {newSelected = newSelected.concat(selected, row);} else if (selectedIndex === 0) {newSelected = newSelected.concat(selected.slice(1));} else if (selectedIndex === selected.length - 1) {newSelected = newSelected.concat(selected.slice(0, -1));} else if (selectedIndex > 0) {newSelected = newSelected.concat(selected.slice(0, selectedIndex), selected.slice(selectedIndex + 1));}setSelected(newSelected);onSelectChange?.(newSelected);};const handleSelectAllClick = (event: React.ChangeEvent<HTMLInputElement>) => {if (event.target.checked) {// 仅选择那些满足 isRowSelectable 条件的行const newSelecteds = data.filter((row) =>columns.every((column) => (column.isRowSelectable ? column.isRowSelectable(row) : true)));setSelected(newSelecteds);onSelectChange?.(newSelecteds);} else {setSelected([]);onSelectChange?.([]);}};const handleChangePage = (_event: unknown, newPage: number) => {setIsCacheCheckFlag(true);setPage(newPage);setIsRestPageIndex(false);};const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => {setIsCacheCheckFlag(true);setRowsPerPage(parseInt(event.target.value, 10));};return (<Box>{showSelect && (<Box sx={{ minHeight: '38px' }}><Grid container><Grid item><Typographysx={{fontSize: '16px',paddingTop: '5px'}}variant="h6">{'已勾选'}:<Buttonsx={{cursor: 'auto',padding: '0',minWidth: '20px'}}>{selected.length}</Button>{'项'}</Typography></Grid>{selected.length > 0 && (<Griditemsx={{marginLeft: '10px'}}><Button onClick={clearSelection}> {'取消选择'}</Button></Grid>)}</Grid></Box>)}<Paper sx={{ width: '100%', overflow: 'hidden' }}><TableContainer component={Paper} sx={{ maxHeight: maxHeight, overflow: 'auto' }}><TablestickyHeaderaria-label="sticky table"sx={{minWidth: 750,tableLayout: 'auto','& .MuiTableCell-root': {borderBottom: '1px solid rgba(224, 224, 224, 1)' // 底部边框}}}><TableHead><TableRow>{showSelect && (<TableCell padding="checkbox"><Checkboxindeterminate={selected.length > 0 && selected.length < totalCount}checked={totalCount > 0 && selected.length === totalCount}onChange={handleSelectAllClick}inputProps={{ 'aria-label': 'select all desserts' }}/></TableCell>)}{columns.map((column) => (<TableCellalign={column.align || 'left'} // 使用 align 属性,如果未指定,默认为左对齐key={column.prop ? column.prop : Math.floor(Math.random() * 10000) + ''}style={{minWidth: column?.minWidth,position: column.fixed ? 'sticky' : undefined,top: 0, // 确保固定列头在顶部right: column.fixed === 'right' ? 0 : undefined,backgroundColor: '#f8fafc', // 确保固定列的背景色不透明zIndex: column.fixed ? 110 : 1, // 确保固定列在滚动时覆盖其他列,1100 是 MUI 中的 AppBar zIndexborderLeft: column.fixed === 'right' ? '1px solid rgba(224, 224, 224, 1)' : undefined,boxShadow: column.fixed === 'right' ? '-2px 0px 3px rgba(0, 0, 0, 0.2)' : undefined}}sortDirection={orderBy === column.prop ? order : false}>{column.titleRender ? (column.titleRender(column, injectionData)) : column.sorting ? (<TableSortLabelactive={orderBy === column.prop}direction={orderBy === column.prop ? order : 'asc'}onClick={() => handleSortRequest(column.prop)}>{column.title}</TableSortLabel>) : (column.title)}</TableCell>))}{columns.map((column) =>column.slot === 'right' ? (<TableCell key={column.prop} style={{ minWidth: column.minWidth }}>{column.title}</TableCell>) : null)}</TableRow></TableHead><TableBody>{loading ? (<TableRow><TableCell colSpan={columns.length + (showSelect ? 1 : 0)} style={{ textAlign: 'center' }}><CircularProgress /></TableCell></TableRow>) : data.length > 0 ? (data.map((row, index) => {const isItemSelected = selected.some((r) => r[rowKey] === row[rowKey]);const labelId = `enhanced-table-checkbox-${index}`;// 使用 column 中的 isRowSelectable 函数来判断行是否可选,如果没有提供,则默认为可选const isSelectable = columns.every((column) =>column.isRowSelectable ? column.isRowSelectable(row) : true);return (<TableRowhoveronClick={showSelect? (event) => {// 检查该行是否可选const isSelectable = columns.every((column) =>column.isRowSelectable ? column.isRowSelectable(row) : true);if (isSelectable) {handleClick(event, row);}}: undefined}role="checkbox"aria-checked={isItemSelected}tabIndex={-1}key={row[rowKey]}selected={isItemSelected}>{showSelect && (<TableCell padding="checkbox"><Checkboxchecked={isItemSelected}disabled={!isSelectable} // 根据 isSelectable 禁用或启用复选框inputProps={{ 'aria-labelledby': labelId }}/></TableCell>)}{columns.map((column) => (<TableCell key={`${row[rowKey]}-${column.prop}`} style={getStickyStyle(column, index)}>{column.render ? column.render(row, injectionData) : row[column?.prop]}</TableCell>))}</TableRow>);})) : (<TableRow><TableCell colSpan={columns.length + (showSelect ? 1 : 0)} align="center">{'暂无数据'}</TableCell></TableRow>)}</TableBody></Table></TableContainer>{showTablePagination && (<Box sx={{ display: 'flex', justifyContent: 'flex-start' }}><TablePaginationrowsPerPageOptions={pagination?.pageSizeOptions || [10, 20, 30, 50, 100]}component="div"count={totalCount}rowsPerPage={rowsPerPage}page={page}sx={{'.MuiTablePagination-toolbar': {alignItems: 'center', // 确保工具栏中的所有元素都垂直居中justifyContent: 'flex-end' // 工具栏内的元素靠右对齐},'.MuiTablePagination-selectLabel': {margin: 0 // 移除默认的外边距},'.MuiTablePagination-select': {margin: 0 // 移除默认的外边距},'.MuiTablePagination-displayedRows': {margin: 0 // 移除默认的外边距},marginLeft: '-7px !important'// 你可以根据需要添加更多的样式规则}}onPageChange={handleChangePage}onRowsPerPageChange={handleChangeRowsPerPage}/></Box>)}</Paper></Box>);}
);export default EnhancedTable;

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 本地部署aniportrait
  • 【unity实战】使用新版输入系统Input System+Rigidbody实现第三人称人物控制器
  • 【2024 CCF编程能力等级认证(GESP)Python 】一级大纲
  • 【图像去噪】论文精读:Multi-level Wavelet-CNN for Image Restoration(MWCNN)
  • 在Supabase创建用户登录并获取token的操作实践
  • 图像尺寸测量仪的精度概念解析
  • 掌握SQL数据分割技巧:垂直与水平分割全解析
  • 学习关系型数据库:在Ubuntu和FreeBSD下安装firebird
  • elementui图标偶尔乱码问题
  • PyCharm 自定义字体大小
  • 提升农业信息化水平,C# ASP.NET Vue果树生长信息管理系统带来全新管理体验
  • Windows 7 Windows Server 2008 R2 简体中文版下载 (updated Aug 2024)
  • c++ STL 容器相关
  • Qt窗口 菜单栏 QMenuBar和的使用及说明
  • java03
  • “寒冬”下的金三银四跳槽季来了,帮你客观分析一下局面
  • JavaScript服务器推送技术之 WebSocket
  • JS数组方法汇总
  • PermissionScope Swift4 兼容问题
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用(一)
  • 嵌入式文件系统
  • 如何设计一个微型分布式架构?
  • 数组的操作
  • 问题之ssh中Host key verification failed的解决
  • 找一份好的前端工作,起点很重要
  • 扩展资源服务器解决oauth2 性能瓶颈
  • 曾刷新两项世界纪录,腾讯优图人脸检测算法 DSFD 正式开源 ...
  • # wps必须要登录激活才能使用吗?
  • ## 基础知识
  • #NOIP 2014#Day.2 T3 解方程
  • #QT项目实战(天气预报)
  • %check_box% in rails :coditions={:has_many , :through}
  • (delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)
  • (LeetCode 49)Anagrams
  • (草履虫都可以看懂的)PyQt子窗口向主窗口传递参数,主窗口接收子窗口信号、参数。
  • (超详细)语音信号处理之特征提取
  • (多级缓存)多级缓存
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (企业 / 公司项目)前端使用pingyin-pro将汉字转成拼音
  • (算法)硬币问题
  • (五)大数据实战——使用模板虚拟机实现hadoop集群虚拟机克隆及网络相关配置
  • (原创)Stanford Machine Learning (by Andrew NG) --- (week 9) Anomaly DetectionRecommender Systems...
  • (转)可以带来幸福的一本书
  • .chm格式文件如何阅读
  • .NET Core 和 .NET Framework 中的 MEF2
  • .NET Core 实现 Redis 批量查询指定格式的Key
  • .NET Framework 4.6.2改进了WPF和安全性
  • .NET 事件模型教程(二)
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件
  • .NET3.5下用Lambda简化跨线程访问窗体控件,避免繁复的delegate,Invoke(转)
  • .net6 webapi log4net完整配置使用流程
  • .Net转前端开发-启航篇,如何定制博客园主题
  • [ 代码审计篇 ] 代码审计案例详解(一) SQL注入代码审计案例
  • [] 与 [[]], -gt 与 > 的比较
  • [Android View] 可绘制形状 (Shape Xml)