React中FullCalendar Resource Timeline View问题:跨视图事件不显示、事件添加无响应及插槽颜色点击切换需求实现
Hey there! Let's break down your issues and build that custom booking calendar you need, step by step.
1. 跨视图事件显示异常:普通视图事件在Resource Timeline不显示
问题根源
Resource Timeline视图是按资源分组渲染的,只有携带resourceId属性的事件才能匹配到对应的资源列。而你在dayGridMonth、timeGridWeek视图添加事件时,代码没有给事件分配resourceId,导致这些事件无法在资源视图中找到对应列,自然看不到。
修复方案
修改handleDateSelect函数,自动获取当前选中的资源ID(仅在Resource视图下存在),并给事件添加resourceId属性。同时,确保初始化事件INITIAL_EVENTS也包含resourceId:
const handleDateSelect = (selectInfo) => { let title = prompt('Please enter a new title for your event') let calendarApi = selectInfo.view.calendar calendarApi.unselect() // 清空日期选中状态 if (title) { // 获取当前选中的资源ID(Resource视图下才有这个属性) const resourceId = selectInfo.resource?.id; calendarApi.addEvent({ id: createEventId(), title, start: selectInfo.startStr, end: selectInfo.endStr, allDay: selectInfo.allDay, // 仅在Resource视图下添加resourceId,不影响普通视图显示 ...(resourceId && { resourceId }) }) } }
同时,更新你的初始化事件数组:
const INITIAL_EVENTS = [ { id: '1', title: 'Sample Booking', start: '2024-05-20', end: '2024-05-21', allDay: true, resourceId: 'a' // 关联到资源a } ]
2. 自定义视图customWeeks添加事件无响应
问题根源
你的FullCalendar配置里有一行错误的events设置:events={{ display: 'background' }}。这行代码会覆盖initialEvents的配置,且不符合FullCalendar的事件格式要求(events应接收数组、函数或URL,而非这种对象),导致事件添加后无法被正确渲染。
修复方案
直接删除这行错误配置,保留initialEvents即可:
<FullCalendar plugins={[interactionPlugin, resourceTimelinePlugin, dayGridPlugin, timeGridPlugin]} headerToolbar={{ left: 'prev', center: 'title', right: 'next,customWeeks,dayGridMonth,timeGridWeek,resourceTimelineWeek' }} initialView='customWeeks' views={{ customWeeks:{ type: 'resourceTimeline', duration: { weeks: 2 }, slotDuration: {days: 1}, buttonText: 'Custom Week' } }} // 移除错误的events={{ display: 'background' }}配置 resourceAreaWidth='80px' weekNumbers={true} resourceAreaHeaderContent=' ' // ...其余配置保持不变 />
结合第一个问题的修复,添加事件时携带resourceId,customWeeks视图就能正常显示新添加的事件了。
3. 核心需求:CustomWeeks视图插槽点击切换颜色(预订日历效果)
要实现插槽初始颜色+点击切换的效果,我们可以通过维护预订状态集合,结合slotRender钩子渲染背景色,再通过点击事件更新状态。
实现步骤
- 添加状态记录已预订的插槽(用
resourceId + 日期字符串作为唯一标识):
const [bookedSlots, setBookedSlots] = useState(new Set());
- 编写
slotRender函数,根据预订状态设置插槽背景色:
const handleSlotRender = (slotInfo) => { // 生成插槽唯一标识:resourceId + 日期(YYYY-MM-DD) const slotKey = `${slotInfo.resource.id}-${slotInfo.startStr.split('T')[0]}`; // 设置初始颜色和预订后颜色 slotInfo.el.style.backgroundColor = bookedSlots.has(slotKey) ? '#ffcccc' : '#e8f4f8'; // 给插槽绑定点击切换事件 slotInfo.el.addEventListener('click', () => { toggleSlotBooking(slotKey); }); }; // 切换预订状态的函数 const toggleSlotBooking = (slotKey) => { setBookedSlots(prev => { const newSet = new Set(prev); newSet.has(slotKey) ? newSet.delete(slotKey) : newSet.add(slotKey); return newSet; }); };
- 在FullCalendar组件中添加
slotRender配置,同时可按需调整select事件(区分添加事件和切换预订):
<FullCalendar // ...其余配置 slotRender={handleSlotRender} // 可选:区分添加事件和切换预订的操作 select={(selectInfo) => { const action = prompt('Choose action:\n1 = Add Event\n2 = Toggle Booking'); if (action === '1') { handleDateSelect(selectInfo); } else if (action === '2') { const slotKey = `${selectInfo.resource.id}-${selectInfo.startStr.split('T')[0]}`; toggleSlotBooking(slotKey); selectInfo.view.calendar.unselect(); } }} />
优化小贴士
- 可以把颜色提取成CSS变量,方便统一维护:
.seats-calendar .fc-slot { --default-slot: #e8f4f8; --booked-slot: #ffcccc; background-color: var(--default-slot); }
然后在slotRender中修改CSS变量:
slotInfo.el.style.setProperty('--default-slot', bookedSlots.has(slotKey) ? '#ffcccc' : '#e8f4f8');
内容的提问来源于stack exchange,提问作者DigitalSM

