文章目录
-
- 在数字化时代,用户体验已成为网站成功的关键因素之一。无论是服务型企业、医疗机构、教育机构还是自由职业者,能够提供便捷的在线预约功能,不仅能显著提升客户满意度,还能大幅提高工作效率。想象一下,一位潜在客户访问您的网站,对您的服务感兴趣,却需要打电话或发邮件才能预约,这种额外的步骤可能导致客户流失。 WordPress作为全球最流行的内容管理系统,拥有超过40%的网站市场份额。其强大的可扩展性使得我们可以通过代码二次开发,为网站添加各种实用功能。本教程将详细指导您如何为WordPress网站集成在线预约与日程管理功能,无需依赖昂贵的第三方插件,完全自主控制数据与用户体验。
-
- 在开始之前,请确保您的开发环境满足以下要求: WordPress 5.0或更高版本 PHP 7.4或更高版本(推荐PHP 8.0+) MySQL 5.6或更高版本 基本的HTML、CSS、JavaScript知识 对WordPress主题开发有一定了解
- 为了避免直接修改主题文件导致更新时丢失更改,我们首先创建一个子主题: 在WordPress的wp-content/themes/目录下创建新文件夹,命名为my-custom-booking-theme 在该文件夹中创建style.css文件,添加以下内容: /* Theme Name: 我的预约主题 Template: twentytwentythree Version: 1.0 Description: 用于在线预约功能的子主题 */ /* 在这里添加自定义样式 */ 创建functions.php文件,用于添加自定义功能: <?php // 子主题的functions.php文件 // 添加父主题样式表 add_action('wp_enqueue_scripts', 'my_theme_enqueue_styles'); function my_theme_enqueue_styles() { wp_enqueue_style('parent-style', get_template_directory_uri() . '/style.css'); wp_enqueue_style('child-style', get_stylesheet_directory_uri() . '/style.css', array('parent-style')); } // 在这里添加自定义功能 ?> 在WordPress后台激活这个子主题。
- 为了高效开发,建议安装以下工具: 代码编辑器(如VS Code、Sublime Text等) 本地开发环境(如XAMPP、MAMP或Local by Flywheel) 浏览器开发者工具 Git版本控制系统(可选但推荐)
-
- 我们需要创建自定义数据库表来存储预约信息。在WordPress中,我们可以使用dbDelta()函数来创建和更新表结构。 在子主题的functions.php文件中添加以下代码: // 创建预约数据表 function create_booking_tables() { global $wpdb; $table_name = $wpdb->prefix . 'bookings'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE $table_name ( id mediumint(9) NOT NULL AUTO_INCREMENT, customer_name varchar(100) NOT NULL, customer_email varchar(100) NOT NULL, customer_phone varchar(20), service_type varchar(100) NOT NULL, booking_date date NOT NULL, booking_time time NOT NULL, duration int NOT NULL DEFAULT 60, status varchar(20) DEFAULT 'pending', notes text, created_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY booking_date (booking_date), KEY status (status) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); // 创建服务类型表 $services_table = $wpdb->prefix . 'booking_services'; $services_sql = "CREATE TABLE $services_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, service_name varchar(100) NOT NULL, description text, duration int NOT NULL DEFAULT 60, price decimal(10,2), is_active tinyint(1) DEFAULT 1, PRIMARY KEY (id) ) $charset_collate;"; dbDelta($services_sql); // 创建员工/资源表 $staff_table = $wpdb->prefix . 'booking_staff'; $staff_sql = "CREATE TABLE $staff_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, staff_name varchar(100) NOT NULL, email varchar(100), services text, // 可提供的服务ID,用逗号分隔 working_hours text, // JSON格式的工作时间 is_active tinyint(1) DEFAULT 1, PRIMARY KEY (id) ) $charset_collate;"; dbDelta($staff_sql); } add_action('after_setup_theme', 'create_booking_tables');
- 除了预约信息,我们还需要管理日程可用性: // 创建可用时间段表 function create_availability_tables() { global $wpdb; $table_name = $wpdb->prefix . 'booking_availability'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE $table_name ( id mediumint(9) NOT NULL AUTO_INCREMENT, staff_id mediumint(9), date date NOT NULL, start_time time NOT NULL, end_time time NOT NULL, max_bookings int DEFAULT 1, booked_count int DEFAULT 0, is_available tinyint(1) DEFAULT 1, PRIMARY KEY (id), KEY date (date), KEY staff_id (staff_id) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); } add_action('after_setup_theme', 'create_availability_tables');
-
- 我们将创建一个短代码,可以在任何页面或文章中添加预约表单: // 预约表单短代码 function booking_form_shortcode($atts) { ob_start(); // 获取短代码属性 $atts = shortcode_atts(array( 'service_id' => '', 'staff_id' => '', ), $atts); // 加载预约表单 include(get_stylesheet_directory() . '/templates/booking-form.php'); return ob_get_clean(); } add_shortcode('booking_form', 'booking_form_shortcode');
- 在子主题目录下创建templates文件夹,然后创建booking-form.php文件: <div class="booking-container"> <div class="booking-progress"> <div class="step active" data-step="1">选择服务</div> <div class="step" data-step="2">选择时间</div> <div class="step" data-step="3">填写信息</div> <div class="step" data-step="4">确认预约</div> </div> <form id="booking-form" method="post" action="<?php echo admin_url('admin-ajax.php'); ?>"> <!-- 步骤1:选择服务 --> <div class="booking-step step-1 active"> <h3>选择服务类型</h3> <div class="service-options"> <?php global $wpdb; $services_table = $wpdb->prefix . 'booking_services'; $services = $wpdb->get_results("SELECT * FROM $services_table WHERE is_active = 1"); foreach($services as $service) { echo '<div class="service-option" data-service-id="' . $service->id . '">'; echo '<h4>' . esc_html($service->service_name) . '</h4>'; echo '<p>' . esc_html($service->description) . '</p>'; echo '<div class="service-meta">'; echo '<span class="duration">' . $service->duration . '分钟</span>'; if($service->price) { echo '<span class="price">¥' . number_format($service->price, 2) . '</span>'; } echo '</div>'; echo '</div>'; } ?> </div> <button type="button" class="btn-next" data-next-step="2">下一步</button> </div> <!-- 步骤2:选择日期和时间 --> <div class="booking-step step-2"> <h3>选择预约时间</h3> <div class="date-selector"> <div class="month-navigation"> <button type="button" class="prev-month"><</button> <h4 class="current-month"></h4> <button type="button" class="next-month">></button> </div> <div class="calendar"></div> </div> <div class="time-slots"> <h4>可用时间段</h4> <div class="slots-container"></div> </div> <div class="step-buttons"> <button type="button" class="btn-prev" data-prev-step="1">上一步</button> <button type="button" class="btn-next" data-next-step="3">下一步</button> </div> </div> <!-- 步骤3:填写个人信息 --> <div class="booking-step step-3"> <h3>填写个人信息</h3> <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="customer_email">邮箱 *</label> <input type="email" id="customer_email" name="customer_email" required> </div> <div class="form-group"> <label for="customer_phone">电话</label> <input type="tel" id="customer_phone" name="customer_phone"> </div> <div class="form-group"> <label for="notes">备注</label> <textarea id="notes" name="notes" rows="3"></textarea> </div> <div class="step-buttons"> <button type="button" class="btn-prev" data-prev-step="2">上一步</button> <button type="button" class="btn-next" data-next-step="4">下一步</button> </div> </div> <!-- 步骤4:确认预约 --> <div class="booking-step step-4"> <h3>确认预约信息</h3> <div class="booking-summary"> <div class="summary-item"> <strong>服务:</strong> <span id="summary-service"></span> </div> <div class="summary-item"> <strong>日期:</strong> <span id="summary-date"></span> </div> <div class="summary-item"> <strong>时间:</strong> <span id="summary-time"></span> </div> <div class="summary-item"> <strong>姓名:</strong> <span id="summary-name"></span> </div> <div class="summary-item"> <strong>邮箱:</strong> <span id="summary-email"></span> </div> <div class="summary-item"> <strong>电话:</strong> <span id="summary-phone"></span> </div> </div> <div class="step-buttons"> <button type="button" class="btn-prev" data-prev-step="3">上一步</button> <button type="submit" class="btn-submit">提交预约</button> </div> </div> <!-- 隐藏字段 --> <input type="hidden" name="action" value="submit_booking"> <input type="hidden" name="service_id" id="service_id"> <input type="hidden" name="booking_date" id="booking_date"> <input type="hidden" name="booking_time" id="booking_time"> <input type="hidden" name="nonce" value="<?php echo wp_create_nonce('booking_nonce'); ?>"> </form> <!-- 成功/错误消息 --> <div id="booking-message" class="hidden"></div> </div>
- 在子主题的style.css文件中添加样式: /* 预约系统样式 */ .booking-container { max-width: 800px; margin: 0 auto; padding: 20px; background: #f9f9f9; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .booking-progress { display: flex; justify-content: space-between; margin-bottom: 30px; position: relative; } .booking-progress:before { content: ''; position: absolute; top: 15px; left: 0; right: 0; height: 2px; background: #ddd; z-index: 1; } .booking-progress .step { position: relative; z-index: 2; background: #fff; padding: 10px 20px; border-radius: 20px; border: 2px solid #ddd; text-align: center; min-width: 120px; } .booking-progress .step.active { border-color: #0073aa; background: #0073aa; color: white; } .booking-step { display: none; } .booking-step.active { display: block; } .service-options { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 20px; margin-bottom: 30px; } .service-option { padding: 20px; border: 2px solid #ddd; border-radius: 8px; cursor: pointer; transition: all 0.3s ease; } .service-option:hover { border-color: #0073aa; transform: translateY(-2px); } .service-option.selected { border-color: #0073aa; background: #f0f8ff; } .service-meta { display: flex; justify-content: space-between; margin-top: 10px; color: #666; } .date-selector { margin-bottom: 30px; } .month-navigation { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } .calendar { display: grid; grid-template-columns: repeat(7, 1fr); gap: 5px; } .calendar-day { padding: 10px; text-align: center; border: 1px solid #ddd; cursor: pointer; border-radius: 4px; } .calendar-day:hover { background: #f0f0f0; } .calendar-day.available { background: #e8f5e9; border-color: #4caf50; } .calendar-day.selected { background: #4caf50; color: white; } .calendar-day.unavailable { background: #ffebee; color: #999; cursor: not-allowed; } .time-slots { margin-bottom: 30px; } .slots-container { display: flex; flex-wrap: wrap; gap: 10px; } .time-slot { padding: 10px 20px; border: 2px solid #ddd; border-radius: 4px; cursor: pointer; } .time-slot:hover { border-color: #0073aa; } .time-slot.selected { background: #0073aa; color: white; border-color: #0073aa; } .time-slot.unavailable { background: #f5f5f5; color: #999; cursor: not-allowed; } .form-group { margin-bottom: 20px; } .form-group label { display: block; margin-bottom: 5px; font-weight: bold; } .form-group input, .form-group textarea { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px; } .booking-summary { background: white; padding: 20px; border-radius: 8px; margin-bottom: 30px; } .summary-item { margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid #eee; } .step-buttons { display: flex; justify-content: space-between; } button { padding: 12px 24px; background: #0073aa; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; } button:hover { background: #005a87; } button.btn-prev { background: #6c757d; } button.btn-prev:hover { background: #545b62; } #booking-message { padding: 15px; margin-top: 20px; border-radius: 4px; display: none; } #booking-message.success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; display: block; } #booking-message.error { background: #f8d7da; color: #721c24;
-
-
- 在functions.php中添加处理预约提交的代码: // 处理预约提交 add_action('wp_ajax_submit_booking', 'handle_booking_submission'); add_action('wp_ajax_nopriv_submit_booking', 'handle_booking_submission'); function handle_booking_submission() { // 验证nonce if (!wp_verify_nonce($_POST['nonce'], 'booking_nonce')) { wp_send_json_error('安全验证失败,请刷新页面重试。'); } // 验证必填字段 $required_fields = ['customer_name', 'customer_email', 'service_id', 'booking_date', 'booking_time']; foreach ($required_fields as $field) { if (empty($_POST[$field])) { wp_send_json_error('请填写所有必填字段。'); } } // 验证邮箱格式 if (!is_email($_POST['customer_email'])) { wp_send_json_error('请输入有效的邮箱地址。'); } // 验证日期格式 $booking_date = DateTime::createFromFormat('Y-m-d', $_POST['booking_date']); if (!$booking_date) { wp_send_json_error('日期格式不正确。'); } // 检查日期是否在未来 $today = new DateTime(); if ($booking_date < $today) { wp_send_json_error('不能预约过去的日期。'); } global $wpdb; $bookings_table = $wpdb->prefix . 'bookings'; // 检查该时间段是否已被预约 $existing_booking = $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM $bookings_table WHERE booking_date = %s AND booking_time = %s AND status IN ('pending', 'confirmed')", $_POST['booking_date'], $_POST['booking_time'] )); if ($existing_booking > 0) { wp_send_json_error('该时间段已被预约,请选择其他时间。'); } // 插入预约数据 $insert_data = array( 'customer_name' => sanitize_text_field($_POST['customer_name']), 'customer_email' => sanitize_email($_POST['customer_email']), 'customer_phone' => sanitize_text_field($_POST['customer_phone'] ?? ''), 'service_id' => intval($_POST['service_id']), 'booking_date' => $_POST['booking_date'], 'booking_time' => $_POST['booking_time'], 'duration' => intval($_POST['duration'] ?? 60), 'notes' => sanitize_textarea_field($_POST['notes'] ?? ''), 'status' => 'pending' ); $result = $wpdb->insert($bookings_table, $insert_data); if ($result) { $booking_id = $wpdb->insert_id; // 发送确认邮件 send_booking_confirmation_email($booking_id, $insert_data); // 发送管理员通知 send_admin_notification_email($booking_id, $insert_data); wp_send_json_success(array( 'message' => '预约提交成功!我们已发送确认邮件到您的邮箱。', 'booking_id' => $booking_id )); } else { wp_send_json_error('预约提交失败,请稍后重试。'); } } // 发送确认邮件给客户 function send_booking_confirmation_email($booking_id, $booking_data) { $to = $booking_data['customer_email']; $subject = '您的预约确认 - ' . get_bloginfo('name'); $message = '<html><body>'; $message .= '<h2>预约确认</h2>'; $message .= '<p>尊敬的 ' . $booking_data['customer_name'] . ',</p>'; $message .= '<p>感谢您的预约,以下是您的预约详情:</p>'; $message .= '<table border="0" cellpadding="10">'; $message .= '<tr><td><strong>预约编号:</strong></td><td>' . $booking_id . '</td></tr>'; $message .= '<tr><td><strong>服务类型:</strong></td><td>' . get_service_name($booking_data['service_id']) . '</td></tr>'; $message .= '<tr><td><strong>预约日期:</strong></td><td>' . $booking_data['booking_date'] . '</td></tr>'; $message .= '<tr><td><strong>预约时间:</strong></td><td>' . $booking_data['booking_time'] . '</td></tr>'; $message .= '<tr><td><strong>预计时长:</strong></td><td>' . $booking_data['duration'] . '分钟</td></tr>'; $message .= '</table>'; $message .= '<p>如需修改或取消预约,请通过以下方式联系我们:</p>'; $message .= '<p>电话:' . get_option('business_phone') . '</p>'; $message .= '<p>邮箱:' . get_option('admin_email') . '</p>'; $message .= '</body></html>'; $headers = array( 'Content-Type: text/html; charset=UTF-8', 'From: ' . get_bloginfo('name') . ' <' . get_option('admin_email') . '>' ); wp_mail($to, $subject, $message, $headers); } // 发送管理员通知 function send_admin_notification_email($booking_id, $booking_data) { $to = get_option('admin_email'); $subject = '新预约通知 - 预约编号:' . $booking_id; $message = '<html><body>'; $message .= '<h2>新预约通知</h2>'; $message .= '<p>您收到一个新的预约请求:</p>'; $message .= '<table border="0" cellpadding="10">'; $message .= '<tr><td><strong>预约编号:</strong></td><td>' . $booking_id . '</td></tr>'; $message .= '<tr><td><strong>客户姓名:</strong></td><td>' . $booking_data['customer_name'] . '</td></tr>'; $message .= '<tr><td><strong>客户邮箱:</strong></td><td>' . $booking_data['customer_email'] . '</td></tr>'; $message .= '<tr><td><strong>客户电话:</strong></td><td>' . $booking_data['customer_phone'] . '</td></tr>'; $message .= '<tr><td><strong>服务类型:</strong></td><td>' . get_service_name($booking_data['service_id']) . '</td></tr>'; $message .= '<tr><td><strong>预约日期:</strong></td><td>' . $booking_data['booking_date'] . '</td></tr>'; $message .= '<tr><td><strong>预约时间:</strong></td><td>' . $booking_data['booking_time'] . '</td></tr>'; $message .= '<tr><td><strong>备注:</strong></td><td>' . $booking_data['notes'] . '</td></tr>'; $message .= '</table>'; $message .= '<p><a href="' . admin_url('admin.php?page=booking-management') . '">点击这里管理预约</a></p>'; $message .= '</body></html>'; $headers = array( 'Content-Type: text/html; charset=UTF-8', 'From: ' . get_bloginfo('name') . ' <' . get_option('admin_email') . '>' ); wp_mail($to, $subject, $message, $headers); } // 获取服务名称 function get_service_name($service_id) { global $wpdb; $services_table = $wpdb->prefix . 'booking_services'; return $wpdb->get_var($wpdb->prepare( "SELECT service_name FROM $services_table WHERE id = %d", $service_id )); }
- // 获取可用时间段 add_action('wp_ajax_get_available_slots', 'get_available_time_slots'); add_action('wp_ajax_nopriv_get_available_slots', 'get_available_time_slots'); function get_available_time_slots() { // 验证nonce if (!wp_verify_nonce($_POST['nonce'], 'booking_nonce')) { wp_send_json_error('安全验证失败。'); } $date = sanitize_text_field($_POST['date']); $service_id = intval($_POST['service_id']); // 验证日期格式 $booking_date = DateTime::createFromFormat('Y-m-d', $date); if (!$booking_date) { wp_send_json_error('日期格式不正确。'); } // 获取服务时长 global $wpdb; $services_table = $wpdb->prefix . 'booking_services'; $service = $wpdb->get_row($wpdb->prepare( "SELECT duration FROM $services_table WHERE id = %d", $service_id )); if (!$service) { wp_send_json_error('服务不存在。'); } $service_duration = $service->duration; // 获取当天的预约情况 $bookings_table = $wpdb->prefix . 'bookings'; $bookings = $wpdb->get_results($wpdb->prepare( "SELECT booking_time, duration FROM $bookings_table WHERE booking_date = %s AND status IN ('pending', 'confirmed')", $date )); // 获取营业时间设置(这里可以扩展为从数据库读取) $business_hours = array( 'start' => '09:00:00', 'end' => '18:00:00', 'lunch_start' => '12:00:00', 'lunch_end' => '13:00:00' ); // 生成时间段 $time_slots = generate_time_slots($business_hours, $service_duration, $bookings); wp_send_json_success(array( 'date' => $date, 'slots' => $time_slots )); } // 生成时间段 function generate_time_slots($business_hours, $service_duration, $existing_bookings) { $slots = array(); // 将时间转换为分钟数便于计算 $start_minutes = time_to_minutes($business_hours['start']); $end_minutes = time_to_minutes($business_hours['end']); $lunch_start = time_to_minutes($business_hours['lunch_start']); $lunch_end = time_to_minutes($business_hours['lunch_end']); // 创建已预约时间段的映射 $booked_slots = array(); foreach ($existing_bookings as $booking) { $booking_start = time_to_minutes($booking->booking_time); $booking_end = $booking_start + $booking->duration; for ($time = $booking_start; $time < $booking_end; $time += 15) { $booked_slots[$time] = true; } } // 生成时间段 $current_time = $start_minutes; while ($current_time + $service_duration <= $end_minutes) { // 跳过午休时间 if ($current_time >= $lunch_start && $current_time < $lunch_end) { $current_time = $lunch_end; continue; } // 检查时间段是否可用 $is_available = true; for ($check_time = $current_time; $check_time < $current_time + $service_duration; $check_time += 15) { if (isset($booked_slots[$check_time]) || ($check_time >= $lunch_start && $check_time < $lunch_end)) { $is_available = false; break; } } if ($is_available) { $slot_time = minutes_to_time($current_time); $slots[] = array( 'time' => $slot_time, 'display' => format_time_display($slot_time), 'available' => true ); } $current_time += 15; // 15分钟间隔 } return $slots; } // 时间转换辅助函数 function time_to_minutes($time) { list($hours, $minutes) = explode(':', $time); return intval($hours) * 60 + intval($minutes); } function minutes_to_time($minutes) { $hours = floor($minutes / 60); $mins = $minutes % 60; return sprintf('%02d:%02d:00', $hours, $mins); } function format_time_display($time) { return date('g:i A', strtotime($time)); }
-
在数字化时代,用户体验已成为网站成功的关键因素之一。无论是服务型企业、医疗机构、教育机构还是自由职业者,能够提供便捷的在线预约功能,不仅能显著提升客户满意度,还能大幅提高工作效率。想象一下,一位潜在客户访问您的网站,对您的服务感兴趣,却需要打电话或发邮件才能预约,这种额外的步骤可能导致客户流失。
WordPress作为全球最流行的内容管理系统,拥有超过40%的网站市场份额。其强大的可扩展性使得我们可以通过代码二次开发,为网站添加各种实用功能。本教程将详细指导您如何为WordPress网站集成在线预约与日程管理功能,无需依赖昂贵的第三方插件,完全自主控制数据与用户体验。
在开始之前,请确保您的开发环境满足以下要求:
- WordPress 5.0或更高版本
- PHP 7.4或更高版本(推荐PHP 8.0+)
- MySQL 5.6或更高版本
- 基本的HTML、CSS、JavaScript知识
- 对WordPress主题开发有一定了解
为了避免直接修改主题文件导致更新时丢失更改,我们首先创建一个子主题:
- 在WordPress的
wp-content/themes/目录下创建新文件夹,命名为my-custom-booking-theme - 在该文件夹中创建
style.css文件,添加以下内容:
/*
Theme Name: 我的预约主题
Template: twentytwentythree
Version: 1.0
Description: 用于在线预约功能的子主题
*/
/* 在这里添加自定义样式 */
- 创建
functions.php文件,用于添加自定义功能:
<?php
// 子主题的functions.php文件
// 添加父主题样式表
add_action('wp_enqueue_scripts', 'my_theme_enqueue_styles');
function my_theme_enqueue_styles() {
wp_enqueue_style('parent-style', get_template_directory_uri() . '/style.css');
wp_enqueue_style('child-style', get_stylesheet_directory_uri() . '/style.css', array('parent-style'));
}
// 在这里添加自定义功能
?>
- 在WordPress后台激活这个子主题。
为了高效开发,建议安装以下工具:
- 代码编辑器(如VS Code、Sublime Text等)
- 本地开发环境(如XAMPP、MAMP或Local by Flywheel)
- 浏览器开发者工具
- Git版本控制系统(可选但推荐)
我们需要创建自定义数据库表来存储预约信息。在WordPress中,我们可以使用dbDelta()函数来创建和更新表结构。
在子主题的functions.php文件中添加以下代码:
// 创建预约数据表
function create_booking_tables() {
global $wpdb;
$table_name = $wpdb->prefix . 'bookings';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
customer_name varchar(100) NOT NULL,
customer_email varchar(100) NOT NULL,
customer_phone varchar(20),
service_type varchar(100) NOT NULL,
booking_date date NOT NULL,
booking_time time NOT NULL,
duration int NOT NULL DEFAULT 60,
status varchar(20) DEFAULT 'pending',
notes text,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY booking_date (booking_date),
KEY status (status)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
// 创建服务类型表
$services_table = $wpdb->prefix . 'booking_services';
$services_sql = "CREATE TABLE $services_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
service_name varchar(100) NOT NULL,
description text,
duration int NOT NULL DEFAULT 60,
price decimal(10,2),
is_active tinyint(1) DEFAULT 1,
PRIMARY KEY (id)
) $charset_collate;";
dbDelta($services_sql);
// 创建员工/资源表
$staff_table = $wpdb->prefix . 'booking_staff';
$staff_sql = "CREATE TABLE $staff_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
staff_name varchar(100) NOT NULL,
email varchar(100),
services text, // 可提供的服务ID,用逗号分隔
working_hours text, // JSON格式的工作时间
is_active tinyint(1) DEFAULT 1,
PRIMARY KEY (id)
) $charset_collate;";
dbDelta($staff_sql);
}
add_action('after_setup_theme', 'create_booking_tables');
除了预约信息,我们还需要管理日程可用性:
// 创建可用时间段表
function create_availability_tables() {
global $wpdb;
$table_name = $wpdb->prefix . 'booking_availability';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
staff_id mediumint(9),
date date NOT NULL,
start_time time NOT NULL,
end_time time NOT NULL,
max_bookings int DEFAULT 1,
booked_count int DEFAULT 0,
is_available tinyint(1) DEFAULT 1,
PRIMARY KEY (id),
KEY date (date),
KEY staff_id (staff_id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
add_action('after_setup_theme', 'create_availability_tables');
我们将创建一个短代码,可以在任何页面或文章中添加预约表单:
// 预约表单短代码
function booking_form_shortcode($atts) {
ob_start();
// 获取短代码属性
$atts = shortcode_atts(array(
'service_id' => '',
'staff_id' => '',
), $atts);
// 加载预约表单
include(get_stylesheet_directory() . '/templates/booking-form.php');
return ob_get_clean();
}
add_shortcode('booking_form', 'booking_form_shortcode');
在子主题目录下创建templates文件夹,然后创建booking-form.php文件:
<div class="booking-container">
<div class="booking-progress">
<div class="step active" data-step="1">选择服务</div>
<div class="step" data-step="2">选择时间</div>
<div class="step" data-step="3">填写信息</div>
<div class="step" data-step="4">确认预约</div>
</div>
<form id="booking-form" method="post" action="<?php echo admin_url('admin-ajax.php'); ?>">
<!-- 步骤1:选择服务 -->
<div class="booking-step step-1 active">
<h3>选择服务类型</h3>
<div class="service-options">
<?php
global $wpdb;
$services_table = $wpdb->prefix . 'booking_services';
$services = $wpdb->get_results("SELECT * FROM $services_table WHERE is_active = 1");
foreach($services as $service) {
echo '<div class="service-option" data-service-id="' . $service->id . '">';
echo '<h4>' . esc_html($service->service_name) . '</h4>';
echo '<p>' . esc_html($service->description) . '</p>';
echo '<div class="service-meta">';
echo '<span class="duration">' . $service->duration . '分钟</span>';
if($service->price) {
echo '<span class="price">¥' . number_format($service->price, 2) . '</span>';
}
echo '</div>';
echo '</div>';
}
?>
</div>
<button type="button" class="btn-next" data-next-step="2">下一步</button>
</div>
<!-- 步骤2:选择日期和时间 -->
<div class="booking-step step-2">
<h3>选择预约时间</h3>
<div class="date-selector">
<div class="month-navigation">
<button type="button" class="prev-month"><</button>
<h4 class="current-month"></h4>
<button type="button" class="next-month">></button>
</div>
<div class="calendar"></div>
</div>
<div class="time-slots">
<h4>可用时间段</h4>
<div class="slots-container"></div>
</div>
<div class="step-buttons">
<button type="button" class="btn-prev" data-prev-step="1">上一步</button>
<button type="button" class="btn-next" data-next-step="3">下一步</button>
</div>
</div>
<!-- 步骤3:填写个人信息 -->
<div class="booking-step step-3">
<h3>填写个人信息</h3>
<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="customer_email">邮箱 *</label>
<input type="email" id="customer_email" name="customer_email" required>
</div>
<div class="form-group">
<label for="customer_phone">电话</label>
<input type="tel" id="customer_phone" name="customer_phone">
</div>
<div class="form-group">
<label for="notes">备注</label>
<textarea id="notes" name="notes" rows="3"></textarea>
</div>
<div class="step-buttons">
<button type="button" class="btn-prev" data-prev-step="2">上一步</button>
<button type="button" class="btn-next" data-next-step="4">下一步</button>
</div>
</div>
<!-- 步骤4:确认预约 -->
<div class="booking-step step-4">
<h3>确认预约信息</h3>
<div class="booking-summary">
<div class="summary-item">
<strong>服务:</strong>
<span id="summary-service"></span>
</div>
<div class="summary-item">
<strong>日期:</strong>
<span id="summary-date"></span>
</div>
<div class="summary-item">
<strong>时间:</strong>
<span id="summary-time"></span>
</div>
<div class="summary-item">
<strong>姓名:</strong>
<span id="summary-name"></span>
</div>
<div class="summary-item">
<strong>邮箱:</strong>
<span id="summary-email"></span>
</div>
<div class="summary-item">
<strong>电话:</strong>
<span id="summary-phone"></span>
</div>
</div>
<div class="step-buttons">
<button type="button" class="btn-prev" data-prev-step="3">上一步</button>
<button type="submit" class="btn-submit">提交预约</button>
</div>
</div>
<!-- 隐藏字段 -->
<input type="hidden" name="action" value="submit_booking">
<input type="hidden" name="service_id" id="service_id">
<input type="hidden" name="booking_date" id="booking_date">
<input type="hidden" name="booking_time" id="booking_time">
<input type="hidden" name="nonce" value="<?php echo wp_create_nonce('booking_nonce'); ?>">
</form>
<!-- 成功/错误消息 -->
<div id="booking-message" class="hidden"></div>
</div>
在子主题的style.css文件中添加样式:
/* 预约系统样式 */
.booking-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
background: #f9f9f9;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.booking-progress {
display: flex;
justify-content: space-between;
margin-bottom: 30px;
position: relative;
}
.booking-progress:before {
content: '';
position: absolute;
top: 15px;
left: 0;
right: 0;
height: 2px;
background: #ddd;
z-index: 1;
}
.booking-progress .step {
position: relative;
z-index: 2;
background: #fff;
padding: 10px 20px;
border-radius: 20px;
border: 2px solid #ddd;
text-align: center;
min-width: 120px;
}
.booking-progress .step.active {
border-color: #0073aa;
background: #0073aa;
color: white;
}
.booking-step {
display: none;
}
.booking-step.active {
display: block;
}
.service-options {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.service-option {
padding: 20px;
border: 2px solid #ddd;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
}
.service-option:hover {
border-color: #0073aa;
transform: translateY(-2px);
}
.service-option.selected {
border-color: #0073aa;
background: #f0f8ff;
}
.service-meta {
display: flex;
justify-content: space-between;
margin-top: 10px;
color: #666;
}
.date-selector {
margin-bottom: 30px;
}
.month-navigation {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.calendar {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 5px;
}
.calendar-day {
padding: 10px;
text-align: center;
border: 1px solid #ddd;
cursor: pointer;
border-radius: 4px;
}
.calendar-day:hover {
background: #f0f0f0;
}
.calendar-day.available {
background: #e8f5e9;
border-color: #4caf50;
}
.calendar-day.selected {
background: #4caf50;
color: white;
}
.calendar-day.unavailable {
background: #ffebee;
color: #999;
cursor: not-allowed;
}
.time-slots {
margin-bottom: 30px;
}
.slots-container {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.time-slot {
padding: 10px 20px;
border: 2px solid #ddd;
border-radius: 4px;
cursor: pointer;
}
.time-slot:hover {
border-color: #0073aa;
}
.time-slot.selected {
background: #0073aa;
color: white;
border-color: #0073aa;
}
.time-slot.unavailable {
background: #f5f5f5;
color: #999;
cursor: not-allowed;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
.booking-summary {
background: white;
padding: 20px;
border-radius: 8px;
margin-bottom: 30px;
}
.summary-item {
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.step-buttons {
display: flex;
justify-content: space-between;
}
button {
padding: 12px 24px;
background: #0073aa;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background: #005a87;
}
button.btn-prev {
background: #6c757d;
}
button.btn-prev:hover {
background: #545b62;
}
#booking-message {
padding: 15px;
margin-top: 20px;
border-radius: 4px;
display: none;
}
#booking-message.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
display: block;
}
#booking-message.error {
background: #f8d7da;
color: #721c24;
在functions.php中添加处理预约提交的代码:
// 处理预约提交
add_action('wp_ajax_submit_booking', 'handle_booking_submission');
add_action('wp_ajax_nopriv_submit_booking', 'handle_booking_submission');
function handle_booking_submission() {
// 验证nonce
if (!wp_verify_nonce($_POST['nonce'], 'booking_nonce')) {
wp_send_json_error('安全验证失败,请刷新页面重试。');
}
// 验证必填字段
$required_fields = ['customer_name', 'customer_email', 'service_id', 'booking_date', 'booking_time'];
foreach ($required_fields as $field) {
if (empty($_POST[$field])) {
wp_send_json_error('请填写所有必填字段。');
}
}
// 验证邮箱格式
if (!is_email($_POST['customer_email'])) {
wp_send_json_error('请输入有效的邮箱地址。');
}
// 验证日期格式
$booking_date = DateTime::createFromFormat('Y-m-d', $_POST['booking_date']);
if (!$booking_date) {
wp_send_json_error('日期格式不正确。');
}
// 检查日期是否在未来
$today = new DateTime();
if ($booking_date < $today) {
wp_send_json_error('不能预约过去的日期。');
}
global $wpdb;
$bookings_table = $wpdb->prefix . 'bookings';
// 检查该时间段是否已被预约
$existing_booking = $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM $bookings_table
WHERE booking_date = %s
AND booking_time = %s
AND status IN ('pending', 'confirmed')",
$_POST['booking_date'],
$_POST['booking_time']
));
if ($existing_booking > 0) {
wp_send_json_error('该时间段已被预约,请选择其他时间。');
}
// 插入预约数据
$insert_data = array(
'customer_name' => sanitize_text_field($_POST['customer_name']),
'customer_email' => sanitize_email($_POST['customer_email']),
'customer_phone' => sanitize_text_field($_POST['customer_phone'] ?? ''),
'service_id' => intval($_POST['service_id']),
'booking_date' => $_POST['booking_date'],
'booking_time' => $_POST['booking_time'],
'duration' => intval($_POST['duration'] ?? 60),
'notes' => sanitize_textarea_field($_POST['notes'] ?? ''),
'status' => 'pending'
);
$result = $wpdb->insert($bookings_table, $insert_data);
if ($result) {
$booking_id = $wpdb->insert_id;
// 发送确认邮件
send_booking_confirmation_email($booking_id, $insert_data);
// 发送管理员通知
send_admin_notification_email($booking_id, $insert_data);
wp_send_json_success(array(
'message' => '预约提交成功!我们已发送确认邮件到您的邮箱。',
'booking_id' => $booking_id
));
} else {
wp_send_json_error('预约提交失败,请稍后重试。');
}
}
// 发送确认邮件给客户
function send_booking_confirmation_email($booking_id, $booking_data) {
$to = $booking_data['customer_email'];
$subject = '您的预约确认 - ' . get_bloginfo('name');
$message = '<html><body>';
$message .= '<h2>预约确认</h2>';
$message .= '<p>尊敬的 ' . $booking_data['customer_name'] . ',</p>';
$message .= '<p>感谢您的预约,以下是您的预约详情:</p>';
$message .= '<table border="0" cellpadding="10">';
$message .= '<tr><td><strong>预约编号:</strong></td><td>' . $booking_id . '</td></tr>';
$message .= '<tr><td><strong>服务类型:</strong></td><td>' . get_service_name($booking_data['service_id']) . '</td></tr>';
$message .= '<tr><td><strong>预约日期:</strong></td><td>' . $booking_data['booking_date'] . '</td></tr>';
$message .= '<tr><td><strong>预约时间:</strong></td><td>' . $booking_data['booking_time'] . '</td></tr>';
$message .= '<tr><td><strong>预计时长:</strong></td><td>' . $booking_data['duration'] . '分钟</td></tr>';
$message .= '</table>';
$message .= '<p>如需修改或取消预约,请通过以下方式联系我们:</p>';
$message .= '<p>电话:' . get_option('business_phone') . '</p>';
$message .= '<p>邮箱:' . get_option('admin_email') . '</p>';
$message .= '</body></html>';
$headers = array(
'Content-Type: text/html; charset=UTF-8',
'From: ' . get_bloginfo('name') . ' <' . get_option('admin_email') . '>'
);
wp_mail($to, $subject, $message, $headers);
}
// 发送管理员通知
function send_admin_notification_email($booking_id, $booking_data) {
$to = get_option('admin_email');
$subject = '新预约通知 - 预约编号:' . $booking_id;
$message = '<html><body>';
$message .= '<h2>新预约通知</h2>';
$message .= '<p>您收到一个新的预约请求:</p>';
$message .= '<table border="0" cellpadding="10">';
$message .= '<tr><td><strong>预约编号:</strong></td><td>' . $booking_id . '</td></tr>';
$message .= '<tr><td><strong>客户姓名:</strong></td><td>' . $booking_data['customer_name'] . '</td></tr>';
$message .= '<tr><td><strong>客户邮箱:</strong></td><td>' . $booking_data['customer_email'] . '</td></tr>';
$message .= '<tr><td><strong>客户电话:</strong></td><td>' . $booking_data['customer_phone'] . '</td></tr>';
$message .= '<tr><td><strong>服务类型:</strong></td><td>' . get_service_name($booking_data['service_id']) . '</td></tr>';
$message .= '<tr><td><strong>预约日期:</strong></td><td>' . $booking_data['booking_date'] . '</td></tr>';
$message .= '<tr><td><strong>预约时间:</strong></td><td>' . $booking_data['booking_time'] . '</td></tr>';
$message .= '<tr><td><strong>备注:</strong></td><td>' . $booking_data['notes'] . '</td></tr>';
$message .= '</table>';
$message .= '<p><a href="' . admin_url('admin.php?page=booking-management') . '">点击这里管理预约</a></p>';
$message .= '</body></html>';
$headers = array(
'Content-Type: text/html; charset=UTF-8',
'From: ' . get_bloginfo('name') . ' <' . get_option('admin_email') . '>'
);
wp_mail($to, $subject, $message, $headers);
}
// 获取服务名称
function get_service_name($service_id) {
global $wpdb;
$services_table = $wpdb->prefix . 'booking_services';
return $wpdb->get_var($wpdb->prepare(
"SELECT service_name FROM $services_table WHERE id = %d",
$service_id
));
}
// 获取可用时间段
add_action('wp_ajax_get_available_slots', 'get_available_time_slots');
add_action('wp_ajax_nopriv_get_available_slots', 'get_available_time_slots');
function get_available_time_slots() {
// 验证nonce
if (!wp_verify_nonce($_POST['nonce'], 'booking_nonce')) {
wp_send_json_error('安全验证失败。');
}
$date = sanitize_text_field($_POST['date']);
$service_id = intval($_POST['service_id']);
// 验证日期格式
$booking_date = DateTime::createFromFormat('Y-m-d', $date);
if (!$booking_date) {
wp_send_json_error('日期格式不正确。');
}
// 获取服务时长
global $wpdb;
$services_table = $wpdb->prefix . 'booking_services';
$service = $wpdb->get_row($wpdb->prepare(
"SELECT duration FROM $services_table WHERE id = %d",
$service_id
));
if (!$service) {
wp_send_json_error('服务不存在。');
}
$service_duration = $service->duration;
// 获取当天的预约情况
$bookings_table = $wpdb->prefix . 'bookings';
$bookings = $wpdb->get_results($wpdb->prepare(
"SELECT booking_time, duration FROM $bookings_table
WHERE booking_date = %s
AND status IN ('pending', 'confirmed')",
$date
));
// 获取营业时间设置(这里可以扩展为从数据库读取)
$business_hours = array(
'start' => '09:00:00',
'end' => '18:00:00',
'lunch_start' => '12:00:00',
'lunch_end' => '13:00:00'
);
// 生成时间段
$time_slots = generate_time_slots($business_hours, $service_duration, $bookings);
wp_send_json_success(array(
'date' => $date,
'slots' => $time_slots
));
}
// 生成时间段
function generate_time_slots($business_hours, $service_duration, $existing_bookings) {
$slots = array();
// 将时间转换为分钟数便于计算
$start_minutes = time_to_minutes($business_hours['start']);
$end_minutes = time_to_minutes($business_hours['end']);
$lunch_start = time_to_minutes($business_hours['lunch_start']);
$lunch_end = time_to_minutes($business_hours['lunch_end']);
// 创建已预约时间段的映射
$booked_slots = array();
foreach ($existing_bookings as $booking) {
$booking_start = time_to_minutes($booking->booking_time);
$booking_end = $booking_start + $booking->duration;
for ($time = $booking_start; $time < $booking_end; $time += 15) {
$booked_slots[$time] = true;
}
}
// 生成时间段
$current_time = $start_minutes;
while ($current_time + $service_duration <= $end_minutes) {
// 跳过午休时间
if ($current_time >= $lunch_start && $current_time < $lunch_end) {
$current_time = $lunch_end;
continue;
}
// 检查时间段是否可用
$is_available = true;
for ($check_time = $current_time; $check_time < $current_time + $service_duration; $check_time += 15) {
if (isset($booked_slots[$check_time]) ||
($check_time >= $lunch_start && $check_time < $lunch_end)) {
$is_available = false;
break;
}
}
if ($is_available) {
$slot_time = minutes_to_time($current_time);
$slots[] = array(
'time' => $slot_time,
'display' => format_time_display($slot_time),
'available' => true
);
}
$current_time += 15; // 15分钟间隔
}
return $slots;
}
// 时间转换辅助函数
function time_to_minutes($time) {
list($hours, $minutes) = explode(':', $time);
return intval($hours) * 60 + intval($minutes);
}
function minutes_to_time($minutes) {
$hours = floor($minutes / 60);
$mins = $minutes % 60;
return sprintf('%02d:%02d:00', $hours, $mins);
}
function format_time_display($time) {
return date('g:i A', strtotime($time));
}
// 获取可用时间段
add_action('wp_ajax_get_available_slots', 'get_available_time_slots');
add_action('wp_ajax_nopriv_get_available_slots', 'get_available_time_slots');
function get_available_time_slots() {
// 验证nonce
if (!wp_verify_nonce($_POST['nonce'], 'booking_nonce')) {
wp_send_json_error('安全验证失败。');
}
$date = sanitize_text_field($_POST['date']);
$service_id = intval($_POST['service_id']);
// 验证日期格式
$booking_date = DateTime::createFromFormat('Y-m-d', $date);
if (!$booking_date) {
wp_send_json_error('日期格式不正确。');
}
// 获取服务时长
global $wpdb;
$services_table = $wpdb->prefix . 'booking_services';
$service = $wpdb->get_row($wpdb->prepare(
"SELECT duration FROM $services_table WHERE id = %d",
$service_id
));
if (!$service) {
wp_send_json_error('服务不存在。');
}
$service_duration = $service->duration;
// 获取当天的预约情况
$bookings_table = $wpdb->prefix . 'bookings';
$bookings = $wpdb->get_results($wpdb->prepare(
"SELECT booking_time, duration FROM $bookings_table
WHERE booking_date = %s
AND status IN ('pending', 'confirmed')",
$date
));
// 获取营业时间设置(这里可以扩展为从数据库读取)
$business_hours = array(
'start' => '09:00:00',
'end' => '18:00:00',
'lunch_start' => '12:00:00',
'lunch_end' => '13:00:00'
);
// 生成时间段
$time_slots = generate_time_slots($business_hours, $service_duration, $bookings);
wp_send_json_success(array(
'date' => $date,
'slots' => $time_slots
));
}
// 生成时间段
function generate_time_slots($business_hours, $service_duration, $existing_bookings) {
$slots = array();
// 将时间转换为分钟数便于计算
$start_minutes = time_to_minutes($business_hours['start']);
$end_minutes = time_to_minutes($business_hours['end']);
$lunch_start = time_to_minutes($business_hours['lunch_start']);
$lunch_end = time_to_minutes($business_hours['lunch_end']);
// 创建已预约时间段的映射
$booked_slots = array();
foreach ($existing_bookings as $booking) {
$booking_start = time_to_minutes($booking->booking_time);
$booking_end = $booking_start + $booking->duration;
for ($time = $booking_start; $time < $booking_end; $time += 15) {
$booked_slots[$time] = true;
}
}
// 生成时间段
$current_time = $start_minutes;
while ($current_time + $service_duration <= $end_minutes) {
// 跳过午休时间
if ($current_time >= $lunch_start && $current_time < $lunch_end) {
$current_time = $lunch_end;
continue;
}
// 检查时间段是否可用
$is_available = true;
for ($check_time = $current_time; $check_time < $current_time + $service_duration; $check_time += 15) {
if (isset($booked_slots[$check_time]) ||
($check_time >= $lunch_start && $check_time < $lunch_end)) {
$is_available = false;
break;
}
}
if ($is_available) {
$slot_time = minutes_to_time($current_time);
$slots[] = array(
'time' => $slot_time,
'display' => format_time_display($slot_time),
'available' => true
);
}
$current_time += 15; // 15分钟间隔
}
return $slots;
}
// 时间转换辅助函数
function time_to_minutes($time) {
list($hours, $minutes) = explode(':', $time);
return intval($hours) * 60 + intval($minutes);
}
function minutes_to_time($minutes) {
$hours = floor($minutes / 60);
$mins = $minutes % 60;
return sprintf('%02d:%02d:00', $hours, $mins);
}
function format_time_display($time) {
return date('g:i A', strtotime($time));
}
// 添加预约管理菜单
add_action('admin_menu', 'register_booking_admin_menu');
function register_booking_admin_menu() {
add_menu_page(
'预约管理',
'预约管理',
'manage_options',
'booking-management',
'render_booking_management_page',
'dashicons-calendar-alt',
30
);
add_submenu_page(
'booking-management',
'所有预约',
'所有预约',
'manage_options',
'booking-management',
'render_booking_management_page'
);
add_submenu_page(
'booking-management',
'服务管理',
'服务管理',
'manage_options',
'booking-services',
'render_services_management_page'
);
add_submenu_page(
'booking-management',
'日程设置',
'日程设置',
'manage_options',
'booking-settings',
'render_booking_settings_page'
);
add_submenu_page(
'booking-management',
'预约统计',
'预约统计',
'manage_options',
'booking-statistics',
'render_booking_statistics_page'
);
}
// 渲染预约管理页面
function render_booking_management_page() {
global $wpdb;
$bookings_table = $wpdb->prefix . 'bookings';
$services_table = $wpdb->prefix . 'booking_services';
// 处理批量操作
if (isset($_POST['bulk_action']) && isset($_POST['booking_ids'])) {
$action = sanitize_text_field($_POST['bulk_action']);
$booking_ids = array_map('intval', $_POST['booking_ids']);
if ($action === 'delete') {
foreach ($booking_ids as $id) {
$wpdb->delete($bookings_table, array('id' => $id));
}
echo '<div class="notice notice-success"><p>已删除选中的预约。</p></div>';
} elseif (in_array($action, ['pending', 'confirmed', 'cancelled', 'completed'])) {
$wpdb->query($wpdb->prepare(
"UPDATE $bookings_table SET status = %s WHERE id IN (" . implode(',', $booking_ids) . ")",
$action
));
echo '<div class="notice notice-success"><p>已更新选中的预约状态。</p></div>';
}
}
// 处理单个操作
if (isset($_GET['action']) && isset($_GET['booking_id'])) {
$action = sanitize_text_field($_GET['action']);
$booking_id = intval($_GET['booking_id']);
switch ($action) {
case 'confirm':
$wpdb->update($bookings_table, array('status' => 'confirmed'), array('id' => $booking_id));
break;
case 'cancel':
$wpdb->update($bookings_table, array('status' => 'cancelled'), array('id' => $booking_id));
break;
case 'delete':
$wpdb->delete($bookings_table, array('id' => $booking_id));
break;
}
wp_redirect(admin_url('admin.php?page=booking-management'));
exit;
}
// 获取预约数据
$per_page = 20;
$current_page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
$offset = ($current_page - 1) * $per_page;
// 构建查询条件
$where_conditions = array('1=1');
$query_params = array();
if (!empty($_GET['status'])) {
$where_conditions[] = 'status = %s';
$query_params[] = sanitize_text_field($_GET['status']);
}
if (!empty($_GET['date_from'])) {
$where_conditions[] = 'booking_date >= %s';
$query_params[] = sanitize_text_field($_GET['date_from']);
}
if (!empty($_GET['date_to'])) {
$where_conditions[] = 'booking_date <= %s';
$query_params[] = sanitize_text_field($_GET['date_to']);
}
if (!empty($_GET['search'])) {
$search_term = '%' . $wpdb->esc_like(sanitize_text_field($_GET['search'])) . '%';
$where_conditions[] = '(customer_name LIKE %s OR customer_email LIKE %s OR customer_phone LIKE %s)';
$query_params[] = $search_term;
$query_params[] = $search_term;
$query_params[] = $search_term;
}
$where_clause = implode(' AND ', $where_conditions);
// 获取总数
$total_query = "SELECT COUNT(*) FROM $bookings_table WHERE $where_clause";
if (!empty($query_params)) {
$total_query = $wpdb->prepare($total_query, $query_params);
}
$total_bookings = $wpdb->get_var($total_query);
// 获取数据
// 添加预约管理菜单
add_action('admin_menu', 'register_booking_admin_menu');
function register_booking_admin_menu() {
add_menu_page(
'预约管理',
'预约管理',
'manage_options',
'booking-management',
'render_booking_management_page',
'dashicons-calendar-alt',
30
);
add_submenu_page(
'booking-management',
'所有预约',
'所有预约',
'manage_options',
'booking-management',
'render_booking_management_page'
);
add_submenu_page(
'booking-management',
'服务管理',
'服务管理',
'manage_options',
'booking-services',
'render_services_management_page'
);
add_submenu_page(
'booking-management',
'日程设置',
'日程设置',
'manage_options',
'booking-settings',
'render_booking_settings_page'
);
add_submenu_page(
'booking-management',
'预约统计',
'预约统计',
'manage_options',
'booking-statistics',
'render_booking_statistics_page'
);
}
// 渲染预约管理页面
function render_booking_management_page() {
global $wpdb;
$bookings_table = $wpdb->prefix . 'bookings';
$services_table = $wpdb->prefix . 'booking_services';
// 处理批量操作
if (isset($_POST['bulk_action']) && isset($_POST['booking_ids'])) {
$action = sanitize_text_field($_POST['bulk_action']);
$booking_ids = array_map('intval', $_POST['booking_ids']);
if ($action === 'delete') {
foreach ($booking_ids as $id) {
$wpdb->delete($bookings_table, array('id' => $id));
}
echo '<div class="notice notice-success"><p>已删除选中的预约。</p></div>';
} elseif (in_array($action, ['pending', 'confirmed', 'cancelled', 'completed'])) {
$wpdb->query($wpdb->prepare(
"UPDATE $bookings_table SET status = %s WHERE id IN (" . implode(',', $booking_ids) . ")",
$action
));
echo '<div class="notice notice-success"><p>已更新选中的预约状态。</p></div>';
}
}
// 处理单个操作
if (isset($_GET['action']) && isset($_GET['booking_id'])) {
$action = sanitize_text_field($_GET['action']);
$booking_id = intval($_GET['booking_id']);
switch ($action) {
case 'confirm':
$wpdb->update($bookings_table, array('status' => 'confirmed'), array('id' => $booking_id));
break;
case 'cancel':
$wpdb->update($bookings_table, array('status' => 'cancelled'), array('id' => $booking_id));
break;
case 'delete':
$wpdb->delete($bookings_table, array('id' => $booking_id));
break;
}
wp_redirect(admin_url('admin.php?page=booking-management'));
exit;
}
// 获取预约数据
$per_page = 20;
$current_page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
$offset = ($current_page - 1) * $per_page;
// 构建查询条件
$where_conditions = array('1=1');
$query_params = array();
if (!empty($_GET['status'])) {
$where_conditions[] = 'status = %s';
$query_params[] = sanitize_text_field($_GET['status']);
}
if (!empty($_GET['date_from'])) {
$where_conditions[] = 'booking_date >= %s';
$query_params[] = sanitize_text_field($_GET['date_from']);
}
if (!empty($_GET['date_to'])) {
$where_conditions[] = 'booking_date <= %s';
$query_params[] = sanitize_text_field($_GET['date_to']);
}
if (!empty($_GET['search'])) {
$search_term = '%' . $wpdb->esc_like(sanitize_text_field($_GET['search'])) . '%';
$where_conditions[] = '(customer_name LIKE %s OR customer_email LIKE %s OR customer_phone LIKE %s)';
$query_params[] = $search_term;
$query_params[] = $search_term;
$query_params[] = $search_term;
}
$where_clause = implode(' AND ', $where_conditions);
// 获取总数
$total_query = "SELECT COUNT(*) FROM $bookings_table WHERE $where_clause";
if (!empty($query_params)) {
$total_query = $wpdb->prepare($total_query, $query_params);
}
$total_bookings = $wpdb->get_var($total_query);
// 获取数据