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

如何在React版FullCalendar年月视图实现半天预订事件正确显示

解决方案:FullCalendar React 实现入住/退房半单元格渐变样式

针对你遇到的两个问题,我们可以通过调整事件范围和重构样式逻辑来彻底解决:

核心思路

  1. 把中间日期的背景事件和首尾的入住/退房样式彻底分离,避免默认背景与自定义渐变叠加;
  2. 用映射表收集每个日期的所有状态,支持同一天多个事件/状态的样式叠加;
  3. 通过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%);
}

解决效果说明

  1. 默认背景与渐变重叠问题:通过把背景事件限制在中间日期,入住/退房日不再有默认全单元格背景,仅显示自定义渐变;
  2. 多事件同一天显示问题:利用background-image叠加和background-blend-mode,不同状态的渐变会混合显示(比如绿色和橙色渐变在同一单元格中同时可见);
  3. 同一天入住+退房场景:通过复合类样式,生成交叉对角线的渐变效果,清晰区分两种状态。

内容的提问来源于stack exchange,提问作者David H.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 10:54:51