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
相关产品推荐
相关产品推荐

