FullCalendar 5中如何修改RRule生成事件并单独维护Done状态
问题
数据库采用RecurringEvent对象存储重复规则(rrule),IndividualEvent对象存储规则的单个偏差,两者均包含"Done"属性。目标是在FullCalendar中让同一重复规则下的每个事件能单独显示自身的"Done"状态,但当前修改单个事件的extendedProps时,同一rrule下的所有事件都会被同步修改。
当前实现代码:
添加重复事件:
calendar.addEvent({ title: item['title'], rrule: { dtstart: item['startDate'], freq: item['frequency'] }, allDay: item['allDay'], backgroundColor: backgroundRgb, textColor: computeTextColor(backgroundRgb), extendedProps: { recurringEventID: item['recurringEventID'], description: item['description'] }, });
修改单个事件状态:
var events = calendar.getEvents(); individualEventList.forEach(function(item){ events.forEach(function(event){ var indivdate = new Date(item['startDate']).toLocaleDateString('en-CA'); var eventdate = event.start.toLocaleDateString('en-CA'); //if the event is on the correct date and is a part of the rrule... if(event.extendedProps.recurringEventID == item['recurringID'] && eventdate == indivdate) { console.log("match!"); event.setExtendedProp('singleID',item['individualEventID']) //then modify its 'done' state event.setExtendedProp('done',item['done']); } }) })
现象:控制台"match!"仅触发一次,但同一rrule下所有事件的extendedProps均显示为已完成。
核心原因
FullCalendar中通过rrule生成的重复事件,本质是同一个事件对象的多个渲染实例,它们共享底层数据结构。修改其中一个实例的extendedProps,会直接修改共享的底层数据,导致所有实例同步变化。
解决方案
方案1:拆分重复事件为独立事件(推荐)
放弃rrule批量生成逻辑,改用rrule.js计算出当前日历视图内的所有重复日期,逐个创建独立事件,再用偏差事件的状态覆盖对应日期的事件。每个事件都是独立对象,修改不会互相影响。
示例代码:
// 1. 处理重复事件,生成当前视图内的所有独立事件 recurringEventList.forEach(function(recurItem) { // 初始化rrule规则 const rule = new RRule({ dtstart: new Date(recurItem['startDate']), freq: RRule[recurItem['frequency'].toUpperCase()] // 需对应RRule常量,如RRule.DAILY }); // 获取当前日历视图的日期范围 const view = calendar.view; const dates = rule.between(view.activeStart, view.activeEnd, true); dates.forEach(function(date) { calendar.addEvent({ title: recurItem['title'], start: date, allDay: recurItem['allDay'], backgroundColor: backgroundRgb, textColor: computeTextColor(backgroundRgb), extendedProps: { recurringEventID: recurItem['recurringEventID'], description: recurItem['description'], done: false // 默认未完成 } }); }); }); // 2. 覆盖偏差事件的状态 individualEventList.forEach(function(item) { const targetDate = new Date(item['startDate']); // 查找对应重复ID和日期的事件 const matchingEvents = calendar.getEvents({ filter: function(event) { return event.extendedProps.recurringEventID === item['recurringID'] && event.start.toLocaleDateString('en-CA') === targetDate.toLocaleDateString('en-CA'); } }); if (matchingEvents.length > 0) { const event = matchingEvents[0]; event.setExtendedProp('singleID', item['individualEventID']); event.setExtendedProp('done', item['done']); // 可选:修改已完成事件的样式 if (item['done']) { event.setProp('backgroundColor', '#cccccc'); } } });
方案2:用exdate排除+独立事件处理偏差
保留rrule生成的重复事件,将已完成的偏差日期加入rrule的exdate排除列表,同时添加独立的已完成事件。原重复事件会跳过排除日期,显示独立的已完成事件。
示例代码:
// 1. 收集需要排除的已完成日期 const excludeDates = []; individualEventList.forEach(function(item) { if (item['done']) { excludeDates.push(new Date(item['startDate']).toISOString()); } }); // 2. 添加带排除规则的重复事件 recurringEventList.forEach(function(item) { const rruleConfig = { dtstart: item['startDate'], freq: item['frequency'], exdate: excludeDates }; calendar.addEvent({ title: item['title'], rrule: rruleConfig, allDay: item['allDay'], backgroundColor: backgroundRgb, textColor: computeTextColor(backgroundRgb), extendedProps: { recurringEventID: item['recurringEventID'], description: item['description'], done: false } }); }); // 3. 添加独立的已完成事件 individualEventList.forEach(function(item) { if (item['done']) { calendar.addEvent({ title: item['title'], start: item['startDate'], allDay: item['allDay'], backgroundColor: '#cccccc', // 用不同样式区分已完成 textColor: '#000000', extendedProps: { recurringEventID: item['recurringID'], singleID: item['individualEventID'], description: item['description'], done: true } }); } });
方案对比
- 方案1适合需要频繁修改单个重复实例状态的场景,所有事件独立,状态隔离彻底;需注意仅计算当前视图内的日期,避免性能问题。
- 方案2利用FullCalendar原生机制,性能更优,但仅适合"已完成"这类需要从原序列中移除并单独显示的场景。
内容的提问来源于stack exchange,提问作者S. B.
相关产品推荐
相关产品推荐

