feat(vip): 新增VIP客户管理功能及预约系统
- 添加VIP客户搜索功能 - 实现VIP客户信息编辑功能 - 新增VIP客户预约记录查看功能 - 添加为VIP客户快速创建预约功能 - 新增预约处理API接口 - 优化移动端交互体验
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
// 配置数据库连接参数
|
||||
$servername = "localhost";
|
||||
$username = "root";
|
||||
$password = "";
|
||||
$dbname = "car_wash_db";
|
||||
|
||||
// 设置响应头为JSON
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// 获取请求参数
|
||||
$phone = $_GET['phone'] ?? '';
|
||||
|
||||
// 验证参数
|
||||
if (empty($phone)) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => '缺少必要的参数',
|
||||
'total' => 0,
|
||||
'pending' => 0,
|
||||
'completed' => 0,
|
||||
'bookings' => []
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 创建数据库连接
|
||||
$conn = new mysqli($servername, $username, $password, $dbname);
|
||||
|
||||
// 检查连接
|
||||
if ($conn->connect_error) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => '数据库连接失败',
|
||||
'total' => 0,
|
||||
'pending' => 0,
|
||||
'completed' => 0,
|
||||
'bookings' => []
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 准备SQL查询 - 查询用户的预约记录
|
||||
// 假设预约表名为bookings,套餐表名为packages
|
||||
$sql = "SELECT b.*, p.package_name
|
||||
FROM bookings b
|
||||
LEFT JOIN packages p ON b.package_id = p.id
|
||||
WHERE b.phone = ?
|
||||
ORDER BY b.booking_date DESC, b.id DESC";
|
||||
|
||||
$stmt = $conn->prepare($sql);
|
||||
$stmt->bind_param("s", $phone);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
$bookings = [];
|
||||
$pending = 0;
|
||||
$completed = 0;
|
||||
|
||||
// 处理查询结果
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$booking = [
|
||||
'id' => $row['id'],
|
||||
'booking_date' => $row['booking_date'],
|
||||
'phone' => $row['phone'],
|
||||
'car_number' => $row['car_number'] ?? '',
|
||||
'car_model' => $row['car_model'] ?? '',
|
||||
'time_slot' => $row['time_slot'] ?? '',
|
||||
'status' => $row['status'] ?? '未知',
|
||||
'package_name' => $row['package_name'] ?? '',
|
||||
'notes' => $row['notes'] ?? ''
|
||||
];
|
||||
|
||||
// 统计不同状态的预约数量
|
||||
if ($booking['status'] === '待服务') {
|
||||
$pending++;
|
||||
} elseif ($booking['status'] === '已完成') {
|
||||
$completed++;
|
||||
}
|
||||
|
||||
$bookings[] = $booking;
|
||||
}
|
||||
|
||||
// 计算总预约数
|
||||
$total = count($bookings);
|
||||
|
||||
// 构建响应数据
|
||||
$response = [
|
||||
'success' => true,
|
||||
'total' => $total,
|
||||
'pending' => $pending,
|
||||
'completed' => $completed,
|
||||
'bookings' => $bookings
|
||||
];
|
||||
|
||||
// 返回JSON数据
|
||||
echo json_encode($response);
|
||||
|
||||
// 关闭数据库连接
|
||||
$stmt->close();
|
||||
$conn->close();
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
// 配置数据库连接参数
|
||||
$servername = "localhost";
|
||||
$username = "root";
|
||||
$password = "";
|
||||
$dbname = "car_wash_db";
|
||||
|
||||
// 检查表单提交
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// 获取表单数据
|
||||
$customer_name = $_POST['customer_name'] ?? '';
|
||||
$phone = $_POST['phone'] ?? '';
|
||||
$car_model = $_POST['car_model'] ?? '';
|
||||
$car_number = $_POST['car_number'] ?? '';
|
||||
$booking_date = $_POST['booking_date'] ?? '';
|
||||
$time_slot = $_POST['time_slot'] ?? '';
|
||||
$package_id = $_POST['package_id'] ?? '';
|
||||
$notes = $_POST['notes'] ?? '';
|
||||
$source = $_POST['source'] ?? ''; // 来源标识,用于确定返回页面
|
||||
|
||||
// 验证必填字段
|
||||
$errors = [];
|
||||
|
||||
if (empty($customer_name)) {
|
||||
$errors[] = '请输入客户姓名';
|
||||
}
|
||||
|
||||
if (empty($phone)) {
|
||||
$errors[] = '请输入手机号码';
|
||||
} elseif (!preg_match('/^1[3-9]\d{9}$/', $phone)) {
|
||||
$errors[] = '请输入正确的手机号码';
|
||||
}
|
||||
|
||||
if (empty($car_number)) {
|
||||
$errors[] = '请输入车牌号';
|
||||
}
|
||||
|
||||
if (empty($booking_date)) {
|
||||
$errors[] = '请选择预约日期';
|
||||
}
|
||||
|
||||
if (empty($time_slot)) {
|
||||
$errors[] = '请选择预约时间';
|
||||
}
|
||||
|
||||
if (empty($package_id)) {
|
||||
$errors[] = '请选择洗车套餐';
|
||||
}
|
||||
|
||||
// 验证日期是否为过去
|
||||
$current_date = date('Y-m-d');
|
||||
if ($booking_date < $current_date) {
|
||||
$errors[] = '不能选择过去的日期';
|
||||
}
|
||||
|
||||
// 如果有错误,返回错误信息
|
||||
if (!empty($errors)) {
|
||||
$error_message = implode('\n', $errors);
|
||||
echo "<script>
|
||||
alert('$error_message');
|
||||
window.history.back();
|
||||
</script>";
|
||||
exit;
|
||||
}
|
||||
|
||||
// 创建数据库连接
|
||||
$conn = new mysqli($servername, $username, $password, $dbname);
|
||||
|
||||
// 检查连接
|
||||
if ($conn->connect_error) {
|
||||
echo "<script>
|
||||
alert('数据库连接失败,请稍后重试');
|
||||
window.history.back();
|
||||
</script>";
|
||||
exit;
|
||||
}
|
||||
|
||||
// 检查是否已经存在相同的预约(同一天、同一时间段、同一车牌号)
|
||||
$check_sql = "SELECT * FROM bookings
|
||||
WHERE booking_date = ? AND time_slot = ? AND car_number = ? AND status != '已取消'";
|
||||
$check_stmt = $conn->prepare($check_sql);
|
||||
$check_stmt->bind_param("sss", $booking_date, $time_slot, $car_number);
|
||||
$check_stmt->execute();
|
||||
$check_result = $check_stmt->get_result();
|
||||
|
||||
if ($check_result->num_rows > 0) {
|
||||
echo "<script>
|
||||
alert('该时间段已存在相同车牌号的预约');
|
||||
window.history.back();
|
||||
</script>";
|
||||
$check_stmt->close();
|
||||
$conn->close();
|
||||
exit;
|
||||
}
|
||||
$check_stmt->close();
|
||||
|
||||
// 插入预约记录
|
||||
$status = '待服务'; // 默认为待服务状态
|
||||
$create_time = date('Y-m-d H:i:s');
|
||||
|
||||
$insert_sql = "INSERT INTO bookings
|
||||
(customer_name, phone, car_model, car_number, booking_date, time_slot, package_id, notes, status, create_time)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
$stmt = $conn->prepare($insert_sql);
|
||||
$stmt->bind_param("ssssssssss", $customer_name, $phone, $car_model, $car_number, $booking_date, $time_slot, $package_id, $notes, $status, $create_time);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
// 预约成功
|
||||
$success_message = '预约添加成功!';
|
||||
|
||||
// 根据来源决定返回页面
|
||||
$redirect_url = $source === 'vip_page' ? 'vip.php' : 'index.php';
|
||||
|
||||
echo "<script>
|
||||
alert('$success_message');
|
||||
window.location.href = '$redirect_url';
|
||||
</script>";
|
||||
} else {
|
||||
// 预约失败
|
||||
echo "<script>
|
||||
alert('预约添加失败,请稍后重试');
|
||||
window.history.back();
|
||||
</script>";
|
||||
}
|
||||
|
||||
// 关闭数据库连接
|
||||
$stmt->close();
|
||||
$conn->close();
|
||||
} else {
|
||||
// 不是POST请求,重定向到首页
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
@@ -46,6 +46,49 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
throw $e; // 重新抛出其他类型的异常
|
||||
}
|
||||
|
||||
} elseif ($action === 'update_vip') {
|
||||
$id = (int)$_POST['vip_id'];
|
||||
$customer_name = trim($_POST['customer_name']);
|
||||
$phone = trim($_POST['phone']);
|
||||
$car_model = trim($_POST['car_model']);
|
||||
$car_number = trim($_POST['car_number']);
|
||||
$email = trim($_POST['email'] ?? '');
|
||||
$birthday = $_POST['birthday'] ?? '';
|
||||
$notes = trim($_POST['notes'] ?? '');
|
||||
|
||||
if (empty($customer_name) || empty($phone)) {
|
||||
throw new Exception('请填写客户姓名和联系电话');
|
||||
}
|
||||
|
||||
// 检查手机号和车牌号组合是否与其他VIP客户冲突
|
||||
$stmt = $pdo->prepare("SELECT id FROM vip_customers WHERE phone = ? AND car_number = ? AND id != ?");
|
||||
$stmt->execute([$phone, $car_number, $id]);
|
||||
if ($stmt->fetch()) {
|
||||
throw new Exception('该手机号和车牌号组合已经被其他VIP客户使用');
|
||||
}
|
||||
|
||||
// 更新VIP客户信息
|
||||
$stmt = $pdo->prepare("UPDATE vip_customers SET
|
||||
customer_name = ?,
|
||||
phone = ?,
|
||||
car_model = ?,
|
||||
car_number = ?,
|
||||
email = ?,
|
||||
birthday = ?,
|
||||
notes = ?,
|
||||
updated_at = NOW()
|
||||
WHERE id = ?");
|
||||
try {
|
||||
$stmt->execute([$customer_name, $phone, $car_model, $car_number, $email, $birthday, $notes, $id]);
|
||||
$success_message = "VIP客户信息更新成功!";
|
||||
} catch (PDOException $e) {
|
||||
// 捕获唯一约束错误
|
||||
if ($e->errorInfo[1] == 1062) { // MySQL唯一键约束错误码
|
||||
throw new Exception('该手机号和车牌号组合已经被其他VIP客户使用');
|
||||
}
|
||||
throw $e; // 重新抛出其他类型的异常
|
||||
}
|
||||
|
||||
} elseif ($action === 'delete_vip') {
|
||||
$id = (int)$_POST['vip_id'];
|
||||
$stmt = $pdo->prepare("DELETE FROM vip_customers WHERE id = ?");
|
||||
@@ -58,9 +101,30 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取所有VIP客户
|
||||
// 获取搜索参数
|
||||
$search_phone = isset($_GET['search_phone']) ? trim($_GET['search_phone']) : '';
|
||||
$search_car_number = isset($_GET['search_car_number']) ? trim($_GET['search_car_number']) : '';
|
||||
|
||||
// 获取所有VIP客户(支持搜索)
|
||||
try {
|
||||
$stmt = $pdo->query("SELECT * FROM vip_customers ORDER BY created_at DESC");
|
||||
// 构建查询语句
|
||||
$query = "SELECT * FROM vip_customers WHERE 1=1";
|
||||
$params = [];
|
||||
|
||||
if (!empty($search_phone)) {
|
||||
$query .= " AND phone LIKE :phone";
|
||||
$params[':phone'] = '%' . $search_phone . '%';
|
||||
}
|
||||
|
||||
if (!empty($search_car_number)) {
|
||||
$query .= " AND car_number LIKE :car_number";
|
||||
$params[':car_number'] = '%' . $search_car_number . '%';
|
||||
}
|
||||
|
||||
$query .= " ORDER BY created_at DESC";
|
||||
|
||||
$stmt = $pdo->prepare($query);
|
||||
$stmt->execute($params);
|
||||
$vip_customers = $stmt->fetchAll();
|
||||
} catch (Exception $e) {
|
||||
$error_message = '获取VIP客户列表失败:' . $e->getMessage();
|
||||
@@ -108,6 +172,27 @@ try {
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="vip-management">
|
||||
<!-- VIP搜索表单 -->
|
||||
<div class="card search-container">
|
||||
<h2>🔍 VIP客户搜索</h2>
|
||||
<form method="GET" class="form search-form">
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="search_phone">手机号</label>
|
||||
<input type="tel" id="search_phone" name="search_phone" placeholder="请输入手机号" value="<?php echo htmlspecialchars($search_phone); ?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="search_car_number">车牌号</label>
|
||||
<input type="text" id="search_car_number" name="search_car_number" placeholder="请输入车牌号" value="<?php echo htmlspecialchars($search_car_number); ?>">
|
||||
</div>
|
||||
<div class="form-group" style="align-self: flex-end;">
|
||||
<button type="submit" class="btn btn-primary">搜索</button>
|
||||
<button type="button" class="btn" onclick="clearSearch()">清空</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- VIP录入表单 -->
|
||||
<div class="card">
|
||||
<h2>➕ 录入新VIP客户</h2>
|
||||
@@ -224,6 +309,25 @@ try {
|
||||
</div>
|
||||
|
||||
<div class="vip-actions">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-primary"
|
||||
onclick="editVip(<?php echo $vip['id']; ?>, '<?php echo htmlspecialchars($vip['customer_name']); ?>',
|
||||
'<?php echo htmlspecialchars($vip['phone']); ?>', '<?php echo htmlspecialchars($vip['car_model']); ?>',
|
||||
'<?php echo htmlspecialchars($vip['car_number']); ?>', '<?php echo htmlspecialchars($vip['email']); ?>',
|
||||
'<?php echo $vip['birthday'] ? date('Y-m-d', strtotime($vip['birthday'])) : ''; ?>',
|
||||
'<?php echo htmlspecialchars($vip['notes']); ?>')">编辑</button>
|
||||
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-info"
|
||||
onclick="viewBookings('<?php echo htmlspecialchars($vip['phone']); ?>', '<?php echo htmlspecialchars($vip['customer_name']); ?>')">查看预约</button>
|
||||
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-success"
|
||||
onclick="addBookingForVip('<?php echo htmlspecialchars($vip['customer_name']); ?>',
|
||||
'<?php echo htmlspecialchars($vip['phone']); ?>',
|
||||
'<?php echo htmlspecialchars($vip['car_model']); ?>',
|
||||
'<?php echo htmlspecialchars($vip['car_number']); ?>')">添加预约</button>
|
||||
|
||||
<form method="POST" style="display: inline;">
|
||||
<input type="hidden" name="vip_id" value="<?php echo $vip['id']; ?>">
|
||||
<button type="submit" name="action" value="delete_vip"
|
||||
@@ -243,7 +347,648 @@ try {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加预约模态框 -->
|
||||
<div id="addBookingModal" class="modal" style="display: none;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2>📝 为客户添加预约</h2>
|
||||
<span class="close-modal" onclick="closeAddBookingModal()">×</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="addBookingForm" method="POST" action="process_booking.php">
|
||||
<!-- 预填充的VIP客户信息 -->
|
||||
<input type="hidden" name="source" value="vip_page">
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="booking_customer_name">客户姓名</label>
|
||||
<input type="text" class="form-control" id="booking_customer_name" name="customer_name" required readonly>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="booking_phone">手机号码</label>
|
||||
<input type="tel" class="form-control" id="booking_phone" name="phone" required readonly>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="booking_car_model">车型</label>
|
||||
<input type="text" class="form-control" id="booking_car_model" name="car_model">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="booking_car_number">车牌号</label>
|
||||
<input type="text" class="form-control" id="booking_car_number" name="car_number" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="booking_date">预约日期</label>
|
||||
<input type="date" class="form-control" id="booking_date" name="booking_date" required min="<?php echo date('Y-m-d'); ?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="booking_time_slot">预约时间</label>
|
||||
<select class="form-control" id="booking_time_slot" name="time_slot" required>
|
||||
<option value="">请选择时间</option>
|
||||
<option value="09:00-10:00">09:00-10:00</option>
|
||||
<option value="10:00-11:00">10:00-11:00</option>
|
||||
<option value="11:00-12:00">11:00-12:00</option>
|
||||
<option value="14:00-15:00">14:00-15:00</option>
|
||||
<option value="15:00-16:00">15:00-16:00</option>
|
||||
<option value="16:00-17:00">16:00-17:00</option>
|
||||
<option value="17:00-18:00">17:00-18:00</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="booking_package_id">洗车套餐</label>
|
||||
<select class="form-control" id="booking_package_id" name="package_id" required>
|
||||
<option value="">请选择套餐</option>
|
||||
<?php
|
||||
// 查询所有可用的洗车套餐
|
||||
$package_result = $conn->query("SELECT id, package_name, price FROM packages ORDER BY price ASC");
|
||||
if ($package_result && $package_result->num_rows > 0) {
|
||||
while ($package = $package_result->fetch_assoc()) {
|
||||
echo "<option value='{$package['id']}'>{$package['package_name']} - ¥{$package['price']}</option>";
|
||||
}
|
||||
}
|
||||
$package_result->free_result();
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="booking_notes">备注</label>
|
||||
<textarea class="form-control" id="booking_notes" name="notes" rows="3"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeAddBookingModal()">取消</button>
|
||||
<button type="submit" class="btn btn-primary">确认添加预约</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- VIP客户预约记录模态框 -->
|
||||
<div id="bookingsModal" class="modal" style="display: none;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2>📋 <span id="customerName">客户</span> 的预约记录</h2>
|
||||
<span class="close-modal" onclick="closeBookingsModal()">×</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="bookingsLoading" style="text-align: center; padding: 20px;">
|
||||
加载预约记录中...
|
||||
</div>
|
||||
<div id="bookingsContent" style="display: none;">
|
||||
<div class="bookings-stats">
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">总预约次数:</span>
|
||||
<span id="totalBookings" class="stat-value">0</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">待服务:</span>
|
||||
<span id="pendingBookings" class="stat-value pending">0</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">已完成:</span>
|
||||
<span id="completedBookings" class="stat-value completed">0</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bookings-list" id="bookingsList">
|
||||
<!-- 预约记录将通过JavaScript动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
<div id="noBookings" style="display: none; text-align: center; padding: 40px; color: #666;">
|
||||
暂无预约记录
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- VIP编辑模态框 -->
|
||||
<div id="editVipModal" class="modal" style="display: none;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2>编辑VIP客户信息</h2>
|
||||
<span class="close-modal" onclick="closeModal()">×</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="editVipForm" method="POST">
|
||||
<input type="hidden" name="action" value="update_vip">
|
||||
<input type="hidden" id="edit_vip_id" name="vip_id">
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="edit_customer_name">客户姓名 *</label>
|
||||
<input type="text" id="edit_customer_name" name="customer_name" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="edit_phone">联系电话 *</label>
|
||||
<input type="tel" id="edit_phone" name="phone" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="edit_car_model">车型</label>
|
||||
<input type="text" id="edit_car_model" name="car_model" placeholder="如:宝马X5">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="edit_car_number">车牌号</label>
|
||||
<input type="text" id="edit_car_number" name="car_number" placeholder="如:京A88888">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="edit_email">邮箱</label>
|
||||
<input type="email" id="edit_email" name="email" placeholder="可选">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="edit_birthday">生日</label>
|
||||
<input type="date" id="edit_birthday" name="birthday">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="edit_notes">备注</label>
|
||||
<textarea id="edit_notes" name="notes" rows="3" placeholder="VIP客户特殊需求或备注信息..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">保存修改</button>
|
||||
<button type="button" class="btn" onclick="closeModal()">取消</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 模态框样式 -->
|
||||
<style>
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
padding-top: 60px;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: #fefefe;
|
||||
margin: 5% auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #888;
|
||||
width: 90%;
|
||||
max-width: 600px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.modal-header h2 {
|
||||
margin: 0;
|
||||
color: var(--el-text-primary);
|
||||
}
|
||||
|
||||
.close-modal {
|
||||
color: #aaa;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.close-modal:hover, .close-modal:focus {
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
/* 搜索样式 */
|
||||
.search-container {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.search-form .form-row {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.search-form .form-group {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.search-form .form-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.search-form .form-group:last-child {
|
||||
align-self: flex-start;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.search-form .form-group:last-child button {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 预约记录样式 */
|
||||
.bookings-stats {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
display: block;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.stat-value.pending {
|
||||
color: #ff9800;
|
||||
}
|
||||
|
||||
.stat-value.completed {
|
||||
color: #4caf50;
|
||||
}
|
||||
|
||||
.bookings-list {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.booking-item {
|
||||
padding: 15px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.booking-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.booking-date {
|
||||
font-weight: bold;
|
||||
color: var(--el-text-primary);
|
||||
}
|
||||
|
||||
.booking-status {
|
||||
padding: 3px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.booking-status.pending {
|
||||
background-color: #fff3cd;
|
||||
color: #856404;
|
||||
}
|
||||
|
||||
.booking-status.completed {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
}
|
||||
|
||||
.booking-status.canceled {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
.booking-details {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.booking-detail-item {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
/* 添加预约表单样式 */
|
||||
#addBookingForm .form-row {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#addBookingForm .form-group {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#addBookingForm label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: 500;
|
||||
color: var(--el-text-primary);
|
||||
}
|
||||
|
||||
#addBookingForm input, #addBookingForm select, #addBookingForm textarea {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#addBookingForm input:read-only {
|
||||
background-color: #f8f9fa;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.bookings-stats {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.booking-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
#addBookingForm .form-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// VIP编辑功能
|
||||
function editVip(id, name, phone, carModel, carNumber, email, birthday, notes) {
|
||||
document.getElementById('edit_vip_id').value = id;
|
||||
document.getElementById('edit_customer_name').value = name;
|
||||
document.getElementById('edit_phone').value = phone;
|
||||
document.getElementById('edit_car_model').value = carModel;
|
||||
document.getElementById('edit_car_number').value = carNumber;
|
||||
document.getElementById('edit_email').value = email || '';
|
||||
document.getElementById('edit_birthday').value = birthday || '';
|
||||
document.getElementById('edit_notes').value = notes || '';
|
||||
|
||||
// 显示模态框
|
||||
document.getElementById('editVipModal').style.display = 'block';
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
document.getElementById('editVipModal').style.display = 'none';
|
||||
}
|
||||
|
||||
// 清空搜索
|
||||
function clearSearch() {
|
||||
document.getElementById('search_phone').value = '';
|
||||
document.getElementById('search_car_number').value = '';
|
||||
// 提交空搜索表单以显示所有结果
|
||||
document.querySelector('.search-form').submit();
|
||||
}
|
||||
|
||||
// 查看VIP客户预约记录
|
||||
function viewBookings(phone, customerName) {
|
||||
document.getElementById('customerName').textContent = customerName;
|
||||
|
||||
// 显示加载状态
|
||||
document.getElementById('bookingsLoading').style.display = 'block';
|
||||
document.getElementById('bookingsContent').style.display = 'none';
|
||||
document.getElementById('noBookings').style.display = 'none';
|
||||
|
||||
// 显示模态框
|
||||
document.getElementById('bookingsModal').style.display = 'block';
|
||||
|
||||
// 使用AJAX获取预约记录
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', 'get_bookings.php?phone=' + encodeURIComponent(phone), true);
|
||||
|
||||
xhr.onload = function() {
|
||||
if (xhr.status === 200) {
|
||||
try {
|
||||
const data = JSON.parse(xhr.responseText);
|
||||
|
||||
// 更新统计信息
|
||||
document.getElementById('totalBookings').textContent = data.total || 0;
|
||||
document.getElementById('pendingBookings').textContent = data.pending || 0;
|
||||
document.getElementById('completedBookings').textContent = data.completed || 0;
|
||||
|
||||
// 更新预约列表
|
||||
const bookingsList = document.getElementById('bookingsList');
|
||||
bookingsList.innerHTML = '';
|
||||
|
||||
if (data.bookings && data.bookings.length > 0) {
|
||||
// 显示预约列表
|
||||
document.getElementById('bookingsContent').style.display = 'block';
|
||||
|
||||
// 为每个预约创建HTML元素
|
||||
data.bookings.forEach(function(booking) {
|
||||
const bookingItem = document.createElement('div');
|
||||
bookingItem.className = 'booking-item';
|
||||
|
||||
// 状态样式类
|
||||
let statusClass = '';
|
||||
switch(booking.status) {
|
||||
case '待服务':
|
||||
statusClass = 'pending';
|
||||
break;
|
||||
case '已完成':
|
||||
statusClass = 'completed';
|
||||
break;
|
||||
default:
|
||||
statusClass = 'canceled';
|
||||
}
|
||||
|
||||
bookingItem.innerHTML = '<div class="booking-header">' +
|
||||
'<div class="booking-date">预约日期:' + (booking.booking_date || '') + '</div>' +
|
||||
'<span class="booking-status ' + statusClass + '">' + booking.status + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="booking-details">' +
|
||||
'<div class="booking-detail-item">车牌号:' + (booking.car_number || '未提供') + '</div>' +
|
||||
'<div class="booking-detail-item">车型:' + (booking.car_model || '未提供') + '</div>' +
|
||||
'<div class="booking-detail-item">洗车套餐:' + (booking.package_name || '未选择') + '</div>' +
|
||||
'<div class="booking-detail-item">预约时间:' + (booking.time_slot || '未选择') + '</div>' +
|
||||
(booking.notes ? '<div class="booking-detail-item">备注:' + booking.notes + '</div>' : '') +
|
||||
'</div>';
|
||||
|
||||
bookingsList.appendChild(bookingItem);
|
||||
});
|
||||
} else {
|
||||
// 显示无预约记录提示
|
||||
document.getElementById('noBookings').style.display = 'block';
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('解析预约数据失败:', e);
|
||||
document.getElementById('noBookings').style.display = 'block';
|
||||
}
|
||||
} else {
|
||||
console.error('获取预约记录失败:', xhr.status);
|
||||
document.getElementById('noBookings').style.display = 'block';
|
||||
}
|
||||
// 隐藏加载状态
|
||||
document.getElementById('bookingsLoading').style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = function() {
|
||||
console.error('网络错误');
|
||||
document.getElementById('bookingsLoading').style.display = 'none';
|
||||
document.getElementById('noBookings').style.display = 'block';
|
||||
};
|
||||
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
|
||||
function closeBookingsModal() {
|
||||
document.getElementById('bookingsModal').style.display = 'none';
|
||||
}
|
||||
|
||||
// 为VIP客户添加预约
|
||||
function addBookingForVip(customerName, phone, carModel, carNumber) {
|
||||
// 预填充表单数据
|
||||
document.getElementById('booking_customer_name').value = customerName;
|
||||
document.getElementById('booking_phone').value = phone;
|
||||
document.getElementById('booking_car_model').value = carModel || '';
|
||||
document.getElementById('booking_car_number').value = carNumber || '';
|
||||
|
||||
// 设置默认预约日期为今天
|
||||
document.getElementById('booking_date').value = new Date().toISOString().split('T')[0];
|
||||
|
||||
// 清空其他字段
|
||||
document.getElementById('booking_time_slot').value = '';
|
||||
document.getElementById('booking_package_id').value = '';
|
||||
document.getElementById('booking_notes').value = '';
|
||||
|
||||
// 显示模态框
|
||||
document.getElementById('addBookingModal').style.display = 'block';
|
||||
}
|
||||
|
||||
function closeAddBookingModal() {
|
||||
document.getElementById('addBookingModal').style.display = 'none';
|
||||
}
|
||||
|
||||
// 点击模态框外部关闭
|
||||
window.onclick = function(event) {
|
||||
const editModal = document.getElementById('editVipModal');
|
||||
const bookingsModal = document.getElementById('bookingsModal');
|
||||
const addBookingModal = document.getElementById('addBookingModal');
|
||||
|
||||
if (event.target === editModal) {
|
||||
closeModal();
|
||||
} else if (event.target === bookingsModal) {
|
||||
closeBookingsModal();
|
||||
} else if (event.target === addBookingModal) {
|
||||
closeAddBookingModal();
|
||||
}
|
||||
}
|
||||
|
||||
// 添加预约表单提交事件
|
||||
document.getElementById('addBookingForm')?.addEventListener('submit', function(e) {
|
||||
// 可以在这里添加额外的表单验证逻辑
|
||||
// 例如验证日期时间是否合理,套餐是否已选择等
|
||||
|
||||
// 验证车牌号
|
||||
const carNumber = document.getElementById('booking_car_number').value.trim();
|
||||
if (!carNumber) {
|
||||
alert('请输入车牌号');
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证预约日期和时间
|
||||
const bookingDate = new Date(document.getElementById('booking_date').value);
|
||||
const timeSlot = document.getElementById('booking_time_slot').value;
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
if (bookingDate < today) {
|
||||
alert('不能选择过去的日期');
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!timeSlot) {
|
||||
alert('请选择预约时间');
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证套餐选择
|
||||
const packageId = document.getElementById('booking_package_id').value;
|
||||
if (!packageId) {
|
||||
alert('请选择洗车套餐');
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 提交表单前可以添加确认提示
|
||||
if (!confirm('确认要添加这个预约吗?')) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// 移动端优化脚本
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 为按钮添加触摸反馈
|
||||
|
||||
Reference in New Issue
Block a user