文章目录
-
- 随着个性化消费时代的到来,文创产品的定制化需求日益增长。无论是文化机构、博物馆、艺术工作室还是文创品牌,都希望通过在线平台为客户提供个性化的产品定制服务。WordPress作为全球最流行的内容管理系统,为文创产品的在线定制提供了理想的平台基础。 本文将详细介绍如何开发一个WordPress文创插件,实现柔性产品配置器和可视化定制功能。通过本教程,您将学会创建一个完整的文创产品定制系统,让用户能够在线设计并预览自己的定制产品。
-
- 首先,我们需要创建插件的基本文件结构: wp-content/plugins/cultural-creator/ ├── cultural-creator.php # 主插件文件 ├── includes/ │ ├── class-product-configurator.php │ ├── class-visual-editor.php │ ├── class-ajax-handler.php │ └── class-database.php ├── assets/ │ ├── css/ │ ├── js/ │ └── images/ ├── templates/ │ ├── configurator-frontend.php │ └── admin-settings.php └── languages/
- <?php /** * Plugin Name: 文创产品定制器 * Plugin URI: https://example.com/cultural-creator * Description: 为WordPress网站提供文创产品可视化定制功能 * Version: 1.0.0 * Author: 文创开发者 * License: GPL v2 or later * Text Domain: cultural-creator */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } // 定义插件常量 define('CC_VERSION', '1.0.0'); define('CC_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('CC_PLUGIN_URL', plugin_dir_url(__FILE__)); // 自动加载类文件 spl_autoload_register(function ($class) { $prefix = 'CulturalCreator_'; $base_dir = CC_PLUGIN_DIR . 'includes/'; $len = strlen($prefix); if (strncmp($prefix, $class, $len) !== 0) { return; } $relative_class = substr($class, $len); $file = $base_dir . 'class-' . str_replace('_', '-', strtolower($relative_class)) . '.php'; if (file_exists($file)) { require $file; } }); // 初始化插件 function cultural_creator_init() { // 检查WordPress版本 if (version_compare(get_bloginfo('version'), '5.0', '<')) { add_action('admin_notices', function() { echo '<div class="notice notice-error"><p>文创产品定制器需要WordPress 5.0或更高版本</p></div>'; }); return; } // 初始化核心类 $configurator = new CulturalCreator_ProductConfigurator(); $visual_editor = new CulturalCreator_VisualEditor(); $ajax_handler = new CulturalCreator_AjaxHandler(); // 注册激活和停用钩子 register_activation_hook(__FILE__, ['CulturalCreator_Database', 'install_tables']); register_deactivation_hook(__FILE__, ['CulturalCreator_Database', 'deactivate']); } add_action('plugins_loaded', 'cultural_creator_init');
-
- <?php // includes/class-database.php class CulturalCreator_Database { public static function install_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); $table_prefix = $wpdb->prefix . 'cc_'; // 产品表 $products_table = $table_prefix . 'products'; $sql_products = "CREATE TABLE IF NOT EXISTS $products_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, name varchar(255) NOT NULL, slug varchar(100) NOT NULL, base_price decimal(10,2) DEFAULT '0.00', base_image varchar(500), product_type varchar(50) DEFAULT 'mug', customizable_areas text, status tinyint(1) DEFAULT 1, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY slug (slug) ) $charset_collate;"; // 设计元素表 $elements_table = $table_prefix . 'design_elements'; $sql_elements = "CREATE TABLE IF NOT EXISTS $elements_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, product_id mediumint(9) NOT NULL, element_type varchar(50) NOT NULL, element_name varchar(255) NOT NULL, element_data text, position_x int DEFAULT 0, position_y int DEFAULT 0, width int DEFAULT 100, height int DEFAULT 100, z_index int DEFAULT 1, is_locked tinyint(1) DEFAULT 0, PRIMARY KEY (id), KEY product_id (product_id) ) $charset_collate;"; // 用户设计表 $designs_table = $table_prefix . 'user_designs'; $sql_designs = "CREATE TABLE IF NOT EXISTS $designs_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, user_id bigint(20) unsigned, session_id varchar(100), product_id mediumint(9) NOT NULL, design_data text NOT NULL, preview_image varchar(500), total_price decimal(10,2) DEFAULT '0.00', 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 session_id (session_id), KEY product_id (product_id) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql_products); dbDelta($sql_elements); dbDelta($sql_designs); } }
- <?php // includes/class-product-configurator.php class CulturalCreator_ProductConfigurator { private $product_id; private $product_data; public function __construct($product_id = null) { if ($product_id) { $this->product_id = $product_id; $this->load_product_data(); } } /** * 加载产品数据 */ private function load_product_data() { global $wpdb; $table_name = $wpdb->prefix . 'cc_products'; $this->product_data = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $table_name WHERE id = %d", $this->product_id) ); } /** * 获取可定制区域配置 * @return array 可定制区域配置数组 */ public function get_customizable_areas() { if (!$this->product_data) { return []; } $areas = json_decode($this->product_data->customizable_areas, true); // 默认区域配置 $default_areas = [ 'front' => [ 'name' => '正面', 'width' => 400, 'height' => 300, 'position' => ['x' => 50, 'y' => 50], 'allow_text' => true, 'allow_image' => true, 'allow_shape' => true ], 'back' => [ 'name' => '背面', 'width' => 400, 'height' => 300, 'position' => ['x' => 500, 'y' => 50], 'allow_text' => true, 'allow_image' => true, 'allow_shape' => false ] ]; return wp_parse_args($areas, $default_areas); } /** * 计算定制产品价格 * @param array $design_elements 设计元素数组 * @return float 总价格 */ public function calculate_price($design_elements) { $base_price = $this->product_data->base_price; $total_price = floatval($base_price); foreach ($design_elements as $element) { switch ($element['type']) { case 'text': // 文字按长度和字体复杂度计价 $text_price = strlen($element['content']) * 0.1; if (isset($element['font']) && $element['font'] !== 'default') { $text_price += 2.0; } $total_price += $text_price; break; case 'image': // 图片按尺寸和复杂度计价 $image_price = 5.0; if (isset($element['width']) && $element['width'] > 200) { $image_price += 3.0; } $total_price += $image_price; break; case 'shape': // 图形按复杂度计价 $shape_price = 3.0; if (isset($element['complexity']) && $element['complexity'] === 'high') { $shape_price += 4.0; } $total_price += $shape_price; break; } } return round($total_price, 2); } }
-
- // assets/js/visual-editor.js class CulturalCreatorVisualEditor { constructor(config) { this.config = config; this.canvas = null; this.ctx = null; this.elements = []; this.selectedElement = null; this.isDragging = false; this.dragOffset = { x: 0, y: 0 }; this.init(); } /** * 初始化编辑器 */ init() { this.createCanvas(); this.loadProductTemplate(); this.setupEventListeners(); this.setupToolbar(); this.render(); } /** * 创建画布 */ createCanvas() { const container = document.getElementById(this.config.containerId); // 创建画布元素 this.canvas = document.createElement('canvas'); this.canvas.width = this.config.width || 800; this.canvas.height = this.config.height || 600; this.canvas.style.border = '1px solid #ddd'; this.canvas.style.backgroundColor = '#f9f9f9'; container.appendChild(this.canvas); this.ctx = this.canvas.getContext('2d'); } /** * 加载产品模板 */ loadProductTemplate() { // 绘制产品基础轮廓 this.ctx.fillStyle = '#ffffff'; this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); // 绘制可定制区域 const areas = this.config.customizableAreas; Object.keys(areas).forEach(areaKey => { const area = areas[areaKey]; // 绘制区域边界 this.ctx.strokeStyle = '#4a90e2'; this.ctx.lineWidth = 2; this.ctx.setLineDash([5, 5]); this.ctx.strokeRect( area.position.x, area.position.y, area.width, area.height ); // 添加区域标签 this.ctx.fillStyle = '#4a90e2'; this.ctx.font = '14px Arial'; this.ctx.fillText( area.name, area.position.x + 5, area.position.y - 5 ); this.ctx.setLineDash([]); }); } /** * 添加文本元素 * @param {Object} options 文本选项 */ addTextElement(options) { const defaultOptions = { id: 'text_' + Date.now(), content: '新文本', x: 100, y: 100, fontSize: 24, fontFamily: 'Arial', color: '#000000', area: 'front' }; const textElement = { ...defaultOptions, ...options, type: 'text' }; this.elements.push(textElement); this.render(); return textElement.id; } /** * 添加图片元素 * @param {Object} options 图片选项 */ addImageElement(options) { const img = new Image(); img.crossOrigin = 'anonymous'; img.onload = () => { const imageElement = { id: 'image_' + Date.now(), image: img, x: options.x || 150, y: options.y || 150, width: options.width || 100, height: options.height || 100, src: options.src, area: options.area || 'front', type: 'image' }; this.elements.push(imageElement); this.render(); }; img.src = options.src; } /** * 渲染所有元素 */ render() { // 清除画布 this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); // 重新绘制产品模板 this.loadProductTemplate(); // 绘制所有元素 this.elements.forEach(element => { this.drawElement(element); }); // 绘制选中元素的控制点 if (this.selectedElement) { this.drawSelectionBox(this.selectedElement); } } /** * 绘制单个元素 * @param {Object} element 元素对象 */ drawElement(element) { this.ctx.save(); switch (element.type) { case 'text': this.ctx.font = `${element.fontSize}px ${element.fontFamily}`; this.ctx.fillStyle = element.color; this.ctx.fillText(element.content, element.x, element.y); break; case 'image': if (element.image && element.image.complete) { this.ctx.drawImage( element.image, element.x, element.y, element.width, element.height ); } break; } this.ctx.restore(); } /** * 设置事件监听器 */ setupEventListeners() { this.canvas.addEventListener('mousedown', this.handleMouseDown.bind(this)); this.canvas.addEventListener('mousemove', this.handleMouseMove.bind(this)); this.canvas.addEventListener('mouseup', this.handleMouseUp.bind(this)); this.canvas.addEventListener('click', this.handleClick.bind(this)); } /** * 处理鼠标按下事件 * @param {Event} e 鼠标事件 */ handleMouseDown(e) { const rect = this.canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; // 检查是否点击了元素 for (let i = this.elements.length - 1; i >= 0; i--) { const element = this.elements[i]; if (this.isPointInElement(x, y, element)) { this.selectedElement = element; this.isDragging = true; // 计算拖拽偏移量 this.dragOffset.x = x - element.x; this.dragOffset.y = y - element.y; this.render(); break; } } } /** * 检查点是否在元素内 * @param {number} x X坐标 * @param {number} y Y坐标 * @param {Object} element 元素对象 * @returns {boolean} 是否在元素内 */ isPointInElement(x, y, element) { switch (element.type) { case 'text': this.ctx.font = `${element.fontSize}px ${element.fontFamily}`; const width = this.ctx.measureText(element.content).width; return x >= element.x && x <= element.x + width && y >= element.y - element.fontSize && y <= element.y; case 'image': return x >= element.x && x <= element.x + element.width && y >= element.y && y <= element.y + element.height; } return false; } /** * 获取设计数据 * @returns {Object} 设计数据 */ getDesignData() { return { elements: this.elements.map(el => { const elementCopy = { ...el }; // 移除图像对象,只保留URL if (elementCopy.type === 'image' && elementCopy.image) { elementCopy.src = elementCopy.src; delete elementCopy.image; } return elementCopy; }), canvasWidth: this.canvas.width, canvasHeight: this.canvas.height, productId: this.config.productId }; } }
- <?php // 在class-product-configurator.php中添加短代码方法 /** * 注册产品配置器短代码 */ public function register_shortcode() { add_shortcode('cultural_configurator', [$this, 'render_configurator']); } /** * 渲染产品配置器 * @param array $atts 短代码属性 * @return string HTML内容 */ public function render_configurator($atts) { $atts = shortcode_atts([ 'product_id' => 0, 'width' => '800', 'height' => '600' ], $atts); if (!$atts['product_id']) { return '<p>请指定产品ID</p>'; } // 加载产品数据 $this->product_id = intval($atts['product_id']); $this->load_product_data(); if (!$this->product_data) { return '<p>产品不存在</p>'; } // 获取可定制区域 $customizable_areas = $this->get_customizable_areas(); // 生成唯一ID $editor_id = 'cc-editor-' . uniqid(); // 输出HTML结构 ob_start(); ?> <div class="cultural-creator-container"> <div class="cc-editor-wrapper"> <div id="<?php echo esc_attr($editor_id); ?>" class="cc-visual-editor"></div> <div class="cc-toolbar"> <div class="cc-tool-group"> <h4>添加元素</h4> <button class="cc-btn cc-add-text" data-type="text"> <span class="dashicons dashicons-text"></span> 添加文字 </button> <button class="cc-btn cc-add-image" data-type="image"> <span class="dashicons dashicons-format-image"></span> 添加图片 </button> <button class="cc-btn cc-add-shape" data-type="shape"> <span class="dashicons dashicons-shapes"></span> 添加图形 </button> </div> <div class="cc-tool-group cc-properties-panel" style="display:none;"> <h4>属性设置</h4> <div class="cc-properties-content"></div> </div> <div class="cc-tool-group"> <h4>操作</h4> <button class="cc-btn cc-save-design"> <span class="dashicons dashicons-saved"></span> 保存设计 </button> <button class="cc-btn cc-reset-design"> <span class="dashicons dashicons-update"></span> 重置 </button> <button class="cc-btn cc-preview-3d"> <span class="dashicons dashicons-visibility"></span> 3D预览 </button> </div> </div> </div> <div class="cc-sidebar"> <div class="cc-price-summary"> <h3>价格明细</h3> <div class="cc-price-breakdown"> <div class="cc-price-item"> <span>基础价格</span> <span class="cc-price-amount">¥<?php echo number_format($this->product_data->base_price, 2); ?></span> </div> <div class="cc-price-item cc-customization-cost"> <span>定制费用</span> <span class="cc-price-amount">¥0.00</span> </div> <div class="cc-price-total"> <span>总计</span> <span class="cc-total-amount">¥<?php echo number_format($this->product_data->base_price, 2); ?></span> </div> </div> </div> <div class="cc-design-options"> <h3>设计选项</h3> <div class="cc-option-group"> <label>选择区域:</label> <select class="cc-area-selector"> <?php foreach ($customizable_areas as $area_key => $area): ?> <option value="<?php echo esc_attr($area_key); ?>"> <?php echo esc_html($area['name']); ?> </option> <?php endforeach; ?> </select> </div> <div class="cc-option-group"> <label>上传图片:</label> <input type="file" class="cc-image-upload" accept="image/*" style="display:none;"> <button class="cc-btn cc-upload-btn">选择图片</button> <div class="cc-upload-preview"></div> </div> <div class="cc-text-options" style="display:none;"> <h4>文字设置</h4> <input type="text" class="cc-text-input" placeholder="输入文字内容"> <select class="cc-font-selector"> <option value="Arial">Arial</option> <option value="SimSun">宋体</option> <option value="Microsoft YaHei">微软雅黑</option> <option value="KaiTi">楷体</option> </select> <input type="color" class="cc-text-color" value="#000000"> <input type="range" class="cc-font-size" min="12" max="72" value="24"> </div> </div> </div> </div> <script type="text/javascript"> jQuery(document).ready(function($) { // 初始化可视化编辑器 const editorConfig = { containerId: '<?php echo esc_js($editor_id); ?>', productId: <?php echo intval($atts['product_id']); ?>, width: <?php echo intval($atts['width']); ?>, height: <?php echo intval($atts['height']); ?>, customizableAreas: <?php echo wp_json_encode($customizable_areas); ?> }; window.ccEditor = new CulturalCreatorVisualEditor(editorConfig); // 绑定工具栏事件 $('.cc-add-text').on('click', function() { const area = $('.cc-area-selector').val(); const textId = ccEditor.addTextElement({ content: '双击编辑文字', area: area, x: 100, y: 100 }); // 显示文字设置面板 $('.cc-text-options').show(); $('.cc-properties-panel').show(); $('.cc-properties-content').html(` <div class="cc-property-item"> <label>文字内容:</label> <input type="text" class="cc-edit-text" value="双击编辑文字"> </div> <div class="cc-property-item"> <label>字体大小:</label> <input type="range" class="cc-edit-fontsize" min="12" max="72" value="24"> </div> <div class="cc-property-item"> <label>字体颜色:</label> <input type="color" class="cc-edit-color" value="#000000"> </div> `); }); // 保存设计 $('.cc-save-design').on('click', function() { const designData = ccEditor.getDesignData(); $.ajax({ url: '<?php echo admin_url('admin-ajax.php'); ?>', type: 'POST', data: { action: 'cc_save_design', nonce: '<?php echo wp_create_nonce('cc_save_design_nonce'); ?>', design_data: designData }, success: function(response) { if (response.success) { alert('设计保存成功!'); // 更新价格 $('.cc-customization-cost .cc-price-amount').text('¥' + response.data.customization_cost); $('.cc-total-amount').text('¥' + response.data.total_price); } else { alert('保存失败:' + response.data); } } }); }); }); </script> <?php return ob_get_clean(); } ## AJAX处理与数据保存 ### 1. AJAX处理器类 <?php// includes/class-ajax-handler.php class CulturalCreator_AjaxHandler { public function __construct() { $this->init_hooks(); } /** * 初始化AJAX钩子 */ private function init_hooks() { // 保存设计 add_action('wp_ajax_cc_save_design', [$this, 'save_design']); add_action('wp_ajax_nopriv_cc_save_design', [$this, 'save_design']); // 加载设计 add_action('wp_ajax_cc_load_design', [$this, 'load_design']); add_action('wp_ajax_nopriv_cc_load_design', [$this, 'load_design']); // 上传图片 add_action('wp_ajax_cc_upload_image', [$this, 'upload_image']); add_action('wp_ajax_nopriv_cc_upload_image', [$this, 'upload_image']); // 获取产品数据 add_action('wp_ajax_cc_get_product', [$this, 'get_product_data']); add_action('wp_ajax_nopriv_cc_get_product', [$this, 'get_product_data']); } /** * 保存用户设计 */ public function save_design() { // 验证nonce if (!wp_verify_nonce($_POST['nonce'], 'cc_save_design_nonce')) { wp_die('安全验证失败'); } $design_data = json_decode(stripslashes($_POST['design_data']), true); if (!$design_data || !isset($design_data['productId'])) { wp_send_json_error('无效的设计数据'); } global $wpdb; $table_name = $wpdb->prefix . 'cc_user_designs'; // 获取用户ID或会话ID $user_id = is_user_logged_in() ? get_current_user_id() : null; $session_id = isset($_COOKIE['cc_session']) ? $_COOKIE['cc_session'] : $this->generate_session_id(); // 计算价格 $configurator = new CulturalCreator_ProductConfigurator($design_data['productId']); $customization_cost = $configurator->calculate_price($design_data['elements']); $base_price = floatval($configurator->product_data->base_price); $total_price = $base_price + $customization_cost; // 生成预览图 $preview_image = $this->generate_preview_image($design_data); // 保存到数据库 $result = $wpdb->insert( $table_name, [ 'user_id' => $user_id, 'session_id' => $session_id, 'product_id' => $design_data['productId'], 'design_data' => json_encode($design_data, JSON_UNESCAPED_UNICODE), 'preview_image' => $preview_image, 'total_price' => $total_price, 'status' => 'draft' ], ['%d', '%s', '%d', '%s', '%s', '%f', '%s'] ); if ($result) { // 设置会话cookie if (!isset($_COOKIE['cc_session'])) { setcookie('cc_session', $session_id, time() + 30 * DAY_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN); } wp_send_json_success([ 'design_id' => $wpdb->insert_id, 'customization_cost' => number_format($customization_cost, 2), 'total_price' => number_format($total_price, 2), 'preview_url' => $preview_image ]); } else { wp_send_json_error('保存失败'); } } /** * 生成预览图片 * @param array $design_data 设计数据 * @return string 图片URL */ private function generate_preview_image($design_data) { // 这里简化处理,实际应该使用GD库或ImageMagick生成图片 $upload_dir = wp_upload_dir(); $filename = 'cc-preview-' . uniqid() . '.png'; $filepath = $upload_dir['path'] . '/' . $filename; $fileurl = $upload_dir['url'] . '/' . $filename; // 在实际应用中,这里应该使用canvas生成图片 // 为了简化,我们创建一个占位图片 $image = imagecreatetruecolor(400, 300); $bg_color = imagecolorallocate($image, 240, 240, 240); $text_color = imagecolorallocate($image, 100, 100, 100); imagefill($image, 0, 0, $bg_color); imagestring($image, 5, 150, 140, '设计预览', $text_color); imagepng($image, $filepath); imagedestroy($image); return $fileurl; } /** * 生成会话ID * @return string 会话ID */ private function generate_session_id() { return md5(uniqid() . $_SERVER['REMOTE_ADDR'] . time()); } /** * 上传图片 */ public function upload_image() { if (!wp_verify_nonce($_POST['nonce'], 'cc_upload_image_nonce')) { wp_die('安全验证失败'); } if (!function_exists('wp_handle_upload')) { require_once(ABSPATH . 'wp-admin/includes/file.php'); } $uploadedfile = $_FILES['image']; $upload_overrides = ['test_form' => false]; $movefile = wp_handle_upload($uploadedfile, $upload_overrides); if ($movefile && !isset($movefile['error'])) { // 创建WordPress附件 $attachment = [ 'post_mime_type' => $movefile['type'], 'post_title' => preg_replace('/.[^.]+$/', '', basename($movefile['file'])), 'post_content' => '', 'post_status' => 'inherit' ]; $attach_id = wp_insert_attachment($attachment, $movefile['file']); // 生成缩略图 require_once(ABSPATH . 'wp-admin/includes/image.php'); $attach_data = wp_generate_attachment_metadata($attach_id, $movefile['file']); wp_update_attachment_metadata($attach_id, $attach_data); wp_send_json_success([ 'url' => $movefile['url'], 'id' => $attach_id ]); } else { wp_send_json_error($movefile['error']); } } } ## 管理后台与产品管理 ### 1. 管理界面 <?php// 在插件主文件中添加管理菜单 add_action('admin_menu', 'cultural_creator_admin_menu'); function cultural_creator_admin_menu() { add_menu_page( '文创产品定制器', '文创定制', 'manage_options', 'cultural-creator', 'cultural_creator_admin_page', 'dashicons-art', 30 ); add_submenu_page( 'cultural-creator', '产品管理', '产品管理', 'manage_options', 'cultural-creator-products', 'cultural_creator_products_page' ); add_submenu_page( 'cultural-creator', '设计管理', '设计管理', 'manage_options', 'cultural-creator-designs', 'cultural_creator_designs_page' ); add_submenu_page( 'cultural-creator', '设置', '设置', 'manage_options', 'cultural-creator-settings', 'cultural_creator_settings_page' ); } function cultural_creator_admin_page() { ?> <div class="wrap"> <h1>文创产品定制器</h1> <div class="cc-admin-dashboard"> <div class="cc-stats-container"> <div class="cc-stat-card"> <h3>总产品数</h3> <p class="cc-stat-number"><?php echo cultural_creator_get_product_count(); ?></p> </div> <div class="cc-stat-card"> <h3>总设计数</h3> <p class="cc-stat-number"><?php echo cultural_creator_get_design_count(); ?></p> </div> <div class="cc-stat-card"> <h3>今日设计</h3> <p class="cc-stat-number"><?php echo cultural_creator_get_today_designs(); ?></p> </div> </div> <div class="cc-quick-actions"> <h2>快速操作</h2> <a href="<?php echo admin_url('admin.php?page=cultural-creator-products&action=add'); ?>" class="button button-primary"> 添加新产品 </a> <a href="<?php echo admin_url('admin.php?page=cultural-creator-designs'); ?>" class="button"> 查看所有设计 </a> </div> </div> </div> <?php } function cultural_creator_products_page() { $action = isset($_GET['action']) ? $_GET['action'] : 'list'; switch ($action) { case 'add': case 'edit': include CC_PLUGIN_DIR . 'templates/admin-product-edit.php'; break; case 'list': default: include CC_PLUGIN_DIR . 'templates/admin-product-list.php'; break; } } ## 样式优化与响应式设计 / assets/css/frontend.css / .cultural-creator-container { display: flex; flex-wrap: wrap; gap: 20px; max-width: 1200px; margin: 0 auto; padding: 20px; } .cc-editor-wrapper { flex: 1; min-width: 300px; } .cc-visual-editor { border: 1px solid #ddd; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .cc-toolbar { background: #f5f5f5; padding: 15px; border-radius: 8px; margin-top: 15px; } .cc-tool-group { margin-bottom: 20px; padding-bottom: 15px; border-bottom: 1px solid #ddd; } .cc-tool-group:last-child { border-bottom: none; margin
随着个性化消费时代的到来,文创产品的定制化需求日益增长。无论是文化机构、博物馆、艺术工作室还是文创品牌,都希望通过在线平台为客户提供个性化的产品定制服务。WordPress作为全球最流行的内容管理系统,为文创产品的在线定制提供了理想的平台基础。
本文将详细介绍如何开发一个WordPress文创插件,实现柔性产品配置器和可视化定制功能。通过本教程,您将学会创建一个完整的文创产品定制系统,让用户能够在线设计并预览自己的定制产品。
首先,我们需要创建插件的基本文件结构:
wp-content/plugins/cultural-creator/
├── cultural-creator.php # 主插件文件
├── includes/
│ ├── class-product-configurator.php
│ ├── class-visual-editor.php
│ ├── class-ajax-handler.php
│ └── class-database.php
├── assets/
│ ├── css/
│ ├── js/
│ └── images/
├── templates/
│ ├── configurator-frontend.php
│ └── admin-settings.php
└── languages/
<?php
/**
* Plugin Name: 文创产品定制器
* Plugin URI: https://example.com/cultural-creator
* Description: 为WordPress网站提供文创产品可视化定制功能
* Version: 1.0.0
* Author: 文创开发者
* License: GPL v2 or later
* Text Domain: cultural-creator
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('CC_VERSION', '1.0.0');
define('CC_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('CC_PLUGIN_URL', plugin_dir_url(__FILE__));
// 自动加载类文件
spl_autoload_register(function ($class) {
$prefix = 'CulturalCreator_';
$base_dir = CC_PLUGIN_DIR . 'includes/';
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
return;
}
$relative_class = substr($class, $len);
$file = $base_dir . 'class-' . str_replace('_', '-', strtolower($relative_class)) . '.php';
if (file_exists($file)) {
require $file;
}
});
// 初始化插件
function cultural_creator_init() {
// 检查WordPress版本
if (version_compare(get_bloginfo('version'), '5.0', '<')) {
add_action('admin_notices', function() {
echo '<div class="notice notice-error"><p>文创产品定制器需要WordPress 5.0或更高版本</p></div>';
});
return;
}
// 初始化核心类
$configurator = new CulturalCreator_ProductConfigurator();
$visual_editor = new CulturalCreator_VisualEditor();
$ajax_handler = new CulturalCreator_AjaxHandler();
// 注册激活和停用钩子
register_activation_hook(__FILE__, ['CulturalCreator_Database', 'install_tables']);
register_deactivation_hook(__FILE__, ['CulturalCreator_Database', 'deactivate']);
}
add_action('plugins_loaded', 'cultural_creator_init');
<?php
/**
* Plugin Name: 文创产品定制器
* Plugin URI: https://example.com/cultural-creator
* Description: 为WordPress网站提供文创产品可视化定制功能
* Version: 1.0.0
* Author: 文创开发者
* License: GPL v2 or later
* Text Domain: cultural-creator
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('CC_VERSION', '1.0.0');
define('CC_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('CC_PLUGIN_URL', plugin_dir_url(__FILE__));
// 自动加载类文件
spl_autoload_register(function ($class) {
$prefix = 'CulturalCreator_';
$base_dir = CC_PLUGIN_DIR . 'includes/';
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
return;
}
$relative_class = substr($class, $len);
$file = $base_dir . 'class-' . str_replace('_', '-', strtolower($relative_class)) . '.php';
if (file_exists($file)) {
require $file;
}
});
// 初始化插件
function cultural_creator_init() {
// 检查WordPress版本
if (version_compare(get_bloginfo('version'), '5.0', '<')) {
add_action('admin_notices', function() {
echo '<div class="notice notice-error"><p>文创产品定制器需要WordPress 5.0或更高版本</p></div>';
});
return;
}
// 初始化核心类
$configurator = new CulturalCreator_ProductConfigurator();
$visual_editor = new CulturalCreator_VisualEditor();
$ajax_handler = new CulturalCreator_AjaxHandler();
// 注册激活和停用钩子
register_activation_hook(__FILE__, ['CulturalCreator_Database', 'install_tables']);
register_deactivation_hook(__FILE__, ['CulturalCreator_Database', 'deactivate']);
}
add_action('plugins_loaded', 'cultural_creator_init');
<?php
// includes/class-database.php
class CulturalCreator_Database {
public static function install_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_prefix = $wpdb->prefix . 'cc_';
// 产品表
$products_table = $table_prefix . 'products';
$sql_products = "CREATE TABLE IF NOT EXISTS $products_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
slug varchar(100) NOT NULL,
base_price decimal(10,2) DEFAULT '0.00',
base_image varchar(500),
product_type varchar(50) DEFAULT 'mug',
customizable_areas text,
status tinyint(1) DEFAULT 1,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY slug (slug)
) $charset_collate;";
// 设计元素表
$elements_table = $table_prefix . 'design_elements';
$sql_elements = "CREATE TABLE IF NOT EXISTS $elements_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
product_id mediumint(9) NOT NULL,
element_type varchar(50) NOT NULL,
element_name varchar(255) NOT NULL,
element_data text,
position_x int DEFAULT 0,
position_y int DEFAULT 0,
width int DEFAULT 100,
height int DEFAULT 100,
z_index int DEFAULT 1,
is_locked tinyint(1) DEFAULT 0,
PRIMARY KEY (id),
KEY product_id (product_id)
) $charset_collate;";
// 用户设计表
$designs_table = $table_prefix . 'user_designs';
$sql_designs = "CREATE TABLE IF NOT EXISTS $designs_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
user_id bigint(20) unsigned,
session_id varchar(100),
product_id mediumint(9) NOT NULL,
design_data text NOT NULL,
preview_image varchar(500),
total_price decimal(10,2) DEFAULT '0.00',
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 session_id (session_id),
KEY product_id (product_id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql_products);
dbDelta($sql_elements);
dbDelta($sql_designs);
}
}
<?php
// includes/class-database.php
class CulturalCreator_Database {
public static function install_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_prefix = $wpdb->prefix . 'cc_';
// 产品表
$products_table = $table_prefix . 'products';
$sql_products = "CREATE TABLE IF NOT EXISTS $products_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
slug varchar(100) NOT NULL,
base_price decimal(10,2) DEFAULT '0.00',
base_image varchar(500),
product_type varchar(50) DEFAULT 'mug',
customizable_areas text,
status tinyint(1) DEFAULT 1,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY slug (slug)
) $charset_collate;";
// 设计元素表
$elements_table = $table_prefix . 'design_elements';
$sql_elements = "CREATE TABLE IF NOT EXISTS $elements_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
product_id mediumint(9) NOT NULL,
element_type varchar(50) NOT NULL,
element_name varchar(255) NOT NULL,
element_data text,
position_x int DEFAULT 0,
position_y int DEFAULT 0,
width int DEFAULT 100,
height int DEFAULT 100,
z_index int DEFAULT 1,
is_locked tinyint(1) DEFAULT 0,
PRIMARY KEY (id),
KEY product_id (product_id)
) $charset_collate;";
// 用户设计表
$designs_table = $table_prefix . 'user_designs';
$sql_designs = "CREATE TABLE IF NOT EXISTS $designs_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
user_id bigint(20) unsigned,
session_id varchar(100),
product_id mediumint(9) NOT NULL,
design_data text NOT NULL,
preview_image varchar(500),
total_price decimal(10,2) DEFAULT '0.00',
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 session_id (session_id),
KEY product_id (product_id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql_products);
dbDelta($sql_elements);
dbDelta($sql_designs);
}
}
<?php
// includes/class-product-configurator.php
class CulturalCreator_ProductConfigurator {
private $product_id;
private $product_data;
public function __construct($product_id = null) {
if ($product_id) {
$this->product_id = $product_id;
$this->load_product_data();
}
}
/**
* 加载产品数据
*/
private function load_product_data() {
global $wpdb;
$table_name = $wpdb->prefix . 'cc_products';
$this->product_data = $wpdb->get_row(
$wpdb->prepare("SELECT * FROM $table_name WHERE id = %d", $this->product_id)
);
}
/**
* 获取可定制区域配置
* @return array 可定制区域配置数组
*/
public function get_customizable_areas() {
if (!$this->product_data) {
return [];
}
$areas = json_decode($this->product_data->customizable_areas, true);
// 默认区域配置
$default_areas = [
'front' => [
'name' => '正面',
'width' => 400,
'height' => 300,
'position' => ['x' => 50, 'y' => 50],
'allow_text' => true,
'allow_image' => true,
'allow_shape' => true
],
'back' => [
'name' => '背面',
'width' => 400,
'height' => 300,
'position' => ['x' => 500, 'y' => 50],
'allow_text' => true,
'allow_image' => true,
'allow_shape' => false
]
];
return wp_parse_args($areas, $default_areas);
}
/**
* 计算定制产品价格
* @param array $design_elements 设计元素数组
* @return float 总价格
*/
public function calculate_price($design_elements) {
$base_price = $this->product_data->base_price;
$total_price = floatval($base_price);
foreach ($design_elements as $element) {
switch ($element['type']) {
case 'text':
// 文字按长度和字体复杂度计价
$text_price = strlen($element['content']) * 0.1;
if (isset($element['font']) && $element['font'] !== 'default') {
$text_price += 2.0;
}
$total_price += $text_price;
break;
case 'image':
// 图片按尺寸和复杂度计价
$image_price = 5.0;
if (isset($element['width']) && $element['width'] > 200) {
$image_price += 3.0;
}
$total_price += $image_price;
break;
case 'shape':
// 图形按复杂度计价
$shape_price = 3.0;
if (isset($element['complexity']) && $element['complexity'] === 'high') {
$shape_price += 4.0;
}
$total_price += $shape_price;
break;
}
}
return round($total_price, 2);
}
}
<?php
// includes/class-product-configurator.php
class CulturalCreator_ProductConfigurator {
private $product_id;
private $product_data;
public function __construct($product_id = null) {
if ($product_id) {
$this->product_id = $product_id;
$this->load_product_data();
}
}
/**
* 加载产品数据
*/
private function load_product_data() {
global $wpdb;
$table_name = $wpdb->prefix . 'cc_products';
$this->product_data = $wpdb->get_row(
$wpdb->prepare("SELECT * FROM $table_name WHERE id = %d", $this->product_id)
);
}
/**
* 获取可定制区域配置
* @return array 可定制区域配置数组
*/
public function get_customizable_areas() {
if (!$this->product_data) {
return [];
}
$areas = json_decode($this->product_data->customizable_areas, true);
// 默认区域配置
$default_areas = [
'front' => [
'name' => '正面',
'width' => 400,
'height' => 300,
'position' => ['x' => 50, 'y' => 50],
'allow_text' => true,
'allow_image' => true,
'allow_shape' => true
],
'back' => [
'name' => '背面',
'width' => 400,
'height' => 300,
'position' => ['x' => 500, 'y' => 50],
'allow_text' => true,
'allow_image' => true,
'allow_shape' => false
]
];
return wp_parse_args($areas, $default_areas);
}
/**
* 计算定制产品价格
* @param array $design_elements 设计元素数组
* @return float 总价格
*/
public function calculate_price($design_elements) {
$base_price = $this->product_data->base_price;
$total_price = floatval($base_price);
foreach ($design_elements as $element) {
switch ($element['type']) {
case 'text':
// 文字按长度和字体复杂度计价
$text_price = strlen($element['content']) * 0.1;
if (isset($element['font']) && $element['font'] !== 'default') {
$text_price += 2.0;
}
$total_price += $text_price;
break;
case 'image':
// 图片按尺寸和复杂度计价
$image_price = 5.0;
if (isset($element['width']) && $element['width'] > 200) {
$image_price += 3.0;
}
$total_price += $image_price;
break;
case 'shape':
// 图形按复杂度计价
$shape_price = 3.0;
if (isset($element['complexity']) && $element['complexity'] === 'high') {
$shape_price += 4.0;
}
$total_price += $shape_price;
break;
}
}
return round($total_price, 2);
}
}
// assets/js/visual-editor.js
class CulturalCreatorVisualEditor {
constructor(config) {
this.config = config;
this.canvas = null;
this.ctx = null;
this.elements = [];
this.selectedElement = null;
this.isDragging = false;
this.dragOffset = { x: 0, y: 0 };
this.init();
}
/**
* 初始化编辑器
*/
init() {
this.createCanvas();
this.loadProductTemplate();
this.setupEventListeners();
this.setupToolbar();
this.render();
}
/**
* 创建画布
*/
createCanvas() {
const container = document.getElementById(this.config.containerId);
// 创建画布元素
this.canvas = document.createElement('canvas');
this.canvas.width = this.config.width || 800;
this.canvas.height = this.config.height || 600;
this.canvas.style.border = '1px solid #ddd';
this.canvas.style.backgroundColor = '#f9f9f9';
container.appendChild(this.canvas);
this.ctx = this.canvas.getContext('2d');
}
/**
* 加载产品模板
*/
loadProductTemplate() {
// 绘制产品基础轮廓
this.ctx.fillStyle = '#ffffff';
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
// 绘制可定制区域
const areas = this.config.customizableAreas;
Object.keys(areas).forEach(areaKey => {
const area = areas[areaKey];
// 绘制区域边界
this.ctx.strokeStyle = '#4a90e2';
this.ctx.lineWidth = 2;
this.ctx.setLineDash([5, 5]);
this.ctx.strokeRect(
area.position.x,
area.position.y,
area.width,
area.height
);
// 添加区域标签
this.ctx.fillStyle = '#4a90e2';
this.ctx.font = '14px Arial';
this.ctx.fillText(
area.name,
area.position.x + 5,
area.position.y - 5
);
this.ctx.setLineDash([]);
});
}
/**
* 添加文本元素
* @param {Object} options 文本选项
*/
addTextElement(options) {
const defaultOptions = {
id: 'text_' + Date.now(),
content: '新文本',
x: 100,
y: 100,
fontSize: 24,
fontFamily: 'Arial',
color: '#000000',
area: 'front'
};
const textElement = { ...defaultOptions, ...options, type: 'text' };
this.elements.push(textElement);
this.render();
return textElement.id;
}
/**
* 添加图片元素
* @param {Object} options 图片选项
*/
addImageElement(options) {
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => {
const imageElement = {
id: 'image_' + Date.now(),
image: img,
x: options.x || 150,
y: options.y || 150,
width: options.width || 100,
height: options.height || 100,
src: options.src,
area: options.area || 'front',
type: 'image'
};
this.elements.push(imageElement);
this.render();
};
img.src = options.src;
}
/**
* 渲染所有元素
*/
render() {
// 清除画布
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// 重新绘制产品模板
this.loadProductTemplate();
// 绘制所有元素
this.elements.forEach(element => {
this.drawElement(element);
});
// 绘制选中元素的控制点
if (this.selectedElement) {
this.drawSelectionBox(this.selectedElement);
}
}
/**
* 绘制单个元素
* @param {Object} element 元素对象
*/
drawElement(element) {
this.ctx.save();
switch (element.type) {
case 'text':
this.ctx.font = `${element.fontSize}px ${element.fontFamily}`;
this.ctx.fillStyle = element.color;
this.ctx.fillText(element.content, element.x, element.y);
break;
case 'image':
if (element.image && element.image.complete) {
this.ctx.drawImage(
element.image,
element.x,
element.y,
element.width,
element.height
);
}
break;
}
this.ctx.restore();
}
/**
* 设置事件监听器
*/
setupEventListeners() {
this.canvas.addEventListener('mousedown', this.handleMouseDown.bind(this));
this.canvas.addEventListener('mousemove', this.handleMouseMove.bind(this));
this.canvas.addEventListener('mouseup', this.handleMouseUp.bind(this));
this.canvas.addEventListener('click', this.handleClick.bind(this));
}
/**
* 处理鼠标按下事件
* @param {Event} e 鼠标事件
*/
handleMouseDown(e) {
const rect = this.canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// 检查是否点击了元素
for (let i = this.elements.length - 1; i >= 0; i--) {
const element = this.elements[i];
if (this.isPointInElement(x, y, element)) {
this.selectedElement = element;
this.isDragging = true;
// 计算拖拽偏移量
this.dragOffset.x = x - element.x;
this.dragOffset.y = y - element.y;
this.render();
break;
}
}
}
/**
* 检查点是否在元素内
* @param {number} x X坐标
* @param {number} y Y坐标
* @param {Object} element 元素对象
* @returns {boolean} 是否在元素内
*/
isPointInElement(x, y, element) {
switch (element.type) {
case 'text':
this.ctx.font = `${element.fontSize}px ${element.fontFamily}`;
const width = this.ctx.measureText(element.content).width;
return x >= element.x &&
x <= element.x + width &&
y >= element.y - element.fontSize &&
y <= element.y;
case 'image':
return x >= element.x &&
x <= element.x + element.width &&
y >= element.y &&
y <= element.y + element.height;
}
return false;
}
/**
* 获取设计数据
* @returns {Object} 设计数据
*/
getDesignData() {
return {
elements: this.elements.map(el => {
const elementCopy = { ...el };
// 移除图像对象,只保留URL
if (elementCopy.type === 'image' && elementCopy.image) {
elementCopy.src = elementCopy.src;
delete elementCopy.image;
}
return elementCopy;
}),
canvasWidth: this.canvas.width,
canvasHeight: this.canvas.height,
productId: this.config.productId
};
}
}
// assets/js/visual-editor.js
class CulturalCreatorVisualEditor {
constructor(config) {
this.config = config;
this.canvas = null;
this.ctx = null;
this.elements = [];
this.selectedElement = null;
this.isDragging = false;
this.dragOffset = { x: 0, y: 0 };
this.init();
}
/**
* 初始化编辑器
*/
init() {
this.createCanvas();
this.loadProductTemplate();
this.setupEventListeners();
this.setupToolbar();
this.render();
}
/**
* 创建画布
*/
createCanvas() {
const container = document.getElementById(this.config.containerId);
// 创建画布元素
this.canvas = document.createElement('canvas');
this.canvas.width = this.config.width || 800;
this.canvas.height = this.config.height || 600;
this.canvas.style.border = '1px solid #ddd';
this.canvas.style.backgroundColor = '#f9f9f9';
container.appendChild(this.canvas);
this.ctx = this.canvas.getContext('2d');
}
/**
* 加载产品模板
*/
loadProductTemplate() {
// 绘制产品基础轮廓
this.ctx.fillStyle = '#ffffff';
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
// 绘制可定制区域
const areas = this.config.customizableAreas;
Object.keys(areas).forEach(areaKey => {
const area = areas[areaKey];
// 绘制区域边界
this.ctx.strokeStyle = '#4a90e2';
this.ctx.lineWidth = 2;
this.ctx.setLineDash([5, 5]);
this.ctx.strokeRect(
area.position.x,
area.position.y,
area.width,
area.height
);
// 添加区域标签
this.ctx.fillStyle = '#4a90e2';
this.ctx.font = '14px Arial';
this.ctx.fillText(
area.name,
area.position.x + 5,
area.position.y - 5
);
this.ctx.setLineDash([]);
});
}
/**
* 添加文本元素
* @param {Object} options 文本选项
*/
addTextElement(options) {
const defaultOptions = {
id: 'text_' + Date.now(),
content: '新文本',
x: 100,
y: 100,
fontSize: 24,
fontFamily: 'Arial',
color: '#000000',
area: 'front'
};
const textElement = { ...defaultOptions, ...options, type: 'text' };
this.elements.push(textElement);
this.render();
return textElement.id;
}
/**
* 添加图片元素
* @param {Object} options 图片选项
*/
addImageElement(options) {
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => {
const imageElement = {
id: 'image_' + Date.now(),
image: img,
x: options.x || 150,
y: options.y || 150,
width: options.width || 100,
height: options.height || 100,
src: options.src,
area: options.area || 'front',
type: 'image'
};
this.elements.push(imageElement);
this.render();
};
img.src = options.src;
}
/**
* 渲染所有元素
*/
render() {
// 清除画布
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// 重新绘制产品模板
this.loadProductTemplate();
// 绘制所有元素
this.elements.forEach(element => {
this.drawElement(element);
});
// 绘制选中元素的控制点
if (this.selectedElement) {
this.drawSelectionBox(this.selectedElement);
}
}
/**
* 绘制单个元素
* @param {Object} element 元素对象
*/
drawElement(element) {
this.ctx.save();
switch (element.type) {
case 'text':
this.ctx.font = `${element.fontSize}px ${element.fontFamily}`;
this.ctx.fillStyle = element.color;
this.ctx.fillText(element.content, element.x, element.y);
break;
case 'image':
if (element.image && element.image.complete) {
this.ctx.drawImage(
element.image,
element.x,
element.y,
element.width,
element.height
);
}
break;
}
this.ctx.restore();
}
/**
* 设置事件监听器
*/
setupEventListeners() {
this.canvas.addEventListener('mousedown', this.handleMouseDown.bind(this));
this.canvas.addEventListener('mousemove', this.handleMouseMove.bind(this));
this.canvas.addEventListener('mouseup', this.handleMouseUp.bind(this));
this.canvas.addEventListener('click', this.handleClick.bind(this));
}
/**
* 处理鼠标按下事件
* @param {Event} e 鼠标事件
*/
handleMouseDown(e) {
const rect = this.canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// 检查是否点击了元素
for (let i = this.elements.length - 1; i >= 0; i--) {
const element = this.elements[i];
if (this.isPointInElement(x, y, element)) {
this.selectedElement = element;
this.isDragging = true;
// 计算拖拽偏移量
this.dragOffset.x = x - element.x;
this.dragOffset.y = y - element.y;
this.render();
break;
}
}
}
/**
* 检查点是否在元素内
* @param {number} x X坐标
* @param {number} y Y坐标
* @param {Object} element 元素对象
* @returns {boolean} 是否在元素内
*/
isPointInElement(x, y, element) {
switch (element.type) {
case 'text':
this.ctx.font = `${element.fontSize}px ${element.fontFamily}`;
const width = this.ctx.measureText(element.content).width;
return x >= element.x &&
x <= element.x + width &&
y >= element.y - element.fontSize &&
y <= element.y;
case 'image':
return x >= element.x &&
x <= element.x + element.width &&
y >= element.y &&
y <= element.y + element.height;
}
return false;
}
/**
* 获取设计数据
* @returns {Object} 设计数据
*/
getDesignData() {
return {
elements: this.elements.map(el => {
const elementCopy = { ...el };
// 移除图像对象,只保留URL
if (elementCopy.type === 'image' && elementCopy.image) {
elementCopy.src = elementCopy.src;
delete elementCopy.image;
}
return elementCopy;
}),
canvasWidth: this.canvas.width,
canvasHeight: this.canvas.height,
productId: this.config.productId
};
}
}
<?php
// 在class-product-configurator.php中添加短代码方法
/**
* 注册产品配置器短代码
*/
public function register_shortcode() {
add_shortcode('cultural_configurator', [$this, 'render_configurator']);
}
/**
* 渲染产品配置器
* @param array $atts 短代码属性
* @return string HTML内容
*/
public function render_configurator($atts) {
$atts = shortcode_atts([
'product_id' => 0,
'width' => '800',
'height' => '600'
], $atts);
if (!$atts['product_id']) {
return '<p>请指定产品ID</p>';
}
// 加载产品数据
$this->product_id = intval($atts['product_id']);
$this->load_product_data();
if (!$this->product_data) {
<?php
// 在class-product-configurator.php中添加短代码方法
/**
* 注册产品配置器短代码
*/
public function register_shortcode() {
add_shortcode('cultural_configurator', [$this, 'render_configurator']);
}
/**
* 渲染产品配置器
* @param array $atts 短代码属性
* @return string HTML内容
*/
public function render_configurator($atts) {
$atts = shortcode_atts([
'product_id' => 0,
'width' => '800',
'height' => '600'
], $atts);
if (!$atts['product_id']) {
return '<p>请指定产品ID</p>';
}
// 加载产品数据
$this->product_id = intval($atts['product_id']);
$this->load_product_data();
if (!$this->product_data) {
return '<p>产品不存在</p>';
}
// 获取可定制区域
$customizable_areas = $this->get_customizable_areas();
// 生成唯一ID
$editor_id = 'cc-editor-' . uniqid();
// 输出HTML结构
ob_start();
?>
<div class="cultural-creator-container">
<div class="cc-editor-wrapper">
<div id="<?php echo esc_attr($editor_id); ?>" class="cc-visual-editor"></div>
<div class="cc-toolbar">
<div class="cc-tool-group">
<h4>添加元素</h4>
<button class="cc-btn cc-add-text" data-type="text">
<span class="dashicons dashicons-text"></span> 添加文字
</button>
<button class="cc-btn cc-add-image" data-type="image">
<span class="dashicons dashicons-format-image"></span> 添加图片
</button>
<button class="cc-btn cc-add-shape" data-type="shape">
<span class="dashicons dashicons-shapes"></span> 添加图形
</button>
</div>
<div class="cc-tool-group cc-properties-panel" style="display:none;">
<h4>属性设置</h4>
<div class="cc-properties-content"></div>
</div>
<div class="cc-tool-group">
<h4>操作</h4>
<button class="cc-btn cc-save-design">
<span class="dashicons dashicons-saved"></span> 保存设计
</button>
<button class="cc-btn cc-reset-design">
<span class="dashicons dashicons-update"></span> 重置
</button>
<button class="cc-btn cc-preview-3d">
<span class="dashicons dashicons-visibility"></span> 3D预览
</button>
</div>
</div>
</div>
<div class="cc-sidebar">
<div class="cc-price-summary">
<h3>价格明细</h3>
<div class="cc-price-breakdown">
<div class="cc-price-item">
<span>基础价格</span>
<span class="cc-price-amount">¥<?php echo number_format($this->product_data->base_price, 2); ?></span>
</div>
<div class="cc-price-item cc-customization-cost">
<span>定制费用</span>
<span class="cc-price-amount">¥0.00</span>
</div>
<div class="cc-price-total">
<span>总计</span>
<span class="cc-total-amount">¥<?php echo number_format($this->product_data->base_price, 2); ?></span>
</div>
</div>
</div>
<div class="cc-design-options">
<h3>设计选项</h3>
<div class="cc-option-group">
<label>选择区域:</label>
<select class="cc-area-selector">
<?php foreach ($customizable_areas as $area_key => $area): ?>
<option value="<?php echo esc_attr($area_key); ?>">
<?php echo esc_html($area['name']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="cc-option-group">
<label>上传图片:</label>
<input type="file" class="cc-image-upload" accept="image/*" style="display:none;">
<button class="cc-btn cc-upload-btn">选择图片</button>
<div class="cc-upload-preview"></div>
</div>
<div class="cc-text-options" style="display:none;">
<h4>文字设置</h4>
<input type="text" class="cc-text-input" placeholder="输入文字内容">
<select class="cc-font-selector">
<option value="Arial">Arial</option>
<option value="SimSun">宋体</option>
<option value="Microsoft YaHei">微软雅黑</option>
<option value="KaiTi">楷体</option>
</select>
<input type="color" class="cc-text-color" value="#000000">
<input type="range" class="cc-font-size" min="12" max="72" value="24">
</div>
</div>
</div>
</div>
<script type="text/javascript">
jQuery(document).ready(function($) {
// 初始化可视化编辑器
const editorConfig = {
containerId: '<?php echo esc_js($editor_id); ?>',
productId: <?php echo intval($atts['product_id']); ?>,
width: <?php echo intval($atts['width']); ?>,
height: <?php echo intval($atts['height']); ?>,
customizableAreas: <?php echo wp_json_encode($customizable_areas); ?>
};
window.ccEditor = new CulturalCreatorVisualEditor(editorConfig);
// 绑定工具栏事件
$('.cc-add-text').on('click', function() {
const area = $('.cc-area-selector').val();
const textId = ccEditor.addTextElement({
content: '双击编辑文字',
area: area,
x: 100,
y: 100
});
// 显示文字设置面板
$('.cc-text-options').show();
$('.cc-properties-panel').show();
$('.cc-properties-content').html(`
<div class="cc-property-item">
<label>文字内容:</label>
<input type="text" class="cc-edit-text" value="双击编辑文字">
</div>
<div class="cc-property-item">
<label>字体大小:</label>
<input type="range" class="cc-edit-fontsize" min="12" max="72" value="24">
</div>
<div class="cc-property-item">
<label>字体颜色:</label>
<input type="color" class="cc-edit-color" value="#000000">
</div>
`);
});
// 保存设计
$('.cc-save-design').on('click', function() {
const designData = ccEditor.getDesignData();
$.ajax({
url: '<?php echo admin_url('admin-ajax.php'); ?>',
type: 'POST',
data: {
action: 'cc_save_design',
nonce: '<?php echo wp_create_nonce('cc_save_design_nonce'); ?>',
design_data: designData
},
success: function(response) {
if (response.success) {
alert('设计保存成功!');
// 更新价格
$('.cc-customization-cost .cc-price-amount').text('¥' + response.data.customization_cost);
$('.cc-total-amount').text('¥' + response.data.total_price);
} else {
alert('保存失败:' + response.data);
}
}
});
});
});
</script>
<?php
return ob_get_clean();
}
## AJAX处理与数据保存
### 1. AJAX处理器类
<?php
// includes/class-ajax-handler.php
class CulturalCreator_AjaxHandler {
public function __construct() {
$this->init_hooks();
}
/**
* 初始化AJAX钩子
*/
private function init_hooks() {
// 保存设计
add_action('wp_ajax_cc_save_design', [$this, 'save_design']);
add_action('wp_ajax_nopriv_cc_save_design', [$this, 'save_design']);
// 加载设计
add_action('wp_ajax_cc_load_design', [$this, 'load_design']);
add_action('wp_ajax_nopriv_cc_load_design', [$this, 'load_design']);
// 上传图片
add_action('wp_ajax_cc_upload_image', [$this, 'upload_image']);
add_action('wp_ajax_nopriv_cc_upload_image', [$this, 'upload_image']);
// 获取产品数据
add_action('wp_ajax_cc_get_product', [$this, 'get_product_data']);
add_action('wp_ajax_nopriv_cc_get_product', [$this, 'get_product_data']);
}
/**
* 保存用户设计
*/
public function save_design() {
// 验证nonce
if (!wp_verify_nonce($_POST['nonce'], 'cc_save_design_nonce')) {
wp_die('安全验证失败');
}
$design_data = json_decode(stripslashes($_POST['design_data']), true);
if (!$design_data || !isset($design_data['productId'])) {
wp_send_json_error('无效的设计数据');
}
global $wpdb;
$table_name = $wpdb->prefix . 'cc_user_designs';
// 获取用户ID或会话ID
$user_id = is_user_logged_in() ? get_current_user_id() : null;
$session_id = isset($_COOKIE['cc_session']) ? $_COOKIE['cc_session'] : $this->generate_session_id();
// 计算价格
$configurator = new CulturalCreator_ProductConfigurator($design_data['productId']);
$customization_cost = $configurator->calculate_price($design_data['elements']);
$base_price = floatval($configurator->product_data->base_price);
$total_price = $base_price + $customization_cost;
// 生成预览图
$preview_image = $this->generate_preview_image($design_data);
// 保存到数据库
$result = $wpdb->insert(
$table_name,
[
'user_id' => $user_id,
'session_id' => $session_id,
'product_id' => $design_data['productId'],
'design_data' => json_encode($design_data, JSON_UNESCAPED_UNICODE),
'preview_image' => $preview_image,
'total_price' => $total_price,
'status' => 'draft'
],
['%d', '%s', '%d', '%s', '%s', '%f', '%s']
);
if ($result) {
// 设置会话cookie
if (!isset($_COOKIE['cc_session'])) {
setcookie('cc_session', $session_id, time() + 30 * DAY_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN);
}
wp_send_json_success([
'design_id' => $wpdb->insert_id,
'customization_cost' => number_format($customization_cost, 2),
'total_price' => number_format($total_price, 2),
'preview_url' => $preview_image
]);
} else {
wp_send_json_error('保存失败');
}
}
/**
* 生成预览图片
* @param array $design_data 设计数据
* @return string 图片URL
*/
private function generate_preview_image($design_data) {
// 这里简化处理,实际应该使用GD库或ImageMagick生成图片
$upload_dir = wp_upload_dir();
$filename = 'cc-preview-' . uniqid() . '.png';
$filepath = $upload_dir['path'] . '/' . $filename;
$fileurl = $upload_dir['url'] . '/' . $filename;
// 在实际应用中,这里应该使用canvas生成图片
// 为了简化,我们创建一个占位图片
$image = imagecreatetruecolor(400, 300);
$bg_color = imagecolorallocate($image, 240, 240, 240);
$text_color = imagecolorallocate($image, 100, 100, 100);
imagefill($image, 0, 0, $bg_color);
imagestring($image, 5, 150, 140, '设计预览', $text_color);
imagepng($image, $filepath);
imagedestroy($image);
return $fileurl;
}
/**
* 生成会话ID
* @return string 会话ID
*/
private function generate_session_id() {
return md5(uniqid() . $_SERVER['REMOTE_ADDR'] . time());
}
/**
* 上传图片
*/
public function upload_image() {
if (!wp_verify_nonce($_POST['nonce'], 'cc_upload_image_nonce')) {
wp_die('安全验证失败');
}
if (!function_exists('wp_handle_upload')) {
require_once(ABSPATH . 'wp-admin/includes/file.php');
}
$uploadedfile = $_FILES['image'];
$upload_overrides = ['test_form' => false];
$movefile = wp_handle_upload($uploadedfile, $upload_overrides);
if ($movefile && !isset($movefile['error'])) {
// 创建WordPress附件
$attachment = [
'post_mime_type' => $movefile['type'],
'post_title' => preg_replace('/.[^.]+$/', '', basename($movefile['file'])),
'post_content' => '',
'post_status' => 'inherit'
];
$attach_id = wp_insert_attachment($attachment, $movefile['file']);
// 生成缩略图
require_once(ABSPATH . 'wp-admin/includes/image.php');
$attach_data = wp_generate_attachment_metadata($attach_id, $movefile['file']);
wp_update_attachment_metadata($attach_id, $attach_data);
wp_send_json_success([
'url' => $movefile['url'],
'id' => $attach_id
]);
} else {
wp_send_json_error($movefile['error']);
}
}
}
## 管理后台与产品管理
### 1. 管理界面
<?php
// 在插件主文件中添加管理菜单
add_action('admin_menu', 'cultural_creator_admin_menu');
function cultural_creator_admin_menu() {
add_menu_page(
'文创产品定制器',
'文创定制',
'manage_options',
'cultural-creator',
'cultural_creator_admin_page',
'dashicons-art',
30
);
add_submenu_page(
'cultural-creator',
'产品管理',
'产品管理',
'manage_options',
'cultural-creator-products',
'cultural_creator_products_page'
);
add_submenu_page(
'cultural-creator',
'设计管理',
'设计管理',
'manage_options',
'cultural-creator-designs',
'cultural_creator_designs_page'
);
add_submenu_page(
'cultural-creator',
'设置',
'设置',
'manage_options',
'cultural-creator-settings',
'cultural_creator_settings_page'
);
}
function cultural_creator_admin_page() {
?>
<div class="wrap">
<h1>文创产品定制器</h1>
<div class="cc-admin-dashboard">
<div class="cc-stats-container">
<div class="cc-stat-card">
<h3>总产品数</h3>
<p class="cc-stat-number"><?php echo cultural_creator_get_product_count(); ?></p>
</div>
<div class="cc-stat-card">
<h3>总设计数</h3>
<p class="cc-stat-number"><?php echo cultural_creator_get_design_count(); ?></p>
</div>
<div class="cc-stat-card">
<h3>今日设计</h3>
<p class="cc-stat-number"><?php echo cultural_creator_get_today_designs(); ?></p>
</div>
</div>
<div class="cc-quick-actions">
<h2>快速操作</h2>
<a href="<?php echo admin_url('admin.php?page=cultural-creator-products&action=add'); ?>"
class="button button-primary">
添加新产品
</a>
<a href="<?php echo admin_url('admin.php?page=cultural-creator-designs'); ?>"
class="button">
查看所有设计
</a>
</div>
</div>
</div>
<?php
}
function cultural_creator_products_page() {
$action = isset($_GET['action']) ? $_GET['action'] : 'list';
switch ($action) {
case 'add':
case 'edit':
include CC_PLUGIN_DIR . 'templates/admin-product-edit.php';
break;
case 'list':
default:
include CC_PLUGIN_DIR . 'templates/admin-product-list.php';
break;
}
}
## 样式优化与响应式设计
/ assets/css/frontend.css /
.cultural-creator-container {
display: flex;
flex-wrap: wrap;
gap: 20px;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.cc-editor-wrapper {
flex: 1;
min-width: 300px;
}
.cc-visual-editor {
border: 1px solid #ddd;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.cc-toolbar {
background: #f5f5f5;
padding: 15px;
border-radius: 8px;
margin-top: 15px;
}
.cc-tool-group {
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #ddd;
}
.cc-tool-group:last-child {
border-bottom: none;
margin


