Angular中FullCalendar自定义5分钟最小时长时间网格视图异常问题
Angular FullCalendar 5分钟槽位时间网格首个槽位样式异常修复
问题背景
我在开发Angular应用,用FullCalendar构建类似谷歌日历的组件,目标是支持添加最小时长5分钟的事件,已配置核心参数:
slotDuration: '00:05:00', slotLabelInterval: '01:00:00',
目前已经实现5分钟事件的创建功能,但自定义时间网格样式时碰到问题:通过CSS调整槽位高度后,次要槽位显示正常,首个时间网格槽位表现不符合预期。
相关代码
App组件TS代码
import { Component, signal, ChangeDetectorRef } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterOutlet } from '@angular/router'; import { FullCalendarModule } from '@fullcalendar/angular'; import { CalendarOptions, DateSelectArg, EventClickArg, EventApi, } from '@fullcalendar/core'; import interactionPlugin from '@fullcalendar/interaction'; import dayGridPlugin from '@fullcalendar/daygrid'; import timeGridPlugin from '@fullcalendar/timegrid'; import { INITIAL_EVENTS, createEventId } from './event-utils'; @Component({ selector: 'app-root', standalone: true, imports: [CommonModule, RouterOutlet, FullCalendarModule], templateUrl: './app.component.html', styleUrl: './app.component.scss', }) export class AppComponent { calendarVisible = signal(true); calendarOptions = signal<CalendarOptions>({ plugins: [interactionPlugin, dayGridPlugin, timeGridPlugin], headerToolbar: { left: 'today prev next title', right: '', }, initialView: 'timeGridWeek', initialEvents: INITIAL_EVENTS, weekends: true, editable: true, selectable: true, selectMirror: true, dayMaxEvents: true, select: this.handleDateSelect.bind(this), eventClick: this.handleEventClick.bind(this), eventsSet: this.handleEvents.bind(this), allDaySlot: false, eventBackgroundColor: '#1e62eccc', eventBorderColor: '#1e62eccc', height: '100vh', slotDuration: '00:15:00', // 注意:此处需改为00:05:00 slotLabelInterval: '01:00:00', scrollTime: '09:00:00', eventDurationEditable: true, eventShortHeight: 40, buttonText: { today: 'This Week', }, nowIndicator: true, dayHeaderFormat: { weekday: 'short', day: 'numeric', month: 'short', }, titleFormat: { year: 'numeric', month: 'long', day: 'numeric', }, titleRangeSeparator: ' - ', dayHeaderContent: (singleDateObject: any) => { const fullDate = singleDateObject.date; const day = fullDate.getDay(); const date = fullDate.getDate(); const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; const today = new Date(); const isToday = fullDate.toDateString() === today.toDateString(); const dateClass = isToday ? 'bg-primary rounded-full h-8 w-8 flex justify-center items-center mt-2 font-normal' : ''; const dayClass = isToday ? 'text-primary text-[10px] uppercase' : 'font-normal text-[10px] uppercase'; return { html: `<p class="${dayClass}">${dayNames[day]}</p> <p class="${dateClass}">${date}</p>`, }; }, }); currentEvents = signal<EventApi[]>([]); constructor(private changeDetector: ChangeDetectorRef) {} handleWeekendsToggle() { this.calendarOptions.update((options) => ({ ...options, weekends: !options.weekends, })); } handleDateSelect(selectInfo: DateSelectArg) { const title = prompt('Please enter a new title for your event'); const calendarApi = selectInfo.view.calendar; calendarApi.unselect(); if (title) { calendarApi.addEvent({ id: createEventId(), title, start: selectInfo.startStr, end: selectInfo.endStr, allDay: selectInfo.allDay, }); } } handleEventClick(clickInfo: EventClickArg) { if ( confirm( `Are you sure you want to delete the event '${clickInfo.event.title}'` ) ) { clickInfo.event.remove(); } } handleEvents(events: EventApi[]) { this.currentEvents.set(events); this.changeDetector.detectChanges(); } }
SCSS代码
/* app.component.scss */ :host { @apply block bg-base-700 text-base-200 h-screen rounded; } .fc-theme-standard { @apply bg-base-700 text-base-200; } .fc-toolbar { @apply bg-base-600 text-base-200 p-2; } .fc-daygrid-day, .fc-timegrid-col { @apply bg-base-600 border-base-500; } .fc-daygrid-day:hover, .fc-timegrid-col:hover { @apply bg-base-500; } .fc-event { @apply bg-primary text-base-200; } .fc-scroller { // @apply scrollbar-thin scrollbar-thumb-base-500 scrollbar-track-base-700; } .fc-button { @apply bg-base-500 text-base-200 hover:bg-base-400 border border-base-500 rounded; } .fc-button-active { @apply bg-primary text-base-200; } ::ng-deep { .fc-theme-standard td, .fc-theme-standard th { @apply border border-base-500; } .fc-v-event .fc-event-main { @apply bg-primary text-base-200 border-transparent; } .fc-timegrid-event-harness-inset .fc-timegrid-event, .fc-timegrid-event.fc-event-mirror, .fc-timegrid-more-link { @apply text-base-200 border-transparent ring-0 shadow; } .fc-theme-standard .fc-scrollgrid { @apply border-base-500 rounded; } .fc-scrollgrid-sync-inner { @apply py-2; } .fc .fc-timegrid-col.fc-day-today { @apply bg-primary/5 text-base-200; } button.fc-button.fc-button-primary { @apply bg-transparent rounded-full focus-within:right-0 focus:ring-0 focus-within:outline-none focus:outline-none active:bg-transparent focus-visible:outline-none focus-visible:ring-0 focus-visible:border-0 outline-none outline-0 outline-offset-0; @apply hover:bg-base-400 first-of-type:px-4 first-of-type:py-1.5 first-of-type:h-max border-0 first-of-type:border pr-2.5 pl-2 last-of-type:pl-2.5 last-of-type:pr-2 py-2 cursor-pointer #{!important}; } button.fc-today-button.fc-button.fc-button-primary { @apply bg-transparent rounded-full text-sm ring-0 border-base-200 text-base-200; } .fc .fc-timegrid-slot { @apply h-4 before:h-4 before:flex; } .fc .fc-timegrid-slot-minor { @apply border-transparent h-4 before:h-4 before:flex; } .fc-timegrid-event-harness { @apply border-base-200 rounded border-l-4; } .fc-v-event .fc-event-main { @apply px-3 py-1; } .fc-timegrid-event-short .fc-event-main-frame { @apply px-0 py-0; } .fc-toolbar-chunk { @apply flex gap-2 items-center; } }
问题分析
首个槽位异常的核心原因是:FullCalendar对scrollTime指定的起始时间槽位(这里是09:00)的标签容器做了特殊布局,默认高度和自定义设置的槽位高度不匹配,导致视觉上出现错位或高度异常。另外当前TS代码中slotDuration还是15分钟,需要先修正为目标的5分钟。
修复方案
1. 修正Calendar配置
先把TS代码中的slotDuration改为5分钟:
slotDuration: '00:05:00',
2. 调整SCSS样式,兼容首个槽位
修改::ng-deep中的槽位相关样式,强制重置首个槽位的标签容器和内容容器高度:
::ng-deep { // ... 保留原有其他样式 // 统一所有槽位高度,覆盖默认计算值 .fc .fc-timegrid-slot { @apply h-4 before:h-4 before:flex; height: 16px !important; } .fc .fc-timegrid-slot-minor { @apply border-transparent h-4 before:h-4 before:flex; } // 修复首个槽位的标签容器高度冲突 .fc-timegrid-axis-chunk > .fc-timegrid-slot-label-frame { @apply h-4; height: 16px !important; } // 确保首个槽位内容容器高度匹配 .fc-timegrid-slot:first-child .fc-timegrid-slot-content { @apply h-4; height: 16px !important; } // 适配短事件高度,避免溢出 .fc-timegrid-event-short .fc-event-main-frame { @apply h-3 py-0 px-2; } }
3. 验证效果
修改后刷新页面,所有槽位(包括首个)的高度会统一为16px(h-4),5分钟事件的创建和显示也会正常适配槽位高度。
内容的提问来源于stack exchange,提问作者justAnotherDev
相关产品推荐
相关产品推荐

