文章目录
-
- 在WordPress网站开发中,经常会遇到需要为客户定制功能的情况。传统的开发模式往往是单向的:客户提出需求→开发者实现→客户验收。这种模式存在沟通成本高、需求理解偏差、反复修改等问题。通过开发一个支持客户协同设计的插件,我们可以让客户在开发过程中直接参与界面设计和功能配置,大大提高开发效率和客户满意度。 本教程将指导您创建一个完整的WordPress插件,支持小批量定制和客户协同设计功能。我们将构建一个简单的产品配置器作为示例,客户可以通过它自定义产品选项并实时预览效果。
- 首先,我们需要创建插件的基本文件结构: <?php /** * Plugin Name: 客户协同设计工具 * Plugin URI: https://yourwebsite.com/ * Description: 支持客户参与设计过程的小批量定制插件 * Version: 1.0.0 * Author: 您的名字 * License: GPL v2 or later */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } // 定义插件常量 define('CCD_PLUGIN_PATH', plugin_dir_path(__FILE__)); define('CCD_PLUGIN_URL', plugin_dir_url(__FILE__)); define('CCD_VERSION', '1.0.0'); // 初始化插件 class ClientCoDesignPlugin { private static $instance = null; public static function get_instance() { if (null === self::$instance) { self::$instance = new self(); } return self::$instance; } private function __construct() { $this->init_hooks(); } private function init_hooks() { // 后台初始化 add_action('admin_init', array($this, 'admin_init')); // 添加管理菜单 add_action('admin_menu', array($this, 'add_admin_menu')); // 前端资源加载 add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_assets')); // 后台资源加载 add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets')); // 注册短代码 add_shortcode('product_configurator', array($this, 'render_configurator')); // AJAX处理 add_action('wp_ajax_save_design', array($this, 'save_design_callback')); add_action('wp_ajax_nopriv_save_design', array($this, 'save_design_callback')); } // 后续方法将在这里添加 } // 启动插件 ClientCoDesignPlugin::get_instance(); ?>
- 协同设计插件需要存储客户的设计选择和配置。我们将创建一个自定义数据库表来存储这些信息: // 在ClientCoDesignPlugin类中添加以下方法 /** * 创建必要的数据库表 */ public function create_database_tables() { global $wpdb; $table_name = $wpdb->prefix . 'client_designs'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE IF NOT EXISTS $table_name ( id mediumint(9) NOT NULL AUTO_INCREMENT, user_id mediumint(9) NOT NULL, project_name varchar(100) NOT NULL, design_data longtext NOT NULL, status varchar(20) DEFAULT 'draft', created_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY user_id (user_id), KEY status (status) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); } /** * 激活插件时创建表 */ public static function activate_plugin() { $plugin = new self(); $plugin->create_database_tables(); // 创建默认选项 add_option('ccd_default_options', array( 'max_designs_per_user' => 10, 'allow_guest_designs' => true, 'default_product_types' => array('T恤', '杯子', '海报') )); } /** * 保存设计数据 */ public function save_design($user_id, $project_name, $design_data) { global $wpdb; $table_name = $wpdb->prefix . 'client_designs'; // 检查用户是否超过最大设计数量限制 $max_designs = get_option('ccd_default_options')['max_designs_per_user']; $user_design_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $table_name WHERE user_id = %d", $user_id ) ); if ($user_design_count >= $max_designs) { return new WP_Error('limit_exceeded', '已达到最大设计数量限制'); } // 插入或更新设计数据 $result = $wpdb->replace( $table_name, array( 'user_id' => $user_id, 'project_name' => $project_name, 'design_data' => json_encode($design_data), 'status' => 'draft' ), array('%d', '%s', '%s', '%s') ); return $result ? $wpdb->insert_id : false; }
- 创建一个直观的前端界面,让客户可以实时配置产品: /** * 渲染产品配置器短代码 */ public function render_configurator($atts) { // 解析短代码属性 $atts = shortcode_atts(array( 'product_type' => 'tshirt', 'show_preview' => 'true' ), $atts, 'product_configurator'); // 获取当前用户ID(访客为0) $user_id = is_user_logged_in() ? get_current_user_id() : 0; // 生成唯一会话ID用于访客 if ($user_id === 0) { if (!isset($_SESSION['guest_session_id'])) { $_SESSION['guest_session_id'] = 'guest_' . wp_generate_uuid4(); } $session_id = $_SESSION['guest_session_id']; } ob_start(); ?> <div class="ccd-configurator-container" data-user-id="<?php echo esc_attr($user_id); ?>" data-product-type="<?php echo esc_attr($atts['product_type']); ?>"> <div class="ccd-header"> <h2>产品定制设计器</h2> <p>实时调整选项,右侧预览将立即更新</p> </div> <div class="ccd-main-content"> <!-- 左侧配置面板 --> <div class="ccd-control-panel"> <div class="ccd-section"> <h3>1. 基本设置</h3> <div class="ccd-control-group"> <label for="ccd-product-color">产品颜色:</label> <select id="ccd-product-color" class="ccd-control" data-target="preview-color"> <option value="#FF0000">红色</option> <option value="#00FF00" selected>绿色</option> <option value="#0000FF">蓝色</option> <option value="#FFFF00">黄色</option> <option value="#FFFFFF">白色</option> </select> </div> <div class="ccd-control-group"> <label for="ccd-product-size">产品尺寸:</label> <select id="ccd-product-size" class="ccd-control" data-target="preview-size"> <option value="S">小号 (S)</option> <option value="M" selected>中号 (M)</option> <option value="L">大号 (L)</option> <option value="XL">加大 (XL)</option> </select> </div> </div> <div class="ccd-section"> <h3>2. 自定义文本</h3> <div class="ccd-control-group"> <label for="ccd-custom-text">文本内容:</label> <input type="text" id="ccd-custom-text" class="ccd-control" data-target="preview-text" placeholder="输入自定义文本" maxlength="50"> </div> <div class="ccd-control-group"> <label for="ccd-text-color">文本颜色:</label> <input type="color" id="ccd-text-color" class="ccd-control" data-target="preview-text-color" value="#000000"> </div> <div class="ccd-control-group"> <label for="ccd-text-size">文本大小:</label> <input type="range" id="ccd-text-size" class="ccd-control" data-target="preview-text-size" min="12" max="72" value="24"> <span class="ccd-range-value">24px</span> </div> </div> <div class="ccd-section"> <h3>3. 上传图案</h3> <div class="ccd-control-group"> <label for="ccd-upload-image">上传图片:</label> <input type="file" id="ccd-upload-image" class="ccd-control" accept="image/*" style="display: none;"> <button type="button" id="ccd-upload-button" class="ccd-button">选择图片</button> <div id="ccd-image-preview" class="ccd-image-preview"></div> </div> <div class="ccd-control-group"> <label for="ccd-image-opacity">图案透明度:</label> <input type="range" id="ccd-image-opacity" class="ccd-control" data-target="preview-image-opacity" min="0" max="100" value="100"> <span class="ccd-range-value">100%</span> </div> </div> <!-- 保存和分享按钮 --> <div class="ccd-actions"> <button type="button" id="ccd-save-design" class="ccd-button ccd-button-primary"> <span class="dashicons dashicons-saved"></span> 保存设计 </button> <button type="button" id="ccd-share-design" class="ccd-button ccd-button-secondary"> <span class="dashicons dashicons-share"></span> 分享设计 </button> <button type="button" id="ccd-reset-design" class="ccd-button"> <span class="dashicons dashicons-update"></span> 重置 </button> </div> </div> <!-- 右侧实时预览 --> <div class="ccd-preview-panel"> <h3>实时预览</h3> <div class="ccd-preview-container"> <div class="ccd-product-preview" id="ccd-product-preview"> <!-- 预览内容将通过JavaScript动态更新 --> <div class="ccd-product-base" id="ccd-product-base"> <div class="ccd-product-text" id="ccd-product-text">示例文本</div> <div class="ccd-product-image" id="ccd-product-image"></div> </div> </div> </div> <div class="ccd-preview-info"> <h4>设计摘要</h4> <ul id="ccd-design-summary"> <li>颜色: <span id="summary-color">绿色</span></li> <li>尺寸: <span id="summary-size">中号 (M)</span></li> <li>自定义文本: <span id="summary-text">无</span></li> </ul> </div> </div> </div> <!-- 设计保存表单 --> <div class="ccd-save-modal" id="ccd-save-modal" style="display: none;"> <div class="ccd-modal-content"> <h3>保存您的设计</h3> <form id="ccd-save-form"> <div class="ccd-form-group"> <label for="ccd-design-name">设计名称:</label> <input type="text" id="ccd-design-name" required placeholder="例如: 公司周年纪念T恤"> </div> <div class="ccd-form-group"> <label for="ccd-design-notes">设计备注:</label> <textarea id="ccd-design-notes" rows="3" placeholder="添加一些备注或说明..."></textarea> </div> <div class="ccd-form-actions"> <button type="submit" class="ccd-button ccd-button-primary">保存设计</button> <button type="button" id="ccd-cancel-save" class="ccd-button">取消</button> </div> </form> </div> </div> </div> <?php return ob_get_clean(); }
- 添加JavaScript代码以实现实时预览和AJAX交互: // 创建文件 /assets/js/ccd-frontend.js jQuery(document).ready(function($) { 'use strict'; // 设计配置对象 var designConfig = { color: '#00FF00', size: 'M', text: '', textColor: '#000000', textSize: 24, image: null, imageOpacity: 100 }; // 初始化配置器 function initConfigurator() { // 绑定控制事件 $('.ccd-control').on('change input', handleControlChange); // 初始化颜色选择器 $('#ccd-text-color').on('change', updateTextColor); // 初始化范围输入 $('input[type="range"]').on('input', function() { $(this).next('.ccd-range-value').text($(this).val() + ($(this).attr('id') === 'ccd-text-size' ? 'px' : '%')); }); // 图片上传 $('#ccd-upload-button').on('click', function() { $('#ccd-upload-image').click(); }); $('#ccd-upload-image').on('change', handleImageUpload); // 按钮事件 $('#ccd-save-design').on('click', openSaveModal); $('#ccd-share-design').on('click', shareDesign); $('#ccd-reset-design').on('click', resetDesign); $('#ccd-cancel-save').on('click', closeSaveModal); // 保存表单提交 $('#ccd-save-form').on('submit', saveDesign); // 初始更新预览 updatePreview(); updateSummary(); } // 处理控制变化 function handleControlChange(e) { var $control = $(this); var controlId = $control.attr('id'); var value = $control.val(); var target = $control.data('target'); // 更新设计配置 switch(controlId) { case 'ccd-product-color': designConfig.color = value; break; case 'ccd-product-size': designConfig.size = value; break; case 'ccd-custom-text': designConfig.text = value; break; case 'ccd-text-size': designConfig.textSize = parseInt(value); break; case 'ccd-image-opacity': designConfig.imageOpacity = parseInt(value); break; } // 更新预览 updatePreview(); updateSummary(); } // 更新文本颜色 function updateTextColor() { designConfig.textColor = $(this).val(); updatePreview(); } // 处理图片上传 function handleImageUpload(e) { var file = e.target.files[0]; if (!file) return; // 验证文件类型 if (!file.type.match('image.*')) { alert('请选择图片文件'); return; } // 验证文件大小(最大2MB) if (file.size > 2 * 1024 * 1024) { alert('图片大小不能超过2MB'); return; } // 创建预览 var reader = new FileReader(); reader.onload = function(e) { designConfig.image = e.target.result; $('#ccd-image-preview').html( '<img src="' + e.target.result + '" alt="上传的图片" style="max-width: 100px;">' ); updatePreview(); }; reader.readAsDataURL(file); } // 更新预览 function updatePreview() { // 更新产品基础颜色 $('#ccd-product-base').css('background-color', designConfig.color); // 更新文本 var $productText = $('#ccd-product-text'); if (designConfig.text) { $productText.text(designConfig.text); $productText.show(); $productText.css({ 'color': designConfig.textColor, 'font-size': designConfig.textSize + 'px' }); } else { $productText.hide(); } // 更新图片 var $productImage = $('#ccd-product-image'); if (designConfig.image) { $productImage.html('<img src="' + designConfig.image + '" alt="设计图案">'); $productImage.find('img').css('opacity', designConfig.imageOpacity / 100); $productImage.show(); } else { $productImage.hide(); } } // 更新设计摘要 function updateSummary() { $('#summary-color').text(getColorName(designConfig.color)); $('#summary-size').text(getSizeName(designConfig.size)); $('#summary-text').text(designConfig.text || '无'); } // 获取颜色名称 function getColorName(hex) { var colors = { '#FF0000': '红色', '#00FF00': '绿色', '#0000FF': '蓝色', '#FFFF00': '黄色', '#FFFFFF': '白色' }; return colors[hex] || hex; } // 获取尺寸名称 function getSizeName(size) { var sizes = { 'S': '小号 (S)', 'M': '中号 (M)', 'L': '大号 (L)', 'XL': '加大 (XL)' }; return sizes[size] || size; } // 打开保存模态框 function openSaveModal() { $('#ccd-save-modal').fadeIn(); $('#ccd-design-name').focus(); } // 关闭保存模态框 function closeSaveModal() { $('#ccd-save-modal').fadeOut(); $('#ccd-save-form')[0].reset(); } // 分享设计 function shareDesign() { // 生成设计快照 html2canvas(document.querySelector('#ccd-product-preview')).then(canvas => { var imageData = canvas.toDataURL('image/png'); // 创建分享模态框 var shareModal = $( '<div class="ccd-share-modal">' + '<div class="ccd-modal-content">' + '<h3>分享设计</h3>' + '<div class="ccd-share-image">' + '<img src="' + imageData + '" alt="设计预览">' + '</div>' + '<div class="ccd-share-options">' + '<button class="ccd-button" id="ccd-download-image">下载图片</button>' + '<button class="ccd-button" id="ccd-copy-link">复制分享链接</button>' + '<button class="ccd-button" id="ccd-close-share">关闭</button>' + '</div>' + '</div>' + '</div>' ); $('body').append(shareModal); // 绑定分享按钮事件 $('#ccd-download-image').on('click', function() { var link = document.createElement('a'); link.download = '我的设计.png'; link.href = imageData; link.click(); }); $('#ccd-copy-link').on('click', function() { // 在实际应用中,这里应该生成一个唯一的分享链接 var shareUrl = window.location.href + '?design=' + btoa(JSON.stringify(designConfig)); navigator.clipboard.writeText(shareUrl).then(function() { alert('分享链接已复制到剪贴板!'); }); }); $('#ccd-close-share').on('click', function() { shareModal.remove(); }); }); } // 重置设计 function resetDesign() { if (confirm('确定要重置所有设置吗?')) { designConfig = { color: '#00FF00', size: 'M', text: '', textColor: '#000000', textSize: 24, image: null, imageOpacity: 100 }; // 重置表单控件 $('#ccd-product-color').val('#00FF00'); $('#ccd-product-size').val('M'); $('#ccd-custom-text').val(''); $('#ccd-text-color').val('#000000'); $('#ccd-text-size').val(24); $('#ccd-image-opacity').val(100); $('#ccd-upload-image').val(''); $('#ccd-image-preview').empty(); // 更新显示值 $('.ccd-range-value').each(function() { var $input = $(this).prev('input'); $(this).text($input.val() + ($input.attr('id') === 'ccd-text-size' ? 'px' : '%')); }); updatePreview(); updateSummary(); } } // 保存设计到服务器 function saveDesign(e) { e.preventDefault(); var designName = $('#ccd-design-name').val(); if (!designName.trim()) { alert('请输入设计名称'); return; } var designNotes = $('#ccd-design-notes').val(); var userId = $('.ccd-configurator-container').data('user-id'); // 显示加载状态 var $submitBtn = $(this).find('button[type="submit"]'); var originalText = $submitBtn.text(); $submitBtn.html('<span class="dashicons dashicons-update"></span> 保存中...').prop('disabled', true); // 准备设计数据 var designData = { config: designConfig, name: designName, notes: designNotes, timestamp: new Date().toISOString(), preview: '' // 在实际应用中,这里可以添加base64编码的预览图 }; // 发送AJAX请求 $.ajax({ url: ccd_ajax.ajax_url, type: 'POST', data: { action: 'save_design', nonce: ccd_ajax.nonce, user_id: userId, design_name: designName, design_data: designData }, success: function(response) { if (response.success) { alert('设计保存成功!'); closeSaveModal(); // 在实际应用中,这里可以重定向到设计管理页面 console.log('设计ID:', response.data.design_id); } else { alert('保存失败: ' + response.data.message); } }, error: function() { alert('网络错误,请稍后重试'); }, complete: function() { $submitBtn.text(originalText).prop('disabled', false); } }); } // 初始化 initConfigurator(); });
- 创建后台管理界面,让开发者可以管理客户的设计和配置选项: // 在ClientCoDesignPlugin类中添加以下方法 /** * 添加管理菜单 */ public function add_admin_menu() { // 主菜单 add_menu_page( '协同设计管理', '协同设计', 'manage_options', 'client-co-design', array($this, 'render_admin_dashboard'), 'dashicons-art', 30 ); // 子菜单 add_submenu_page( 'client-co-design', '设计管理', '设计管理', 'manage_options', 'ccd-designs', array($this, 'render_designs_page') ); add_submenu_page( 'client-co-design', '产品配置', '产品配置', 'manage_options', 'ccd-products', array($this, 'render_products_page') ); add_submenu_page( 'client-co-design', '设置', '设置', 'manage_options', 'ccd-settings', array($this, 'render_settings_page') ); } /** * 渲染管理仪表板 */ public function render_admin_dashboard() { global $wpdb; $table_name = $wpdb->prefix . 'client_designs'; // 获取统计数据 $total_designs = $wpdb->get_var("SELECT COUNT(*) FROM $table_name"); $active_designs = $wpdb->get_var( $wpdb->prepare("SELECT COUNT(*) FROM $table_name WHERE status = %s", 'active') ); $recent_designs = $wpdb->get_results( "SELECT * FROM $table_name ORDER BY created_at DESC LIMIT 5" ); ?> <div class="wrap ccd-admin-dashboard"> <h1>协同设计管理仪表板</h1> <div class="ccd-stats-container"> <div class="ccd-stat-card"> <h3>总设计数</h3> <div class="ccd-stat-number"><?php echo esc_html($total_designs); ?></div> </div> <div class="ccd-stat-card"> <h3>活跃设计</h3> <div class="ccd-stat-number"><?php echo esc_html($active_designs); ?></div> </div> <div class="ccd-stat-card"> <h3>今日新增</h3> <div class="ccd-stat-number"> <?php $today = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $table_name WHERE DATE(created_at) = %s", current_time('Y-m-d') ) ); echo esc_html($today); ?> </div> </div> </div> <div class="ccd-recent-designs"> <h2>最近的设计</h2> <?php if ($recent_designs): ?> <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 foreach ($recent_designs as $design): $user_info = get_userdata($design->user_id); $username = $user_info ? $user_info->display_name : '访客'; ?> <tr> <td><?php echo esc_html($design->id); ?></td> <td><?php echo esc_html($design->project_name); ?></td> <td><?php echo esc_html($username); ?></td> <td> <span class="ccd-status-badge ccd-status-<?php echo esc_attr($design->status); ?>"> <?php $status_labels = array( 'draft' => '草稿', 'active' => '活跃', 'completed' => '完成' ); echo esc_html($status_labels[$design->status] ?? $design->status); ?> </span> </td> <td><?php echo esc_html(date('Y-m-d H:i', strtotime($design->created_at))); ?></td> <td> <button class="button button-small view-design" data-design-id="<?php echo esc_attr($design->id); ?>"> 查看 </button> <button class="button button-small edit-design" data-design-id="<?php echo esc_attr($design->id); ?>"> 编辑 </button> </td> </tr> <?php endforeach; ?> </tbody> </table> <?php else: ?> <p>暂无设计数据。</p> <?php endif; ?> </div> </div> <?php } /** * 渲染设计管理页面 */ public function render_designs_page() { global $wpdb; $table_name = $wpdb->prefix . 'client_designs'; $per_page = 20; $current_page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1; $offset = ($current_page - 1) * $per_page; // 获取筛选条件 $status_filter = isset($_GET['status']) ? sanitize_text_field($_GET['status']) : ''; $search_term = isset($_GET['s']) ? sanitize_text_field($_GET['s']) : ''; // 构建查询 $where_clauses = array('1=1'); $query_params = array(); if ($status_filter) { $where_clauses[] = "status = %s"; $query_params[] = $status_filter; } if ($search_term) { $where_clauses[] = "(project_name LIKE %s OR id = %s)"; $query_params[] = '%' . $wpdb->esc_like($search_term) . '%'; $query_params[] = $search_term; } $where_sql = implode(' AND ', $where_clauses); // 获取总记录数 $count_query = "SELECT COUNT(*) FROM $table_name WHERE $where_sql"; if ($query_params) { $count_query = $wpdb->prepare($count_query, $query_params); } $total_items = $wpdb->get_var($count_query); // 获取当前页数据 $data_query = "SELECT * FROM $table_name WHERE $where_sql ORDER BY created_at DESC LIMIT %d OFFSET %d"; $query_params[] = $per_page; $query_params[] = $offset; $data_query = $wpdb->prepare($data_query, $query_params); $designs = $wpdb->get_results($data_query); ?> <div class="wrap"> <h1 class="wp-heading-inline">设计管理</h1> <form method="get" class="ccd-filter-form"> <input type="hidden" name="page" value="ccd-designs"> <div class="ccd-filters"> <select name="status" class="ccd-filter-select"> <option value="">所有状态</option> <option value="draft" <?php selected($status_filter, 'draft'); ?>>草稿</option> <option value="active" <?php selected($status_filter, 'active'); ?>>活跃</option> <option value="completed" <?php selected($status_filter, 'completed'); ?>>完成</option> </select> <input type="text" name="s" placeholder="搜索设计名称或ID" value="<?php echo esc_attr($search_term); ?>" class="ccd-search-input"> <button type="submit" class="button">筛选</button> <?php if ($status_filter || $search_term): ?> <a href="<?php echo admin_url('admin.php?page=ccd-designs'); ?>" class="button"> 清除筛选 </a> <?php endif; ?> </div> </form> <div class="ccd-designs-list"> <?php if ($designs): ?> <table class="wp-list-table widefat fixed striped"> <thead> <tr> <th>ID</th> <th>设计名称</th> <th>用户</th> <th>状态</th> <th>创建时间</th> <th>更新时间</th> <th>操作</th> </tr> </thead> <tbody> <?php foreach ($designs as $design): $user_info = get_userdata($design->user_id); $username = $user_info ? $user_info->display_name : '访客'; $design_data = json_decode($design->design_data, true); ?> <tr> <td><?php echo esc_html($design->id); ?></td> <td> <strong><?php echo esc_html($design->project_name); ?></strong> <?php if (!empty($design_data['notes'])): ?> <br><small><?php echo esc_html(substr($design_data['notes'], 0, 50)); ?>...</small> <?php endif; ?> </td> <td><?php echo esc_html($username); ?></td> <td> <select class="ccd-status-select" data-design-id="<?php echo esc_attr($design->id); ?>"> <option value="draft" <?php selected($design->status, 'draft'); ?>>草稿</option> <option value="active" <?php selected($design->status, 'active'); ?>>活跃</option> <option value="completed" <?php selected($design->status, 'completed'); ?>>完成</option> </select> </td> <td><?php echo esc_html(date('Y-m-d H:i', strtotime($design->created_at))); ?></td> <td><?php echo esc_html(date('Y-m-d H:i', strtotime($design->updated_at))); ?></td> <td> <div class="ccd-action-buttons"> <button class="button button-small ccd-view-design" data-design-id="<?php echo esc_attr($design->id); ?>"> 查看详情 </button> <button class="button button-small ccd-export-design" data-design-id="<?php echo esc_attr($design->id); ?>"> 导出 </button> <button class="button button-small ccd-delete-design" data-design-id="<?php echo esc_attr($design->id); ?>" onclick="return confirm('确定要删除这个设计吗?')"> 删除 </button> </div> </td> </tr> <?php endforeach; ?> </tbody> </table> <!-- 分页 --> <div class="ccd-pagination"> <?php $total_pages = ceil($total_items / $per_page); $base_url = admin_url('admin.php?page=ccd-designs'); if ($total_pages > 1) { echo paginate_links(array( 'base' => add_query_arg('paged', '%#%', $base_url), 'format' => '', 'prev_text' => '«', 'next_text' => '»', 'total' => $total_pages, 'current' => $current_page, 'add_args' => array( 'status' => $status_filter, 's' => $search_term ) )); } ?> </div> <?php else: ?> <p>没有找到匹配的设计。</p> <?php endif; ?> </div> </div> <?php }
- /** * 保存设计AJAX回调 */ public function save_design_callback() { // 验证nonce
在WordPress网站开发中,经常会遇到需要为客户定制功能的情况。传统的开发模式往往是单向的:客户提出需求→开发者实现→客户验收。这种模式存在沟通成本高、需求理解偏差、反复修改等问题。通过开发一个支持客户协同设计的插件,我们可以让客户在开发过程中直接参与界面设计和功能配置,大大提高开发效率和客户满意度。
本教程将指导您创建一个完整的WordPress插件,支持小批量定制和客户协同设计功能。我们将构建一个简单的产品配置器作为示例,客户可以通过它自定义产品选项并实时预览效果。
首先,我们需要创建插件的基本文件结构:
<?php
/**
* Plugin Name: 客户协同设计工具
* Plugin URI: https://yourwebsite.com/
* Description: 支持客户参与设计过程的小批量定制插件
* Version: 1.0.0
* Author: 您的名字
* License: GPL v2 or later
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('CCD_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('CCD_PLUGIN_URL', plugin_dir_url(__FILE__));
define('CCD_VERSION', '1.0.0');
// 初始化插件
class ClientCoDesignPlugin {
private static $instance = null;
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
$this->init_hooks();
}
private function init_hooks() {
// 后台初始化
add_action('admin_init', array($this, 'admin_init'));
// 添加管理菜单
add_action('admin_menu', array($this, 'add_admin_menu'));
// 前端资源加载
add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_assets'));
// 后台资源加载
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets'));
// 注册短代码
add_shortcode('product_configurator', array($this, 'render_configurator'));
// AJAX处理
add_action('wp_ajax_save_design', array($this, 'save_design_callback'));
add_action('wp_ajax_nopriv_save_design', array($this, 'save_design_callback'));
}
// 后续方法将在这里添加
}
// 启动插件
ClientCoDesignPlugin::get_instance();
?>
协同设计插件需要存储客户的设计选择和配置。我们将创建一个自定义数据库表来存储这些信息:
// 在ClientCoDesignPlugin类中添加以下方法
/**
* 创建必要的数据库表
*/
public function create_database_tables() {
global $wpdb;
$table_name = $wpdb->prefix . 'client_designs';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
user_id mediumint(9) NOT NULL,
project_name varchar(100) NOT NULL,
design_data longtext NOT NULL,
status varchar(20) DEFAULT 'draft',
created_at datetime DEFAULT CURRENT_TIMESTAMP,
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY user_id (user_id),
KEY status (status)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
/**
* 激活插件时创建表
*/
public static function activate_plugin() {
$plugin = new self();
$plugin->create_database_tables();
// 创建默认选项
add_option('ccd_default_options', array(
'max_designs_per_user' => 10,
'allow_guest_designs' => true,
'default_product_types' => array('T恤', '杯子', '海报')
));
}
/**
* 保存设计数据
*/
public function save_design($user_id, $project_name, $design_data) {
global $wpdb;
$table_name = $wpdb->prefix . 'client_designs';
// 检查用户是否超过最大设计数量限制
$max_designs = get_option('ccd_default_options')['max_designs_per_user'];
$user_design_count = $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT(*) FROM $table_name WHERE user_id = %d",
$user_id
)
);
if ($user_design_count >= $max_designs) {
return new WP_Error('limit_exceeded', '已达到最大设计数量限制');
}
// 插入或更新设计数据
$result = $wpdb->replace(
$table_name,
array(
'user_id' => $user_id,
'project_name' => $project_name,
'design_data' => json_encode($design_data),
'status' => 'draft'
),
array('%d', '%s', '%s', '%s')
);
return $result ? $wpdb->insert_id : false;
}
创建一个直观的前端界面,让客户可以实时配置产品:
/**
* 渲染产品配置器短代码
*/
public function render_configurator($atts) {
// 解析短代码属性
$atts = shortcode_atts(array(
'product_type' => 'tshirt',
'show_preview' => 'true'
), $atts, 'product_configurator');
// 获取当前用户ID(访客为0)
$user_id = is_user_logged_in() ? get_current_user_id() : 0;
// 生成唯一会话ID用于访客
if ($user_id === 0) {
if (!isset($_SESSION['guest_session_id'])) {
$_SESSION['guest_session_id'] = 'guest_' . wp_generate_uuid4();
}
$session_id = $_SESSION['guest_session_id'];
}
ob_start();
?>
<div class="ccd-configurator-container" data-user-id="<?php echo esc_attr($user_id); ?>" data-product-type="<?php echo esc_attr($atts['product_type']); ?>">
<div class="ccd-header">
<h2>产品定制设计器</h2>
<p>实时调整选项,右侧预览将立即更新</p>
</div>
<div class="ccd-main-content">
<!-- 左侧配置面板 -->
<div class="ccd-control-panel">
<div class="ccd-section">
<h3>1. 基本设置</h3>
<div class="ccd-control-group">
<label for="ccd-product-color">产品颜色:</label>
<select id="ccd-product-color" class="ccd-control" data-target="preview-color">
<option value="#FF0000">红色</option>
<option value="#00FF00" selected>绿色</option>
<option value="#0000FF">蓝色</option>
<option value="#FFFF00">黄色</option>
<option value="#FFFFFF">白色</option>
</select>
</div>
<div class="ccd-control-group">
<label for="ccd-product-size">产品尺寸:</label>
<select id="ccd-product-size" class="ccd-control" data-target="preview-size">
<option value="S">小号 (S)</option>
<option value="M" selected>中号 (M)</option>
<option value="L">大号 (L)</option>
<option value="XL">加大 (XL)</option>
</select>
</div>
</div>
<div class="ccd-section">
<h3>2. 自定义文本</h3>
<div class="ccd-control-group">
<label for="ccd-custom-text">文本内容:</label>
<input type="text" id="ccd-custom-text" class="ccd-control"
data-target="preview-text"
placeholder="输入自定义文本"
maxlength="50">
</div>
<div class="ccd-control-group">
<label for="ccd-text-color">文本颜色:</label>
<input type="color" id="ccd-text-color" class="ccd-control"
data-target="preview-text-color"
value="#000000">
</div>
<div class="ccd-control-group">
<label for="ccd-text-size">文本大小:</label>
<input type="range" id="ccd-text-size" class="ccd-control"
data-target="preview-text-size"
min="12" max="72" value="24">
<span class="ccd-range-value">24px</span>
</div>
</div>
<div class="ccd-section">
<h3>3. 上传图案</h3>
<div class="ccd-control-group">
<label for="ccd-upload-image">上传图片:</label>
<input type="file" id="ccd-upload-image" class="ccd-control"
accept="image/*" style="display: none;">
<button type="button" id="ccd-upload-button" class="ccd-button">选择图片</button>
<div id="ccd-image-preview" class="ccd-image-preview"></div>
</div>
<div class="ccd-control-group">
<label for="ccd-image-opacity">图案透明度:</label>
<input type="range" id="ccd-image-opacity" class="ccd-control"
data-target="preview-image-opacity"
min="0" max="100" value="100">
<span class="ccd-range-value">100%</span>
</div>
</div>
<!-- 保存和分享按钮 -->
<div class="ccd-actions">
<button type="button" id="ccd-save-design" class="ccd-button ccd-button-primary">
<span class="dashicons dashicons-saved"></span> 保存设计
</button>
<button type="button" id="ccd-share-design" class="ccd-button ccd-button-secondary">
<span class="dashicons dashicons-share"></span> 分享设计
</button>
<button type="button" id="ccd-reset-design" class="ccd-button">
<span class="dashicons dashicons-update"></span> 重置
</button>
</div>
</div>
<!-- 右侧实时预览 -->
<div class="ccd-preview-panel">
<h3>实时预览</h3>
<div class="ccd-preview-container">
<div class="ccd-product-preview" id="ccd-product-preview">
<!-- 预览内容将通过JavaScript动态更新 -->
<div class="ccd-product-base" id="ccd-product-base">
<div class="ccd-product-text" id="ccd-product-text">示例文本</div>
<div class="ccd-product-image" id="ccd-product-image"></div>
</div>
</div>
</div>
<div class="ccd-preview-info">
<h4>设计摘要</h4>
<ul id="ccd-design-summary">
<li>颜色: <span id="summary-color">绿色</span></li>
<li>尺寸: <span id="summary-size">中号 (M)</span></li>
<li>自定义文本: <span id="summary-text">无</span></li>
</ul>
</div>
</div>
</div>
<!-- 设计保存表单 -->
<div class="ccd-save-modal" id="ccd-save-modal" style="display: none;">
<div class="ccd-modal-content">
<h3>保存您的设计</h3>
<form id="ccd-save-form">
<div class="ccd-form-group">
<label for="ccd-design-name">设计名称:</label>
<input type="text" id="ccd-design-name" required
placeholder="例如: 公司周年纪念T恤">
</div>
<div class="ccd-form-group">
<label for="ccd-design-notes">设计备注:</label>
<textarea id="ccd-design-notes" rows="3"
placeholder="添加一些备注或说明..."></textarea>
</div>
<div class="ccd-form-actions">
<button type="submit" class="ccd-button ccd-button-primary">保存设计</button>
<button type="button" id="ccd-cancel-save" class="ccd-button">取消</button>
</div>
</form>
</div>
</div>
</div>
<?php
return ob_get_clean();
}
添加JavaScript代码以实现实时预览和AJAX交互:
// 创建文件 /assets/js/ccd-frontend.js
jQuery(document).ready(function($) {
'use strict';
// 设计配置对象
var designConfig = {
color: '#00FF00',
size: 'M',
text: '',
textColor: '#000000',
textSize: 24,
image: null,
imageOpacity: 100
};
// 初始化配置器
function initConfigurator() {
// 绑定控制事件
$('.ccd-control').on('change input', handleControlChange);
// 初始化颜色选择器
$('#ccd-text-color').on('change', updateTextColor);
// 初始化范围输入
$('input[type="range"]').on('input', function() {
$(this).next('.ccd-range-value').text($(this).val() +
($(this).attr('id') === 'ccd-text-size' ? 'px' : '%'));
});
// 图片上传
$('#ccd-upload-button').on('click', function() {
$('#ccd-upload-image').click();
});
$('#ccd-upload-image').on('change', handleImageUpload);
// 按钮事件
$('#ccd-save-design').on('click', openSaveModal);
$('#ccd-share-design').on('click', shareDesign);
$('#ccd-reset-design').on('click', resetDesign);
$('#ccd-cancel-save').on('click', closeSaveModal);
// 保存表单提交
$('#ccd-save-form').on('submit', saveDesign);
// 初始更新预览
updatePreview();
updateSummary();
}
// 处理控制变化
function handleControlChange(e) {
var $control = $(this);
var controlId = $control.attr('id');
var value = $control.val();
var target = $control.data('target');
// 更新设计配置
switch(controlId) {
case 'ccd-product-color':
designConfig.color = value;
break;
case 'ccd-product-size':
designConfig.size = value;
break;
case 'ccd-custom-text':
designConfig.text = value;
break;
case 'ccd-text-size':
designConfig.textSize = parseInt(value);
break;
case 'ccd-image-opacity':
designConfig.imageOpacity = parseInt(value);
break;
}
// 更新预览
updatePreview();
updateSummary();
}
// 更新文本颜色
function updateTextColor() {
designConfig.textColor = $(this).val();
updatePreview();
}
// 处理图片上传
function handleImageUpload(e) {
var file = e.target.files[0];
if (!file) return;
// 验证文件类型
if (!file.type.match('image.*')) {
alert('请选择图片文件');
return;
}
// 验证文件大小(最大2MB)
if (file.size > 2 * 1024 * 1024) {
alert('图片大小不能超过2MB');
return;
}
// 创建预览
var reader = new FileReader();
reader.onload = function(e) {
designConfig.image = e.target.result;
$('#ccd-image-preview').html(
'<img src="' + e.target.result + '" alt="上传的图片" style="max-width: 100px;">'
);
updatePreview();
};
reader.readAsDataURL(file);
}
// 更新预览
function updatePreview() {
// 更新产品基础颜色
$('#ccd-product-base').css('background-color', designConfig.color);
// 更新文本
var $productText = $('#ccd-product-text');
if (designConfig.text) {
$productText.text(designConfig.text);
$productText.show();
$productText.css({
'color': designConfig.textColor,
'font-size': designConfig.textSize + 'px'
});
} else {
$productText.hide();
}
// 更新图片
var $productImage = $('#ccd-product-image');
if (designConfig.image) {
$productImage.html('<img src="' + designConfig.image + '" alt="设计图案">');
$productImage.find('img').css('opacity', designConfig.imageOpacity / 100);
$productImage.show();
} else {
$productImage.hide();
}
}
// 更新设计摘要
function updateSummary() {
$('#summary-color').text(getColorName(designConfig.color));
$('#summary-size').text(getSizeName(designConfig.size));
$('#summary-text').text(designConfig.text || '无');
}
// 获取颜色名称
function getColorName(hex) {
var colors = {
'#FF0000': '红色',
'#00FF00': '绿色',
'#0000FF': '蓝色',
'#FFFF00': '黄色',
'#FFFFFF': '白色'
};
return colors[hex] || hex;
}
// 获取尺寸名称
function getSizeName(size) {
var sizes = {
'S': '小号 (S)',
'M': '中号 (M)',
'L': '大号 (L)',
'XL': '加大 (XL)'
};
return sizes[size] || size;
}
// 打开保存模态框
function openSaveModal() {
$('#ccd-save-modal').fadeIn();
$('#ccd-design-name').focus();
}
// 关闭保存模态框
function closeSaveModal() {
$('#ccd-save-modal').fadeOut();
$('#ccd-save-form')[0].reset();
}
// 分享设计
function shareDesign() {
// 生成设计快照
html2canvas(document.querySelector('#ccd-product-preview')).then(canvas => {
var imageData = canvas.toDataURL('image/png');
// 创建分享模态框
var shareModal = $(
'<div class="ccd-share-modal">' +
'<div class="ccd-modal-content">' +
'<h3>分享设计</h3>' +
'<div class="ccd-share-image">' +
'<img src="' + imageData + '" alt="设计预览">' +
'</div>' +
'<div class="ccd-share-options">' +
'<button class="ccd-button" id="ccd-download-image">下载图片</button>' +
'<button class="ccd-button" id="ccd-copy-link">复制分享链接</button>' +
'<button class="ccd-button" id="ccd-close-share">关闭</button>' +
'</div>' +
'</div>' +
'</div>'
);
$('body').append(shareModal);
// 绑定分享按钮事件
$('#ccd-download-image').on('click', function() {
var link = document.createElement('a');
link.download = '我的设计.png';
link.href = imageData;
link.click();
});
$('#ccd-copy-link').on('click', function() {
// 在实际应用中,这里应该生成一个唯一的分享链接
var shareUrl = window.location.href + '?design=' + btoa(JSON.stringify(designConfig));
navigator.clipboard.writeText(shareUrl).then(function() {
alert('分享链接已复制到剪贴板!');
});
});
$('#ccd-close-share').on('click', function() {
shareModal.remove();
});
});
}
// 重置设计
function resetDesign() {
if (confirm('确定要重置所有设置吗?')) {
designConfig = {
color: '#00FF00',
size: 'M',
text: '',
textColor: '#000000',
textSize: 24,
image: null,
imageOpacity: 100
};
// 重置表单控件
$('#ccd-product-color').val('#00FF00');
$('#ccd-product-size').val('M');
$('#ccd-custom-text').val('');
$('#ccd-text-color').val('#000000');
$('#ccd-text-size').val(24);
$('#ccd-image-opacity').val(100);
$('#ccd-upload-image').val('');
$('#ccd-image-preview').empty();
// 更新显示值
$('.ccd-range-value').each(function() {
var $input = $(this).prev('input');
$(this).text($input.val() + ($input.attr('id') === 'ccd-text-size' ? 'px' : '%'));
});
updatePreview();
updateSummary();
}
}
// 保存设计到服务器
function saveDesign(e) {
e.preventDefault();
var designName = $('#ccd-design-name').val();
if (!designName.trim()) {
alert('请输入设计名称');
return;
}
var designNotes = $('#ccd-design-notes').val();
var userId = $('.ccd-configurator-container').data('user-id');
// 显示加载状态
var $submitBtn = $(this).find('button[type="submit"]');
var originalText = $submitBtn.text();
$submitBtn.html('<span class="dashicons dashicons-update"></span> 保存中...').prop('disabled', true);
// 准备设计数据
var designData = {
config: designConfig,
name: designName,
notes: designNotes,
timestamp: new Date().toISOString(),
preview: '' // 在实际应用中,这里可以添加base64编码的预览图
};
// 发送AJAX请求
$.ajax({
url: ccd_ajax.ajax_url,
type: 'POST',
data: {
action: 'save_design',
nonce: ccd_ajax.nonce,
user_id: userId,
design_name: designName,
design_data: designData
},
success: function(response) {
if (response.success) {
alert('设计保存成功!');
closeSaveModal();
// 在实际应用中,这里可以重定向到设计管理页面
console.log('设计ID:', response.data.design_id);
} else {
alert('保存失败: ' + response.data.message);
}
},
error: function() {
alert('网络错误,请稍后重试');
},
complete: function() {
$submitBtn.text(originalText).prop('disabled', false);
}
});
}
// 初始化
initConfigurator();
});
创建后台管理界面,让开发者可以管理客户的设计和配置选项:
// 在ClientCoDesignPlugin类中添加以下方法
/**
* 添加管理菜单
*/
public function add_admin_menu() {
// 主菜单
add_menu_page(
'协同设计管理',
'协同设计',
'manage_options',
'client-co-design',
array($this, 'render_admin_dashboard'),
'dashicons-art',
30
);
// 子菜单
add_submenu_page(
'client-co-design',
'设计管理',
'设计管理',
'manage_options',
'ccd-designs',
array($this, 'render_designs_page')
);
add_submenu_page(
'client-co-design',
'产品配置',
'产品配置',
'manage_options',
'ccd-products',
array($this, 'render_products_page')
);
add_submenu_page(
'client-co-design',
'设置',
'设置',
'manage_options',
'ccd-settings',
array($this, 'render_settings_page')
);
}
/**
* 渲染管理仪表板
*/
public function render_admin_dashboard() {
global $wpdb;
$table_name = $wpdb->prefix . 'client_designs';
// 获取统计数据
$total_designs = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
$active_designs = $wpdb->get_var(
$wpdb->prepare("SELECT COUNT(*) FROM $table_name WHERE status = %s", 'active')
);
$recent_designs = $wpdb->get_results(
"SELECT * FROM $table_name ORDER BY created_at DESC LIMIT 5"
);
?>
<div class="wrap ccd-admin-dashboard">
<h1>协同设计管理仪表板</h1>
<div class="ccd-stats-container">
<div class="ccd-stat-card">
<h3>总设计数</h3>
<div class="ccd-stat-number"><?php echo esc_html($total_designs); ?></div>
</div>
<div class="ccd-stat-card">
<h3>活跃设计</h3>
<div class="ccd-stat-number"><?php echo esc_html($active_designs); ?></div>
</div>
<div class="ccd-stat-card">
<h3>今日新增</h3>
<div class="ccd-stat-number">
<?php
$today = $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT(*) FROM $table_name WHERE DATE(created_at) = %s",
current_time('Y-m-d')
)
);
echo esc_html($today);
?>
</div>
</div>
</div>
<div class="ccd-recent-designs">
<h2>最近的设计</h2>
<?php if ($recent_designs): ?>
<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 foreach ($recent_designs as $design):
$user_info = get_userdata($design->user_id);
$username = $user_info ? $user_info->display_name : '访客';
?>
<tr>
<td><?php echo esc_html($design->id); ?></td>
<td><?php echo esc_html($design->project_name); ?></td>
<td><?php echo esc_html($username); ?></td>
<td>
<span class="ccd-status-badge ccd-status-<?php echo esc_attr($design->status); ?>">
<?php
$status_labels = array(
'draft' => '草稿',
'active' => '活跃',
'completed' => '完成'
);
echo esc_html($status_labels[$design->status] ?? $design->status);
?>
</span>
</td>
<td><?php echo esc_html(date('Y-m-d H:i', strtotime($design->created_at))); ?></td>
<td>
<button class="button button-small view-design" data-design-id="<?php echo esc_attr($design->id); ?>">
查看
</button>
<button class="button button-small edit-design" data-design-id="<?php echo esc_attr($design->id); ?>">
编辑
</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<p>暂无设计数据。</p>
<?php endif; ?>
</div>
</div>
<?php
}
/**
* 渲染设计管理页面
*/
public function render_designs_page() {
global $wpdb;
$table_name = $wpdb->prefix . 'client_designs';
$per_page = 20;
$current_page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
$offset = ($current_page - 1) * $per_page;
// 获取筛选条件
$status_filter = isset($_GET['status']) ? sanitize_text_field($_GET['status']) : '';
$search_term = isset($_GET['s']) ? sanitize_text_field($_GET['s']) : '';
// 构建查询
$where_clauses = array('1=1');
$query_params = array();
if ($status_filter) {
$where_clauses[] = "status = %s";
$query_params[] = $status_filter;
}
if ($search_term) {
$where_clauses[] = "(project_name LIKE %s OR id = %s)";
$query_params[] = '%' . $wpdb->esc_like($search_term) . '%';
$query_params[] = $search_term;
}
$where_sql = implode(' AND ', $where_clauses);
// 获取总记录数
$count_query = "SELECT COUNT(*) FROM $table_name WHERE $where_sql";
if ($query_params) {
$count_query = $wpdb->prepare($count_query, $query_params);
}
$total_items = $wpdb->get_var($count_query);
// 获取当前页数据
$data_query = "SELECT * FROM $table_name WHERE $where_sql ORDER BY created_at DESC LIMIT %d OFFSET %d";
$query_params[] = $per_page;
$query_params[] = $offset;
$data_query = $wpdb->prepare($data_query, $query_params);
$designs = $wpdb->get_results($data_query);
?>
<div class="wrap">
<h1 class="wp-heading-inline">设计管理</h1>
<form method="get" class="ccd-filter-form">
<input type="hidden" name="page" value="ccd-designs">
<div class="ccd-filters">
<select name="status" class="ccd-filter-select">
<option value="">所有状态</option>
<option value="draft" <?php selected($status_filter, 'draft'); ?>>草稿</option>
<option value="active" <?php selected($status_filter, 'active'); ?>>活跃</option>
<option value="completed" <?php selected($status_filter, 'completed'); ?>>完成</option>
</select>
<input type="text" name="s" placeholder="搜索设计名称或ID"
value="<?php echo esc_attr($search_term); ?>" class="ccd-search-input">
<button type="submit" class="button">筛选</button>
<?php if ($status_filter || $search_term): ?>
<a href="<?php echo admin_url('admin.php?page=ccd-designs'); ?>" class="button">
清除筛选
</a>
<?php endif; ?>
</div>
</form>
<div class="ccd-designs-list">
<?php if ($designs): ?>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th>ID</th>
<th>设计名称</th>
<th>用户</th>
<th>状态</th>
<th>创建时间</th>
<th>更新时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php foreach ($designs as $design):
$user_info = get_userdata($design->user_id);
$username = $user_info ? $user_info->display_name : '访客';
$design_data = json_decode($design->design_data, true);
?>
<tr>
<td><?php echo esc_html($design->id); ?></td>
<td>
<strong><?php echo esc_html($design->project_name); ?></strong>
<?php if (!empty($design_data['notes'])): ?>
<br><small><?php echo esc_html(substr($design_data['notes'], 0, 50)); ?>...</small>
<?php endif; ?>
</td>
<td><?php echo esc_html($username); ?></td>
<td>
<select class="ccd-status-select" data-design-id="<?php echo esc_attr($design->id); ?>">
<option value="draft" <?php selected($design->status, 'draft'); ?>>草稿</option>
<option value="active" <?php selected($design->status, 'active'); ?>>活跃</option>
<option value="completed" <?php selected($design->status, 'completed'); ?>>完成</option>
</select>
</td>
<td><?php echo esc_html(date('Y-m-d H:i', strtotime($design->created_at))); ?></td>
<td><?php echo esc_html(date('Y-m-d H:i', strtotime($design->updated_at))); ?></td>
<td>
<div class="ccd-action-buttons">
<button class="button button-small ccd-view-design"
data-design-id="<?php echo esc_attr($design->id); ?>">
查看详情
</button>
<button class="button button-small ccd-export-design"
data-design-id="<?php echo esc_attr($design->id); ?>">
导出
</button>
<button class="button button-small ccd-delete-design"
data-design-id="<?php echo esc_attr($design->id); ?>"
onclick="return confirm('确定要删除这个设计吗?')">
删除
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<!-- 分页 -->
<div class="ccd-pagination">
<?php
$total_pages = ceil($total_items / $per_page);
$base_url = admin_url('admin.php?page=ccd-designs');
if ($total_pages > 1) {
echo paginate_links(array(
'base' => add_query_arg('paged', '%#%', $base_url),
'format' => '',
'prev_text' => '«',
'next_text' => '»',
'total' => $total_pages,
'current' => $current_page,
'add_args' => array(
'status' => $status_filter,
's' => $search_term
)
));
}
?>
</div>
<?php else: ?>
<p>没有找到匹配的设计。</p>
<?php endif; ?>
</div>
</div>
<?php
}
/**
* 保存设计AJAX回调
*/
public function save_design_callback() {
// 验证nonce
/**
* 保存设计AJAX回调
*/
public function save_design_callback() {
// 验证nonce


