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

FullCalendar React多日事件渲染自定义:中间日期误判有事件

问题:FullCalendar多日事件中间日期误判有事件的修复

需求:修改FullCalendar React组件的多日事件(3天及以上)渲染逻辑,在dayGridMonth初始视图下,仅在事件的起始日期和结束日期渲染事件,不渲染中间日期。

现有问题:即使renderEventContent在中间日期返回null,dateHasEvent仍判定该日期存在事件,导致日期单元格显示有事件标记(has-event类)但实际无内容。


现有代码

FullCalendar组件声明

<FullCalendar
            ref={calendarRef}
            plugins={[dayGridPlugin]}
            initialView="dayGridMonth"
            themeSystem="bootstrap"
            dayMaxEvents={0}
            direction={isRTL ? 'rtl' : 'ltr'}
            stickyHeaderDates={false}
            editable={false}
            selectable={false}
            selectMirror
            eventTimeFormat={eventTimeFormat}
            events={events}
            initialDate={new Date()}
            style={{ fontSize: '5px' }}
            headerToolbar={{
              start: 'title',
              center: '',
              end: `today prev,next`
            }}
            buttonText={{
              today: t('common:today'),
              month: t('common:month'),
              week: t('common:week'),
              day: t('common:day'),
              list: t('common:list')
            }}
            moreLinkDidMount={args => dateHasEvent(args)}
            eventContent={renderEventContent}
          />

dateHasEvent函数

const dateHasEvent = info => {
  const dayCell = info?.el?.closest('.fc-daygrid-day');

  if (dayCell) {
    const datCellNumber = dayCell?.querySelector('.fc-daygrid-day-number');

    if (datCellNumber) {
      datCellNumber?.classList?.add('has-event');
    }

    const dayEvents = dayCell
      ?.querySelector('.fc-daygrid-day-events')
      ?.querySelector('.fc-daygrid-day-bottom');

    if (dayEvents) {
      const dayEventsLink = dayEvents?.querySelector('a');

      if (dayEventsLink) {
        dayCell?.addEventListener('mouseover', function () {
          let popOver = document?.querySelector('.fc-popover');

          if (popOver) {
            popOver?.querySelector('.fc-popover-close').click();
          }

          dayEventsLink?.click();
        });
      }

      dayEvents?.classList?.add('day-cell-events-removal');
    }
  }
};

renderEventContent函数

const renderEventContent = eventInfo => {
  if (!eventInfo.isEnd && !eventInfo.isStart) {
    return null;
  }
  return (
    <>
      {eventInfo.isStart && eventInfo.isStart != eventInfo.isEnd && (
        <div
          style={{
            backgroundColor: 'green',
            padding: '5px 0px 5px 0px',
            borderRadius: '20px',
            textAlign: 'center',
            fontWeight: 'bold'
          }}
        >
          {eventInfo.event.title}
        </div>
      )}
      {eventInfo.isEnd && (
        <div
          style={{
            backgroundColor: 'red',
            padding: '5px 5px 5px 5px',
            borderRadius: '20px',
            textAlign: 'center',
            fontWeight: 'bold'
          }}
        >
          {eventInfo.event.title}
        </div>
      )}
    </>
  );
};

解决方案

问题根源在于moreLinkDidMount触发的dateHasEvent函数,只要日期关联了事件(哪怕是中间日期)就会添加has-event类并处理事件逻辑,忽略了该日期是否是事件的起止节点。

修改方案1:在dateHasEvent中判断是否为事件起止日期

修改dateHasEvent函数,先判断当前日期是否是事件的起始或结束日期,仅在符合条件时执行后续逻辑:

const dateHasEvent = info => {
  const event = info.event;
  const dayCell = info?.el?.closest('.fc-daygrid-day');
  if (!dayCell) return;

  // 获取当前单元格的日期(格式化为YYYY-MM-DD)
  const cellDate = dayCell.dataset.date;
  const startDate = event.start.toISOString().split('T')[0];
  const endDate = event.end.toISOString().split('T')[0];

  // 如果是事件的中间日期,移除可能存在的has-event类并返回
  if (cellDate !== startDate && cellDate !== endDate) {
    const datCellNumber = dayCell.querySelector('.fc-daygrid-day-number');
    datCellNumber?.classList?.remove('has-event');
    return;
  }

  // 原有的起止日期处理逻辑
  const datCellNumber = dayCell.querySelector('.fc-daygrid-day-number');
  if (datCellNumber) {
    datCellNumber.classList.add('has-event');
  }

  const dayEvents = dayCell
    ?.querySelector('.fc-daygrid-day-events')
    ?.querySelector('.fc-daygrid-day-bottom');

  if (dayEvents) {
    const dayEventsLink = dayEvents.querySelector('a');
    if (dayEventsLink) {
      dayCell.addEventListener('mouseover', function () {
        const popOver = document.querySelector('.fc-popover');
        if (popOver) {
          popOver.querySelector('.fc-popover-close').click();
        }
        dayEventsLink.click();
      });
    }
    dayEvents.classList.add('day-cell-events-removal');
  }
};

修改方案2:在renderEventContent中控制单元格样式

另一种思路是将has-event类的添加/移除逻辑移到renderEventContent中,仅在渲染事件内容时添加类,中间日期直接移除类:

const renderEventContent = eventInfo => {
  const dayCell = eventInfo.el.closest('.fc-daygrid-day');
  const datCellNumber = dayCell?.querySelector('.fc-daygrid-day-number');

  if (!eventInfo.isEnd && !eventInfo.isStart) {
    // 中间日期:移除事件标记类
    datCellNumber?.classList?.remove('has-event');
    // 移除事件容器的自定义类
    const dayEvents = dayCell?.querySelector('.fc-daygrid-day-events')?.querySelector('.fc-daygrid-day-bottom');
    dayEvents?.classList?.remove('day-cell-events-removal');
    return null;
  }

  // 起止日期:添加事件标记类
  datCellNumber?.classList?.add('has-event');
  // 添加事件容器的自定义类
  const dayEvents = dayCell?.querySelector('.fc-daygrid-day-events')?.querySelector('.fc-daygrid-day-bottom');
  dayEvents?.classList?.add('day-cell-events-removal');

  // 绑定鼠标事件(如果需要)
  const dayEventsLink = dayEvents?.querySelector('a');
  if (dayEventsLink) {
    dayCell.addEventListener('mouseover', function () {
      const popOver = document.querySelector('.fc-popover');
      if (popOver) {
        popOver.querySelector('.fc-popover-close').click();
      }
      dayEventsLink.click();
    });
  }

  return (
    <>
      {eventInfo.isStart && eventInfo.isStart !== eventInfo.isEnd && (
        <div
          style={{
            backgroundColor: 'green',
            padding: '5px 0',
            borderRadius: '20px',
            textAlign: 'center',
            fontWeight: 'bold'
          }}
        >
          {eventInfo.event.title}
        </div>
      )}
      {eventInfo.isEnd && (
        <div
          style={{
            backgroundColor: 'red',
            padding: '5px',
            borderRadius: '20px',
            textAlign: 'center',
            fontWeight: 'bold'
          }}
        >
          {eventInfo.event.title}
        </div>
      )}
    </>
  );
};

使用方案2时,可以移除FullCalendar组件上的moreLinkDidMount属性,避免重复处理。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 10:55:57