c64651d6c7
refactor(预约统计): 修改查询逻辑只计算有效预约
1160 lines
53 KiB
PHP
1160 lines
53 KiB
PHP
<?php
|
||
session_start();
|
||
require_once 'db_connect.php';
|
||
|
||
$message = '';
|
||
$success_message = '';
|
||
|
||
// 处理表单提交
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
try {
|
||
if (isset($_POST['action'])) {
|
||
$action = $_POST['action'];
|
||
|
||
if ($action === 'add_vip') {
|
||
$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 = ?");
|
||
$stmt->execute([$phone, $car_number]);
|
||
if ($stmt->fetch()) {
|
||
throw new Exception('该手机号和车牌号组合已经是VIP客户');
|
||
}
|
||
|
||
// 插入VIP客户
|
||
$stmt = $pdo->prepare("INSERT INTO vip_customers
|
||
(customer_name, phone, car_model, car_number, email, birthday, notes)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?)");
|
||
try {
|
||
$stmt->execute([$customer_name, $phone, $car_model, $car_number, $email, $birthday, $notes]);
|
||
$success_message = "VIP客户录入成功!";
|
||
} catch (PDOException $e) {
|
||
// 捕获唯一约束错误
|
||
if ($e->errorInfo[1] == 1062) { // MySQL唯一键约束错误码
|
||
throw new Exception('该手机号和车牌号组合已经是VIP客户');
|
||
}
|
||
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 = ?");
|
||
$stmt->execute([$id]);
|
||
$success_message = "VIP客户删除成功!";
|
||
|
||
} elseif ($action === 'convert_from_booking') {
|
||
// 从预约记录转换为VIP客户
|
||
$booking_id = isset($_POST['booking_id']) ? (int)$_POST['booking_id'] : 0;
|
||
|
||
if ($booking_id <= 0) {
|
||
throw new Exception('无效的预约ID');
|
||
}
|
||
|
||
// 获取预约信息
|
||
$stmt = $pdo->prepare("SELECT customer_name, phone, car_model, car_number FROM bookings WHERE id = ?");
|
||
$stmt->execute([$booking_id]);
|
||
$booking = $stmt->fetch();
|
||
|
||
if (!$booking) {
|
||
throw new Exception('预约记录不存在');
|
||
}
|
||
|
||
// 检查是否已经是VIP客户
|
||
$stmt = $pdo->prepare("SELECT id FROM vip_customers WHERE phone = ? AND car_number = ?");
|
||
$stmt->execute([$booking['phone'], $booking['car_number']]);
|
||
if ($stmt->fetch()) {
|
||
throw new Exception('该客户已经是VIP客户');
|
||
}
|
||
|
||
// 插入VIP客户
|
||
$stmt = $pdo->prepare("INSERT INTO vip_customers
|
||
(customer_name, phone, car_model, car_number, notes)
|
||
VALUES (?, ?, ?, ?, ?)");
|
||
try {
|
||
$notes = '从预约记录 #' . $booking_id . ' 自动转换';
|
||
$stmt->execute([
|
||
$booking['customer_name'],
|
||
$booking['phone'],
|
||
$booking['car_model'] ?? '',
|
||
$booking['car_number'] ?? '',
|
||
$notes
|
||
]);
|
||
|
||
// 更新该客户的所有预约记录的member_type为VIP会员
|
||
$stmt = $pdo->prepare("UPDATE bookings SET member_type = 'VIP会员' WHERE phone = ? AND car_number = ?");
|
||
$stmt->execute([$booking['phone'], $booking['car_number']]);
|
||
|
||
$success_message = "客户已成功转换为VIP客户!";
|
||
} catch (PDOException $e) {
|
||
if ($e->errorInfo[1] == 1062) {
|
||
throw new Exception('该客户已经是VIP客户');
|
||
}
|
||
throw $e;
|
||
}
|
||
}
|
||
}
|
||
} catch (Exception $e) {
|
||
$message = $e->getMessage();
|
||
}
|
||
}
|
||
|
||
// 获取搜索参数
|
||
$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 {
|
||
// 构建查询语句
|
||
$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();
|
||
$vip_customers = [];
|
||
}
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||
<meta name="format-detection" content="telephone=no">
|
||
<meta name="description" content="VIP客户管理,录入和管理VIP客户信息">
|
||
<meta name="keywords" content="VIP管理,客户管理,会员管理">
|
||
<title>张老师撸车(私家车库)工作室</title>
|
||
<link rel="stylesheet" href="style.css">
|
||
<script src="mobile-nav.js" defer></script>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<header class="header">
|
||
<button class="mobile-menu-toggle" onclick="toggleMobileMenu()" aria-label="菜单">
|
||
<span></span>
|
||
<span></span>
|
||
<span></span>
|
||
</button>
|
||
<h1>🚗 张老师撸车工作室 - VIP管理</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">待处理预约</a>
|
||
<a href="packages.php" class="nav-link">套餐管理</a>
|
||
<a href="vip.php" class="nav-link active">VIP管理</a>
|
||
<a href="announcement.php" class="nav-link">今日待办</a>
|
||
</nav>
|
||
</header>
|
||
|
||
<!-- 移动端导航菜单 -->
|
||
<div class="mobile-nav-overlay" onclick="closeMobileMenu()"></div>
|
||
<nav class="mobile-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">待处理预约</a>
|
||
<a href="packages.php" class="nav-link">套餐管理</a>
|
||
<a href="vip.php" class="nav-link active">VIP管理</a>
|
||
<a href="announcement.php" class="nav-link">今日待办</a>
|
||
</nav>
|
||
|
||
<?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 ($message): ?>
|
||
<div class="message error-message" style="background-color: #fee; color: #c33; border-color: #fcc;">
|
||
<?= htmlspecialchars($message) ?>
|
||
</div>
|
||
<?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>
|
||
<form method="POST" class="form">
|
||
<input type="hidden" name="action" value="add_vip">
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="customer_name">客户姓名 *</label>
|
||
<input type="text" id="customer_name" name="customer_name" required>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="phone">联系电话 *</label>
|
||
<input type="tel" id="phone" name="phone" required>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="car_model">车型</label>
|
||
<input type="text" id="car_model" name="car_model" placeholder="如:宝马X5">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="car_number">车牌号</label>
|
||
<input type="text" id="car_number" name="car_number" placeholder="如:京A88888">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="email">邮箱</label>
|
||
<input type="email" id="email" name="email" placeholder="可选">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="birthday">生日</label>
|
||
<input type="date" id="birthday" name="birthday">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="notes">备注</label>
|
||
<textarea id="notes" name="notes" rows="3" placeholder="VIP客户特殊需求或备注信息..."></textarea>
|
||
</div>
|
||
|
||
<div class="form-actions">
|
||
<button type="submit" class="btn btn-primary">录入VIP客户</button>
|
||
<button type="reset" class="btn">重置</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- 从预约转换为VIP -->
|
||
<div class="card">
|
||
<h2>📋 从预约记录转换为VIP客户</h2>
|
||
<p style="color: #666; margin-bottom: 15px;">选择预约记录,快速将普通客户转换为VIP客户</p>
|
||
<?php
|
||
// 获取可以转换为VIP的普通客户预约记录(最近30天,且不是VIP客户)
|
||
try {
|
||
$stmt = $pdo->prepare("SELECT DISTINCT b.id, b.customer_name, b.phone, b.car_model, b.car_number,
|
||
COUNT(*) as booking_count,
|
||
MAX(b.start_time) as last_booking_time
|
||
FROM bookings b
|
||
LEFT JOIN vip_customers v ON b.phone = v.phone AND b.car_number = v.car_number AND v.is_active = 1
|
||
WHERE b.member_type = '普通客户'
|
||
AND v.id IS NULL
|
||
AND b.start_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
|
||
GROUP BY b.phone, b.car_number, b.customer_name, b.car_model
|
||
ORDER BY last_booking_time DESC
|
||
LIMIT 20");
|
||
$stmt->execute();
|
||
$convertible_bookings = $stmt->fetchAll();
|
||
} catch (Exception $e) {
|
||
$convertible_bookings = [];
|
||
}
|
||
?>
|
||
|
||
<?php if (empty($convertible_bookings)): ?>
|
||
<div class="empty-message">暂无可转换的普通客户</div>
|
||
<?php else: ?>
|
||
<div style="max-height: 400px; overflow-y: auto;">
|
||
<?php foreach ($convertible_bookings as $booking): ?>
|
||
<div style="padding: 12px; border: 1px solid #e0e0e0; border-radius: 6px; margin-bottom: 10px; background: #f9f9f9;">
|
||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||
<div style="flex: 1;">
|
||
<strong><?php echo htmlspecialchars($booking['customer_name']); ?></strong><br>
|
||
<span style="color: #666; font-size: 0.9em;">
|
||
手机号:<?php echo htmlspecialchars($booking['phone']); ?> |
|
||
车牌号:<?php echo htmlspecialchars($booking['car_number']); ?><br>
|
||
<?php if ($booking['car_model']): ?>
|
||
车型:<?php echo htmlspecialchars($booking['car_model']); ?> |
|
||
<?php endif; ?>
|
||
预约次数:<?php echo $booking['booking_count']; ?>次
|
||
</span>
|
||
</div>
|
||
<form method="POST" style="margin-left: 15px;">
|
||
<input type="hidden" name="action" value="convert_from_booking">
|
||
<input type="hidden" name="booking_id" value="<?php echo $booking['id']; ?>">
|
||
<button type="submit" class="btn btn-sm" style="background-color: #ffd700; color: #333; font-weight: bold;"
|
||
onclick="return confirm('确定要将 <?php echo htmlspecialchars($booking['customer_name']); ?> 转换为VIP客户吗?')">
|
||
👑 转为VIP
|
||
</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<!-- VIP客户列表 -->
|
||
<div class="card">
|
||
<h2>👑 VIP客户列表 (共 <?php echo count($vip_customers); ?> 位)</h2>
|
||
|
||
<?php if (empty($vip_customers)): ?>
|
||
<div class="empty-message">暂无VIP客户</div>
|
||
<?php else: ?>
|
||
<div class="vip-grid">
|
||
<?php foreach ($vip_customers as $vip): ?>
|
||
<div class="vip-card">
|
||
<div class="vip-header">
|
||
<h3><?php echo htmlspecialchars($vip['customer_name']); ?></h3>
|
||
<div class="vip-status">👑 VIP</div>
|
||
</div>
|
||
|
||
<div class="vip-details">
|
||
<div class="detail-item">
|
||
<span class="detail-label">联系电话:</span>
|
||
<span class="detail-value"><?php echo htmlspecialchars($vip['phone']); ?></span>
|
||
</div>
|
||
|
||
<?php if ($vip['car_model']): ?>
|
||
<div class="detail-item">
|
||
<span class="detail-label">车型:</span>
|
||
<span class="detail-value"><?php echo htmlspecialchars($vip['car_model']); ?></span>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($vip['car_number']): ?>
|
||
<div class="detail-item">
|
||
<span class="detail-label">车牌号:</span>
|
||
<span class="detail-value"><?php echo htmlspecialchars($vip['car_number']); ?></span>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($vip['email']): ?>
|
||
<div class="detail-item">
|
||
<span class="detail-label">邮箱:</span>
|
||
<span class="detail-value"><?php echo htmlspecialchars($vip['email']); ?></span>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($vip['birthday']): ?>
|
||
<div class="detail-item">
|
||
<span class="detail-label">生日:</span>
|
||
<span class="detail-value"><?php echo date('Y-m-d', strtotime($vip['birthday'])); ?></span>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<?php if ($vip['notes']): ?>
|
||
<div class="vip-notes">
|
||
<span class="detail-label">备注:</span>
|
||
<span><?php echo htmlspecialchars($vip['notes']); ?></span>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<div class="vip-meta">
|
||
<span>录入时间:<?php echo $vip['created_at']; ?></span>
|
||
</div>
|
||
|
||
<script>
|
||
// 函数定义占位符,确保按钮点击时函数已定义
|
||
function editVip() {};
|
||
function viewBookings() {};
|
||
function addBookingForVip() {};
|
||
</script>
|
||
<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"
|
||
class="btn btn-sm btn-danger"
|
||
onclick="return confirm('确定要删除这个VIP客户吗?')">删除</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="text-align: center; margin-top: 2rem;">
|
||
<a href="index.php" class="btn">返回预约页面</a>
|
||
</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
|
||
// 查询所有可用的洗车套餐
|
||
// #region agent log
|
||
$log_data = json_encode(['location' => 'vip.php:417', 'message' => 'Loading packages', 'data' => ['using_pdo' => isset($pdo), 'using_conn' => isset($conn)], 'timestamp' => time() * 1000, 'sessionId' => 'debug-session', 'runId' => 'run1', 'hypothesisId' => 'B']);
|
||
file_put_contents('.cursor/debug.log', $log_data . "\n", FILE_APPEND);
|
||
// #endregion
|
||
try {
|
||
$stmt = $pdo->query("SELECT id, package_name, price FROM packages WHERE is_active = 1 ORDER BY price ASC");
|
||
$packages = $stmt->fetchAll();
|
||
foreach ($packages as $package) {
|
||
echo "<option value='{$package['id']}'>{$package['package_name']} - ¥{$package['price']}</option>";
|
||
}
|
||
} catch (Exception $e) {
|
||
echo "<option value=''>加载套餐失败</option>";
|
||
}
|
||
?>
|
||
</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>
|
||
// DOM加载完成后重新定义函数
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// VIP编辑功能
|
||
window.editVip = function(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';
|
||
};
|
||
|
||
window.closeModal = function() {
|
||
document.getElementById('editVipModal').style.display = 'none';
|
||
};
|
||
|
||
// 清空搜索
|
||
window.clearSearch = function() {
|
||
document.getElementById('search_phone').value = '';
|
||
document.getElementById('search_car_number').value = '';
|
||
// 提交空搜索表单以显示所有结果
|
||
document.querySelector('.search-form').submit();
|
||
};
|
||
|
||
// 查看VIP客户预约记录
|
||
window.viewBookings = function(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';
|
||
} finally {
|
||
// 隐藏加载状态
|
||
document.getElementById('bookingsLoading').style.display = 'none';
|
||
}
|
||
} 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();
|
||
};
|
||
|
||
|
||
window.closeBookingsModal = function() {
|
||
document.getElementById('bookingsModal').style.display = 'none';
|
||
};
|
||
|
||
// 为VIP客户添加预约
|
||
window.addBookingForVip = function(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 || '';
|
||
|
||
// 设置默认预约日期为今天
|
||
const today = new Date();
|
||
const year = today.getFullYear();
|
||
const month = String(today.getMonth() + 1).padStart(2, '0');
|
||
const day = String(today.getDate()).padStart(2, '0');
|
||
document.getElementById('booking_date').value = `${year}-${month}-${day}`;
|
||
|
||
// 清空其他字段
|
||
document.getElementById('booking_time_slot').value = '';
|
||
document.getElementById('booking_package_id').value = '';
|
||
document.getElementById('booking_notes').value = '';
|
||
|
||
// 显示模态框
|
||
document.getElementById('addBookingModal').style.display = 'block';
|
||
};
|
||
|
||
window.closeAddBookingModal = function() {
|
||
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();
|
||
}
|
||
};
|
||
|
||
// 添加预约表单提交事件
|
||
if (document.getElementById('addBookingForm')) {
|
||
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;
|
||
});
|
||
}
|
||
|
||
// 移动端优化脚本
|
||
// 为按钮添加触摸反馈
|
||
const buttons = document.querySelectorAll('.btn');
|
||
buttons.forEach(btn => {
|
||
btn.addEventListener('touchstart', function() {
|
||
this.style.transform = 'translateY(1px)';
|
||
});
|
||
btn.addEventListener('touchend', function() {
|
||
this.style.transform = 'translateY(-2px)';
|
||
});
|
||
});
|
||
|
||
// 检测设备类型
|
||
const isMobile = /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||
if (isMobile) {
|
||
document.body.classList.add('mobile-device');
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|