如何实现FullCalendar事件超出工作时间后自动顺延至下一个工作日?
问题描述
我用React结合FullCalendar开发任务计划应用,已实现带特定时长的可拖拽事件。当前问题:当事件超出当天工作时间范围时,希望自动顺延至下一个工作日继续,而非限制在当天结束或显示在非工作时间里导致时间逻辑混乱。
已设置slotMinTime、slotMaxTime和自定义businessHours,试过nextDayThreshold但没达到预期。期望事件全部时长都显示在日历可见工作时间内,不隐藏在非工作时段。
现有代码
可拖拽事件配置
const containerEl = document.querySelector("#events"); let draggable = new Draggable(containerEl, { itemSelector: ".event", eventData: (eventEl) => { return { id: eventEl.innerText, title: eventEl.innerText, duration: eventEl.getAttribute('eventduration'), backgroundColor: eventEl.getAttribute('eventcolor'), borderColor: eventEl.getAttribute('eventcolor'), textColor: eventEl.getAttribute('eventtextcolor') }; } });
事件列表(任务)
<ul id="events" className="d-flex flex-wrap"> { data.map(item => { if(item.duration > 0) { let textClass = 'text-white'; if(item.textColor === '#000') { textClass = 'text-dark'; } return ( <li className={"event badge bg-" + item.status + " " + textClass + " d-block mb-2 me-2"} eventtextcolor={item.textColor} eventcolor={item.backgroundColor} eventduration={new Date(item.duration * 1000).toISOString().substring(11, 16)}>{item.title}</li> ) } }) } </ul>
日历配置
<FullCalendar plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]} initialView="timeGridWeek" editable slotMinTime="06:00" slotMaxTime="19:00" eventDurationEditable={false} droppable businessHours={{ daysOfWeek: [1, 2, 3, 4, 5, 6], // 周一到周六 startTime: '08:00', endTime: '17:00', }} slotDuration={'00:10:00'} allDaySlot={false} />
解决方案
要实现事件自动顺延至工作日,需手动计算事件起止时间,跳过非工作时段。核心逻辑是在事件被拖拽到日历(eventReceive)或移动(eventDrop)时,重新计算结束时间,确保所有时长落在工作时间内。
步骤1:编写工作时间计算工具函数
实现工具函数处理时间转换、工作日判断及实际结束时间计算:
// 转换HH:mm格式为分钟数 const timeToMinutes = (timeStr) => { const [hours, minutes] = timeStr.split(':').map(Number); return hours * 60 + minutes; }; // 判断日期是否为工作日(匹配businessHours的daysOfWeek) const isWorkDay = (date) => { const dayOfWeek = date.getDay(); // 0=周日,1=周一...6=周六 return [1,2,3,4,5,6].includes(dayOfWeek); }; // 获取当天工作起止时间(分钟数) const getDailyWorkBounds = () => { return { start: timeToMinutes('08:00'), end: timeToMinutes('17:00') }; }; // 计算实际结束时间:从startDate开始,经过指定工作时长后的时间 const calculateWorkingEndTime = (startDate, totalMinutes) => { const currentDate = new Date(startDate); let remainingMinutes = totalMinutes; const workBounds = getDailyWorkBounds(); while (remainingMinutes > 0) { // 非工作日直接跳转到下一天 if (!isWorkDay(currentDate)) { currentDate.setDate(currentDate.getDate() + 1); currentDate.setHours(8, 0, 0, 0); continue; } const currentMinutes = currentDate.getHours() * 60 + currentDate.getMinutes(); let availableMinutes = 0; if (currentMinutes < workBounds.start) { // 未到工作时间,跳转到当日工作开始时间 currentDate.setHours(8, 0, 0, 0); availableMinutes = workBounds.end - workBounds.start; } else if (currentMinutes < workBounds.end) { // 计算当日剩余工作时间 availableMinutes = workBounds.end - currentMinutes; } else { // 当日工作时间已过,跳转到下一个工作日开始 currentDate.setDate(currentDate.getDate() + 1); currentDate.setHours(8, 0, 0, 0); continue; } // 消耗当日可使用的时长 const useMinutes = Math.min(remainingMinutes, availableMinutes); remainingMinutes -= useMinutes; currentDate.setMinutes(currentDate.getMinutes() + useMinutes); } return currentDate; };
步骤2:修改日历配置,添加事件处理回调
在FullCalendar组件中添加eventReceive和eventDrop回调,更新事件结束时间:
<FullCalendar plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]} initialView="timeGridWeek" editable slotMinTime="06:00" slotMaxTime="19:00" eventDurationEditable={false} droppable businessHours={{ daysOfWeek: [1, 2, 3, 4, 5, 6], startTime: '08:00', endTime: '17:00', }} slotDuration={'00:10:00'} allDaySlot={false} // 处理拖拽进来的事件 eventReceive={(info) => { const durationMinutes = timeToMinutes(info.event.duration); const endTime = calculateWorkingEndTime(info.event.start, durationMinutes); info.event.setEnd(endTime); }} // 处理已存在事件的移动 eventDrop={(info) => { const durationMinutes = timeToMinutes(info.event.duration); const endTime = calculateWorkingEndTime(info.event.start, durationMinutes); info.event.setEnd(endTime); }} />
步骤3:优化拖拽事件的duration传递
在Draggable的eventData中,将duration转换为分钟数存入扩展属性,方便后续计算:
let draggable = new Draggable(containerEl, { itemSelector: ".event", eventData: (eventEl) => { const durationStr = eventEl.getAttribute('eventduration'); const durationMinutes = timeToMinutes(durationStr); return { id: eventEl.innerText, title: eventEl.innerText, duration: durationStr, extendedProps: { durationMinutes }, backgroundColor: eventEl.getAttribute('eventcolor'), borderColor: eventEl.getAttribute('eventcolor'), textColor: eventEl.getAttribute('eventtextcolor') }; } });
修改完成后,拖拽事件到日历上时,会自动跳过非工作时间,顺延至下一个工作日,所有时长均显示在工作时段内。
内容的提问来源于stack exchange,提问作者Lewis Self
相关产品推荐
相关产品推荐

