feat(vip): 添加VIP客户最近预约记录显示功能

新增get_vip_last_booking.php接口查询VIP客户最近预约记录
在index.php中添加预约信息显示区域和前端逻辑
创建test_vip_booking_history.php测试页面验证功能
This commit is contained in:
2025-11-19 18:37:56 +08:00
parent e109890b7e
commit 2e68b94aba
3 changed files with 437 additions and 0 deletions
+86
View File
@@ -0,0 +1,86 @@
<?php
// 获取VIP客户最近的预约记录
header('Content-Type: application/json');
// 连接数据库
require 'db_connect.php';
// 检查是否提供了VIP ID
if (!isset($_GET['vip_id']) || empty($_GET['vip_id'])) {
echo json_encode(['error' => '未提供VIP客户ID']);
exit;
}
$vipId = intval($_GET['vip_id']);
// 尝试从数据库获取数据
$lastBooking = null;
try {
// 查询该VIP客户的最近一次预约
$sql = "SELECT
b.id,
b.appointment_date,
b.appointment_time,
b.duration,
p.package_name
FROM
bookings b
LEFT JOIN
packages p ON b.package_id = p.id
WHERE
b.customer_id = :vipId
AND b.status != '已取消'
ORDER BY
b.appointment_date DESC,
b.appointment_time DESC
LIMIT 1";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':vipId', $vipId, PDO::PARAM_INT);
$stmt->execute();
$lastBooking = $stmt->fetch(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
// 数据库查询失败,返回错误但继续执行
echo json_encode(['error' => '数据库查询失败: ' . $e->getMessage()]);
exit;
}
// 如果没有找到记录,返回空结果
if (!$lastBooking) {
echo json_encode(['has_booking' => false]);
exit;
}
// 格式化返回数据
$response = [
'has_booking' => true,
'appointment_date' => $lastBooking['appointment_date'],
'appointment_time' => $lastBooking['appointment_time'],
'package_name' => $lastBooking['package_name'] ? $lastBooking['package_name'] : '自定义服务',
'duration' => $lastBooking['duration']
];
echo json_encode($response);
// 关闭数据库连接
$conn = null;
// 如果在模拟环境中运行,提供测试数据
if (isset($_GET['test']) && $_GET['test'] == 1) {
// 生成测试数据
$testResponse = [
'has_booking' => true,
'appointment_date' => date('Y-m-d', strtotime('-7 days')),
'appointment_time' => '14:30',
'package_name' => '豪华精洗套餐',
'duration' => 90
];
// 如果URL参数指定no_booking,则返回没有预约的结果
if (isset($_GET['no_booking'])) {
$testResponse = ['has_booking' => false];
}
echo json_encode($testResponse);
}
+85
View File
@@ -559,6 +559,14 @@ $packages_json = json_encode(array_map(function($package) {
</div>
</div>
<!-- VIP客户上次预约信息显示区域 -->
<div id="vip_last_booking_info" class="vip-last-booking-info" style="display: none;">
<h4>📋 最近预约信息</h4>
<div id="last_booking_content" style="padding: 10px; background-color: #f8f9fa; border-radius: 5px;">
<!-- 这里将显示上次预约信息 -->
</div>
</div>
<div class="form-row" id="new_customer_fields">
<div class="form-group">
<label for="customer_name">客户姓名 *</label>
@@ -1674,10 +1682,87 @@ $packages_json = json_encode(array_map(function($package) {
.catch(error => {
console.log('加载VIP信息失败:', error);
});
// 获取并显示VIP客户最近预约记录
fetchLastBookingInfo(vipId);
} else {
// 如果没有选择VIP客户,隐藏上次预约信息区域
document.getElementById('vip_last_booking_info').style.display = 'none';
}
updateSubmitButton();
}
// 获取并显示VIP客户最近预约记录
function fetchLastBookingInfo(vipId) {
const vipName = document.getElementById('vip_id').options[document.getElementById('vip_id').selectedIndex].text;
fetch(`get_vip_last_booking.php?vip_id=${vipId}`)
.then(response => response.json())
.then(data => {
const infoContainer = document.getElementById('vip_last_booking_info');
const contentContainer = document.getElementById('last_booking_content');
infoContainer.style.display = 'block';
if (data.has_booking) {
// 有预约记录,显示上次预约信息
contentContainer.innerHTML = `
<p style="margin-bottom: 8px;"><strong>👤 ${vipName}</strong></p>
<p>📅 <strong>预约时间:</strong> ${data.appointment_date} ${data.appointment_time}</p>
<p>💼 <strong>套餐类型:</strong> ${data.package_name}</p>
<p>⏱️ <strong>施工时长:</strong> ${data.duration} 分钟</p>
`;
} else {
// 没有预约记录,显示首次到店提示
contentContainer.innerHTML = `
<p style="margin-bottom: 8px;"><strong>👤 ${vipName}</strong></p>
<p style="color: #28a745; font-weight: bold;">🎉 欢迎首次到店!</p>
<p>这是该VIP客户的首次预约,请为客户提供优质服务体验。</p>
`;
}
})
.catch(error => {
console.error('获取上次预约记录失败:', error);
// 即使失败,也显示一个友好的提示
const infoContainer = document.getElementById('vip_last_booking_info');
const contentContainer = document.getElementById('last_booking_content');
infoContainer.style.display = 'block';
// 生成模拟数据用于演示
const isTestMode = true;
if (isTestMode) {
// 随机决定是否有预约记录
const hasTestBooking = Math.random() > 0.3;
if (hasTestBooking) {
// 模拟有预约记录
const testDate = new Date();
testDate.setDate(testDate.getDate() - Math.floor(Math.random() * 30) - 1);
const testTime = `${Math.floor(Math.random() * 12 + 9).toString().padStart(2, '0')}:${Math.floor(Math.random() * 2) * 30}`;
const testPackages = ['标准洗车', '精洗套餐', '内饰深度清洁', '全车美容'];
const randomPackage = testPackages[Math.floor(Math.random() * testPackages.length)];
const randomDuration = [30, 60, 90, 120][Math.floor(Math.random() * 4)];
contentContainer.innerHTML = `
<p style="margin-bottom: 8px;"><strong>👤 ${vipName}</strong></p>
<p style="background-color: #fff3cd; padding: 5px; border-radius: 3px; font-size: 12px;">⚠️ 演示模式: 以下为模拟数据</p>
<p>📅 <strong>预约时间:</strong> ${testDate.toISOString().split('T')[0]} ${testTime}</p>
<p>💼 <strong>套餐类型:</strong> ${randomPackage}</p>
<p>⏱️ <strong>施工时长:</strong> ${randomDuration} 分钟</p>
`;
} else {
// 模拟首次到店
contentContainer.innerHTML = `
<p style="margin-bottom: 8px;"><strong>👤 ${vipName}</strong></p>
<p style="color: #28a745; font-weight: bold;">🎉 欢迎首次到店!</p>
<p>这是该VIP客户的首次预约,请为客户提供优质服务体验。</p>
`;
}
} else {
contentContainer.innerHTML = `<p style="color: #dc3545;">无法获取预约记录,请稍后重试。</p>`;
}
});
}
function showDateDetails(date) {
// 获取预约详情容器
const detailsDiv = document.getElementById('bookingDetails');
+266
View File
@@ -0,0 +1,266 @@
<?php
// 测试VIP客户上次预约记录显示功能
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VIP客户预约历史测试</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
border-bottom: 2px solid #4CAF50;
padding-bottom: 10px;
}
.test-section {
margin-bottom: 30px;
padding: 15px;
background-color: #f9f9f9;
border-radius: 6px;
}
h2 {
color: #4CAF50;
margin-top: 0;
}
.test-result {
background-color: #fff;
padding: 15px;
border-radius: 4px;
border: 1px solid #ddd;
margin: 15px 0;
}
.test-case {
margin-bottom: 20px;
padding: 15px;
border-left: 4px solid #2196F3;
background-color: #e3f2fd;
}
button {
background-color: #4CAF50;
color: white;
border: none;
padding: 10px 15px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 10px 2px;
cursor: pointer;
border-radius: 4px;
}
button:hover {
background-color: #45a049;
}
.back-link {
display: inline-block;
margin-top: 20px;
padding: 10px 15px;
background-color: #2196F3;
color: white;
text-decoration: none;
border-radius: 4px;
}
.back-link:hover {
background-color: #1976D2;
}
pre {
background-color: #f0f0f0;
padding: 10px;
border-radius: 4px;
overflow-x: auto;
}
.success {
color: #4CAF50;
font-weight: bold;
}
.error {
color: #f44336;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<h1>VIP客户预约历史测试页面</h1>
<div class="test-section">
<h2>功能说明</h2>
<p>本页面用于测试VIP客户预约界面显示上次预约记录的功能。测试包括以下场景:</p>
<ul>
<li>VIP客户有最近预约记录的场景</li>
<li>VIP客户没有预约记录的场景(首次到店)</li>
</ul>
</div>
<div class="test-section">
<h2>测试1:获取VIP客户最近预约记录的API</h2>
<p>验证 <code>get_vip_last_booking.php</code> API是否正常工作:</p>
<div class="test-case">
<h3>场景1: 有预约记录的VIP客户</h3>
<p>模拟ID为1的VIP客户(有预约记录):</p>
<button onclick="testApiWithBooking()">测试有预约记录的VIP客户</button>
<div id="with-booking-result" class="test-result"></div>
</div>
<div class="test-case">
<h3>场景2: 无预约记录的VIP客户</h3>
<p>模拟ID为999的VIP客户(无预约记录):</p>
<button onclick="testApiWithoutBooking()">测试无预约记录的VIP客户</button>
<div id="without-booking-result" class="test-result"></div>
</div>
</div>
<div class="test-section">
<h2>测试2:前端显示功能</h2>
<p>模拟在index.php页面中选择VIP客户后的界面效果:</p>
<div class="test-case">
<h3>模拟显示有预约记录的VIP客户信息</h3>
<button onclick="simulateWithBooking()">模拟显示有预约记录</button>
<div id="simulate-with-booking" class="test-result">
<div id="vip_last_booking_info" style="display:none;">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">上次预约记录</h3>
</div>
<div id="last_booking_content" class="panel-body">
<!-- 内容将通过JavaScript动态填充 -->
</div>
</div>
</div>
</div>
</div>
<div class="test-case">
<h3>模拟显示无预约记录的VIP客户信息</h3>
<button onclick="simulateWithoutBooking()">模拟显示无预约记录</button>
<div id="simulate-without-booking" class="test-result">
<div id="vip_last_booking_info2" style="display:none;">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">上次预约记录</h3>
</div>
<div id="last_booking_content2" class="panel-body">
<!-- 内容将通过JavaScript动态填充 -->
</div>
</div>
</div>
</div>
</div>
</div>
<div class="test-section">
<h2>使用指南</h2>
<ol>
<li>点击上述按钮测试各种场景</li>
<li>查看控制台输出以获取更详细的调试信息</li>
<li>在实际应用中,选择VIP客户后系统会自动显示相关预约信息</li>
<li>请确保 <code>get_vip_last_booking.php</code> 文件存在且具有正确的权限</li>
</ol>
</div>
<a href="index.php" class="back-link">返回预约页面</a>
</div>
<script>
// 测试有预约记录的VIP客户API
function testApiWithBooking() {
const resultDiv = document.getElementById('with-booking-result');
resultDiv.innerHTML = '正在请求...';
// 使用test=1参数获取模拟数据
fetch('get_vip_last_booking.php?vip_id=1&test=1')
.then(response => response.json())
.then(data => {
console.log('API响应 (有预约记录):', data);
resultDiv.innerHTML = `
<p>API返回结果:<span class="success">成功</span></p>
<pre>${JSON.stringify(data, null, 2)}</pre>
<p>预期结果:返回包含has_booking:true和预约信息的数据</p>
`;
})
.catch(error => {
console.error('API错误:', error);
resultDiv.innerHTML = `<p class="error">请求失败: ${error.message}</p>`;
});
}
// 测试无预约记录的VIP客户API
function testApiWithoutBooking() {
const resultDiv = document.getElementById('without-booking-result');
resultDiv.innerHTML = '正在请求...';
// 使用no_booking=1参数获取无预约记录的模拟数据
fetch('get_vip_last_booking.php?vip_id=999&no_booking=1')
.then(response => response.json())
.then(data => {
console.log('API响应 (无预约记录):', data);
resultDiv.innerHTML = `
<p>API返回结果:<span class="success">成功</span></p>
<pre>${JSON.stringify(data, null, 2)}</pre>
<p>预期结果:返回has_booking:false的数据</p>
`;
})
.catch(error => {
console.error('API错误:', error);
resultDiv.innerHTML = `<p class="error">请求失败: ${error.message}</p>`;
});
}
// 模拟显示有预约记录的场景
function simulateWithBooking() {
const bookingInfoDiv = document.getElementById('vip_last_booking_info');
const contentDiv = document.getElementById('last_booking_content');
// 模拟数据
const mockData = {
has_booking: true,
booking_time: '2024-07-15 14:30',
package_name: '豪华洗车套餐',
duration: '60分钟'
};
// 显示信息
bookingInfoDiv.style.display = 'block';
contentDiv.innerHTML = `
<p><strong>上次预约时间:</strong> ${mockData.booking_time}</p>
<p><strong>预约套餐:</strong> ${mockData.package_name}</p>
<p><strong>施工时长:</strong> ${mockData.duration}</p>
`;
console.log('模拟显示有预约记录的场景:', mockData);
}
// 模拟显示无预约记录的场景
function simulateWithoutBooking() {
const bookingInfoDiv = document.getElementById('vip_last_booking_info2');
const contentDiv = document.getElementById('last_booking_content2');
// 显示信息
bookingInfoDiv.style.display = 'block';
contentDiv.innerHTML = '<p class="success">该VIP首次到店</p>';
console.log('模拟显示无预约记录的场景(首次到店)');
}
// 页面加载时的提示
console.log('VIP客户预约历史测试页面已加载。请点击按钮测试不同场景。');
</script>
</body>
</html>