当前位置:首页 > React

react实现携程日历

2026-01-26 11:10:58React

实现携程日历的React组件

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

日期选择与区间高亮

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

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>

月份切换与布局

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

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的最佳实践。

react实现携程日历

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

相关文章

vue日历实现

vue日历实现

Vue 日历组件实现方法 基础日历实现 使用第三方库如v-calendar快速搭建日历功能: npm install v-calendar 引入并注册组件: import VCalendar f…

vue页面实现日历

vue页面实现日历

实现基础日历布局 使用Vue的模板语法构建日历的基本HTML结构,通常需要包含星期标题和日期格子。月份切换按钮可以通过v-on绑定事件。 <template> <div cla…

vue表格实现日历

vue表格实现日历

Vue 表格实现日历的方法 基础表格结构 使用 el-table 或原生 HTML 表格,通过 v-for 循环生成日历格子。月份天数通过 new Date() 计算,动态渲染表格内容。 <…

基于vue实现日历

基于vue实现日历

实现基础日历结构 使用Vue的模板语法构建日历的HTML结构,通常包含星期标题和日期格子。月份切换按钮通过v-on绑定事件。 <template> <div class="c…

vue实现多选日历

vue实现多选日历

Vue 实现多选日历 使用 Vue 实现多选日历功能可以借助现有的日历库或手动构建。以下是两种常见方法: 使用第三方库(如 V-Calendar) 安装 V-Calendar 库: npm i…

vue实现值班日历

vue实现值班日历

Vue 实现值班日历的方法 使用 FullCalendar 插件 FullCalendar 是一个功能强大的日历插件,支持 Vue 集成。安装依赖: npm install @fullcalend…