zhou zhou
昨天 28238befb9bb4546ab1a2b4942354e20bb3bdced
rsf-admin/src/page/components/StickyDataTable.jsx
@@ -1,17 +1,104 @@
import React from 'react';
import { DataTable } from 'react-admin';
import { DataTable, useDataTableDataContext, useTranslate } from 'react-admin';
import { TableFooter, TableRow, TableCell } from '@mui/material';
/**
 * 计算函数映射
 */
const calculators = {
    sum: (data, field) => data.reduce((acc, record) => acc + (Number(record[field]) || 0), 0),
    count: (data, field) => data.filter(record => record[field] != null && record[field] !== '').length,
    avg: (data, field) => {
        const validData = data.filter(record => record[field] != null);
        if (validData.length === 0) return 0;
        const sum = validData.reduce((acc, record) => acc + (Number(record[field]) || 0), 0);
        return (sum / validData.length).toFixed(2);
    },
    min: (data, field) => {
        const values = data.map(record => Number(record[field]) || 0);
        return values.length > 0 ? Math.min(...values) : 0;
    },
    max: (data, field) => {
        const values = data.map(record => Number(record[field]) || 0);
        return values.length > 0 ? Math.max(...values) : 0;
    },
};
/**
 * 计算类型的中文前缀映射
 */
const typeLabels = {
    sum: '合计',
    count: '数量',
    avg: '平均',
    min: '最小',
    max: '最大',
};
/**
 * 内部 Footer 组件
 * @param {Object} props
 * @param {Array} props.footerConfig - footer 配置数组
 * @param {string} props.footerLabel - 第一列显示的标签,默认'合计'
 */
const StickyTableFooter = ({ footerConfig, footerLabel = '合计' }) => {
    const data = useDataTableDataContext();
    const translate = useTranslate();
    const results = footerConfig.map(config => {
        const { field, type = 'sum', label, render } = config;
        const calculator = calculators[type];
        const value = calculator ? calculator(data, field) : 0;
        const typePrefix = typeLabels[type] || '合计';
        // 支持自定义渲染
        if (render) {
            return { label, value: render(value, data), typePrefix };
        }
        // 获取翻译后的标签
        const displayLabel = label ? (label.startsWith('table.') || label.startsWith('common.') ? translate(label) : label) : field;
        return { label: displayLabel, value, typePrefix };
    });
    return (
        <TableFooter>
            <TableRow>
                {results.map((item, index) => (
                    <TableCell key={index} variant="footer" align="left">
                        {item.label} {item.typePrefix}: {item.value}
                    </TableCell>
                ))}
                <TableCell colSpan={99} />
            </TableRow>
        </TableFooter>
    );
};
/**
 * StickyDataTable Component
 * 
 * 封装 react-admin 的 DataTable,实现传入列名即可固定列。
 * 封装 react-admin 的 DataTable,实现传入列名即可固定列,支持配置化 footer 汇总。
 * 
 * @param {Object} props
 * @param {string[]} props.stickyLeft - 需要固定在左侧的字段 source 列表
 * @param {string[]} props.stickyRight - 需要固定在右侧的字段 source 列表
 * @param {Array} props.footerConfig - footer 汇总配置,格式:[{ field: 'anfme', type: 'sum', label: 'table.field.xxx' }]
 *   - field: 要计算的字段名
 *   - type: 计算类型,支持 'sum' | 'count' | 'avg' | 'min' | 'max',默认 'sum'
 *   - label: 显示的标签,支持翻译 key 或直接显示的文本
 *   - render: 可选,自定义渲染函数 (value, data) => ReactNode
 * @param {string} props.footerLabel - footer 第一列标签,默认'合计'
 */
export const StickyDataTable = ({ stickyLeft = [], stickyRight = [], children, ...props }) => {
export const StickyDataTable = ({
    stickyLeft = [],
    stickyRight = [],
    footerConfig,
    footerLabel = '合计',
    children,
    ...props
}) => {
    // 递归处理 Children,确保即便是 Fragment 包裹的列也能被处理
    const processChildren = (children) => {
@@ -63,12 +150,28 @@
        });
    };
    // 构建 foot 属性
    const footerComponent = footerConfig && footerConfig.length > 0
        ? () => <StickyTableFooter footerConfig={footerConfig} footerLabel={footerLabel} />
        : undefined;
    return (
        <DataTable {...props} sx={{
        <DataTable {...props} foot={footerComponent} sx={{
            '& .MuiTableCell-head': {
                zIndex: 4,
                borderBottom: 'none' // 遵循之前的优化,去除表头下边框
            },
            '& .MuiTableFooter-root': {
                position: 'sticky',
                bottom: 0,
                zIndex: 3,
                backgroundColor: '#FFFFFF',
            },
            '& .MuiTableFooter-root .MuiTableCell-root': {
                backgroundColor: '#f5f5f5',
                fontWeight: 'bold',
                borderTop: '2px solid #e0e0e0',
            },
        }}>
            {processChildren(children)}
        </DataTable>