1114 lines
48 KiB
PHP
1114 lines
48 KiB
PHP
<?php
|
||
date_default_timezone_set('Asia/Shanghai');
|
||
// pending_bookings.php - 待预约页面,处理WPS表单数据
|
||
session_start();
|
||
require_once 'db_connect.php';
|
||
|
||
// 处理表单提交
|
||
$success_message = '';
|
||
$error_message = '';
|
||
|
||
// 如果有预约转换请求
|
||
if (isset($_POST['action']) && $_POST['action'] == 'convert_to_booking' && isset($_POST['submission_id'])) {
|
||
$submission_id = $_POST['submission_id'];
|
||
$selected_date = $_POST['selected_date'];
|
||
$selected_time = $_POST['selected_time'];
|
||
$selected_package = $_POST['selected_package'];
|
||
$custom_notes = $_POST['custom_notes'];
|
||
$total_price = $_POST['total_price'];
|
||
$duration = $_POST['duration'];
|
||
|
||
try {
|
||
// 获取WPS表单提交数据
|
||
$stmt = $pdo->prepare("SELECT * FROM wps_form_submissions WHERE id = ?");
|
||
$stmt->execute([$submission_id]);
|
||
$submission = $stmt->fetch();
|
||
|
||
if (!$submission) {
|
||
throw new Exception("表单提交数据不存在");
|
||
}
|
||
|
||
// 获取套餐信息
|
||
$stmt = $pdo->prepare("SELECT * FROM packages WHERE id = ?");
|
||
$stmt->execute([$selected_package]);
|
||
$package = $stmt->fetch();
|
||
|
||
if (!$package) {
|
||
throw new Exception("选择的套餐不存在");
|
||
}
|
||
|
||
// 计算开始和结束时间
|
||
$start_time = $selected_date . ' ' . $selected_time;
|
||
$end_time = date('Y-m-d H:i:s', strtotime($start_time) + $duration * 60);
|
||
|
||
// 将数据插入到正式预约表
|
||
$stmt = $pdo->prepare("INSERT INTO bookings (
|
||
customer_name, phone, car_model, car_number, package_id, custom_services,
|
||
start_time, end_time, duration, total_price, notes, status,
|
||
member_type, source
|
||
) VALUES (
|
||
?, ?, ?, ?, ?, ?,
|
||
?, ?, ?, ?, ?, ?,
|
||
?, ?
|
||
)");
|
||
|
||
$stmt->execute([
|
||
$submission['name'],
|
||
$submission['mobile'],
|
||
$submission['car_type'],
|
||
$submission['license_plate'],
|
||
$selected_package,
|
||
$custom_notes,
|
||
$start_time,
|
||
$end_time,
|
||
$duration,
|
||
$total_price,
|
||
$submission['remarks'],
|
||
'已确认', // 默认设置为已确认
|
||
'普通客户', // 默认普通客户,可根据需要调整
|
||
'其他' // 默认来源,可根据需要调整
|
||
]);
|
||
|
||
$booking_id = $pdo->lastInsertId();
|
||
|
||
// 更新WPS表单提交状态为已处理
|
||
$stmt = $pdo->prepare("UPDATE wps_form_submissions SET status = 'processed' WHERE id = ?");
|
||
$stmt->execute([$submission_id]);
|
||
|
||
// 生成预约成功信息
|
||
$booking_success_msg = "预约成功!\n\n客户:{$submission['name']}\n手机号:{$submission['mobile']}\n车牌号:{$submission['license_plate']}\n车型:{$submission['car_type']}\n\n预约时间:{$start_time}\n服务项目:{$package['package_name']}\n服务时长:{$duration}分钟\n总价:{$total_price}元\n\n感谢您的预约!";
|
||
|
||
$success_message = "预约转换成功!预约ID:{$booking_id}";
|
||
|
||
// 存储预约成功信息到会话,以便在页面上显示
|
||
$_SESSION['booking_success_msg'] = $booking_success_msg;
|
||
|
||
} catch (Exception $e) {
|
||
$error_message = '预约转换失败:' . $e->getMessage();
|
||
}
|
||
}
|
||
|
||
// 获取待处理的WPS表单提交
|
||
try {
|
||
$stmt = $pdo->prepare("SELECT * FROM wps_form_submissions WHERE status = 'pending' ORDER BY create_time DESC");
|
||
$stmt->execute();
|
||
$pending_submissions = $stmt->fetchAll();
|
||
} catch (Exception $e) {
|
||
$error_message = '获取待处理表单失败:' . $e->getMessage();
|
||
$pending_submissions = [];
|
||
}
|
||
|
||
// 获取所有套餐
|
||
try {
|
||
$stmt = $pdo->prepare("SELECT * FROM packages WHERE is_active = true");
|
||
$stmt->execute();
|
||
$packages = $stmt->fetchAll();
|
||
} catch (Exception $e) {
|
||
$error_message = '获取套餐信息失败:' . $e->getMessage();
|
||
$packages = [];
|
||
}
|
||
|
||
// 获取当前日期及未来7天的日期
|
||
$current_date = date('Y-m-d');
|
||
$available_dates = [];
|
||
for ($i = 0; $i < 7; $i++) {
|
||
$available_dates[] = date('Y-m-d', strtotime($current_date . " +$i days"));
|
||
}
|
||
|
||
// 获取所有预约数据,用于显示空闲时间段
|
||
$booking_schedule = [];
|
||
$bookings_by_date = [];
|
||
|
||
try {
|
||
// 获取所有未来的预约
|
||
$stmt = $pdo->prepare("SELECT * FROM bookings WHERE end_time > NOW() ORDER BY start_time ASC");
|
||
$stmt->execute();
|
||
$all_bookings = $stmt->fetchAll();
|
||
|
||
// 处理预约数据
|
||
foreach ($all_bookings as $booking) {
|
||
$start_date = date('Y-m-d', strtotime($booking['start_time']));
|
||
$end_date = date('Y-m-d', strtotime($booking['end_time']));
|
||
$start_time = date('H:i', strtotime($booking['start_time']));
|
||
$end_time = date('H:i', strtotime($booking['end_time']));
|
||
|
||
// 处理跨天预约
|
||
$is_cross_day = $start_date != $end_date;
|
||
|
||
// 添加到预约日程
|
||
if (!isset($booking_schedule[$start_date])) {
|
||
$booking_schedule[$start_date] = [];
|
||
}
|
||
|
||
if (!isset($bookings_by_date[$start_date])) {
|
||
$bookings_by_date[$start_date] = [];
|
||
}
|
||
|
||
// 主预约记录
|
||
$bookings_by_date[$start_date][] = [
|
||
'booking_id' => $booking['id'],
|
||
'customer_name' => $booking['customer_name'],
|
||
'car_model' => $booking['car_model'],
|
||
'car_number' => $booking['car_number'],
|
||
'status' => $booking['status'],
|
||
'start_time' => $start_time,
|
||
'end_time' => $end_time,
|
||
'is_cross_day' => $is_cross_day
|
||
];
|
||
|
||
// 如果是跨天预约,添加第二天的记录
|
||
if ($is_cross_day) {
|
||
if (!isset($bookings_by_date[$end_date])) {
|
||
$bookings_by_date[$end_date] = [];
|
||
}
|
||
|
||
$bookings_by_date[$end_date][] = [
|
||
'booking_id' => $booking['id'],
|
||
'customer_name' => $booking['customer_name'],
|
||
'car_model' => $booking['car_model'],
|
||
'car_number' => $booking['car_number'],
|
||
'status' => $booking['status'],
|
||
'start_time' => $start_time,
|
||
'end_time' => $end_time,
|
||
'is_cross_day' => $is_cross_day
|
||
];
|
||
}
|
||
}
|
||
} catch (Exception $e) {
|
||
$error_message = '获取预约数据失败:' . $e->getMessage();
|
||
}
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>待预约处理 - 洗车预约系统</title>
|
||
<link rel="stylesheet" href="style.css">
|
||
<style>
|
||
/* 待预约页面特定样式 */
|
||
.pending-bookings-container {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
padding: 20px;
|
||
}
|
||
|
||
.pending-card {
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||
margin-bottom: 20px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.pending-header {
|
||
background: #f5f5f5;
|
||
padding: 15px 20px;
|
||
border-bottom: 1px solid #ddd;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.pending-details {
|
||
padding: 20px;
|
||
}
|
||
|
||
.detail-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||
gap: 15px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.detail-item {
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.detail-label {
|
||
font-weight: bold;
|
||
color: #666;
|
||
}
|
||
|
||
.convert-form {
|
||
background: #f9f9f9;
|
||
padding: 20px;
|
||
border-top: 1px solid #ddd;
|
||
}
|
||
|
||
.form-row {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 15px;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.form-group {
|
||
flex: 1;
|
||
min-width: 200px;
|
||
}
|
||
|
||
.form-group label {
|
||
display: block;
|
||
margin-bottom: 5px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.form-group input, .form-group select, .form-group textarea {
|
||
width: 100%;
|
||
padding: 8px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.btn {
|
||
padding: 10px 20px;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: #007bff;
|
||
color: white;
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
background: #0056b3;
|
||
}
|
||
|
||
.booking-success {
|
||
background: #d4edda;
|
||
border: 1px solid #c3e6cb;
|
||
color: #155724;
|
||
padding: 15px;
|
||
border-radius: 4px;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.copy-message {
|
||
background: #fff;
|
||
border: 1px solid #ddd;
|
||
padding: 10px;
|
||
border-radius: 4px;
|
||
margin-top: 10px;
|
||
font-family: monospace;
|
||
white-space: pre-wrap;
|
||
}
|
||
|
||
.copy-btn {
|
||
background: #28a745;
|
||
color: white;
|
||
padding: 5px 10px;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
margin-top: 10px;
|
||
}
|
||
|
||
.status-badge {
|
||
padding: 5px 10px;
|
||
border-radius: 12px;
|
||
font-size: 12px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.status-pending {
|
||
background: #ffc107;
|
||
color: #212529;
|
||
}
|
||
|
||
/* 日历和时间选择样式 */
|
||
.calendar-container {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.calendar-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.calendar-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
|
||
gap: 10px;
|
||
}
|
||
|
||
.calendar-day {
|
||
padding: 15px;
|
||
background: #f5f5f5;
|
||
border-radius: 4px;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.calendar-day:hover {
|
||
background: #e9ecef;
|
||
}
|
||
|
||
.calendar-day.selected {
|
||
background: #007bff;
|
||
color: white;
|
||
}
|
||
|
||
.time-slots-container {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.time-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
|
||
gap: 10px;
|
||
}
|
||
|
||
.time-slot {
|
||
padding: 10px;
|
||
border-radius: 4px;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.time-slot.available {
|
||
background: #d4edda;
|
||
color: #155724;
|
||
}
|
||
|
||
.time-slot.booked {
|
||
background: #f8d7da;
|
||
color: #721c24;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.time-slot.past {
|
||
background: #e9ecef;
|
||
color: #6c757d;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.time-slot.selected {
|
||
background: #007bff;
|
||
color: white;
|
||
}
|
||
|
||
.time-slot:hover.available {
|
||
background: #c3e6cb;
|
||
}
|
||
|
||
/* 套餐信息样式 */
|
||
.package-info {
|
||
background: #f8f9fa;
|
||
border: 1px solid #e9ecef;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
margin-bottom: 20px;
|
||
display: none;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.package-info:hover {
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
||
}
|
||
|
||
.package-details {
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.package-details h4 {
|
||
margin: 0 0 12px 0;
|
||
color: #2c3e50;
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.package-meta {
|
||
display: flex;
|
||
gap: 16px;
|
||
margin: 15px 0;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
}
|
||
|
||
.package-meta span {
|
||
background: #e3f2fd;
|
||
color: #1976d2;
|
||
padding: 6px 12px;
|
||
border-radius: 20px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
#packageServices_<?php echo $submission['id']; ?> {
|
||
margin-top: 15px;
|
||
padding: 15px;
|
||
background: #ffffff;
|
||
border: 1px solid #e0e0e0;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
#packageServices_<?php echo $submission['id']; ?> strong {
|
||
color: #2c3e50;
|
||
font-size: 16px;
|
||
display: block;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
#packageServices_<?php echo $submission['id']; ?> br {
|
||
display: none;
|
||
}
|
||
|
||
#packageServices_<?php echo $submission['id']; ?>::before {
|
||
content: '';
|
||
}
|
||
|
||
#packageServices_<?php echo $submission['id']; ?>::after {
|
||
content: '';
|
||
}
|
||
|
||
.service-list {
|
||
margin: 10px 0 0 20px;
|
||
padding: 0;
|
||
}
|
||
|
||
.service-list li {
|
||
margin-bottom: 6px;
|
||
color: #424242;
|
||
line-height: 1.6;
|
||
font-size: 15px;
|
||
}
|
||
|
||
.service-list li::marker {
|
||
color: #1976d2;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.booking-detail-item {
|
||
background: #fff;
|
||
border: 1px solid #e9ecef;
|
||
border-radius: 4px;
|
||
padding: 10px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.booking-time {
|
||
font-weight: bold;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.status-已确认 {
|
||
color: #28a745;
|
||
}
|
||
|
||
.status-待服务 {
|
||
color: #ffc107;
|
||
}
|
||
|
||
.status-已完成 {
|
||
color: #6c757d;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="pending-bookings-container">
|
||
<header class="header">
|
||
<h1>🚗 洗车预约系统 - 待预约处理</h1>
|
||
<nav class="nav">
|
||
<a href="index.php" class="nav-link">预约洗车</a>
|
||
<a href="bookings.php" class="nav-link">预约管理</a>
|
||
<a href="pending_bookings.php" class="nav-link active">待预约处理</a>
|
||
<a href="packages.php" class="nav-link">套餐管理</a>
|
||
<a href="vip.php" class="nav-link">VIP管理</a>
|
||
<a href="announcement.php" class="nav-link">今日待办</a>
|
||
</nav>
|
||
</header>
|
||
|
||
<?php if (isset($success_message)): ?>
|
||
<div class="success-message"><?php echo $success_message; ?></div>
|
||
<?php endif; ?>
|
||
|
||
<?php if (isset($error_message)): ?>
|
||
<div class="error-message">
|
||
<?php echo $error_message; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<!-- 预约成功信息显示 -->
|
||
<?php if (isset($_SESSION['booking_success_msg'])): ?>
|
||
<div class="booking-success">
|
||
<h3>预约成功信息已生成</h3>
|
||
<div class="copy-message" id="successMessage">
|
||
<?php echo htmlspecialchars($_SESSION['booking_success_msg']); ?>
|
||
</div>
|
||
<button class="copy-btn" onclick="copyMessage()">复制信息</button>
|
||
</div>
|
||
<?php unset($_SESSION['booking_success_msg']); ?>
|
||
<?php endif; ?>
|
||
|
||
<div class="card">
|
||
<h2>待处理预约 (共 <?php echo count($pending_submissions); ?> 条)</h2>
|
||
|
||
<?php if (empty($pending_submissions)): ?>
|
||
<div class="empty-message">暂无待处理的预约请求</div>
|
||
<?php else: ?>
|
||
<?php foreach ($pending_submissions as $submission): ?>
|
||
<div class="pending-card">
|
||
<div class="pending-header">
|
||
<h3><?php echo htmlspecialchars($submission['name']); ?> 的预约请求</h3>
|
||
<span class="status-badge status-pending">待处理</span>
|
||
</div>
|
||
|
||
<div class="pending-details">
|
||
<div class="detail-grid">
|
||
<div class="detail-item">
|
||
<span class="detail-label">联系方式:</span>
|
||
<span class="detail-value"><?php echo htmlspecialchars($submission['mobile']); ?></span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">车牌号:</span>
|
||
<span class="detail-value"><?php echo htmlspecialchars($submission['license_plate']); ?></span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">车型:</span>
|
||
<span class="detail-value"><?php echo htmlspecialchars($submission['car_type']); ?></span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">是否有车衣:</span>
|
||
<span class="detail-value"><?php echo htmlspecialchars($submission['has_car_coat']); ?></span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">洗车习惯:</span>
|
||
<span class="detail-value"><?php echo htmlspecialchars($submission['car_wash_habit']); ?></span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">撸车经验:</span>
|
||
<span class="detail-value"><?php echo htmlspecialchars($submission['car_wash_experience']); ?></span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">洗车频率:</span>
|
||
<span class="detail-value"><?php echo htmlspecialchars($submission['wash_frequency']); ?></span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">年龄段:</span>
|
||
<span class="detail-value"><?php echo htmlspecialchars($submission['age_group']); ?></span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">自动编号:</span>
|
||
<span class="detail-value"><?php echo htmlspecialchars($submission['auto_number']); ?></span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">提交时间:</span>
|
||
<span class="detail-value"><?php echo htmlspecialchars($submission['create_time']); ?></span>
|
||
</div>
|
||
</div>
|
||
|
||
<?php if (!empty($submission['remarks'])): ?>
|
||
<div class="detail-item">
|
||
<span class="detail-label">备注:</span>
|
||
<span class="detail-value"><?php echo htmlspecialchars($submission['remarks']); ?></span>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<div class="convert-form">
|
||
<h4>转换为正式预约</h4>
|
||
<form method="POST" action="pending_bookings.php">
|
||
<input type="hidden" name="action" value="convert_to_booking">
|
||
<input type="hidden" name="submission_id" value="<?php echo $submission['id']; ?>">
|
||
|
||
<div class="form-group">
|
||
<label for="selected_package_<?php echo $submission['id']; ?>">选择套餐:</label>
|
||
<select id="selected_package_<?php echo $submission['id']; ?>" name="selected_package" required onchange="updatePackageInfo(<?php echo $submission['id']; ?>)">
|
||
<option value="">请选择套餐</option>
|
||
<?php foreach ($packages as $package): ?>
|
||
<option value="<?php echo $package['id']; ?>"
|
||
data-duration="<?php echo $package['base_duration']; ?>"
|
||
data-price="<?php echo $package['price']; ?>"
|
||
data-services='<?php echo htmlspecialchars($package['services']); ?>'>
|
||
<?php echo htmlspecialchars($package['package_name']); ?> - ¥<?php echo number_format($package['price'], 2); ?>
|
||
</option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="package-info" id="packageInfo_<?php echo $submission['id']; ?>">
|
||
<div class="package-details">
|
||
<h4 id="packageName_<?php echo $submission['id']; ?>"></h4>
|
||
<div class="package-meta">
|
||
<span id="packageDuration_<?php echo $submission['id']; ?>"></span>
|
||
<span id="packagePrice_<?php echo $submission['id']; ?>"></span>
|
||
</div>
|
||
<div class="form-group" style="margin-top: 15px;">
|
||
<label for="total_price_<?php echo $submission['id']; ?>">最终价格 (¥)</label>
|
||
<input type="number" id="total_price_<?php echo $submission['id']; ?>" name="total_price" min="0" step="0.01" value="0" placeholder="输入最终价格" required>
|
||
<input type="hidden" id="package_price_<?php echo $submission['id']; ?>" value="0">
|
||
</div>
|
||
<div id="packageServices_<?php echo $submission['id']; ?>"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="calendar-container">
|
||
<div class="calendar-header">
|
||
<h5>选择预约日期</h5>
|
||
</div>
|
||
<div class="calendar-grid" id="calendarGrid_<?php echo $submission['id']; ?>">
|
||
<?php foreach ($available_dates as $date): ?>
|
||
<div class="calendar-day"
|
||
data-date="<?php echo $date; ?>"
|
||
onclick="selectDate(<?php echo $submission['id']; ?>, '<?php echo $date; ?>')">
|
||
<div><?php echo date('m月d日', strtotime($date)); ?></div>
|
||
<div><?php echo date('D', strtotime($date)); ?></div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<input type="hidden" id="selected_date_<?php echo $submission['id']; ?>" name="selected_date" value="<?php echo date('Y-m-d'); ?>">
|
||
<input type="hidden" id="selected_time_<?php echo $submission['id']; ?>" name="selected_time" value="" required>
|
||
</div>
|
||
|
||
<div class="time-slots-container">
|
||
<h5>选择预约时间</h5>
|
||
<div class="time-grid" id="timeGrid_<?php echo $submission['id']; ?>">
|
||
<!-- 时间格子将通过JavaScript生成 -->
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="duration_<?php echo $submission['id']; ?>">服务时长(分钟)</label>
|
||
<div class="quick-duration">
|
||
<button type="button" class="duration-btn" data-duration="60" onclick="selectDuration(<?php echo $submission['id']; ?>, 60)">1小时</button>
|
||
<button type="button" class="duration-btn" data-duration="90" onclick="selectDuration(<?php echo $submission['id']; ?>, 90)">1.5小时</button>
|
||
<button type="button" class="duration-btn" data-duration="120" onclick="selectDuration(<?php echo $submission['id']; ?>, 120)">2小时</button>
|
||
<button type="button" class="duration-btn" data-duration="240" onclick="selectDuration(<?php echo $submission['id']; ?>, 240)">4小时</button>
|
||
<button type="button" class="duration-btn" data-duration="360" onclick="selectDuration(<?php echo $submission['id']; ?>, 360)">6小时</button>
|
||
<button type="button" class="duration-btn" data-duration="480" onclick="selectDuration(<?php echo $submission['id']; ?>, 480)">8小时</button>
|
||
<button type="button" class="duration-btn" data-duration="600" onclick="selectDuration(<?php echo $submission['id']; ?>, 600)">10小时</button>
|
||
<button type="button" class="duration-btn" data-duration="720" onclick="selectDuration(<?php echo $submission['id']; ?>, 720)">12小时</button>
|
||
<input type="number" id="customDuration_<?php echo $submission['id']; ?>" min="30" step="30" value="60" style="width: 80px; margin-left: 10px;">
|
||
<button type="button" class="btn btn-sm" onclick="applyCustomDuration(<?php echo $submission['id']; ?>)">确定</button>
|
||
</div>
|
||
<input type="number" id="duration_<?php echo $submission['id']; ?>" name="duration" min="30" step="30" value="60" required style="margin-top: 10px;">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="custom_notes_<?php echo $submission['id']; ?>">自定义服务需求:</label>
|
||
<textarea id="custom_notes_<?php echo $submission['id']; ?>" name="custom_notes" rows="3"
|
||
placeholder="如有特殊需求,请在此说明..."></textarea>
|
||
</div>
|
||
|
||
<button type="submit" class="btn btn-primary">确认预约</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// 复制预约成功信息到剪贴板
|
||
function copyMessage() {
|
||
const message = document.getElementById('successMessage');
|
||
const textArea = document.createElement('textarea');
|
||
textArea.value = message.textContent;
|
||
document.body.appendChild(textArea);
|
||
textArea.select();
|
||
document.execCommand('copy');
|
||
document.body.removeChild(textArea);
|
||
|
||
// 显示复制成功提示
|
||
alert('预约信息已复制到剪贴板!');
|
||
}
|
||
|
||
// 工作时间设置
|
||
const workingHours = {
|
||
start: 0, // 00:00
|
||
end: 24, // 24:00
|
||
slotDuration: 30 // 30分钟一个时段
|
||
};
|
||
|
||
// 预约数据 - 为每个提交创建独立的预约数据对象
|
||
const allBookingsByDate = <?php echo json_encode($bookings_by_date); ?>;
|
||
|
||
// 页面加载时初始化所有预约的日期、时间和套餐信息
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
<?php foreach ($pending_submissions as $submission): ?>
|
||
// 初始化日期
|
||
selectDate(<?php echo $submission['id']; ?>, '<?php echo date('Y-m-d'); ?>');
|
||
|
||
// 初始化默认套餐信息(如果有默认选择的话)
|
||
const packageSelect = document.getElementById('selected_package_<?php echo $submission['id']; ?>');
|
||
if (packageSelect.value) {
|
||
updatePackageInfo(<?php echo $submission['id']; ?>);
|
||
} else if (packageSelect.options.length > 1) {
|
||
// 默认选择第一个套餐
|
||
packageSelect.selectedIndex = 1;
|
||
updatePackageInfo(<?php echo $submission['id']; ?>);
|
||
}
|
||
<?php endforeach; ?>
|
||
});
|
||
|
||
// 选择日期
|
||
function selectDate(submissionId, date) {
|
||
const calendarGrid = document.getElementById('calendarGrid_' + submissionId);
|
||
const timeGrid = document.getElementById('timeGrid_' + submissionId);
|
||
const selectedDateInput = document.getElementById('selected_date_' + submissionId);
|
||
|
||
// 更新日期输入
|
||
selectedDateInput.value = date;
|
||
|
||
// 更新日历选中状态
|
||
calendarGrid.querySelectorAll('.calendar-day').forEach(day => {
|
||
day.classList.remove('selected');
|
||
});
|
||
|
||
// 找到当前日历中的日期元素
|
||
const calendarDays = calendarGrid.querySelectorAll('.calendar-day');
|
||
calendarDays.forEach(day => {
|
||
if (day.dataset.date === date) {
|
||
day.classList.add('selected');
|
||
}
|
||
});
|
||
|
||
// 生成时间段
|
||
generateTimeSlots(submissionId, date);
|
||
}
|
||
|
||
// 生成时间段
|
||
function generateTimeSlots(submissionId, date) {
|
||
const timeGrid = document.getElementById('timeGrid_' + submissionId);
|
||
const selectedTimeInput = document.getElementById('selected_time_' + submissionId);
|
||
|
||
timeGrid.innerHTML = '';
|
||
|
||
// 获取当天已有预约
|
||
const dayBookings = allBookingsByDate[date] || [];
|
||
|
||
// 生成时间段
|
||
for (let hour = workingHours.start; hour < workingHours.end; hour++) {
|
||
for (let minute = 0; minute < 60; minute += workingHours.slotDuration) {
|
||
const timeString = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
|
||
const slotTime = new Date(`${date} ${timeString}:00`);
|
||
const now = new Date();
|
||
const isPast = slotTime <= now;
|
||
const isBooked = checkTimeSlotBooked(date, timeString);
|
||
|
||
const slotDiv = document.createElement('div');
|
||
slotDiv.className = `time-slot ${isPast ? 'past' : ''} ${isBooked ? 'booked' : 'available'}`;
|
||
slotDiv.textContent = timeString;
|
||
slotDiv.dataset.time = timeString;
|
||
|
||
// 只有可用的时间段才添加点击事件
|
||
if (!isPast && !isBooked) {
|
||
slotDiv.onclick = () => selectTimeSlot(submissionId, timeString);
|
||
} else {
|
||
// 禁用不可点击的时间段
|
||
slotDiv.style.cursor = 'not-allowed';
|
||
}
|
||
|
||
timeGrid.appendChild(slotDiv);
|
||
}
|
||
}
|
||
|
||
selectedTimeInput.value = '';
|
||
}
|
||
|
||
// 检查时间段是否已被预约
|
||
function checkTimeSlotBooked(date, time) {
|
||
const bookings = allBookingsByDate[date] || [];
|
||
|
||
for (let booking of bookings) {
|
||
const bookingStart = booking.start_time;
|
||
const bookingEnd = booking.end_time;
|
||
|
||
// 处理跨天预约的情况
|
||
if (booking.is_cross_day) {
|
||
// 如果是跨天预约的第二天记录,所有时间都应该是已被预约的(从00:00到结束时间)
|
||
if (time < bookingEnd) {
|
||
return true; // 该时间段已被预约
|
||
}
|
||
} else {
|
||
// 正常预约检查
|
||
if (time >= bookingStart && time < bookingEnd) {
|
||
return true; // 该时间段已被预约
|
||
}
|
||
}
|
||
}
|
||
return false; // 该时间段可用
|
||
}
|
||
|
||
// 选择时间
|
||
function selectTimeSlot(submissionId, time) {
|
||
const selectedTimeInput = document.getElementById('selected_time_' + submissionId);
|
||
|
||
// 更新时间输入
|
||
selectedTimeInput.value = time;
|
||
|
||
// 更新时间段选中状态
|
||
const timeGrid = document.getElementById('timeGrid_' + submissionId);
|
||
timeGrid.querySelectorAll('.time-slot').forEach(slot => {
|
||
slot.classList.remove('selected');
|
||
});
|
||
|
||
const slotElement = timeGrid.querySelector(`[data-time="${time}"]`);
|
||
if (slotElement) {
|
||
slotElement.classList.add('selected');
|
||
}
|
||
}
|
||
|
||
// 更新套餐信息
|
||
function updatePackageInfo(submissionId) {
|
||
console.log('=== updatePackageInfo called with submissionId:', submissionId, '===');
|
||
|
||
// 直接获取表单元素,确保在正确的表单上下文中操作
|
||
const packageSelect = document.getElementById('selected_package_' + submissionId);
|
||
if (!packageSelect) {
|
||
console.error('❌ Package select not found for submissionId:', submissionId);
|
||
return;
|
||
}
|
||
|
||
// 获取当前表单
|
||
const form = packageSelect.closest('form');
|
||
if (!form) {
|
||
console.error('❌ Form not found for submissionId:', submissionId);
|
||
return;
|
||
}
|
||
|
||
const selectedOption = packageSelect.options[packageSelect.selectedIndex];
|
||
const packageInfoDiv = document.getElementById('packageInfo_' + submissionId);
|
||
|
||
console.log('Selected option:', selectedOption, 'value:', selectedOption ? selectedOption.value : 'null');
|
||
|
||
if (selectedOption && selectedOption.value) {
|
||
// 使用getAttribute方法获取data属性,确保兼容所有浏览器
|
||
const duration = parseInt(selectedOption.getAttribute('data-duration'));
|
||
const price = parseFloat(selectedOption.getAttribute('data-price'));
|
||
const servicesJSON = selectedOption.getAttribute('data-services');
|
||
let services = [];
|
||
|
||
// 尝试解析servicesJSON,如果解析失败则将其视为逗号分隔字符串
|
||
if (servicesJSON) {
|
||
try {
|
||
// 尝试作为JSON解析
|
||
services = JSON.parse(servicesJSON);
|
||
console.log('✅ Parsed services as JSON:', services);
|
||
} catch (e) {
|
||
// 如果JSON解析失败,尝试作为逗号分隔字符串处理
|
||
console.log('⚠️ JSON parse failed, treating as comma-separated string:', servicesJSON);
|
||
services = servicesJSON.split(',').map(service => service.trim()).filter(service => service.length > 0);
|
||
console.log('✅ Converted to array:', services);
|
||
}
|
||
}
|
||
|
||
console.log('Package data:', {duration, price, services: services.length});
|
||
|
||
// 更新套餐信息显示
|
||
const packageNameElement = document.getElementById('packageName_' + submissionId);
|
||
const packageDurationElement = document.getElementById('packageDuration_' + submissionId);
|
||
const packagePriceElement = document.getElementById('packagePrice_' + submissionId);
|
||
|
||
if (packageNameElement) packageNameElement.textContent = selectedOption.textContent;
|
||
if (packageDurationElement) packageDurationElement.textContent = `基础时长: ${duration}分钟`;
|
||
if (packagePriceElement) packagePriceElement.textContent = `套餐价格: ¥${price.toFixed(2)}`;
|
||
|
||
// 更新服务项目显示
|
||
const servicesContainer = document.getElementById('packageServices_' + submissionId);
|
||
if (servicesContainer) {
|
||
if (services && services.length > 0) {
|
||
servicesContainer.innerHTML = '<strong>包含服务:</strong><ul class="service-list">' +
|
||
services.map(service => `<li>${service}</li>`).join('') + '</ul>';
|
||
} else {
|
||
servicesContainer.innerHTML = '';
|
||
}
|
||
}
|
||
|
||
// 使用两种方式获取时长输入框,确保万无一失
|
||
const durationInputById = document.getElementById('duration_' + submissionId);
|
||
const durationInputByName = form.querySelector('input[name="duration"]');
|
||
|
||
console.log('Duration inputs found - by ID:', durationInputById ? 'Yes' : 'No', 'by Name:', durationInputByName ? 'Yes' : 'No');
|
||
|
||
// 优先使用ID获取的元素,如果不存在则使用name获取的元素
|
||
const durationInput = durationInputById || durationInputByName;
|
||
|
||
if (durationInput) {
|
||
// 强制设置时长值
|
||
durationInput.value = duration;
|
||
// 触发change事件,确保所有依赖此输入的功能都能更新
|
||
durationInput.dispatchEvent(new Event('change'));
|
||
console.log('✅ Successfully updated duration input to:', duration);
|
||
} else {
|
||
console.error('❌ Cannot find duration input for submissionId:', submissionId);
|
||
}
|
||
|
||
// 同时更新自定义时长输入框
|
||
const customDurationInput = document.getElementById('customDuration_' + submissionId);
|
||
if (customDurationInput) {
|
||
customDurationInput.value = duration;
|
||
console.log('✅ Successfully updated custom duration input to:', duration);
|
||
} else {
|
||
console.error('❌ Cannot find custom duration input for submissionId:', submissionId);
|
||
}
|
||
|
||
// 更新价格输入框
|
||
const totalPriceInput = document.getElementById('total_price_' + submissionId);
|
||
if (totalPriceInput) {
|
||
totalPriceInput.value = price.toFixed(2);
|
||
console.log('✅ Updated total price to:', price.toFixed(2));
|
||
}
|
||
|
||
// 更新快捷选择按钮状态
|
||
console.log('Calling selectDuration with:', submissionId, duration);
|
||
selectDuration(submissionId, duration);
|
||
|
||
// 显示套餐信息
|
||
if (packageInfoDiv) {
|
||
packageInfoDiv.style.display = 'block';
|
||
}
|
||
} else {
|
||
if (packageInfoDiv) {
|
||
packageInfoDiv.style.display = 'none';
|
||
}
|
||
console.log('❌ No package selected');
|
||
}
|
||
}
|
||
|
||
// 快捷选择服务时长
|
||
function selectDuration(submissionId, minutes) {
|
||
console.log('=== selectDuration called with submissionId:', submissionId, 'minutes:', minutes, '===');
|
||
|
||
// 先检查minutes是否有效
|
||
if (isNaN(minutes) || minutes <= 0) {
|
||
console.error('❌ Invalid minutes value:', minutes);
|
||
return;
|
||
}
|
||
|
||
// 使用两种方式获取表单,确保找到正确的表单
|
||
let form = null;
|
||
|
||
// 方式1:通过套餐选择元素获取表单
|
||
const packageSelect = document.getElementById('selected_package_' + submissionId);
|
||
if (packageSelect) {
|
||
form = packageSelect.closest('form');
|
||
console.log('Form found via packageSelect:', form ? 'Yes' : 'No');
|
||
}
|
||
|
||
// 方式2:如果方式1失败,通过时长输入框获取表单
|
||
if (!form) {
|
||
const durationInputById = document.getElementById('duration_' + submissionId);
|
||
if (durationInputById) {
|
||
form = durationInputById.closest('form');
|
||
console.log('Form found via durationInputById:', form ? 'Yes' : 'No');
|
||
}
|
||
}
|
||
|
||
// 方式3:如果还是失败,尝试通过packageInfo找到表单
|
||
if (!form) {
|
||
const packageInfo = document.getElementById('packageInfo_' + submissionId);
|
||
if (packageInfo) {
|
||
form = packageInfo.closest('form');
|
||
console.log('Form found via packageInfo:', form ? 'Yes' : 'No');
|
||
}
|
||
}
|
||
|
||
// 方式4:如果还是失败,尝试通过自定义时长输入框找到表单
|
||
if (!form) {
|
||
const customDurationInput = document.getElementById('customDuration_' + submissionId);
|
||
if (customDurationInput) {
|
||
form = customDurationInput.closest('form');
|
||
console.log('Form found via customDurationInput:', form ? 'Yes' : 'No');
|
||
}
|
||
}
|
||
|
||
if (!form) {
|
||
console.error('❌ Form not found for submissionId:', submissionId);
|
||
return;
|
||
}
|
||
|
||
// 使用两种方式获取时长输入框,确保万无一失
|
||
const durationInputById = document.getElementById('duration_' + submissionId);
|
||
const durationInputByName = form.querySelector('input[name="duration"]');
|
||
const durationInput = durationInputById || durationInputByName;
|
||
|
||
if (durationInput) {
|
||
// 强制设置时长值
|
||
durationInput.value = minutes;
|
||
// 触发change事件,确保所有依赖此输入的功能都能更新
|
||
durationInput.dispatchEvent(new Event('change'));
|
||
console.log('✅ Duration input updated to:', minutes);
|
||
} else {
|
||
console.error('❌ Duration input not found for submissionId:', submissionId);
|
||
}
|
||
|
||
// 更新自定义时长输入框
|
||
const customDurationInput = document.getElementById('customDuration_' + submissionId);
|
||
if (customDurationInput) {
|
||
customDurationInput.value = minutes;
|
||
console.log('✅ Custom duration input updated to:', minutes);
|
||
} else {
|
||
console.error('❌ Custom duration input not found for submissionId:', submissionId);
|
||
}
|
||
|
||
// 获取当前表单的时长按钮
|
||
const durationButtons = form.querySelectorAll('.duration-btn');
|
||
console.log('Found duration buttons:', durationButtons.length);
|
||
|
||
if (durationButtons.length === 0) {
|
||
console.warn('⚠️ No duration buttons found in form');
|
||
return;
|
||
}
|
||
|
||
// 更新按钮选中状态
|
||
let buttonFound = false;
|
||
durationButtons.forEach(btn => {
|
||
// 使用getAttribute获取data属性,确保兼容所有浏览器
|
||
const btnDuration = parseInt(btn.getAttribute('data-duration'));
|
||
console.log('Checking button:', btn.textContent, 'data-duration:', btnDuration);
|
||
|
||
if (btnDuration === minutes) {
|
||
btn.classList.add('selected');
|
||
console.log('✅ Added selected class to button with duration:', btnDuration);
|
||
buttonFound = true;
|
||
} else {
|
||
btn.classList.remove('selected');
|
||
}
|
||
});
|
||
|
||
if (!buttonFound) {
|
||
console.warn('⚠️ No matching duration button found for:', minutes, 'minutes');
|
||
}
|
||
}
|
||
|
||
// 应用自定义时长
|
||
function applyCustomDuration(submissionId) {
|
||
const customDurationInput = document.getElementById('customDuration_' + submissionId);
|
||
const customDuration = parseInt(customDurationInput.value);
|
||
const maxDuration = 720; // 最大时长为12小时(720分钟)
|
||
|
||
if (customDuration < 30) {
|
||
alert('最小服务时长为30分钟');
|
||
customDurationInput.value = 30;
|
||
selectDuration(submissionId, 30);
|
||
} else if (customDuration > maxDuration) {
|
||
alert('最大服务时长为12小时(720分钟)');
|
||
customDurationInput.value = maxDuration;
|
||
selectDuration(submissionId, maxDuration);
|
||
} else {
|
||
// 确保时长是30的倍数
|
||
const roundedDuration = Math.round(customDuration / 30) * 30;
|
||
customDurationInput.value = roundedDuration;
|
||
selectDuration(submissionId, roundedDuration);
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|