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

如何在FullCalendar中合并(折叠/展开)相似事件

嘿,我之前也在FullCalendar里折腾过这个折叠同标题重叠事件的需求,官方确实没直接提供这个功能,不过咱们可以自己动手实现!给你分享一个我试过可行的方案:

实现思路

核心是在所有事件渲染完成后,按标题分组并筛选出时间重叠的事件,然后给每组添加折叠/展开的触发控件,通过CSS和JS控制事件的显示隐藏,完美实现你要的折叠效果。

1. 核心代码实现

首先在FullCalendar的配置里,利用eventsRender钩子(所有事件渲染完成后触发)来处理分组和折叠逻辑:

document.addEventListener('DOMContentLoaded', function() {
  const calendarEl = document.getElementById('calendar');
  const calendar = new FullCalendar.Calendar(calendarEl, {
    // 你的基础日历配置(比如视图、事件源等)
    initialView: 'dayGridMonth',
    events: [
      // 示例事件数据,替换成你的实际数据
      {id: '1', title: '重复会议', start: '2024-05-20T09:00:00', end: '2024-05-20T10:30:00'},
      {id: '2', title: '重复会议', start: '2024-05-20T10:00:00', end: '2024-05-20T11:30:00'},
      {id: '3', title: '单独任务', start: '2024-05-20T14:00:00', end: '2024-05-20T15:00:00'},
    ],
    eventsRender: function(info) {
      // 1. 按事件标题分组
      const eventGroups = {};
      const eventEls = info.el.querySelectorAll('.fc-event');
      
      eventEls.forEach(el => {
        const title = el.querySelector('.fc-event-title').textContent;
        const eventId = el.dataset.eventId;
        const event = calendar.getEventById(eventId);
        
        if (!eventGroups[title]) {
          eventGroups[title] = [];
        }
        eventGroups[title].push({ el, event });
      });

      // 2. 处理每个分组,筛选重叠事件并添加折叠功能
      Object.values(eventGroups).forEach(group => {
        if (group.length <= 1) return; // 单个事件无需处理

        // 筛选出当前分组中时间重叠的事件
        const overlappingEvents = group.filter((item, index) => {
          if (index === 0) return true;
          const prevEvent = group[index-1].event;
          return isEventsOverlapping(item.event, prevEvent);
        });

        if (overlappingEvents.length <= 1) return;

        // 给第一个事件添加折叠触发三角
        const firstEventEl = overlappingEvents[0].el;
        const caret = document.createElement('span');
        caret.className = 'fc-event-caret';
        caret.textContent = '▼';
        firstEventEl.appendChild(caret);

        // 默认隐藏组内其他重叠事件
        overlappingEvents.slice(1).forEach(item => {
          item.el.classList.add('fc-event-hidden');
        });

        // 绑定点击事件切换展开/收起状态
        caret.addEventListener('click', () => {
          const isExpanded = caret.textContent === '▼';
          caret.textContent = isExpanded ? '▲' : '▼';
          overlappingEvents.slice(1).forEach(item => {
            item.el.classList.toggle('fc-event-hidden');
          });
        });
      });
    }
  });

  calendar.render();
});

// 辅助函数:判断两个事件是否时间重叠
function isEventsOverlapping(eventA, eventB) {
  return eventA.start < eventB.end && eventA.end > eventB.start;
}

2. 配套CSS样式

添加以下CSS来控制隐藏状态和三角标记的样式:

/* 隐藏折叠的事件 */
.fc-event-hidden {
  display: none !important;
}

/* 折叠三角的样式 */
.fc-event-caret {
  float: right;
  cursor: pointer;
  font-size: 0.8em;
  margin-left: 6px;
  color: #666;
}

3. 关键注意事项

  • 如果你的事件是异步加载的(比如从接口获取),eventsRender钩子依然能正常触发,因为它会在所有事件渲染完成后执行
  • 重叠判断逻辑可以根据需求调整:比如你可以改成只合并完全重叠的事件,或者调整时间范围的判断规则
  • 如果你用的是FullCalendar v5+,注意事件元素的类名可能略有变化(比如.fc-event-main),需要对应修改代码中的选择器
  • 可以进一步优化分组逻辑,比如只处理同一天内的同标题重叠事件,避免跨天事件被错误折叠

这个方案不会像你之前找到的案例那样把事件合并成一个,而是保留原事件的独立性,通过显示隐藏实现折叠/展开,完全符合你的需求!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 09:30:52