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

Reactjs实现嵌套动态表格展示学校作息数据的解决方案求助

实现方案

核心思路

首先对原始扁平数据做结构化转换,按「学校类型→学校名称」两层分组,同时固定周一到周六的列顺序,缺失的日期数据自动留空,渲染时通过rowspan属性实现分组单元格跨行合并,完全匹配预期展示效果。

完整实现代码(React + TypeScript)

import React from 'react';

// 原始作息数据
const schoolTimings = [
  {
    schoolType: 'Primary School',
    list: [
      { school: 'ABC', day: 'Monday', open: '08:00', close: '12:30' },
      { school: 'ABC', day: 'Tuesday', open: '08:00', close: '12:30' },
      { school: 'ABC', day: 'Thursday', open: '08:00', close: '12:30' },
      { school: 'ABC', day: 'Friday', open: '08:00', close: '12:30' },
      { school: 'ABC', day: 'Saturday', open: '08:00', close: '12:30' },
      { school: 'XYZ', day: 'Monday', open: '08:00', close: '12:00' },
      { school: 'XYZ', day: 'Tuesday', open: '08:00', close: '12:00' },
      { school: 'XYZ', day: 'Wednesday', open: '08:00', close: '12:00' },
      { school: 'XYZ', day: 'Thursday', open: '08:00', close: '12:00' },
      { school: 'XYZ', day: 'Friday', open: '08:00', close: '12:00' },
      { school: 'XYZ', day: 'Saturday', open: '08:00', close: '12:00' }
    ]
  },
  {
    schoolType: 'Secondary School',
    list: [
      { school: 'PQR', day: 'Monday', open: '08:00', close: '15:30' },
      { school: 'PQR', day: 'Tuesday', open: '08:00', close: '15:30' },
      { school: 'PQR', day: 'Wednesday', open: '08:00', close: '15:30' },
      { school: 'PQR', day: 'Thursday', open: '08:00', close: '15:30' },
      { school: 'PQR', day: 'Friday', open: '08:00', close: '15:30' },
      { school: 'PQR', day: 'Saturday', open: '08:00', close: '15:30' },
      { school: 'XYZ', day: 'Monday', open: '08:00', close: '17:00' },
      { school: 'XYZ', day: 'Tuesday', open: '08:00', close: '17:00' },
      { school: 'XYZ', day: 'Wednesday', open: '08:00', close: '17:00' },
      { school: 'XYZ', day: 'Thursday', open: '08:00', close: '17:00' },
      { school: 'XYZ', day: 'Friday', open: '08:00', close: '17:00' },
      { school: 'XYZ', day: 'Saturday', open: '08:00', close: '17:00' }
    ]
  }
];

// 固定星期列顺序
const WEEK_DAYS = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];

type TimingItem = {
  school: string;
  day: string;
  open: string;
  close: string;
};

type ProcessedData = {
  schoolType: string;
  schools: {
    name: string;
    dayTimings: Record<string, { open: string; close: string } | undefined>;
  }[];
};

const SchoolTimetable: React.FC = () => {
  // 数据结构化预处理
  const processedData: ProcessedData[] = schoolTimings.map(typeItem => {
    const schoolMap = new Map<string, TimingItem[]>();
    typeItem.list.forEach(item => {
      if (!schoolMap.has(item.school)) {
        schoolMap.set(item.school, []);
      }
      schoolMap.get(item.school)!.push(item);
    });

    return {
      schoolType: typeItem.schoolType,
      schools: Array.from(schoolMap.entries()).map(([schoolName, timingList]) => {
        const dayTimings: Record<string, { open: string; close: string }> = {};
        timingList.forEach(item => {
          dayTimings[item.day] = { open: item.open, close: item.close };
        });
        return {
          name: schoolName,
          dayTimings
        };
      })
    };
  });

  // 计算学校类型单元格跨行数
  const getTypeRowSpan = (typeIndex: number) => {
    return processedData[typeIndex].schools.length;
  };

  return (
    <table border={1} cellPadding={8} cellSpacing={0} style={{ width: '100%', borderCollapse: 'collapse' }}>
      <thead>
        <tr>
          <th>学校类型</th>
          <th>学校名称</th>
          {WEEK_DAYS.map(day => (
            <th key={day}>{day}</th>
          ))}
        </tr>
      </thead>
      <tbody>
        {processedData.map((typeItem, typeIndex) => (
          <React.Fragment key={typeItem.schoolType}>
            {typeItem.schools.map((school, schoolIndex) => (
              <tr key={`${typeItem.schoolType}-${school.name}`}>
                {/* 仅该类型下第一行渲染学校类型单元格,设置跨行合并 */}
                {schoolIndex === 0 && (
                  <td rowSpan={getTypeRowSpan(typeIndex)} style={{ fontWeight: 'bold' }}>
                    {typeItem.schoolType === 'Primary School' ? '小学' : '中学'}
                  </td>
                )}
                <td>{school.name}</td>
                {WEEK_DAYS.map(day => {
                  const timing = school.dayTimings[day];
                  return (
                    <td key={day}>
                      {timing ? `${timing.open} ~ ${timing.close}` : '-'}
                    </td>
                  );
                })}
              </tr>
            ))}
          </React.Fragment>
        ))}
      </tbody>
    </table>
  );
};

export default SchoolTimetable;

说明

  • 自动合并相同学校类型的分组单元格,学校类型已做中文映射,可自行修改规则
  • 无作息数据的日期(如ABC小学周三)自动显示占位符-
  • 内置基础表格样式,可根据业务需求自行调整样式属性
  • 后续新增学校类型、学校、作息数据时无需修改渲染逻辑,兼容性强

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 04:12:03