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
相关产品推荐
相关产品推荐

