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

React中FullCalendar如何为工作日与周末设置差异化时间分组?

需求实现:FullCalendar timeGridWeek视图工作日/周末差异化时间分组及额外列插入

问题描述

已给FullCalendar的timeGridWeek视图配置左侧时间分组为「6-2、2-10、10-6」,工作日显示正常,但希望周末(周六、周日)将时间分组改为「6am-6pm、6pm-6am」,理想状态下能在周五与周六之间插入额外列实现区分效果。

试过自定义视图等多种方法,但始终无法理清如何扩展现有视图或创建自定义视图,因场景小众,明确需要自定义功能但无从下手,技术栈为React。

当前配置代码

<FullCalendar
  schedulerLicenseKey="redacted"
  plugins={[
    dayGridPlugin, 
    timeGridPlugin, 
    interactionPlugin, 
    timeLinePlugin, 
    resourceTimelinePlugin,
    scrollGridPlugin
  ]}
  timeZone="GMT"
  initialView={view}
  firstDay={1}
  headerToolbar={showToolbar ? {
    left: allowNavigation ? 'prev,next today' : '',
    center: 'title',
    right: allowNavigation ? 'timeGridWeek,timeGridDay,dayGridMonth' : ''
  } : false}
  events={events}
  contentHeight={750}
  eventClick={handleEventClick}
  slotMinTime="06:00:00"
  slotMaxTime="30:00:00"
  expandRows={true}
  stickyHeaderDates={true}
  nowIndicator={nowIndicator}
  slotDuration="08:00:00"
  slotLabelInterval="08:00:00"
  slotLabelFormat={(arg: { date: { marker: Date } }) => {
    const hour = arg.date.marker.getHours();
    if (hour === 7) return '6-2';
    if (hour === 15) return '2-10';
    return '10-6';
  }}
  allDaySlot={false}
  dayHeaderFormat={{
    weekday: view === 'dayGridMonth' ? 'short' : 'short',
    day: view === 'dayGridMonth' ? 'numeric' : 'numeric',
    omitCommas: true
  }}
  views={{
    dayGridMonth: {
      dayHeaderFormat: { weekday: 'long' }
    },
    timeGridWeek: {
      dayHeaderFormat: { weekday: 'short', day: showDateNumbers ? 'numeric' : undefined }
    },
    timeGridDay: {
      dayHeaderFormat: { weekday: 'long', day: showDateNumbers ? 'numeric' : undefined, month: 'long' }
    },
    timeGridMonth: {
      type: 'timeGrid',
      duration: { months: 1 },
      dayMinWidth: 150,
      buttonText: 'timeline',
      dayHeaderFormat: {
        weekday: 'short',
        day: 'numeric',
        month: 'short',
        omitCommas: true
      },
      eventMaxStack: 6,
      eventContent: (arg) => {
        const content = renderEventContent({ event: arg.event });
        return {
          html: `<div class="event-content-wrapper">${
            typeof content === 'string' ? content : ReactDOMServer.renderToString(content as ReactElement)
          }</div>`
        };
      }
    }
  }}
  eventClassNames={(arg) => eventClassNames?.({ event: arg.event }) || ''}
  eventContent={(arg) => renderEventContent({ event: arg.event })}
  dayMaxEvents={true}
/>

解决方案

一、工作日/周末差异化时间分组

核心是在slotLabelFormat中结合日期判断周几,动态返回分组标签,同时动态调整slot的时间间隔配置:

  1. 修改slotLabelFormat回调
    通过arg.date.marker获取完整日期,判断是否为周末,返回对应标签:
slotLabelFormat={(arg) => {
  const date = arg.date.marker;
  const dayOfWeek = date.getDay(); // 0=周日,1-5=周一至周五,6=周六
  const hour = date.getHours();

  // 周末逻辑
  if (dayOfWeek === 0 || dayOfWeek === 6) {
    if (hour === 6) return '6am-6pm';
    if (hour === 18) return '6pm-6am';
    return ''; // 中间小时不显示标签
  }

  // 工作日逻辑
  if (hour === 7) return '6-2';
  if (hour === 15) return '2-10';
  return '10-6';
}}
  1. 动态调整slotDuration和slotLabelInterval
    通过视图钩子监听日期变化,判断当前周是否包含周末,动态修改配置:
import { useRef } from 'react';

const calendarRef = useRef(null);

const handleViewUpdate = (info) => {
  const view = info.view;
  const startDate = new Date(view.activeStart);
  const endDate = new Date(view.activeEnd);
  let hasWeekend = false;

  // 遍历日期判断是否包含周末
  const currentDate = new Date(startDate);
  while (currentDate < endDate) {
    const day = currentDate.getDay();
    if (day === 0 || day === 6) {
      hasWeekend = true;
      break;
    }
    currentDate.setDate(currentDate.getDate() + 1);
  }

  const calendarApi = calendarRef.current.getApi();
  if (hasWeekend) {
    calendarApi.setOption('slotDuration', '12:00:00');
    calendarApi.setOption('slotLabelInterval', '12:00:00');
  } else {
    calendarApi.setOption('slotDuration', '08:00:00');
    calendarApi.setOption('slotLabelInterval', '08:00:00');
  }
};

// 在FullCalendar组件中添加钩子
<FullCalendar
  ref={calendarRef}
  // ...其他配置
  viewDidMount={handleViewUpdate}
  viewDidUpdate={handleViewUpdate}
/>

二、周五与周六之间插入额外列

推荐使用资源视图方案,符合FullCalendar设计规范:

  1. 切换为resourceTimeGridWeek视图
    利用已引入的resourceTimelinePlugin,将日期转为资源,并在周五后插入分隔资源:
const getWeekResources = (startDate) => {
  const resources = [];
  const currentDate = new Date(startDate);
  // firstDay=1(周一),循环7天
  for (let i = 0; i < 7; i++) {
    resources.push({
      id: `day-${i}`,
      title: `${currentDate.toLocaleDateString('en-US', { weekday: 'short', day: 'numeric' })}`,
      date: new Date(currentDate)
    });
    // 周五是第4个索引(0=周一,4=周五),插入分隔列
    if (i === 4) {
      resources.push({
        id: 'separator',
        title: '',
        className: 'weekend-separator'
      });
    }
    currentDate.setDate(currentDate.getDate() + 1);
  }
  return resources;
};

// 修改FullCalendar配置
<FullCalendar
  ref={calendarRef}
  initialView="resourceTimeGridWeek"
  resources={getWeekResources(new Date())}
  // 日期切换时更新资源
  datesSet={(info) => {
    const resources = getWeekResources(info.start);
    calendarRef.current.getApi().removeAllResources();
    calendarRef.current.getApi().addResource(resources);
  }}
  // ...其他配置
/>
  1. CSS美化分隔列
.weekend-separator {
  width: 20px;
  background-color: #f5f5f5;
  border-left: 2px solid #ddd;
}

三、注意事项

  • 动态修改配置时依赖FullCalendar的setOptionAPI,需确保版本兼容性
  • 时区为GMT,处理日期时需注意时区转换,避免周几判断错误
  • 资源视图方案维护性更强,优先选择;若需快速实现,也可通过DOM操作直接插入列(但需处理视图更新时的重新渲染问题)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 22:17:07