马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
以下是一份 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([]);
- }
- };
- // isDisableFirstRequest
- const [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>
- <Typography
- sx={{
- fontSize: '16px',
- paddingTop: '5px'
- }}
- variant="h6"
- >
- {'已勾选'}:
- <Button
- sx={{
- cursor: 'auto',
- padding: '0',
- minWidth: '20px'
- }}
- >
- {selected.length}
- </Button>
- {'项'}
- </Typography>
- </Grid>
- {selected.length > 0 && (
- <Grid
- item
- sx={{
- marginLeft: '10px'
- }}
- >
- <Button onClick={clearSelection}> {'取消选择'}</Button>
- </Grid>
- )}
- </Grid>
- </Box>
- )}
- <Paper sx={{ width: '100%', overflow: 'hidden' }}>
- <TableContainer component={Paper} sx={{ maxHeight: maxHeight, overflow: 'auto' }}>
- <Table
- stickyHeader
- aria-label="sticky table"
- sx={{
- minWidth: 750,
- tableLayout: 'auto',
- '& .MuiTableCell-root': {
- borderBottom: '1px solid rgba(224, 224, 224, 1)' // 底部边框
- }
- }}
- >
- <TableHead>
- <TableRow>
- {showSelect && (
- <TableCell padding="checkbox">
- <Checkbox
- indeterminate={selected.length > 0 && selected.length < totalCount}
- checked={totalCount > 0 && selected.length === totalCount}
- onChange={handleSelectAllClick}
- inputProps={{ 'aria-label': 'select all desserts' }}
- />
- </TableCell>
- )}
- {columns.map((column) => (
- <TableCell
- align={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 zIndex
- 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
- }}
- sortDirection={orderBy === column.prop ? order : false}
- >
- {column.titleRender ? (
- column.titleRender(column, injectionData)
- ) : column.sorting ? (
- <TableSortLabel
- active={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 (
- <TableRow
- hover
- onClick={
- 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">
- <Checkbox
- checked={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' }}>
- <TablePagination
- rowsPerPageOptions={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;
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |