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

PHP+Ajax管理员预订日历无法正常切换前后月份的问题

问题诊断与解决方案

你现在遇到的核心问题是:每次切换月份时,后端都是基于服务器当前时间(time())来加减一个月,而不是跟踪用户当前正在查看的月份。比如第一次点next是当前时间+1月,再点prev又回到当前时间-1月,而非基于刚才的+1月再减1月,所以才会卡在两个固定月份来回跳。

下面是具体的修复步骤,从前端到后端逐步调整:


1. 前端跟踪当前月份与年份

首先要在页面中保存当前显示的年月,让每次切换都基于这个值计算,而非服务器时间。

修改index.php的年月显示元素

给#current_month添加data属性存储初始年月:

<h4 id="current_month" style="color: #fff;" 
    data-month="<?php echo $month; ?>" 
    data-year="<?php echo $year; ?>">
    <?php echo date("F, Y ",mktime(0,0,0,$month,$day,$year));?>
</h4>

(这里假设你的$month和$year是初始显示的年月,和showCalendar()里的变量一致)

重构Ajax函数swapContent

合并两次$.post请求(避免时序问题),并传递当前/目标年月给后端:

function swapContent(action){
    // 获取当前显示的年月
    let currentMonth = parseInt($("#current_month").data("month"));
    let currentYear = parseInt($("#current_month").data("year"));
    let targetMonth, targetYear;

    // 计算目标年月(处理跨年情况)
    if(action === "next"){
        targetMonth = currentMonth + 1;
        targetYear = currentYear;
        if(targetMonth > 12){
            targetMonth = 1;
            targetYear += 1;
        }
    } else if(action === "prev"){
        targetMonth = currentMonth - 1;
        targetYear = currentYear;
        if(targetMonth < 1){
            targetMonth = 12;
            targetYear -= 1;
        }
    }

    $.post("includes/js/booking_calendar.php", {
        action: action,
        targetMonth: targetMonth,
        targetYear: targetYear
    }, function(r){
        const response = JSON.parse(r);
        // 更新年月显示与存储的data属性
        $("#current_month")
            .html(response.monthText)
            .data("month", targetMonth)
            .data("year", targetYear);
        // 更新日历内容
        $("#availibility-calendar").html(response.calendarHtml).show();
    });
}

2. 后端修改booking_calendar.php

现在后端不再依赖服务器时间,而是直接使用前端传递的目标年月生成内容:

<?php
include '../../../inc/config.php';
include '../../../inc/booking.inc.php';
include '../../../inc/booking-view.inc.php';

// 接收前端参数
$targetMonth = $_POST['targetMonth'] ?? date('m');
$targetYear = $_POST['targetYear'] ?? date('Y');

// 生成年月文本
$monthText = date("F, Y", mktime(0,0,0,$targetMonth,1,$targetYear));

// 生成目标月份的日历HTML
$calendar = new viewBook();
$calendarHtml = $calendar->showCalendar($targetMonth, $targetYear);

// 返回JSON格式响应
echo json_encode([
    'monthText' => $monthText,
    'calendarHtml' => $calendarHtml
]);
?>

3. 重构showCalendar方法

让它支持接收自定义年月参数,同时优化数据库查询性能:

public function showCalendar($month = null, $year = null){
    date_default_timezone_set('Europe/Riga');
    
    // 无参数时默认使用当前时间
    if(is_null($month) || is_null($year)){
        $date = time();
        $month = date ("m", $date);
        $year = date("Y", $date);
    }
    
    $first_day = mktime(0,0,0,$month, 1, $year);
    $day_of_week = date('D', $first_day);
    switch ($day_of_week) {
        case 'Mon': $blank = 0; break;
        case 'Tue': $blank = 1; break;
        case 'Wed': $blank = 2; break;
        case 'Thu': $blank = 3; break;
        case 'Fri': $blank = 4; break;
        case 'Sat': $blank = 5; break;
        case 'Sun': $blank = 6; break;
    }

    $days_in_month = cal_days_in_month(CAL_GREGORIAN , $month, $year);
    
    // 捕获输出(替代直接echo,方便返回字符串)
    ob_start();
    
    echo '<table class="calendar-table" border="1" >';
    echo '<tr><td style="width:60px">Mon</td><td style="width:60px">Tue</td><td style="width:60px">Wed</td><td style="width:60px">Thu</td><td style="width:60px">Fri</td><td style="width:60px">Sat</td><td style="width:60px">Sun</td></tr>';
    $day_count = 1;
    echo '<tr>';
    while ($blank > 0) {
        echo '<td></td>';
        $blank = $blank - 1;
        $day_count++;
    }
    $day_num = 1;
    
    // 优化:把数据库查询移到循环外,避免重复查询
    $sql = "SELECT date_from, date_to FROM booking_2 ORDER BY book2_id";
    $result = $this->connect()->query($sql);
    $bookedRanges = [];
    if($result->num_rows > 0){
        while($row = $result->fetch_assoc()){
            $time_from = strtotime($row['date_from']);
            $time_to = strtotime($row['date_to']);
            // 只保留当月内的预订区间
            $fromDay = max(1, idate('d', $time_from));
            $toDay = min($days_in_month, idate('d', $time_to));
            if($fromDay <= $toDay){
                $bookedRanges[] = ['from' => $fromDay, 'to' => $toDay];
            }
        }
    }
    
    while ($day_num <= $days_in_month) {
        // 检查当前日期是否在预订范围内
        $isBooked = false;
        foreach($bookedRanges as $range){
            if($day_num >= $range['from'] && $day_num <= $range['to']){
                $isBooked = true;
                break;
            }
        }
        
        echo $isBooked 
            ? '<td style="background-color:red">' . $day_num . '</td>'
            : '<td>' . $day_num . '</td>';
        
        $day_num++;
        $day_count++;
        if ($day_count > 7) {
            echo '</tr><tr>';
            $day_count = 1;
        }
    }
    
    while ($day_count <= 7) {
        echo '<td></td>';
        $day_count++;
    }
    echo '</tr>';
    echo '</table>';
    
    // 返回捕获的HTML内容
    return ob_get_clean();
}

4. 清理冗余代码

删除原来的showNextCalendar和showPrevCalendar方法,现在showCalendar可以处理任意年月的日历生成。


测试验证

现在点击prev/next按钮,前端会跟踪当前显示的年月,每次计算目标年月并传递给后端,后端生成对应的日历返回,就能实现连续无刷新切换任意月份了。

内容的提问来源于stack exchange,提问作者kaspianso

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:45:15