feat(vip): 添加VIP客户管理功能
- 创建vip_customers表存储VIP客户信息 - 实现VIP客户添加、查看和删除功能 - 在预约页面增加VIP客户选择功能 - 添加VIP管理页面和API接口
This commit is contained in:
@@ -2,6 +2,21 @@
|
||||
CREATE DATABASE IF NOT EXISTS carwash_booking;
|
||||
USE carwash_booking;
|
||||
|
||||
-- 创建VIP客户表
|
||||
CREATE TABLE IF NOT EXISTS vip_customers (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
customer_name VARCHAR(100) NOT NULL COMMENT '客户姓名',
|
||||
phone VARCHAR(20) NOT NULL UNIQUE COMMENT '联系电话(唯一)',
|
||||
car_model VARCHAR(50) COMMENT '车型',
|
||||
car_number VARCHAR(20) COMMENT '车牌号',
|
||||
email VARCHAR(100) COMMENT '邮箱地址',
|
||||
birthday DATE COMMENT '生日',
|
||||
notes TEXT COMMENT '备注信息',
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NULL
|
||||
);
|
||||
|
||||
-- 创建套餐表
|
||||
CREATE TABLE IF NOT EXISTS packages (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Cache-Control: no-cache, must-revalidate');
|
||||
|
||||
// 获取客户ID
|
||||
$id = $_GET['id'] ?? null;
|
||||
|
||||
if (!$id) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => '缺少客户ID参数'], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 数据库连接配置
|
||||
$host = 'localhost';
|
||||
$dbname = 'carwash_db';
|
||||
$username = 'root';
|
||||
$password = '';
|
||||
|
||||
try {
|
||||
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $username, $password);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
// 查询指定ID的VIP客户
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id, customer_name, phone, car_model, car_number, email, birthday, notes, is_active
|
||||
FROM vip_customers
|
||||
WHERE id = ? AND is_active = 1
|
||||
");
|
||||
$stmt->execute([$id]);
|
||||
|
||||
$vipCustomer = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($vipCustomer) {
|
||||
echo json_encode($vipCustomer, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
} else {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'VIP客户不存在'], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
} catch(PDOException $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'error' => '数据库连接失败',
|
||||
'message' => $e->getMessage()
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Cache-Control: no-cache, must-revalidate');
|
||||
|
||||
// 数据库连接配置
|
||||
$host = 'localhost';
|
||||
$dbname = 'carwash_db';
|
||||
$username = 'root';
|
||||
$password = '';
|
||||
|
||||
try {
|
||||
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $username, $password);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
// 查询所有VIP客户,按创建时间倒序排列
|
||||
$stmt = $pdo->query("
|
||||
SELECT id, customer_name, phone, car_model, car_number, email, birthday, is_active
|
||||
FROM vip_customers
|
||||
WHERE is_active = 1
|
||||
ORDER BY created_at DESC
|
||||
");
|
||||
|
||||
$vipCustomers = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode($vipCustomers, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
|
||||
} catch(PDOException $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'error' => '数据库连接失败',
|
||||
'message' => $e->getMessage()
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
?>
|
||||
@@ -8,8 +8,31 @@ $success_message = '';
|
||||
// 处理表单提交
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
try {
|
||||
$customer_type = $_POST['customer_type'];
|
||||
$vip_id = isset($_POST['vip_id']) ? (int)$_POST['vip_id'] : 0;
|
||||
|
||||
// 如果选择VIP客户,从VIP表获取信息
|
||||
if ($customer_type === 'vip' && $vip_id > 0) {
|
||||
$stmt = $pdo->prepare("SELECT * FROM vip_customers WHERE id = ? AND is_active = 1");
|
||||
$stmt->execute([$vip_id]);
|
||||
$vip_customer = $stmt->fetch();
|
||||
|
||||
if (!$vip_customer) {
|
||||
throw new Exception('选择的VIP客户无效');
|
||||
}
|
||||
|
||||
// 使用VIP客户信息
|
||||
$customer_name = $vip_customer['customer_name'];
|
||||
$phone = $vip_customer['phone'];
|
||||
$car_model = $vip_customer['car_model'] ?: $car_model; // 允许覆盖
|
||||
$car_number = $vip_customer['car_number'] ?: $car_number; // 允许覆盖
|
||||
$member_type = 'VIP会员';
|
||||
} else {
|
||||
// 新客户录入
|
||||
$customer_name = trim($_POST['customer_name']);
|
||||
$phone = trim($_POST['phone']);
|
||||
}
|
||||
|
||||
$car_model = trim($_POST['car_model']);
|
||||
$car_number = trim($_POST['car_number']);
|
||||
$package_id = (int)$_POST['package_id'];
|
||||
@@ -27,6 +50,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
throw new Exception('请填写所有必填字段');
|
||||
}
|
||||
|
||||
// 验证VIP客户或新客户的必填字段
|
||||
if ($customer_type === 'vip') {
|
||||
if (empty($vip_id)) {
|
||||
throw new Exception('请选择一个VIP客户');
|
||||
}
|
||||
} else {
|
||||
if (empty($customer_name) || empty($phone)) {
|
||||
throw new Exception('请填写客户姓名和联系电话');
|
||||
}
|
||||
}
|
||||
|
||||
// 验证套餐
|
||||
if ($package_id) {
|
||||
$stmt = $pdo->prepare("SELECT * FROM packages WHERE id = ? AND is_active = 1");
|
||||
@@ -157,6 +191,7 @@ $packages_json = json_encode(array_map(function($package) {
|
||||
<a href="index.php" class="nav-link active">预约洗车</a>
|
||||
<a href="bookings.php" class="nav-link">预约管理</a>
|
||||
<a href="packages.php" class="nav-link">套餐管理</a>
|
||||
<a href="vip.php" class="nav-link">VIP管理</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
@@ -226,14 +261,33 @@ $packages_json = json_encode(array_map(function($package) {
|
||||
<div class="booking-form-section">
|
||||
<h2>📋 预约信息</h2>
|
||||
<form method="POST" class="form" id="bookingForm">
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="customer_type">客户类型 *</label>
|
||||
<select id="customer_type" name="customer_type" required onchange="handleCustomerTypeChange()">
|
||||
<option value="new">新客户</option>
|
||||
<option value="vip">VIP客户</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="vip_select_group" style="display: none;">
|
||||
<label for="vip_id">选择VIP客户</label>
|
||||
<select id="vip_id" name="vip_id" onchange="loadVIPInfo()">
|
||||
<option value="">请选择VIP客户</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row" id="new_customer_fields">
|
||||
<div class="form-group">
|
||||
<label for="customer_name">客户姓名 *</label>
|
||||
<input type="text" id="customer_name" name="customer_name" required>
|
||||
<input type="text" id="customer_name" name="customer_name" placeholder="请输入客户姓名">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="phone">联系电话 *</label>
|
||||
<input type="tel" id="phone" name="phone" required>
|
||||
<input type="tel" id="phone" name="phone" placeholder="请输入联系电话">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
@@ -361,6 +415,9 @@ $packages_json = json_encode(array_map(function($package) {
|
||||
// 默认选择今天的日期并显示时间段
|
||||
selectDate(todayStr);
|
||||
|
||||
// 加载VIP客户列表
|
||||
loadVIPCustomers();
|
||||
|
||||
// 移动端优化
|
||||
if (/Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent)) {
|
||||
document.body.classList.add('mobile-device');
|
||||
@@ -371,6 +428,73 @@ $packages_json = json_encode(array_map(function($package) {
|
||||
}
|
||||
});
|
||||
|
||||
// 加载VIP客户列表
|
||||
function loadVIPCustomers() {
|
||||
// 这里将从数据库获取VIP客户列表
|
||||
// 在实际应用中,您可能需要通过AJAX获取
|
||||
fetch('get_vip_customers.php')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const vipSelect = document.getElementById('vip_id');
|
||||
vipSelect.innerHTML = '<option value="">请选择VIP客户</option>';
|
||||
data.forEach(vip => {
|
||||
const option = document.createElement('option');
|
||||
option.value = vip.id;
|
||||
option.textContent = `${vip.customer_name} (${vip.phone})`;
|
||||
vipSelect.appendChild(option);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('加载VIP客户列表失败:', error);
|
||||
// 如果加载失败,显示空列表
|
||||
});
|
||||
}
|
||||
|
||||
// 处理客户类型变更
|
||||
function handleCustomerTypeChange() {
|
||||
const customerType = document.getElementById('customer_type').value;
|
||||
const vipSelectGroup = document.getElementById('vip_select_group');
|
||||
const newCustomerFields = document.getElementById('new_customer_fields');
|
||||
|
||||
if (customerType === 'vip') {
|
||||
vipSelectGroup.style.display = 'block';
|
||||
newCustomerFields.style.display = 'none';
|
||||
// 隐藏新客户字段的必填属性
|
||||
document.getElementById('customer_name').required = false;
|
||||
document.getElementById('phone').required = false;
|
||||
} else {
|
||||
vipSelectGroup.style.display = 'none';
|
||||
newCustomerFields.style.display = 'flex';
|
||||
// 显示新客户字段的必填属性
|
||||
document.getElementById('customer_name').required = true;
|
||||
document.getElementById('phone').required = true;
|
||||
}
|
||||
|
||||
updateSubmitButton();
|
||||
}
|
||||
|
||||
// 加载VIP客户信息
|
||||
function loadVIPInfo() {
|
||||
const vipId = document.getElementById('vip_id').value;
|
||||
if (vipId) {
|
||||
// 这里将从数据库获取VIP客户详细信息
|
||||
fetch(`get_vip_customer.php?id=${vipId}`)
|
||||
.then(response => response.json())
|
||||
.then(vip => {
|
||||
if (vip.car_model) {
|
||||
document.getElementById('car_model').value = vip.car_model;
|
||||
}
|
||||
if (vip.car_number) {
|
||||
document.getElementById('car_number').value = vip.car_number;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('加载VIP信息失败:', error);
|
||||
});
|
||||
}
|
||||
updateSubmitButton();
|
||||
}
|
||||
|
||||
function showDateDetails(date) {
|
||||
// 获取预约详情容器
|
||||
const detailsDiv = document.getElementById('bookingDetails');
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
<?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 = ?");
|
||||
$stmt->execute([$phone]);
|
||||
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 (?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$customer_name, $phone, $car_model, $car_number, $email, $birthday, $notes]);
|
||||
|
||||
$success_message = "VIP客户录入成功!";
|
||||
|
||||
} elseif ($action === 'delete_vip') {
|
||||
$id = (int)$_POST['vip_id'];
|
||||
$stmt = $pdo->prepare("DELETE FROM vip_customers WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$success_message = "VIP客户删除成功!";
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// 获取所有VIP客户
|
||||
try {
|
||||
$stmt = $pdo->query("SELECT * FROM vip_customers ORDER BY created_at DESC");
|
||||
$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>VIP管理 - 洗车预约系统</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<h1>👑 VIP客户管理</h1>
|
||||
<nav class="nav">
|
||||
<a href="index.php" class="nav-link">预约洗车</a>
|
||||
<a href="bookings.php" class="nav-link">预约管理</a>
|
||||
<a href="packages.php" class="nav-link">套餐管理</a>
|
||||
<a href="vip.php" class="nav-link active">VIP管理</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<?php if (isset($success_message)): ?>
|
||||
<div class="success-message"><?php echo $success_message; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($error_message)): ?>
|
||||
<div class="error-message"><?php echo $error_message; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($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">
|
||||
<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客户列表 (共 <?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>
|
||||
|
||||
<div class="vip-actions">
|
||||
<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>
|
||||
|
||||
<script>
|
||||
// 移动端优化脚本
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 为按钮添加触摸反馈
|
||||
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>
|
||||
Reference in New Issue
Block a user