
本文详解如何基于 angularjs 和 php 构建一个智能日期选择器:每月首次加载时自动从后端获取数据库中已占用/不可用日期,并实时禁用对应日期,支持加载状态提示与无 jquery 依赖。
本文详解如何基于 angularjs 和 php 构建一个智能日期选择器:每月首次加载时自动从后端获取数据库中已占用/不可用日期,并实时禁用对应日期,支持加载状态提示与无 jquery 依赖。
在实际业务场景中(如酒店预订、会议室预约、服务排期等),仅靠前端静态禁用日期远远不够——真正可用性必须由后端数据库实时校验。本文提供一套轻量、可扩展、完全基于 AngularJS 生态的解决方案,不引入 jQuery 或额外 UI 库,核心采用 AngularUI Bootstrap 的 datepickerPopup 指令,并结合自定义逻辑实现“按月动态加载 + 异步禁用”。
✅ 技术选型说明
- 前端框架:AngularJS 1.x(推荐 v1.8+)
- 日期组件:AngularUI Bootstrap DatepickerPopup(官方维护、无 jQuery 依赖、支持 dateDisabled 回调)
- 后端接口:PHP + MySQL(返回指定月份内所有不可用日期数组,格式为 YYYY-MM-DD)
? 核心实现步骤
1. 后端 PHP 接口(/api/get_blocked_dates.php)
<?php header('Content-Type: application/json; charset=utf-8');
require_once 'db.php'; // 数据库连接
$month = $_GET['month'] ?? date('Y-m'); // 示例:2024-05
if (!preg_match('/^\d{4}-\d{2}$/', $month)) {
http_response_code(400);
echo json_encode(['error' => 'Invalid month format']);
exit;
}
$stmt = $pdo->prepare("SELECT DATE(date) as blocked_date FROM bookings WHERE DATE_FORMAT(date, '%Y-%m') = ?");
$stmt->execute([$month]);
$blocked = $stmt->fetchAll(PDO::FETCH_COLUMN);
echo json_encode(['blocked' => $blocked]);
2. AngularJS 控制器逻辑(含加载状态与缓存)
app.controller('BookingCtrl', ['$scope', '$http', '$timeout', function($scope, $http, $timeout) {
$scope.date = null;
$scope.isMonthLoading = false;
$scope.blockedDates = {};
// 判断某日期是否应被禁用(dateDisabled 回调)
$scope.dateDisabled = function(date, mode) {
if (mode !== 'day') return false;
const yyyymm = date.getFullYear() + '-' + String(date.getMonth() + 1).padStart(2, '0');
const yyyymmdd = date.toISOString().split('T')[0]; // '2024-05-15'
// 若该月尚未加载,显示加载中并临时禁用所有日期
if ($scope.isMonthLoading || !$scope.blockedDates[yyyymm]) {
return true;
}
return $scope.blockedDates[yyyymm].includes(yyyymmdd);
};
// 每次月份变更时触发加载(监听 datepicker 内部事件)
$scope.$watch('date', function(newVal) {
if (!newVal) return;
const yyyymm = newVal.getFullYear() + '-' + String(newVal.getMonth() + 1).padStart(2, '0');
// 避免重复请求已加载过的月份
if ($scope.blockedDates[yyyymm] !== undefined) return;
$scope.isMonthLoading = true;
$http.get('/api/get_blocked_dates.php?month=' + yyyymm)
.then(function(res) {
$scope.blockedDates[yyyymm] = res.data.blocked || [];
})
.catch(function() {
$scope.blockedDates[yyyymm] = []; // 出错时默认全开放
})
.finally(function() {
$scope.isMonthLoading = false;
});
});
// 可选:初始加载当前月
const now = new Date();
$scope.date = now;
}]);
3. HTML 模板(集成加载指示器)
<div ng-controller="BookingCtrl">
<div class="input-group">
<input type="text" class="form-control" uib-datepicker-popup="yyyy-MM-dd" ng-model="date" is-open="popup.opened" date-disabled="dateDisabled(date, mode)" close-text="Close" ng-required="true"><span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="openCalendar()">
<i class="glyphicon glyphicon-calendar"></i>
</button>
</span>
</div>
<!-- 加载状态提示(可替换为进度条或骨架屏) -->
<div ng-if="isMonthLoading" class="text-muted small mt-2">
<i class="fa fa-spinner fa-spin"></i> 正在加载 {{ date | date:'yyyy年MM月' }} 可用日期...
</div>
</div>
⚠ 注意事项与最佳实践
- 性能优化:使用 $scope.blockedDates 对象按 YYYY-MM 键缓存结果,避免高频重复请求;
- 错误兜底:后端异常时仍需返回空数组,确保 UI 不崩溃;
- 时区一致性:PHP 和 JavaScript 均建议统一使用 UTC 或服务所在时区,避免 2024-05-31 被解析为 2024-06-01;
- 无障碍支持:禁用日期仍保留在 DOM 中(仅视觉灰显+不可交互),符合 WCAG 标准;
- 扩展建议:如需支持多日禁用规则(如节假日、维护期),可在 PHP 接口中叠加查询逻辑,保持前端接口契约不变。
通过以上方案,你将获得一个真正“懂业务”的日期选择器——它不只是渲染日历,而是与数据库联动、按需加载、平滑反馈,且完全扎根于 AngularJS 原生能力,无需妥协于 jQuery 插件或重型 UI 框架。











