当前位置:首页 > React

react实现携程日历

2026-01-26 11:10:58React

实现携程日历的React组件

创建一个类似携程的日历组件需要处理日期选择、区间高亮、禁用日期等逻辑。以下是关键实现步骤:

日期选择与区间高亮

使用date-fns库处理日期操作,实现开始/结束日期选择和区间高亮效果:

react实现携程日历

import { format, addDays, isBefore, isAfter, isSameDay } from 'date-fns';

function DateRangePicker({ startDate, endDate, onDateSelect }) {
  const handleDateClick = (date) => {
    if (!startDate || (startDate && endDate)) {
      onDateSelect({ startDate: date, endDate: null });
    } else if (isBefore(date, startDate)) {
      onDateSelect({ startDate: date, endDate: startDate });
    } else {
      onDateSelect({ startDate, endDate: date });
    }
  };

  const renderDays = () => {
    const days = [];
    const currentDate = new Date();

    for (let i = 0; i < 42; i++) { // 6周
      const date = addDays(currentDate, i);
      const isSelected = 
        (startDate && isSameDay(date, startDate)) || 
        (endDate && isSameDay(date, endDate));
      const isInRange = 
        startDate && endDate && 
        isAfter(date, startDate) && 
        isBefore(date, endDate);

      days.push(
        <div 
          key={date}
          className={`day ${isSelected ? 'selected' : ''} ${isInRange ? 'in-range' : ''}`}
          onClick={() => handleDateClick(date)}
        >
          {format(date, 'd')}
        </div>
      );
    }

    return days;
  };

  return <div className="date-range-picker">{renderDays()}</div>;
}

禁用日期处理

添加禁用日期逻辑,比如限制可选日期范围或排除特定日期:

const isDateDisabled = (date) => {
  const today = new Date();
  const maxSelectableDate = addDays(today, 180); // 限制半年内
  return isBefore(date, today) || isAfter(date, maxSelectableDate);
};

// 在renderDays中修改
const disabled = isDateDisabled(date);
<div 
  className={`day ${disabled ? 'disabled' : ''}`}
  onClick={!disabled ? () => handleDateClick(date) : undefined}
>
  {format(date, 'd')}
</div>

月份切换与布局

实现月份导航和符合携程视觉风格的布局:

react实现携程日历

function Calendar() {
  const [currentMonth, setCurrentMonth] = useState(new Date());

  const prevMonth = () => setCurrentMonth(subMonths(currentMonth, 1));
  const nextMonth = () => setCurrentMonth(addMonths(currentMonth, 1));

  return (
    <div className="ctrip-calendar">
      <div className="header">
        <button onClick={prevMonth}>&lt;</button>
        <h3>{format(currentMonth, 'yyyy年MM月')}</h3>
        <button onClick={nextMonth}>&gt;</button>
      </div>
      <div className="weekdays">
        {['日', '一', '二', '三', '四', '五', '六'].map(day => (
          <div key={day}>{day}</div>
        ))}
      </div>
      <DateRangePicker 
        startDate={startDate} 
        endDate={endDate} 
        onDateSelect={setDates}
      />
    </div>
  );
}

样式设计

添加CSS实现携程风格的视觉效果:

.ctrip-calendar {
  width: 350px;
  border: 1px solid #eee;
  border-radius: 8px;
  padding: 10px;
}

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
}

.weekdays {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  text-align: center;
  color: #666;
}

.date-range-picker {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 5px;
}

.day {
  padding: 8px;
  text-align: center;
  cursor: pointer;
  border-radius: 4px;
}

.day.selected {
  background-color: #008489;
  color: white;
}

.day.in-range {
  background-color: #e6f7ff;
}

.day.disabled {
  color: #ccc;
  cursor: not-allowed;
}

完整组件集成

将所有功能集成到完整组件中:

import React, { useState } from 'react';
import { 
  format, addDays, subMonths, addMonths, 
  isBefore, isAfter, isSameDay 
} from 'date-fns';

export default function CtripCalendar() {
  const [currentMonth, setCurrentMonth] = useState(new Date());
  const [selectedDates, setSelectedDates] = useState({
    startDate: null,
    endDate: null
  });

  return (
    <div className="ctrip-calendar-container">
      <Calendar 
        currentMonth={currentMonth}
        onMonthChange={setCurrentMonth}
        selectedDates={selectedDates}
        onDateSelect={setSelectedDates}
      />
    </div>
  );
}

优化与扩展

  1. 添加动画过渡效果提升用户体验
  2. 实现多个月份并排显示
  3. 集成价格日历功能(显示每日价格)
  4. 添加节假日特殊标记
  5. 实现移动端触摸滑动切换月份

以上实现提供了携程日历的核心功能,可根据实际需求调整样式和交互细节。使用date-fns处理日期操作比原生Date API更可靠,组件设计遵循了React的最佳实践。

标签: 携程日历
分享给朋友:

相关文章

js实现日历

js实现日历

实现日历的基本思路 使用JavaScript实现日历的核心是动态生成日期表格,并处理月份切换逻辑。需要计算当前月的天数、起始星期几,并动态渲染到页面上。 获取当前日期信息 通过Date对象获取当前年…

vue实现日历

vue实现日历

Vue 实现日历组件 使用 Vue 实现日历组件可以通过自定义开发或借助第三方库完成。以下是两种常见方法: 方法一:自定义日历组件 创建基础日历结构,利用 Vue 的响应式特性动态渲染日期。核心逻…

vue实现全年日历

vue实现全年日历

实现全年日历的基本思路 使用Vue实现全年日历需要结合日期计算、数据渲染和交互逻辑。核心在于生成全年12个月的日历数据,并通过组件化方式展示。以下是具体实现方法: 安装依赖(可选) 如需更便捷的日期…

vue实现日历组件

vue实现日历组件

Vue 日历组件实现步骤 基础结构搭建 使用Vue 3的Composition API或Options API创建组件框架。核心结构包括年份/月份切换按钮、星期标题栏和日期网格。 <tem…

vue实现日历效果

vue实现日历效果

Vue实现日历效果的方法 使用第三方库 Vue生态中有多个成熟的日历组件库,如v-calendar或vue2-calendar,可以快速实现日历功能。安装后直接引入组件即可使用。 npm inst…

vue实现日历方案

vue实现日历方案

vue实现日历方案 使用第三方库(推荐方案) 推荐使用成熟的日历组件库,如v-calendar或fullcalendar-vue,它们提供丰富的功能和定制选项。 安装v-calendar:…