文章目录
-
- 在当今数字化时代,网站已不仅仅是信息展示的平台,更是企业与客户互动的重要渠道。无论是服务行业、咨询机构、医疗机构还是教育机构,在线预约与日程管理功能都已成为提升用户体验、优化运营效率的关键工具。通过集成这些功能,您可以: 减少人工沟通成本,实现24/7自助预约 避免时间冲突,提高资源利用率 提升专业形象,增强客户信任感 自动化提醒功能,降低爽约率 本教程将指导您通过WordPress代码二次开发,为您的网站添加完整的在线预约与日程管理系统,无需依赖昂贵的第三方插件,完全自主控制数据与功能。
- 在开始编码之前,请确保您的WordPress环境满足以下要求: WordPress 5.0或更高版本 PHP 7.4或更高版本 MySQL 5.6或更高版本 已安装并激活一个支持子主题的WordPress主题 首先,我们需要创建一个自定义插件来承载我们的预约系统。在wp-content/plugins/目录下创建新文件夹custom-booking-system,并在其中创建主插件文件: <?php /** * Plugin Name: 自定义预约与日程管理系统 * Plugin URI: https://yourwebsite.com/ * Description: 为WordPress网站添加完整的在线预约与日程管理功能 * Version: 1.0.0 * Author: 您的名称 * License: GPL v2 or later */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } // 定义插件常量 define('CBS_VERSION', '1.0.0'); define('CBS_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('CBS_PLUGIN_URL', plugin_dir_url(__FILE__)); // 初始化插件 function cbs_init() { // 检查必要组件 if (!function_exists('register_post_type')) { wp_die('此插件需要WordPress 3.0或更高版本。'); } // 加载文本域(用于国际化) load_plugin_textdomain('custom-booking-system', false, dirname(plugin_basename(__FILE__)) . '/languages'); } add_action('plugins_loaded', 'cbs_init'); // 激活插件时执行的操作 function cbs_activate() { // 创建数据库表 cbs_create_tables(); // 设置默认选项 cbs_set_default_options(); // 刷新重写规则 flush_rewrite_rules(); } register_activation_hook(__FILE__, 'cbs_activate'); // 停用插件时执行的操作 function cbs_deactivate() { // 清理临时数据 // 注意:这里我们不删除数据,以便用户重新激活时保留数据 flush_rewrite_rules(); } register_deactivation_hook(__FILE__, 'cbs_deactivate');
- 预约系统需要存储预约数据、服务项目、员工信息和时间安排。我们将创建以下数据库表: // 创建数据库表 function cbs_create_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); $table_prefix = $wpdb->prefix . 'cbs_'; // 预约表 $bookings_table = $table_prefix . 'bookings'; $services_table = $table_prefix . 'services'; $staff_table = $table_prefix . 'staff'; $schedule_table = $table_prefix . 'schedules'; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); // 创建服务项目表 $sql_services = "CREATE TABLE IF NOT EXISTS $services_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL, description text, duration int NOT NULL DEFAULT 60 COMMENT '服务时长(分钟)', price decimal(10,2) DEFAULT 0.00, active tinyint(1) DEFAULT 1, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) $charset_collate;"; // 创建员工表 $sql_staff = "CREATE TABLE IF NOT EXISTS $staff_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, user_id bigint(20) unsigned, name varchar(100) NOT NULL, email varchar(100), phone varchar(20), services text COMMENT '可提供的服务ID,逗号分隔', work_hours text COMMENT '工作时间安排', active tinyint(1) DEFAULT 1, PRIMARY KEY (id), FOREIGN KEY (user_id) REFERENCES {$wpdb->users}(ID) ON DELETE SET NULL ) $charset_collate;"; // 创建预约表 $sql_bookings = "CREATE TABLE IF NOT EXISTS $bookings_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, booking_code varchar(20) NOT NULL UNIQUE COMMENT '预约编号', customer_name varchar(100) NOT NULL, customer_email varchar(100) NOT NULL, customer_phone varchar(20), service_id mediumint(9) NOT NULL, staff_id mediumint(9), booking_date date NOT NULL, start_time time NOT NULL, end_time time NOT NULL, status varchar(20) DEFAULT 'pending' COMMENT 'pending, confirmed, cancelled, completed', notes text, ip_address varchar(45), created_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), FOREIGN KEY (service_id) REFERENCES $services_table(id) ON DELETE CASCADE, FOREIGN KEY (staff_id) REFERENCES $staff_table(id) ON DELETE SET NULL ) $charset_collate;"; // 创建日程表 $sql_schedules = "CREATE TABLE IF NOT EXISTS $schedule_table ( 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 COMMENT '该时段最大预约数', booked_count int DEFAULT 0 COMMENT '已预约数', is_dayoff tinyint(1) DEFAULT 0 COMMENT '是否休息日', PRIMARY KEY (id), FOREIGN KEY (staff_id) REFERENCES $staff_table(id) ON DELETE CASCADE, UNIQUE KEY unique_schedule (staff_id, date, start_time) ) $charset_collate;"; // 执行SQL dbDelta($sql_services); dbDelta($sql_staff); dbDelta($sql_bookings); dbDelta($sql_schedules); // 添加示例数据(仅当表为空时) cbs_add_sample_data($services_table, $staff_table); } // 添加示例数据 function cbs_add_sample_data($services_table, $staff_table) { global $wpdb; // 检查服务表是否为空 $service_count = $wpdb->get_var("SELECT COUNT(*) FROM $services_table"); if ($service_count == 0) { $wpdb->insert($services_table, [ 'name' => '基础咨询', 'description' => '30分钟的专业咨询服务', 'duration' => 30, 'price' => 50.00 ]); $wpdb->insert($services_table, [ 'name' => '深度服务', 'description' => '60分钟的深度服务', 'duration' => 60, 'price' => 100.00 ]); } // 检查员工表是否为空 $staff_count = $wpdb->get_var("SELECT COUNT(*) FROM $staff_table"); if ($staff_count == 0) { $wpdb->insert($staff_table, [ 'name' => '张顾问', 'email' => 'consultant@example.com', 'phone' => '13800138000', 'services' => '1,2' ]); } }
- 接下来,我们创建前端预约表单,让用户可以轻松选择服务、日期和时间: // 预约表单短代码 function cbs_booking_form_shortcode($atts) { // 提取短代码属性 $atts = shortcode_atts([ 'service_id' => 0, 'staff_id' => 0, 'title' => '在线预约' ], $atts, 'booking_form'); ob_start(); // 开始输出缓冲 // 加载必要样式和脚本 wp_enqueue_style('cbs-frontend-style', CBS_PLUGIN_URL . 'assets/css/frontend.css'); wp_enqueue_script('cbs-frontend-script', CBS_PLUGIN_URL . 'assets/js/frontend.js', ['jquery', 'jquery-ui-datepicker'], CBS_VERSION, true); // 本地化脚本,传递数据给JavaScript wp_localize_script('cbs-frontend-script', 'cbs_ajax', [ 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('cbs_booking_nonce') ]); // 加载jQuery UI日期选择器样式 wp_enqueue_style('jquery-ui-style', '//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css'); ?> <div class="cbs-booking-container"> <h2><?php echo esc_html($atts['title']); ?></h2> <div id="cbs-booking-message" class="cbs-message" style="display:none;"></div> <form id="cbs-booking-form" method="post"> <!-- 客户信息 --> <div class="cbs-form-section"> <h3>您的信息</h3> <div class="cbs-form-group"> <label for="cbs-customer-name">姓名 *</label> <input type="text" id="cbs-customer-name" name="customer_name" required> </div> <div class="cbs-form-group"> <label for="cbs-customer-email">邮箱 *</label> <input type="email" id="cbs-customer-email" name="customer_email" required> </div> <div class="cbs-form-group"> <label for="cbs-customer-phone">电话</label> <input type="tel" id="cbs-customer-phone" name="customer_phone"> </div> </div> <!-- 服务选择 --> <div class="cbs-form-section"> <h3>选择服务</h3> <div class="cbs-form-group"> <label for="cbs-service">服务项目 *</label> <select id="cbs-service" name="service_id" required> <option value="">请选择服务项目</option> <?php global $wpdb; $services = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}cbs_services WHERE active = 1"); foreach ($services as $service) { $selected = ($service->id == $atts['service_id']) ? 'selected' : ''; echo '<option value="' . esc_attr($service->id) . '" ' . $selected . '>' . esc_html($service->name) . ' (' . esc_html($service->duration) . '分钟)' . '</option>'; } ?> </select> </div> </div> <!-- 日期选择 --> <div class="cbs-form-section"> <h3>选择日期</h3> <div class="cbs-form-group"> <label for="cbs-booking-date">预约日期 *</label> <input type="text" id="cbs-booking-date" name="booking_date" class="cbs-datepicker" required readonly> <p class="cbs-description">请选择未来30天内的日期</p> </div> </div> <!-- 时间选择 --> <div class="cbs-form-section"> <h3>选择时间</h3> <div class="cbs-form-group"> <label>可用时间段</label> <div id="cbs-time-slots"> <p>请先选择日期以查看可用时间段</p> </div> </div> </div> <!-- 备注 --> <div class="cbs-form-section"> <h3>备注信息</h3> <div class="cbs-form-group"> <label for="cbs-notes">特殊要求或备注</label> <textarea id="cbs-notes" name="notes" rows="3"></textarea> </div> </div> <!-- 提交按钮 --> <div class="cbs-form-section"> <input type="hidden" name="action" value="cbs_submit_booking"> <?php wp_nonce_field('cbs_booking_action', 'cbs_booking_nonce'); ?> <button type="submit" id="cbs-submit-booking" class="cbs-submit-btn">提交预约</button> </div> </form> </div> <?php return ob_get_clean(); // 返回缓冲内容 } add_shortcode('booking_form', 'cbs_booking_form_shortcode');
- 现在我们需要创建AJAX处理函数,用于处理表单提交和获取可用时间: // 获取可用时间段的AJAX处理 function cbs_get_available_times() { // 验证nonce check_ajax_referer('cbs_booking_nonce', 'nonce'); $date = sanitize_text_field($_POST['date']); $service_id = intval($_POST['service_id']); if (empty($date) || empty($service_id)) { wp_send_json_error('缺少必要参数'); } global $wpdb; // 获取服务时长 $service = $wpdb->get_row($wpdb->prepare( "SELECT duration FROM {$wpdb->prefix}cbs_services WHERE id = %d", $service_id )); if (!$service) { wp_send_json_error('服务不存在'); } $duration = $service->duration; // 获取所有员工的工作时间 $staff_schedules = $wpdb->get_results($wpdb->prepare( "SELECT s.*, st.name as staff_name FROM {$wpdb->prefix}cbs_schedules s LEFT JOIN {$wpdb->prefix}cbs_staff st ON s.staff_id = st.id WHERE s.date = %s AND s.is_dayoff = 0", $date )); // 获取该日期已有的预约 $existing_bookings = $wpdb->get_results($wpdb->prepare( "SELECT start_time, end_time, staff_id FROM {$wpdb->prefix}cbs_bookings WHERE booking_date = %s AND status IN ('pending', 'confirmed')", $date )); // 生成可用时间段 $available_slots = []; foreach ($staff_schedules as $schedule) { $start = strtotime($schedule->start_time); $end = strtotime($schedule->end_time); // 以30分钟为间隔生成时间段 for ($time = $start; $time < $end; $time += 30 * 60) { $slot_end = $time + ($duration * 60); // 检查是否超出工作时间 if ($slot_end > $end) { continue; } // 检查该时间段是否已满 $booked_count = 0; foreach ($existing_bookings as $booking) { $booking_start = strtotime($booking->start_time); $booking_end = strtotime($booking->end_time); // 检查时间是否冲突 if ($time < $booking_end && $slot_end > $booking_start) { if ($booking->staff_id == $schedule->staff_id || $booking->staff_id === null) { $booked_count++; } } } // 如果未超过最大预约数,则添加为可用时间段 if ($booked_count < $schedule->max_bookings) { $available_slots[] = [ 'time' => date('H:i', $time), 'staff_id' => $schedule->staff_id, 'staff_name' => $schedule->staff_name, 'end_time' => date('H:i', $slot_end) ]; } } } // 按时间排序 usort($available_slots, function($a, $b) { return strcmp($a['time'], $b['time']); }); wp_send_json_success($available_slots); } add_action('wp_ajax_cbs_get_available_times', 'cbs_get_available_times'); add_action('wp_ajax_nopriv_cbs_get_available_times', 'cbs_get_available_times'); // 处理预约提交 function cbs_submit_booking() { // 验证nonce if (!isset($_POST['cbs_booking_nonce']) || !wp_verify_nonce($_POST['cbs_booking_nonce'], 'cbs_booking_action')) { wp_die('安全验证失败'); } // 验证并清理数据 $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 = sanitize_text_field($_POST['booking_date']); $start_time = sanitize_text_field($_POST['start_time']); $notes = sanitize_textarea_field($_POST['notes']); // 基本验证 if (empty($customer_name) || empty($customer_email) || empty($service_id) || empty($booking_date) || empty($start_time)) { wp_die('请填写所有必填字段'); } // 验证日期格式 if (!preg_match('/^d{4}-d{2}-d{2}$/', $booking_date)) { wp_die('日期格式不正确'); } // 验证时间格式 if (!preg_match('/^d{2}:d{2}$/', $start_time)) { wp_die('时间格式不正确'); } global $wpdb; // 获取服务信息 $service = $wpdb->get_row($wpdb->prepare( "SELECT duration FROM {$wpdb->prefix}cbs_services WHERE id = %d", $service_id )); if (!$service) { wp_die('选择的服务不存在'); } // 计算结束时间 $start_timestamp = strtotime($booking_date . ' ' . $start_time); $end_timestamp = $start_timestamp + ($service->duration * 60); $end_time = date('H:i', $end_timestamp); // 检查时间是否可用 $is_available = cbs_check_time_availability($booking_date, $start_time, $end_time, $service_id); if (!$is_available) { wp_die('选择的时间段已被预约,请选择其他时间'); } // 生成预约编号 $booking_code = 'BK' . date('Ymd') . strtoupper(wp_generate_password(6, false)); // 插入预约数据 $insert_data = [ 'booking_code' => $booking_code, 'customer_name' => $customer_name, 'customer_email' => $customer_email, 'customer_phone' => $customer_phone, 'service_id' => $service_id, 'booking_date' => $booking_date, 'start_time' => $start_time, 'end_time' => $end_time, 'status' => 'pending', 'notes' => $notes, 'ip_address' => $_SERVER['REMOTE_ADDR'] ]; $result = $wpdb->insert( $wpdb->prefix . 'cbs_bookings', $insert_data ); if ($result === false) { wp_die('预约提交失败,请稍后重试'); } $booking_id = $wpdb->insert_id; // 发送确认邮件 cbs_send_confirmation_email($booking_id); // 返回成功信息 $success_message = sprintf( '<div class="cbs-success-message"> <h3>预约提交成功!</h3> <p>您的预约编号:<strong>%s</strong></p> <p>我们已向您的邮箱发送确认邮件,请注意查收。</p> <p>您可以在预约管理页面查看预约状态。</p> </div>', $booking_code ); wp_die($success_message); }add_action('wp_ajax_cbs_submit_booking', 'cbs_submit_booking');add_action('wp_ajax_nopriv_cbs_submit_booking', 'cbs_submit_booking'); // 检查时间可用性function cbs_check_time_availability($date, $start_time, $end_time, $service_id, $exclude_booking_id = 0) { global $wpdb; $query = $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}cbs_bookings WHERE booking_date = %s AND status IN ('pending', 'confirmed') AND id != %d AND ( (start_time < %s AND end_time > %s) OR (start_time >= %s AND start_time < %s) )", $date, $exclude_booking_id, $end_time, $start_time, $start_time, $end_time ); $conflict_count = $wpdb->get_var($query); return $conflict_count == 0; } ## 后台管理界面开发 创建后台管理界面,让管理员可以管理预约、服务和员工: // 添加管理菜单function cbs_add_admin_menu() { // 主菜单 add_menu_page( '预约管理', '预约系统', 'manage_options', 'cbs-dashboard', 'cbs_dashboard_page', 'dashicons-calendar-alt', 30 ); // 子菜单 add_submenu_page( 'cbs-dashboard', '所有预约', '所有预约', 'manage_options', 'cbs-bookings', 'cbs_bookings_page' ); add_submenu_page( 'cbs-dashboard', '服务管理', '服务管理', 'manage_options', 'cbs-services', 'cbs_services_page' ); add_submenu_page( 'cbs-dashboard', '员工管理', '员工管理', 'manage_options', 'cbs-staff', 'cbs_staff_page' ); add_submenu_page( 'cbs-dashboard', '日程设置', '日程设置', 'manage_options', 'cbs-schedules', 'cbs_schedules_page' ); add_submenu_page( 'cbs-dashboard', '系统设置', '系统设置', 'manage_options', 'cbs-settings', 'cbs_settings_page' ); }add_action('admin_menu', 'cbs_add_admin_menu'); // 预约管理页面function cbs_bookings_page() { global $wpdb; // 处理批量操作 if (isset($_POST['action']) && isset($_POST['booking_ids'])) { cbs_process_bulk_actions(); } // 处理单个操作 if (isset($_GET['action']) && isset($_GET['id'])) { cbs_process_single_action(); } // 获取预约数据 $bookings = $wpdb->get_results(" SELECT b.*, s.name as service_name FROM {$wpdb->prefix}cbs_bookings b LEFT JOIN {$wpdb->prefix}cbs_services s ON b.service_id = s.id ORDER BY b.booking_date DESC, b.start_time DESC LIMIT 100 "); ?> <div class="wrap"> <h1 class="wp-heading-inline">预约管理</h1> <a href="<?php echo admin_url('admin.php?page=cbs-dashboard'); ?>" class="page-title-action">返回仪表板</a> <hr class="wp-header-end"> <form method="post" action=""> <?php wp_nonce_field('cbs_bulk_action', 'cbs_bulk_nonce'); ?> <div class="tablenav top"> <div class="alignleft actions bulkactions"> <select name="action" id="bulk-action-selector-top"> <option value="-1">批量操作</option> <option value="confirm">确认预约</option> <option value="cancel">取消预约</option> <option value="delete">删除预约</option> </select> <input type="submit" id="doaction" class="button action" value="应用"> </div> <br class="clear"> </div> <table class="wp-list-table widefat fixed striped"> <thead> <tr> <td id="cb" class="manage-column column-cb check-column"> <input type="checkbox" id="cb-select-all-1"> </td> <th>预约编号</th> <th>客户姓名</th> <th>服务项目</th> <th>预约时间</th> <th>状态</th> <th>操作</th> </tr> </thead> <tbody> <?php if (empty($bookings)): ?> <tr> <td colspan="7">暂无预约记录</td> </tr> <?php else: ?> <?php foreach ($bookings as $booking): ?> <tr> <th scope="row" class="check-column"> <input type="checkbox" name="booking_ids[]" value="<?php echo $booking->id; ?>"> </th> <td><?php echo esc_html($booking->booking_code); ?></td> <td> <?php echo esc_html($booking->customer_name); ?><br> <small><?php echo esc_html($booking->customer_email); ?></small> </td> <td><?php echo esc_html($booking->service_name); ?></td> <td> <?php echo date('Y-m-d', strtotime($booking->booking_date)); ?><br> <?php echo $booking->start_time; ?> - <?php echo $booking->end_time; ?> </td> <td> <?php $status_labels = [ 'pending' => '<span class="cbs-status pending">待确认</span>', 'confirmed' => '<span class="cbs-status confirmed">已确认</span>', 'cancelled' => '<span class="cbs-status cancelled">已取消</span>', 'completed' => '<span class="cbs-status completed">已完成</span>' ]; echo $status_labels[$booking->status] ?? $booking->status; ?> </td> <td> <a href="<?php echo admin_url('admin.php?page=cbs-bookings&action=view&id=' . $booking->id); ?>" class="button button-small">查看</a> <a href="<?php echo admin_url('admin.php?page=cbs-bookings&action=edit&id=' . $booking->id); ?>" class="button button-small">编辑</a> <?php if ($booking->status == 'pending'): ?> <a href="<?php echo admin_url('admin.php?page=cbs-bookings&action=confirm&id=' . $booking->id); ?>" class="button button-small button-primary">确认</a> <?php endif; ?> </td> </tr> <?php endforeach; ?> <?php endif; ?> </tbody> </table> </form> </div> <style> .cbs-status { padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: bold; } .cbs-status.pending { background: #f0ad4e; color: white; } .cbs-status.confirmed { background: #5cb85c; color: white; } .cbs-status.cancelled { background: #d9534f; color: white; } .cbs-status.completed { background: #337ab7; color: white; } </style> <?php } // 服务管理页面function cbs_services_page() { global $wpdb; // 处理表单提交 if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (isset($_POST['add_service'])) { cbs_add_service(); } elseif (isset($_POST['update_service'])) { cbs_update_service(); } } // 处理删除 if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) { cbs_delete_service(intval($_GET['id'])); } // 获取所有服务 $services = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}cbs_services ORDER BY id DESC"); ?> <div class="wrap"> <h1>服务管理</h1> <div class="cbs-admin-container"> <!-- 添加服务表单 --> <div class="cbs-admin-card"> <h2><?php echo isset($_GET['edit']) ? '编辑服务' : '添加新服务'; ?></h2> <form method="post" action=""> <?php if (isset($_GET['edit'])) { $edit_id = intval($_GET['edit']); $service = $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}cbs_services WHERE id = %d", $edit_id )); } ?> <table class="form-table"> <tr> <th><label for="service_name">服务名称</label></th> <td> <input type="text" id="service_name" name="service_name" value="<?php echo isset($service) ? esc_attr($service->name) : ''; ?>" class="regular-text" required> </td> </tr> <tr> <th><label for="service_description">服务描述</label></th> <td> <textarea id="service_description" name="service_description" rows="3" class="large-text"><?php echo isset($service) ? esc_textarea($service->description) : ''; ?></textarea> </td> </tr> <tr> <th><label for="service_duration">服务时长(分钟)</label></th> <td> <input type="number" id="service_duration" name="service_duration" value="<?php echo isset($service) ? esc_attr($service->duration) : '60'; ?>" min="15" step="15" required> <p class="description">建议设置为15的倍数</p> </td> </tr> <tr> <th><label for="service_price">价格(元)</label></th> <td> <input type="number" id="service_price" name="service_price" value="<?php echo isset($service) ? esc_attr($service->price) : '0'; ?>" min="0" step="0.01" class="small-text"> </td> </tr> <tr> <th><label for="service_active">状态</label></th> <td> <label> <input type="checkbox" id="service_active" name="service_active" value="1" <?php echo (isset($service) && $service->active) || !isset($service) ? 'checked' : ''; ?>> 启用此服务 </label> </td> </tr> </table> <?php if (isset($_GET['edit'])): ?> <input type="hidden" name="service_id" value="<?php echo $edit_id; ?>"> <?php wp_nonce_field('cbs_update_service', 'cbs_service_nonce'); ?> <p class="submit"> <input type="submit" name="update_service" class="button button-primary" value="更新服务"> <a href="<?php echo admin_url('admin.php?page=cbs-services'); ?>" class="button">取消</a> </p> <?php else: ?> <?php wp_nonce_field('cbs_add_service', 'cbs_service_nonce'); ?> <p class="submit"> <input type="submit" name="add_service" class="button button-primary" value="添加服务"> </p> <?php endif; ?> </form> </div> <!-- 服务列表 --> <div class="cbs-admin-card"> <h2>服务列表</h2> <table class="wp-list-table widefat fixed striped"> <thead> <tr> <th>ID</th> <th>服务名称</th> <th>时长</th> <th>价格</th> <th>状态</th> <th>操作</th> </tr> </thead> <tbody> <?php if (empty($services)): ?> <tr> <td colspan="6">暂无服务项目</td> </tr> <?php else: ?> <?php foreach ($services as $service): ?> <tr> <td><?php echo $service->id; ?></td> <td> <strong><?php echo esc_html($service->name); ?></strong> <?php if ($service->description): ?> <p class="description"><?php echo esc_html($service->description); ?></p> <?php endif; ?> </td> <td><?php echo $service->duration; ?>分钟</td> <td><?php echo $service->price ? '¥' . number_format($service->price, 2) : '免费'; ?></td> <td> <?php if ($service->active): ?> <span class="dashicons dashicons-yes" style="color: #46b450;"></span> 启用 <?php else: ?> <span class="dashicons dashicons-no" style="color: #dc3232;"></span> 停用 <?php endif; ?> </td> <td> <a href="<?php echo admin_url('admin.php?page=cbs-services&edit=' . $service->id); ?>" class="button button-small">编辑</a> <a href="<?php echo admin_url('admin.php?page=cbs-services&action=delete&id=' . $service->id); ?>" class="button button-small button-link-delete" onclick="return confirm('确定要删除这个服务吗?');">删除</a> </td> </tr> <?php endforeach; ?> <?php endif; ?> </tbody> </table> </div> </div> </div> <style> .cbs-admin-container { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 20px; } .cbs-admin-card { background: white; padding: 20px; border: 1px solid #ccd0d4;
在当今数字化时代,网站已不仅仅是信息展示的平台,更是企业与客户互动的重要渠道。无论是服务行业、咨询机构、医疗机构还是教育机构,在线预约与日程管理功能都已成为提升用户体验、优化运营效率的关键工具。通过集成这些功能,您可以:
- 减少人工沟通成本,实现24/7自助预约
- 避免时间冲突,提高资源利用率
- 提升专业形象,增强客户信任感
- 自动化提醒功能,降低爽约率
本教程将指导您通过WordPress代码二次开发,为您的网站添加完整的在线预约与日程管理系统,无需依赖昂贵的第三方插件,完全自主控制数据与功能。
在开始编码之前,请确保您的WordPress环境满足以下要求:
- WordPress 5.0或更高版本
- PHP 7.4或更高版本
- MySQL 5.6或更高版本
- 已安装并激活一个支持子主题的WordPress主题
首先,我们需要创建一个自定义插件来承载我们的预约系统。在wp-content/plugins/目录下创建新文件夹custom-booking-system,并在其中创建主插件文件:
<?php
/**
* Plugin Name: 自定义预约与日程管理系统
* Plugin URI: https://yourwebsite.com/
* Description: 为WordPress网站添加完整的在线预约与日程管理功能
* Version: 1.0.0
* Author: 您的名称
* License: GPL v2 or later
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('CBS_VERSION', '1.0.0');
define('CBS_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('CBS_PLUGIN_URL', plugin_dir_url(__FILE__));
// 初始化插件
function cbs_init() {
// 检查必要组件
if (!function_exists('register_post_type')) {
wp_die('此插件需要WordPress 3.0或更高版本。');
}
// 加载文本域(用于国际化)
load_plugin_textdomain('custom-booking-system', false, dirname(plugin_basename(__FILE__)) . '/languages');
}
add_action('plugins_loaded', 'cbs_init');
// 激活插件时执行的操作
function cbs_activate() {
// 创建数据库表
cbs_create_tables();
// 设置默认选项
cbs_set_default_options();
// 刷新重写规则
flush_rewrite_rules();
}
register_activation_hook(__FILE__, 'cbs_activate');
// 停用插件时执行的操作
function cbs_deactivate() {
// 清理临时数据
// 注意:这里我们不删除数据,以便用户重新激活时保留数据
flush_rewrite_rules();
}
register_deactivation_hook(__FILE__, 'cbs_deactivate');
预约系统需要存储预约数据、服务项目、员工信息和时间安排。我们将创建以下数据库表:
// 创建数据库表
function cbs_create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_prefix = $wpdb->prefix . 'cbs_';
// 预约表
$bookings_table = $table_prefix . 'bookings';
$services_table = $table_prefix . 'services';
$staff_table = $table_prefix . 'staff';
$schedule_table = $table_prefix . 'schedules';
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
// 创建服务项目表
$sql_services = "CREATE TABLE IF NOT EXISTS $services_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL,
description text,
duration int NOT NULL DEFAULT 60 COMMENT '服务时长(分钟)',
price decimal(10,2) DEFAULT 0.00,
active tinyint(1) DEFAULT 1,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) $charset_collate;";
// 创建员工表
$sql_staff = "CREATE TABLE IF NOT EXISTS $staff_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
user_id bigint(20) unsigned,
name varchar(100) NOT NULL,
email varchar(100),
phone varchar(20),
services text COMMENT '可提供的服务ID,逗号分隔',
work_hours text COMMENT '工作时间安排',
active tinyint(1) DEFAULT 1,
PRIMARY KEY (id),
FOREIGN KEY (user_id) REFERENCES {$wpdb->users}(ID) ON DELETE SET NULL
) $charset_collate;";
// 创建预约表
$sql_bookings = "CREATE TABLE IF NOT EXISTS $bookings_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
booking_code varchar(20) NOT NULL UNIQUE COMMENT '预约编号',
customer_name varchar(100) NOT NULL,
customer_email varchar(100) NOT NULL,
customer_phone varchar(20),
service_id mediumint(9) NOT NULL,
staff_id mediumint(9),
booking_date date NOT NULL,
start_time time NOT NULL,
end_time time NOT NULL,
status varchar(20) DEFAULT 'pending' COMMENT 'pending, confirmed, cancelled, completed',
notes text,
ip_address varchar(45),
created_at datetime DEFAULT CURRENT_TIMESTAMP,
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
FOREIGN KEY (service_id) REFERENCES $services_table(id) ON DELETE CASCADE,
FOREIGN KEY (staff_id) REFERENCES $staff_table(id) ON DELETE SET NULL
) $charset_collate;";
// 创建日程表
$sql_schedules = "CREATE TABLE IF NOT EXISTS $schedule_table (
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 COMMENT '该时段最大预约数',
booked_count int DEFAULT 0 COMMENT '已预约数',
is_dayoff tinyint(1) DEFAULT 0 COMMENT '是否休息日',
PRIMARY KEY (id),
FOREIGN KEY (staff_id) REFERENCES $staff_table(id) ON DELETE CASCADE,
UNIQUE KEY unique_schedule (staff_id, date, start_time)
) $charset_collate;";
// 执行SQL
dbDelta($sql_services);
dbDelta($sql_staff);
dbDelta($sql_bookings);
dbDelta($sql_schedules);
// 添加示例数据(仅当表为空时)
cbs_add_sample_data($services_table, $staff_table);
}
// 添加示例数据
function cbs_add_sample_data($services_table, $staff_table) {
global $wpdb;
// 检查服务表是否为空
$service_count = $wpdb->get_var("SELECT COUNT(*) FROM $services_table");
if ($service_count == 0) {
$wpdb->insert($services_table, [
'name' => '基础咨询',
'description' => '30分钟的专业咨询服务',
'duration' => 30,
'price' => 50.00
]);
$wpdb->insert($services_table, [
'name' => '深度服务',
'description' => '60分钟的深度服务',
'duration' => 60,
'price' => 100.00
]);
}
// 检查员工表是否为空
$staff_count = $wpdb->get_var("SELECT COUNT(*) FROM $staff_table");
if ($staff_count == 0) {
$wpdb->insert($staff_table, [
'name' => '张顾问',
'email' => 'consultant@example.com',
'phone' => '13800138000',
'services' => '1,2'
]);
}
}
接下来,我们创建前端预约表单,让用户可以轻松选择服务、日期和时间:
// 预约表单短代码
function cbs_booking_form_shortcode($atts) {
// 提取短代码属性
$atts = shortcode_atts([
'service_id' => 0,
'staff_id' => 0,
'title' => '在线预约'
], $atts, 'booking_form');
ob_start(); // 开始输出缓冲
// 加载必要样式和脚本
wp_enqueue_style('cbs-frontend-style', CBS_PLUGIN_URL . 'assets/css/frontend.css');
wp_enqueue_script('cbs-frontend-script', CBS_PLUGIN_URL . 'assets/js/frontend.js', ['jquery', 'jquery-ui-datepicker'], CBS_VERSION, true);
// 本地化脚本,传递数据给JavaScript
wp_localize_script('cbs-frontend-script', 'cbs_ajax', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('cbs_booking_nonce')
]);
// 加载jQuery UI日期选择器样式
wp_enqueue_style('jquery-ui-style', '//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css');
?>
<div class="cbs-booking-container">
<h2><?php echo esc_html($atts['title']); ?></h2>
<div id="cbs-booking-message" class="cbs-message" style="display:none;"></div>
<form id="cbs-booking-form" method="post">
<!-- 客户信息 -->
<div class="cbs-form-section">
<h3>您的信息</h3>
<div class="cbs-form-group">
<label for="cbs-customer-name">姓名 *</label>
<input type="text" id="cbs-customer-name" name="customer_name" required>
</div>
<div class="cbs-form-group">
<label for="cbs-customer-email">邮箱 *</label>
<input type="email" id="cbs-customer-email" name="customer_email" required>
</div>
<div class="cbs-form-group">
<label for="cbs-customer-phone">电话</label>
<input type="tel" id="cbs-customer-phone" name="customer_phone">
</div>
</div>
<!-- 服务选择 -->
<div class="cbs-form-section">
<h3>选择服务</h3>
<div class="cbs-form-group">
<label for="cbs-service">服务项目 *</label>
<select id="cbs-service" name="service_id" required>
<option value="">请选择服务项目</option>
<?php
global $wpdb;
$services = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}cbs_services WHERE active = 1");
foreach ($services as $service) {
$selected = ($service->id == $atts['service_id']) ? 'selected' : '';
echo '<option value="' . esc_attr($service->id) . '" ' . $selected . '>'
. esc_html($service->name) . ' (' . esc_html($service->duration) . '分钟)'
. '</option>';
}
?>
</select>
</div>
</div>
<!-- 日期选择 -->
<div class="cbs-form-section">
<h3>选择日期</h3>
<div class="cbs-form-group">
<label for="cbs-booking-date">预约日期 *</label>
<input type="text" id="cbs-booking-date" name="booking_date" class="cbs-datepicker" required readonly>
<p class="cbs-description">请选择未来30天内的日期</p>
</div>
</div>
<!-- 时间选择 -->
<div class="cbs-form-section">
<h3>选择时间</h3>
<div class="cbs-form-group">
<label>可用时间段</label>
<div id="cbs-time-slots">
<p>请先选择日期以查看可用时间段</p>
</div>
</div>
</div>
<!-- 备注 -->
<div class="cbs-form-section">
<h3>备注信息</h3>
<div class="cbs-form-group">
<label for="cbs-notes">特殊要求或备注</label>
<textarea id="cbs-notes" name="notes" rows="3"></textarea>
</div>
</div>
<!-- 提交按钮 -->
<div class="cbs-form-section">
<input type="hidden" name="action" value="cbs_submit_booking">
<?php wp_nonce_field('cbs_booking_action', 'cbs_booking_nonce'); ?>
<button type="submit" id="cbs-submit-booking" class="cbs-submit-btn">提交预约</button>
</div>
</form>
</div>
<?php
return ob_get_clean(); // 返回缓冲内容
}
add_shortcode('booking_form', 'cbs_booking_form_shortcode');
现在我们需要创建AJAX处理函数,用于处理表单提交和获取可用时间:
// 获取可用时间段的AJAX处理
function cbs_get_available_times() {
// 验证nonce
check_ajax_referer('cbs_booking_nonce', 'nonce');
$date = sanitize_text_field($_POST['date']);
$service_id = intval($_POST['service_id']);
if (empty($date) || empty($service_id)) {
wp_send_json_error('缺少必要参数');
}
global $wpdb;
// 获取服务时长
$service = $wpdb->get_row($wpdb->prepare(
"SELECT duration FROM {$wpdb->prefix}cbs_services WHERE id = %d",
$service_id
));
if (!$service) {
wp_send_json_error('服务不存在');
}
$duration = $service->duration;
// 获取所有员工的工作时间
$staff_schedules = $wpdb->get_results($wpdb->prepare(
"SELECT s.*, st.name as staff_name
FROM {$wpdb->prefix}cbs_schedules s
LEFT JOIN {$wpdb->prefix}cbs_staff st ON s.staff_id = st.id
WHERE s.date = %s AND s.is_dayoff = 0",
$date
));
// 获取该日期已有的预约
$existing_bookings = $wpdb->get_results($wpdb->prepare(
"SELECT start_time, end_time, staff_id
FROM {$wpdb->prefix}cbs_bookings
WHERE booking_date = %s AND status IN ('pending', 'confirmed')",
$date
));
// 生成可用时间段
$available_slots = [];
foreach ($staff_schedules as $schedule) {
$start = strtotime($schedule->start_time);
$end = strtotime($schedule->end_time);
// 以30分钟为间隔生成时间段
for ($time = $start; $time < $end; $time += 30 * 60) {
$slot_end = $time + ($duration * 60);
// 检查是否超出工作时间
if ($slot_end > $end) {
continue;
}
// 检查该时间段是否已满
$booked_count = 0;
foreach ($existing_bookings as $booking) {
$booking_start = strtotime($booking->start_time);
$booking_end = strtotime($booking->end_time);
// 检查时间是否冲突
if ($time < $booking_end && $slot_end > $booking_start) {
if ($booking->staff_id == $schedule->staff_id || $booking->staff_id === null) {
$booked_count++;
}
}
}
// 如果未超过最大预约数,则添加为可用时间段
if ($booked_count < $schedule->max_bookings) {
$available_slots[] = [
'time' => date('H:i', $time),
'staff_id' => $schedule->staff_id,
'staff_name' => $schedule->staff_name,
'end_time' => date('H:i', $slot_end)
];
}
}
}
// 按时间排序
usort($available_slots, function($a, $b) {
return strcmp($a['time'], $b['time']);
});
wp_send_json_success($available_slots);
}
add_action('wp_ajax_cbs_get_available_times', 'cbs_get_available_times');
add_action('wp_ajax_nopriv_cbs_get_available_times', 'cbs_get_available_times');
// 处理预约提交
function cbs_submit_booking() {
// 验证nonce
if (!isset($_POST['cbs_booking_nonce']) ||
!wp_verify_nonce($_POST['cbs_booking_nonce'], 'cbs_booking_action')) {
wp_die('安全验证失败');
}
// 验证并清理数据
$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 = sanitize_text_field($_POST['booking_date']);
$start_time = sanitize_text_field($_POST['start_time']);
$notes = sanitize_textarea_field($_POST['notes']);
// 基本验证
if (empty($customer_name) || empty($customer_email) || empty($service_id) || empty($booking_date) || empty($start_time)) {
wp_die('请填写所有必填字段');
}
// 验证日期格式
if (!preg_match('/^d{4}-d{2}-d{2}$/', $booking_date)) {
wp_die('日期格式不正确');
}
// 验证时间格式
if (!preg_match('/^d{2}:d{2}$/', $start_time)) {
wp_die('时间格式不正确');
}
global $wpdb;
// 获取服务信息
$service = $wpdb->get_row($wpdb->prepare(
"SELECT duration FROM {$wpdb->prefix}cbs_services WHERE id = %d",
$service_id
));
if (!$service) {
wp_die('选择的服务不存在');
}
// 计算结束时间
$start_timestamp = strtotime($booking_date . ' ' . $start_time);
$end_timestamp = $start_timestamp + ($service->duration * 60);
$end_time = date('H:i', $end_timestamp);
// 检查时间是否可用
$is_available = cbs_check_time_availability($booking_date, $start_time, $end_time, $service_id);
if (!$is_available) {
wp_die('选择的时间段已被预约,请选择其他时间');
}
// 生成预约编号
$booking_code = 'BK' . date('Ymd') . strtoupper(wp_generate_password(6, false));
// 插入预约数据
$insert_data = [
'booking_code' => $booking_code,
'customer_name' => $customer_name,
'customer_email' => $customer_email,
'customer_phone' => $customer_phone,
'service_id' => $service_id,
'booking_date' => $booking_date,
'start_time' => $start_time,
'end_time' => $end_time,
'status' => 'pending',
'notes' => $notes,
'ip_address' => $_SERVER['REMOTE_ADDR']
];
$result = $wpdb->insert(
$wpdb->prefix . 'cbs_bookings',
$insert_data
);
if ($result === false) {
wp_die('预约提交失败,请稍后重试');
}
$booking_id = $wpdb->insert_id;
// 发送确认邮件
cbs_send_confirmation_email($booking_id);
// 返回成功信息
$success_message = sprintf(
'<div class="cbs-success-message">
<h3>预约提交成功!</h3>
<p>您的预约编号:<strong>%s</strong></p>
<p>我们已向您的邮箱发送确认邮件,请注意查收。</p>
<p>您可以在预约管理页面查看预约状态。</p>
</div>',
$booking_code
);
wp_die($success_message);
}
add_action('wp_ajax_cbs_submit_booking', 'cbs_submit_booking');
add_action('wp_ajax_nopriv_cbs_submit_booking', 'cbs_submit_booking');
// 检查时间可用性
function cbs_check_time_availability($date, $start_time, $end_time, $service_id, $exclude_booking_id = 0) {
global $wpdb;
$query = $wpdb->prepare(
"SELECT COUNT(*) FROM {$wpdb->prefix}cbs_bookings
WHERE booking_date = %s
AND status IN ('pending', 'confirmed')
AND id != %d
AND (
(start_time < %s AND end_time > %s) OR
(start_time >= %s AND start_time < %s)
)",
$date,
$exclude_booking_id,
$end_time,
$start_time,
$start_time,
$end_time
);
$conflict_count = $wpdb->get_var($query);
return $conflict_count == 0;
}
## 后台管理界面开发
创建后台管理界面,让管理员可以管理预约、服务和员工:
// 添加管理菜单
function cbs_add_admin_menu() {
// 主菜单
add_menu_page(
'预约管理',
'预约系统',
'manage_options',
'cbs-dashboard',
'cbs_dashboard_page',
'dashicons-calendar-alt',
30
);
// 子菜单
add_submenu_page(
'cbs-dashboard',
'所有预约',
'所有预约',
'manage_options',
'cbs-bookings',
'cbs_bookings_page'
);
add_submenu_page(
'cbs-dashboard',
'服务管理',
'服务管理',
'manage_options',
'cbs-services',
'cbs_services_page'
);
add_submenu_page(
'cbs-dashboard',
'员工管理',
'员工管理',
'manage_options',
'cbs-staff',
'cbs_staff_page'
);
add_submenu_page(
'cbs-dashboard',
'日程设置',
'日程设置',
'manage_options',
'cbs-schedules',
'cbs_schedules_page'
);
add_submenu_page(
'cbs-dashboard',
'系统设置',
'系统设置',
'manage_options',
'cbs-settings',
'cbs_settings_page'
);
}
add_action('admin_menu', 'cbs_add_admin_menu');
// 预约管理页面
function cbs_bookings_page() {
global $wpdb;
// 处理批量操作
if (isset($_POST['action']) && isset($_POST['booking_ids'])) {
cbs_process_bulk_actions();
}
// 处理单个操作
if (isset($_GET['action']) && isset($_GET['id'])) {
cbs_process_single_action();
}
// 获取预约数据
$bookings = $wpdb->get_results("
SELECT b.*, s.name as service_name
FROM {$wpdb->prefix}cbs_bookings b
LEFT JOIN {$wpdb->prefix}cbs_services s ON b.service_id = s.id
ORDER BY b.booking_date DESC, b.start_time DESC
LIMIT 100
");
?>
<div class="wrap">
<h1 class="wp-heading-inline">预约管理</h1>
<a href="<?php echo admin_url('admin.php?page=cbs-dashboard'); ?>" class="page-title-action">返回仪表板</a>
<hr class="wp-header-end">
<form method="post" action="">
<?php wp_nonce_field('cbs_bulk_action', 'cbs_bulk_nonce'); ?>
<div class="tablenav top">
<div class="alignleft actions bulkactions">
<select name="action" id="bulk-action-selector-top">
<option value="-1">批量操作</option>
<option value="confirm">确认预约</option>
<option value="cancel">取消预约</option>
<option value="delete">删除预约</option>
</select>
<input type="submit" id="doaction" class="button action" value="应用">
</div>
<br class="clear">
</div>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<td id="cb" class="manage-column column-cb check-column">
<input type="checkbox" id="cb-select-all-1">
</td>
<th>预约编号</th>
<th>客户姓名</th>
<th>服务项目</th>
<th>预约时间</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php if (empty($bookings)): ?>
<tr>
<td colspan="7">暂无预约记录</td>
</tr>
<?php else: ?>
<?php foreach ($bookings as $booking): ?>
<tr>
<th scope="row" class="check-column">
<input type="checkbox" name="booking_ids[]" value="<?php echo $booking->id; ?>">
</th>
<td><?php echo esc_html($booking->booking_code); ?></td>
<td>
<?php echo esc_html($booking->customer_name); ?><br>
<small><?php echo esc_html($booking->customer_email); ?></small>
</td>
<td><?php echo esc_html($booking->service_name); ?></td>
<td>
<?php echo date('Y-m-d', strtotime($booking->booking_date)); ?><br>
<?php echo $booking->start_time; ?> - <?php echo $booking->end_time; ?>
</td>
<td>
<?php
$status_labels = [
'pending' => '<span class="cbs-status pending">待确认</span>',
'confirmed' => '<span class="cbs-status confirmed">已确认</span>',
'cancelled' => '<span class="cbs-status cancelled">已取消</span>',
'completed' => '<span class="cbs-status completed">已完成</span>'
];
echo $status_labels[$booking->status] ?? $booking->status;
?>
</td>
<td>
<a href="<?php echo admin_url('admin.php?page=cbs-bookings&action=view&id=' . $booking->id); ?>"
class="button button-small">查看</a>
<a href="<?php echo admin_url('admin.php?page=cbs-bookings&action=edit&id=' . $booking->id); ?>"
class="button button-small">编辑</a>
<?php if ($booking->status == 'pending'): ?>
<a href="<?php echo admin_url('admin.php?page=cbs-bookings&action=confirm&id=' . $booking->id); ?>"
class="button button-small button-primary">确认</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</form>
</div>
<style>
.cbs-status {
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
font-weight: bold;
}
.cbs-status.pending { background: #f0ad4e; color: white; }
.cbs-status.confirmed { background: #5cb85c; color: white; }
.cbs-status.cancelled { background: #d9534f; color: white; }
.cbs-status.completed { background: #337ab7; color: white; }
</style>
<?php
}
// 服务管理页面
function cbs_services_page() {
global $wpdb;
// 处理表单提交
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['add_service'])) {
cbs_add_service();
} elseif (isset($_POST['update_service'])) {
cbs_update_service();
}
}
// 处理删除
if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) {
cbs_delete_service(intval($_GET['id']));
}
// 获取所有服务
$services = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}cbs_services ORDER BY id DESC");
?>
<div class="wrap">
<h1>服务管理</h1>
<div class="cbs-admin-container">
<!-- 添加服务表单 -->
<div class="cbs-admin-card">
<h2><?php echo isset($_GET['edit']) ? '编辑服务' : '添加新服务'; ?></h2>
<form method="post" action="">
<?php
if (isset($_GET['edit'])) {
$edit_id = intval($_GET['edit']);
$service = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}cbs_services WHERE id = %d",
$edit_id
));
}
?>
<table class="form-table">
<tr>
<th><label for="service_name">服务名称</label></th>
<td>
<input type="text" id="service_name" name="service_name"
value="<?php echo isset($service) ? esc_attr($service->name) : ''; ?>"
class="regular-text" required>
</td>
</tr>
<tr>
<th><label for="service_description">服务描述</label></th>
<td>
<textarea id="service_description" name="service_description"
rows="3" class="large-text"><?php echo isset($service) ? esc_textarea($service->description) : ''; ?></textarea>
</td>
</tr>
<tr>
<th><label for="service_duration">服务时长(分钟)</label></th>
<td>
<input type="number" id="service_duration" name="service_duration"
value="<?php echo isset($service) ? esc_attr($service->duration) : '60'; ?>"
min="15" step="15" required>
<p class="description">建议设置为15的倍数</p>
</td>
</tr>
<tr>
<th><label for="service_price">价格(元)</label></th>
<td>
<input type="number" id="service_price" name="service_price"
value="<?php echo isset($service) ? esc_attr($service->price) : '0'; ?>"
min="0" step="0.01" class="small-text">
</td>
</tr>
<tr>
<th><label for="service_active">状态</label></th>
<td>
<label>
<input type="checkbox" id="service_active" name="service_active" value="1"
<?php echo (isset($service) && $service->active) || !isset($service) ? 'checked' : ''; ?>>
启用此服务
</label>
</td>
</tr>
</table>
<?php if (isset($_GET['edit'])): ?>
<input type="hidden" name="service_id" value="<?php echo $edit_id; ?>">
<?php wp_nonce_field('cbs_update_service', 'cbs_service_nonce'); ?>
<p class="submit">
<input type="submit" name="update_service" class="button button-primary" value="更新服务">
<a href="<?php echo admin_url('admin.php?page=cbs-services'); ?>" class="button">取消</a>
</p>
<?php else: ?>
<?php wp_nonce_field('cbs_add_service', 'cbs_service_nonce'); ?>
<p class="submit">
<input type="submit" name="add_service" class="button button-primary" value="添加服务">
</p>
<?php endif; ?>
</form>
</div>
<!-- 服务列表 -->
<div class="cbs-admin-card">
<h2>服务列表</h2>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th>ID</th>
<th>服务名称</th>
<th>时长</th>
<th>价格</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php if (empty($services)): ?>
<tr>
<td colspan="6">暂无服务项目</td>
</tr>
<?php else: ?>
<?php foreach ($services as $service): ?>
<tr>
<td><?php echo $service->id; ?></td>
<td>
<strong><?php echo esc_html($service->name); ?></strong>
<?php if ($service->description): ?>
<p class="description"><?php echo esc_html($service->description); ?></p>
<?php endif; ?>
</td>
<td><?php echo $service->duration; ?>分钟</td>
<td><?php echo $service->price ? '¥' . number_format($service->price, 2) : '免费'; ?></td>
<td>
<?php if ($service->active): ?>
<span class="dashicons dashicons-yes" style="color: #46b450;"></span> 启用
<?php else: ?>
<span class="dashicons dashicons-no" style="color: #dc3232;"></span> 停用
<?php endif; ?>
</td>
<td>
<a href="<?php echo admin_url('admin.php?page=cbs-services&edit=' . $service->id); ?>"
class="button button-small">编辑</a>
<a href="<?php echo admin_url('admin.php?page=cbs-services&action=delete&id=' . $service->id); ?>"
class="button button-small button-link-delete"
onclick="return confirm('确定要删除这个服务吗?');">删除</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<style>
.cbs-admin-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-top: 20px;
}
.cbs-admin-card {
background: white;
padding: 20px;
border: 1px solid #ccd0d4;


