You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Material UI EnhancedTable实现全列过滤(不使用DataGrid)

给Material UI EnhancedTable实现列过滤功能

直接上轻量落地方案——在每个列头加小型输入框做实时模糊过滤,不用复杂模态框,快速满足需求:

步骤1:维护过滤状态

在组件中添加状态存储各列的过滤值:

import { useState } from 'react';
// ...其他原有导入

const EnhancedTable = () => {
  // ...保留原有排序、选中项等状态
  const [filters, setFilters] = useState({}); // 结构:{ 列字段名: 过滤文本 }

步骤2:改造表头,添加过滤输入框

在TableHead的每个TableCell里,追加一个小型TextField,绑定对应列的过滤值:

<TableHead>
  <TableRow>
    {/* 保留原有选择列(如果存在) */}
    <TableCell padding="checkbox">
      <Checkbox
        indeterminate={numSelected > 0 && numSelected < rows.length}
        checked={rows.length > 0 && numSelected === rows.length}
        onChange={handleSelectAllClick}
        inputProps={{ 'aria-label': 'select all items' }}
      />
    </TableCell>
    {/* 遍历列定义,渲染列头+过滤框 */}
    {columns.map((column) => (
      <TableCell
        key={column.id}
        align={column.align}
        padding={column.disablePadding ? 'none' : 'normal'}
        sortDirection={orderBy === column.id ? order : false}
      >
        <TableSortLabel
          active={orderBy === column.id}
          direction={orderBy === column.id ? order : 'asc'}
          onClick={() => handleRequestSort(event, column.id)}
        >
          {column.label}
          {orderBy === column.id ? (
            <Box component="span" sx={{ visibility: 'visible' }}>{sortIcon}</Box>
          ) : (
            <Box component="span" sx={{ visibility: 'hidden' }}>{sortIcon}</Box>
          )}
        </TableSortLabel>
        {/* 新增过滤输入框 */}
        <TextField
          size="small"
          placeholder="过滤..."
          value={filters[column.id] || ''}
          onChange={(e) => {
            setFilters(prev => ({
              ...prev,
              [column.id]: e.target.value.trim().toLowerCase()
            }));
          }}
          sx={{ mt: 0.5, width: '100%' }}
        />
      </TableCell>
    ))}
  </TableRow>
</TableHead>

步骤3:实现过滤逻辑

编写函数对原始数据做模糊匹配过滤:

const getFilteredRows = () => {
  // 无过滤条件时直接返回原始数据
  if (Object.values(filters).every(val => !val)) return rows;
  
  return rows.filter(row => {
    // 所有列的过滤条件都满足才保留该行
    return Object.entries(filters).every(([key, filterText]) => {
      if (!filterText) return true; // 该列无过滤条件,直接通过
      const cellValue = row[key]?.toString().toLowerCase();
      return cellValue?.includes(filterText);
    });
  });
};

// 在组件内获取过滤后的数据
const filteredRows = getFilteredRows();

步骤4:渲染过滤后的数据

把原来渲染rows的地方替换成filteredRows,比如表格体部分:

<TableBody>
  {/* 将原rows替换为filteredRows */}
  {stableSort(filteredRows, getComparator(order, orderBy))
    .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
    .map((row, index) => {
      const isItemSelected = isSelected(row.id);
      const labelId = `enhanced-table-checkbox-${index}`;

      return (
        <TableRow
          hover
          onClick={(event) => handleClick(event, row.id)}
          role="checkbox"
          aria-checked={isItemSelected}
          tabIndex={-1}
          key={row.id}
          selected={isItemSelected}
        >
          <TableCell padding="checkbox">
            <Checkbox checked={isItemSelected} inputProps={{ 'aria-labelledby': labelId }} />
          </TableCell>
          {columns.map((column) => {
            const value = row[column.id];
            return (
              <TableCell key={column.id} align={column.align}>
                {column.format && typeof value === 'number' ? column.format(value) : value}
              </TableCell>
            );
          })}
        </TableRow>
      );
    })}
  {/* 保留原有空行占位逻辑 */}
  {emptyRows > 0 && (
    <TableRow style={{ height: 53 * emptyRows }}>
      <TableCell colSpan={columns.length + 1} />
    </TableRow>
  )}
</TableBody>

可选优化

  • 若需精确匹配,将includes替换为===即可
  • 给输入框加防抖(比如用useDebounce钩子),减少频繁过滤的性能消耗
  • 针对日期、数字类型列,可单独编写专属过滤逻辑(如数字范围匹配)

内容的提问来源于stack exchange,提问作者Akinalp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 07:15:56