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

如何在时间线视图中按自定义天数设置自定义插槽标签?

当然可行!完全不用修改monthName数组,你可以通过FullCalendar的内置配置和事件,轻松实现按自定义天数划分的阶段标签。下面给你两种实用的实现思路,按需选择:

方案1:利用slotLabelFormat自定义槽位标签

这种方式适合只想在时间槽上显示阶段名称的场景,通过回调函数计算每个日期所属的阶段,精准控制标签显示时机。

document.addEventListener('DOMContentLoaded', function() {
  // 先定义你的阶段规则:[阶段名称, 该阶段天数]
  const phaseConfig = [
    { name: '阶段1', days: 20 },
    { name: '阶段2', days: 55 },
    { name: '阶段3', days: 150 }
  ];

  const calendar = new FullCalendar.Calendar(document.getElementById('calendar'), {
    defaultView: 'timeline12Month',
    views: {
      timeline12Month: {
        type: 'timeline',
        duration: { week: 52 },
        slotDuration: { day: 1 }, // 强制每个槽位对应1天,确保计算精准
        slotLabelFormat: function(date) {
          // 计算当前日期与视图起始日的天数差
          const viewStart = calendar.view.activeStart;
          const dayDifference = Math.floor((date - viewStart) / (1000 * 60 * 60 * 24));
          
          // 匹配对应的阶段
          let currentPhase = '后续阶段';
          let cumulativeDays = 0;
          for (let phase of phaseConfig) {
            cumulativeDays += phase.days;
            if (dayDifference < cumulativeDays) {
              currentPhase = phase.name;
              break;
            }
          }

          // 只在阶段的第一天显示标签,避免重复刷屏
          const phaseStartDays = phaseConfig.reduce((acc, cur, idx) => {
            acc.push(idx === 0 ? 0 : acc[idx-1] + cur.days);
            return acc;
          }, []);
          if (phaseStartDays.includes(dayDifference)) {
            return currentPhase;
          }
          return ''; // 非阶段起始日不显示标签
        }
      }
    }
  });

  calendar.render();
});
方案2:通过datesRender事件手动添加阶段标签(带样式)

如果需要更灵活的视觉效果(比如给阶段加背景色、标签居中显示),可以用视图渲染事件手动生成标签和样式,自由度更高。

document.addEventListener('DOMContentLoaded', function() {
  const phaseConfig = [
    { name: '阶段1', days: 20, bgColor: '#e8f5e9' },
    { name: '阶段2', days: 55, bgColor: '#fff3e0' },
    { name: '阶段3', days: 150, bgColor: '#f3e5f5' }
  ];

  const calendar = new FullCalendar.Calendar(document.getElementById('calendar'), {
    defaultView: 'timeline12Month',
    views: {
      timeline12Month: {
        type: 'timeline',
        duration: { week: 52 },
        slotDuration: { day: 1 }
      }
    },
    datesRender: function(info) {
      // 先清除旧标签,避免重复渲染
      info.el.querySelectorAll('.custom-phase-tag').forEach(tag => tag.remove());
      
      const viewStart = info.view.activeStart;
      let dayOffset = 0;

      phaseConfig.forEach(phase => {
        // 计算当前阶段的起止日期
        const phaseStart = new Date(viewStart);
        phaseStart.setDate(viewStart.getDate() + dayOffset);
        const phaseEnd = new Date(viewStart);
        phaseEnd.setDate(viewStart.getDate() + dayOffset + phase.days - 1);

        // 给阶段内的所有槽位添加背景色
        const allSlots = info.el.querySelectorAll('.fc-slats .fc-time-grid-slot');
        let phaseSlotCount = 0;
        allSlots.forEach(slot => {
          const slotDate = new Date(slot.dataset.date);
          if (slotDate >= phaseStart && slotDate <= phaseEnd) {
            slot.style.backgroundColor = phase.bgColor;
            phaseSlotCount++;
          }
        });

        // 在阶段中间位置添加居中标签
        if (phaseSlotCount > 0) {
          const firstSlot = info.el.querySelector(`.fc-slats .fc-time-grid-slot[data-date="${phaseStart.toISOString().split('T')[0]}"]`);
          if (firstSlot) {
            const tagEl = document.createElement('div');
            tagEl.className = 'custom-phase-tag';
            tagEl.textContent = phase.name;
            tagEl.style.cssText = `
              position: absolute;
              top: 50%;
              left: 50%;
              transform: translate(-50%, -50%);
              font-weight: bold;
              width: ${phaseSlotCount * (100 / info.view.slotCount)}%;
              margin-left: -${(phaseSlotCount * (100 / info.view.slotCount))/2}%;
            `;
            firstSlot.appendChild(tagEl);
          }
        }

        dayOffset += phase.days;
      });
    }
  });

  calendar.render();
});

注意事项

  • 两种方案都依赖slotDuration: { day: 1 },确保每个时间槽对应一天,天数计算不会出错。
  • 如果视图的起始日期动态变化,代码会自动基于当前视图的起始日重新计算阶段,无需额外调整。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:47:38