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

基于React+Tailwind实现带固定表头的可滚动动态表格

实现固定表头+滚动表格主体(React + Tailwind)

完全可以实现你要的效果,原生<tbody>无法直接设置滚动是因为表格布局的特性限制,我们可以通过拆分表头和表体、添加滚动容器的方式解决,同时支持动态添加行。

核心实现思路

  • 将表头与表体拆分为两个独立的<table>,放入同一容器保证列宽对齐
  • 给表体容器设置固定高度和overflow-y: auto,仅在表体区域显示滚动条
  • 通过统一列宽设置避免表头与表体错位
  • 维护表格数据状态,实现动态添加行功能

修正后的完整代码

import { useState } from 'react';

const AreaRow = ({ area, dependsOn, collaborators, selected, onRowSelect }) => (
  <tr className="border-b border-gray-200 hover:bg-gray-50">
    <td className="p-2">
      <input type="checkbox" checked={selected} onChange={onRowSelect} />
    </td>
    <td className="p-2">{area}</td>
    <td className="p-2">{dependsOn}</td>
    <td className="p-2">{collaborators}</td>
    <td className="p-2">
      <button className="text-blue-600 hover:text-blue-800">...</button>
    </td>
  </tr>
);

const DynamicTable = () => {
  // 初始表格数据
  const [areas, setAreas] = useState([
    { area: "市场部", dependsOn: "总经理", collaborators: 5 },
    { area: "技术部", dependsOn: "CTO", collaborators: 12 },
    { area: "人事部", dependsOn: "总经理", collaborators: 3 },
  ]);
  const [selectedRows, setSelectedRows] = useState(Array(areas.length).fill(false));

  // 全选逻辑
  const handleHeaderCheckboxChange = (e) => {
    setSelectedRows(Array(areas.length).fill(e.target.checked));
  };

  // 单行选择逻辑
  const toggleRowSelection = (index) => {
    setSelectedRows(prev => {
      const newArr = [...prev];
      newArr[index] = !newArr[index];
      return newArr;
    });
  };

  // 动态添加新行
  const addNewRow = () => {
    setAreas(prev => [...prev, { area: "", dependsOn: "", collaborators: 0 }]);
    setSelectedRows(prev => [...prev, false]);
  };

  return (
    <div className="w-full overflow-hidden">
      {/* 固定表头 */}
      <table className="border-collapse w-full">
        <thead className="[&>tr>th]:text-left [&>tr>th]:p-2 [&>tr>th]:text-[#251AC7] [&>tr>th]:font-medium">
          <tr className="bg-[#f5f6f8]">
            <th style={{ width: '10%' }}>
              <input type="checkbox" onChange={handleHeaderCheckboxChange} />
            </th>
            <th style={{ width: '30%' }}>Áreas</th>
            <th style={{ width: '20%' }}>Depende de</th>
            <th style={{ width: '10%' }}>Colaboradores</th>
            <th style={{ width: '5%' }}></th>
          </tr>
        </thead>
      </table>

      {/* 可滚动的表体容器 */}
      <div className="h-[330px] overflow-y-auto">
        <table className="border-collapse w-full">
          <tbody className="[&>tr>td]:text-left">
            {areas.map((area, i) => (
              <AreaRow
                key={i}
                area={area.area}
                dependsOn={area.dependsOn}
                collaborators={area.collaborators}
                selected={selectedRows[i]}
                onRowSelect={() => toggleRowSelection(i)}
              />
            ))}
          </tbody>
        </table>
      </div>

      {/* 添加行按钮 */}
      <button 
        onClick={addNewRow}
        className="mt-3 px-4 py-2 bg-[#251AC7] text-white rounded hover:bg-opacity-90"
      >
        添加新行
      </button>
    </div>
  );
};

export default DynamicTable;

关键修改说明

  • 拆分表头表体:将原单个<table>拆分为两个,表头独立存在,表体放入带滚动属性的<div>中,实现表头固定
  • 滚动容器配置:给表体外层<div>设置h-[330px] overflow-y-auto,仅表体区域显示垂直滚动条
  • 列宽对齐:保持表头与表体的列宽设置完全一致,避免布局错位
  • 动态行逻辑:新增addNewRow函数,通过更新状态数组实现添加空行,同步维护选中状态数组
  • 样式优化:添加行边框和hover效果提升可读性,移除原tbody上无效的边框设置

额外注意事项

  • 若需更精准的列宽控制,可替换百分比为固定像素值(如w-[100px]),确保列宽完全匹配
  • 若需横向滚动,可给最外层容器添加overflow-x-auto,如需表头同步横向滚动,可给表头列添加sticky left-0定位
  • 尽量避免用索引作为key,如果数据有唯一ID,优先使用唯一ID以提升渲染性能

内容的提问来源于stack exchange,提问作者La Bola Al Riel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 15:01:06