如何动态实现agendaDay的rowspan?如何让资源表头与事件宽度一致?
针对fullCalendar AgendaDay视图的两个问题解决方案
我来帮你搞定这两个fullCalendar的问题,下面是具体的解决方案,都是基于你提供的代码来调整的:
一、动态实现AgendaDay视图的rowspan效果
AgendaDay结合资源分组(groupByDateAndResource)时,rowspan主要分资源表头合并和事件单元格合并两种场景,分别给你对应的实现方式:
1. 资源表头的rowspan(同一组资源合并表头)
如果你的资源需要按组合并表头,先给资源数据添加group标识(比如{id: 'res1', title: 'Room 1', group: 'Class A'}),然后通过resourceRender钩子动态设置rowspan:
resourceRender: function(resourceObj, labelTds, bodyTds) { // 先清除之前的rowspan,避免重复渲染冲突 labelTds.removeAttr('rowspan'); // 筛选同一组的所有资源 const sameGroupResources = $('#day-calendar').fullCalendar('getResources').filter(r => r.group === resourceObj.group); if (sameGroupResources.length > 1 && resourceObj === sameGroupResources[0]) { // 给组内第一个资源的表头设置rowspan labelTds.attr('rowspan', sameGroupResources.length); // 隐藏同组其他资源的表头 sameGroupResources.slice(1).forEach(r => { $('#day-calendar .fc-resource[data-resource-id="' + r.id + '"] .fc-resource-label').hide(); }); } }
2. 事件单元格的rowspan(同一资源下连续事件合并)
如果要把同一资源下的多个连续事件合并成一个单元格,在eventAfterAllRender钩子中计算事件时长并修改rowspan:
eventAfterAllRender: function(view) { // 按资源ID分组事件 const resourceEventsMap = {}; $('#day-calendar .fc-event').each(function() { const resourceId = $(this).data('resourceId'); if (!resourceEventsMap[resourceId]) resourceEventsMap[resourceId] = []; resourceEventsMap[resourceId].push(this); }); // 处理每组事件的合并 Object.values(resourceEventsMap).forEach(events => { if (events.length <= 1) return; // 计算合并后的rowspan:总时长 / 单个时间槽时长 const firstEventEl = $(events[0]); const event = firstEventEl.data('event'); const totalMinutes = moment.duration(event.end.diff(event.start)).asMinutes(); const slotMinutes = view.slotDuration.asMinutes(); const rowspan = Math.ceil(totalMinutes / slotMinutes); // 设置第一个事件的rowspan,隐藏其他事件 firstEventEl.attr('rowspan', rowspan); events.slice(1).forEach(el => $(el).hide()); }); }
二、修改特定列表头样式 + 资源表头与事件宽度同步
1. 动态修改带标题的特定列表头样式
fullCalendar的资源表头有fc-resource-label类,你可以通过资源标题或资源ID精准定位,然后修改样式:
- 静态CSS方式(直接写样式):
/* 匹配标题包含"Venue 1"的表头 */ .fc-resource-label:contains("Venue 1") { background: #f5f5f5; font-weight: bold; } /* 匹配特定ID的资源表头 */ .fc-resource[data-resource-id="res-2"] .fc-resource-label { color: #2f54eb; }
- 动态JS方式(事件加载后修改):
在eventsRender钩子中,根据事件关联的资源动态调整:
eventsRender: function(events, view) { // 示例:修改第一个事件对应的资源表头样式 if (events.length === 0) return; const targetResourceId = events[0].resourceId; $('#day-calendar .fc-resource[data-resource-id="' + targetResourceId + '"] .fc-resource-label') .css('background-color', '#e6f7ff') .addClass('custom-header-style'); }
2. 让资源表头与事件宽度保持一致
你已经通过eventAfterRender把事件设为200px,只需在eventAfterAllRender中同步资源表头和列的宽度即可:
eventAfterAllRender: function(view) { // 获取事件的实际宽度(如果是固定200px也可以直接写死) const eventWidth = $('#day-calendar .fc-event').first().width() || 200; // 同步资源表头和资源列的宽度 $('#day-calendar .fc-resource-label, #day-calendar .fc-resource').css('width', `${eventWidth}px`); }
如果需要动态适配容器宽度(比如资源数量变化时自动调整),可以先计算宽度再同步:
// 先计算动态宽度:容器宽度 / 资源数量 const containerWidth = $('#day-calendar').width(); const resourceCount = $('#day-calendar').fullCalendar('getResources').length; const dynamicWidth = Math.floor(containerWidth / resourceCount); // 在eventAfterRender中设置事件宽度 eventAfterRender: function(event, element, view) { $(element).css('width', `${dynamicWidth}px`); }, // 在eventAfterAllRender中同步表头宽度 eventAfterAllRender: function(view) { $('#day-calendar .fc-resource-label, #day-calendar .fc-resource').css('width', `${dynamicWidth}px`); }
整合后的完整代码
把上面的逻辑整合到你的initCalendar函数中:
function initCalendar(myresources) { $('#day-calendar').fullCalendar({ defaultView: 'agendaDay', defaultDate: $scope.setDate, aspectRatio: 1.8, editable: true, selectable: true, eventLimit: true, slotEventOverlap: true, minTime: $scope.lessonStartTime, maxTime: $scope.lessonEndTime, contentHeight: 'auto', slotLabelInterval: $scope.lessonTimeInterval, slotLabelFormat: "HH:mm:A", header: { left: '', center: 'Time', right: '' }, views: { agendaDay: { groupByDateAndResource: true, } }, selectHelper: true, resourceLabelText: "Venue", allDaySlot: false, resources: myresources, events: eventsData, eventAfterRender: function(event, element, view) { $(element).css('width', '200px'); }, // 资源表头rowspan逻辑 resourceRender: function(resourceObj, labelTds, bodyTds) { labelTds.removeAttr('rowspan'); const sameGroupResources = $('#day-calendar').fullCalendar('getResources').filter(r => r.group === resourceObj.group); if (sameGroupResources.length > 1 && resourceObj === sameGroupResources[0]) { labelTds.attr('rowspan', sameGroupResources.length); sameGroupResources.slice(1).forEach(r => { $('#day-calendar .fc-resource[data-resource-id="' + r.id + '"] .fc-resource-label').hide(); }); } }, // 事件合并+宽度同步逻辑 eventAfterAllRender: function(view) { // 同步资源表头与事件宽度 const eventWidth = $('#day-calendar .fc-event').first().width() || 200; $('#day-calendar .fc-resource-label, #day-calendar .fc-resource').css('width', `${eventWidth}px`); // 事件单元格合并逻辑(可选) const resourceEventsMap = {}; $('#day-calendar .fc-event').each(function() { const resourceId = $(this).data('resourceId'); if (!resourceEventsMap[resourceId]) resourceEventsMap[resourceId] = []; resourceEventsMap[resourceId].push(this); }); Object.values(resourceEventsMap).forEach(events => { if (events.length <= 1) return; const firstEventEl = $(events[0]); const event = firstEventEl.data('event'); const totalMinutes = moment.duration(event.end.diff(event.start)).asMinutes(); const slotMinutes = view.slotDuration.asMinutes(); const rowspan = Math.ceil(totalMinutes / slotMinutes); firstEventEl.attr('rowspan', rowspan); events.slice(1).forEach(el => $(el).hide()); }); }, // 动态修改特定表头样式 eventsRender: function(events, view) { if (events.length === 0) return; const targetResourceId = events[0].resourceId; $('#day-calendar .fc-resource[data-resource-id="' + targetResourceId + '"] .fc-resource-label') .css('background-color', '#e6f7ff') .addClass('custom-header-style'); } }) }
内容的提问来源于stack exchange,提问作者Vish153
相关产品推荐
相关产品推荐

