feat(vip): 添加VIP客户最近预约记录显示功能
新增get_vip_last_booking.php接口查询VIP客户最近预约记录 在index.php中添加预约信息显示区域和前端逻辑 创建test_vip_booking_history.php测试页面验证功能
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -558,6 +558,14 @@ $packages_json = json_encode(array_map(function($package) {
|
||||
<div id="vip_search_results" class="vip-search-results" style="display: none;"></div>
|
||||
</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">
|
||||
@@ -1674,9 +1682,86 @@ $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) {
|
||||
// 获取预约详情容器
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user