如何在React版FullCalendar年月视图实现半天预订事件正确显示
解决方案:FullCalendar React 实现入住/退房半单元格渐变样式
针对你遇到的两个问题,我们可以通过调整事件范围和重构样式逻辑来彻底解决:
核心思路
- 把中间日期的背景事件和首尾的入住/退房样式彻底分离,避免默认背景与自定义渐变叠加;
- 用映射表收集每个日期的所有状态,支持同一天多个事件/状态的样式叠加;
- 通过CSS多重渐变和混合模式,实现多个渐变样式的同时显示。
步骤1:修改React组件代码
调整事件生成逻辑,排除入住和退房日的背景事件,同时构建日期状态映射表:
"use client" import React from "react" import FullCalendar from "@fullcalendar/react" import multiMonthPlugin from "@fullcalendar/multimonth" import { Booking } from "@/types/types" import { formatBookingToEvent } from "@/lib/utils" import { EventInput } from "@fullcalendar/core/index.js" // 简易日期工具函数(替代date-fns) const addDays = (date: Date, days: number) => { const newDate = new Date(date) newDate.setDate(newDate.getDate() + days) return newDate } const formatDateStr = (date: Date) => date.toISOString().split("T")[0] const MultiMonthCalendar = ({ bookings }: { bookings: Booking[] }) => { // 生成中间日期的背景事件(排除入住、退房日) const events: EventInput = bookings.flatMap((booking) => { const arrivalDate = new Date(booking.arrivalDate) const departureDate = new Date(booking.departureDate) const bgStart = addDays(arrivalDate, 1) const bgEnd = departureDate // 若预订仅1天,无中间日期,跳过生成背景事件 if (bgStart >= bgEnd) return [] return [formatBookingToEvent({ ...booking, arrivalDate: formatDateStr(bgStart), departureDate: formatDateStr(bgEnd) })] }) // 构建日期-状态映射表,收集每个日期的所有入住/退房状态 const dateStatusMap = bookings.reduce((map, booking) => { const statusSuffix = booking.bookingStatus === "Confirmed" ? "confirmed" : "pending" const arrivalStr = booking.arrivalDate const departureStr = booking.departureDate // 记录入住状态 map[arrivalStr] = [...(map[arrivalStr] || []), `check-in-${statusSuffix}`] // 记录退房状态 map[departureStr] = [...(map[departureStr] || []), `check-out-${statusSuffix}`] return map }, {} as Record<string, string[]>) return ( <div className="text-sm mt-10"> <FullCalendar plugins={[multiMonthPlugin]} initialView="multiMonthYear" height={850} eventDisplay="background" events={events} eventContent={(arg) => ( <div className="flex h-full items-end"> <p className="text-xs">{arg.event.title}</p> </div> )} eventClick={(info) => console.log(info.event.id)} dayCellClassNames={(arg) => { const dateStr = formatDateStr(arg.date) return dateStatusMap[dateStr] || [] }} /> </div> ) } export default MultiMonthCalendar
步骤2:优化CSS样式
使用background-image叠加渐变,并通过混合模式实现多样式共存,同时处理同一天多种状态的组合:
/* FC FULLCALENDAR 基础样式 */ .fc-bg-event { opacity: 50% !important; cursor: pointer; } /* 基础渐变样式:用background-image允许多重叠加 */ .check-in-pending { background-image: linear-gradient(to left top, var(--pending) 50%, transparent 50%); background-repeat: no-repeat; background-size: 100% 100%; cursor: pointer; background-blend-mode: overlay; } .check-out-pending { background-image: linear-gradient(to left top, transparent 50%, var(--pending) 50%); background-repeat: no-repeat; background-size: 100% 100%; cursor: pointer; background-blend-mode: overlay; } .check-in-confirmed { background-image: linear-gradient(to left top, var(--confirmed) 50%, transparent 50%); background-repeat: no-repeat; background-size: 100% 100%; cursor: pointer; background-blend-mode: overlay; } .check-out-confirmed { background-image: linear-gradient(to left top, transparent 50%, var(--confirmed) 50%); background-repeat: no-repeat; background-size: 100% 100%; cursor: pointer; background-blend-mode: overlay; } /* 同一天多状态组合样式 */ .check-in-confirmed.check-out-confirmed { background-image: linear-gradient(to left top, var(--confirmed) 50%, transparent 50%), linear-gradient(to left top, transparent 50%, var(--confirmed) 50%); } .check-in-pending.check-out-pending { background-image: linear-gradient(to left top, var(--pending) 50%, transparent 50%), linear-gradient(to left top, transparent 50%, var(--pending) 50%); } .check-in-confirmed.check-out-pending { background-image: linear-gradient(to left top, var(--confirmed) 50%, transparent 50%), linear-gradient(to left top, transparent 50%, var(--pending) 50%); } .check-in-pending.check-out-confirmed { background-image: linear-gradient(to left top, var(--pending) 50%, transparent 50%), linear-gradient(to left top, transparent 50%, var(--confirmed) 50%); }
解决效果说明
- 默认背景与渐变重叠问题:通过把背景事件限制在中间日期,入住/退房日不再有默认全单元格背景,仅显示自定义渐变;
- 多事件同一天显示问题:利用
background-image叠加和background-blend-mode,不同状态的渐变会混合显示(比如绿色和橙色渐变在同一单元格中同时可见); - 同一天入住+退房场景:通过复合类样式,生成交叉对角线的渐变效果,清晰区分两种状态。
内容的提问来源于stack exchange,提问作者David H.
相关产品推荐
相关产品推荐

