文章目录[隐藏]
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('CSD_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('CSD_PLUGIN_URL', plugin_dir_url(__FILE__));
define('CSD_VERSION', '1.0.0');
// 初始化插件
class ClientSelfDesign {
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() {
// 注册激活和停用钩子
register_activation_hook(__FILE__, array($this, 'activate'));
register_deactivation_hook(__FILE__, array($this, 'deactivate'));
// 初始化插件功能
add_action('init', array($this, 'init'));
// 添加管理菜单
add_action('admin_menu', array($this, 'add_admin_menu'));
// 注册短代码
add_shortcode('csd_designer', array($this, 'render_designer_shortcode'));
// 注册AJAX处理
add_action('wp_ajax_csd_save_design', array($this, 'save_design'));
add_action('wp_ajax_nopriv_csd_save_design', array($this, 'save_design'));
}
public function activate() {
// 创建数据库表
$this->create_database_table();
// 设置默认选项
add_option('csd_default_settings', array(
'primary_color' => '#3498db',
'secondary_color' => '#2ecc71',
'max_designs_per_user' => 5,
'allow_public_designs' => true
));
}
public function deactivate() {
// 清理临时数据
delete_transient('csd_recent_designs');
}
public function init() {
// 加载文本域
load_plugin_textdomain('csd', false, dirname(plugin_basename(__FILE__)) . '/languages');
}
// 其他方法将在下面实现
}
// 启动插件
ClientSelfDesign::get_instance();
?>
三、数据库设计与数据存储
接下来,我们创建存储用户设计的数据库表:
// 在ClientSelfDesign类中添加以下方法
private function create_database_table() {
global $wpdb;
$table_name = $wpdb->prefix . 'csd_designs';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
user_id bigint(20) NOT NULL,
design_name varchar(100) NOT NULL,
design_data longtext NOT NULL,
design_type varchar(50) DEFAULT 'basic',
is_public tinyint(1) DEFAULT 0,
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 design_type (design_type)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
// 添加版本号,便于后续升级
add_option('csd_db_version', '1.0');
}
四、前端设计器界面实现
创建用户自助设计的前端界面:
// 在ClientSelfDesign类中添加以下方法
public function render_designer_shortcode($atts) {
// 短代码属性处理
$atts = shortcode_atts(array(
'type' => 'basic',
'max_elements' => 10
), $atts, 'csd_designer');
// 检查用户权限
if (!is_user_logged_in()) {
return '<div class="csd-login-required">' .
__('请先登录以使用设计工具', 'csd') .
wp_login_url(get_permalink()) .
'</div>';
}
// 获取用户现有设计数量
$user_id = get_current_user_id();
$design_count = $this->get_user_design_count($user_id);
$max_designs = get_option('csd_default_settings')['max_designs_per_user'];
if ($design_count >= $max_designs) {
return '<div class="csd-limit-reached">' .
sprintf(__('您已达到最大设计数量(%d),请删除一些旧设计以创建新设计', 'csd'), $max_designs) .
'</div>';
}
// 输出设计器HTML
ob_start();
?>
<div id="csd-designer-container" class="csd-designer" data-type="<?php echo esc_attr($atts['type']); ?>">
<div class="csd-header">
<h2><?php _e('自助设计工具', 'csd'); ?></h2>
<div class="csd-design-count">
<?php printf(__('已使用: %d/%d', 'csd'), $design_count, $max_designs); ?>
</div>
</div>
<div class="csd-toolbar">
<div class="csd-tool-group">
<button type="button" class="csd-tool-btn" data-tool="text">
<span class="dashicons dashicons-editor-textcolor"></span>
<?php _e('添加文本', 'csd'); ?>
</button>
<button type="button" class="csd-tool-btn" data-tool="image">
<span class="dashicons dashicons-format-image"></span>
<?php _e('添加图片', 'csd'); ?>
</button>
<button type="button" class="csd-tool-btn" data-tool="shape">
<span class="dashicons dashicons-forms"></span>
<?php _e('添加形状', 'csd'); ?>
</button>
</div>
<div class="csd-color-picker">
<label><?php _e('主题色:', 'csd'); ?></label>
<input type="color" id="csd-primary-color" value="#3498db">
<input type="color" id="csd-secondary-color" value="#2ecc71">
</div>
</div>
<div class="csd-workspace">
<div class="csd-canvas" id="csd-design-canvas">
<!-- 设计元素将在这里动态添加 -->
<div class="csd-default-message">
<?php _e('从工具栏选择工具开始设计', 'csd'); ?>
</div>
</div>
<div class="csd-properties-panel">
<h3><?php _e('属性设置', 'csd'); ?></h3>
<div id="csd-properties-form">
<!-- 属性表单将根据选中元素动态加载 -->
</div>
</div>
</div>
<div class="csd-footer">
<div class="csd-design-info">
<input type="text" id="csd-design-name"
placeholder="<?php esc_attr_e('输入设计名称', 'csd'); ?>"
maxlength="100">
<label>
<input type="checkbox" id="csd-design-public">
<?php _e('公开此设计', 'csd'); ?>
</label>
</div>
<div class="csd-actions">
<button type="button" id="csd-preview-btn" class="button">
<?php _e('预览', 'csd'); ?>
</button>
<button type="button" id="csd-save-btn" class="button button-primary">
<?php _e('保存设计', 'csd'); ?>
</button>
<button type="button" id="csd-reset-btn" class="button button-secondary">
<?php _e('重置', 'csd'); ?>
</button>
</div>
</div>
<!-- 预览模态框 -->
<div id="csd-preview-modal" class="csd-modal" style="display:none;">
<div class="csd-modal-content">
<span class="csd-close-modal">×</span>
<h3><?php _e('设计预览', 'csd'); ?></h3>
<div id="csd-preview-content"></div>
</div>
</div>
</div>
<!-- 加载JavaScript -->
<script type="text/javascript">
var csd_ajax_url = '<?php echo admin_url('admin-ajax.php'); ?>';
var csd_nonce = '<?php echo wp_create_nonce('csd_save_design_nonce'); ?>';
var csd_user_id = <?php echo $user_id; ?>;
</script>
<?php
return ob_get_clean();
}
// 获取用户设计数量
private function get_user_design_count($user_id) {
global $wpdb;
$table_name = $wpdb->prefix . 'csd_designs';
return $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM $table_name WHERE user_id = %d",
$user_id
));
}
五、JavaScript交互与AJAX处理
创建前端交互逻辑:
// 创建文件: assets/js/csd-designer.js
jQuery(document).ready(function($) {
'use strict';
var CSD_Designer = {
currentTool: null,
selectedElement: null,
designData: {
elements: [],
colors: {
primary: '#3498db',
secondary: '#2ecc71'
},
settings: {}
},
init: function() {
this.bindEvents();
this.loadColorSettings();
},
bindEvents: function() {
// 工具按钮点击
$('.csd-tool-btn').on('click', this.handleToolClick.bind(this));
// 颜色选择器变化
$('#csd-primary-color, #csd-secondary-color').on('change', this.handleColorChange.bind(this));
// 保存设计
$('#csd-save-btn').on('click', this.saveDesign.bind(this));
// 预览设计
$('#csd-preview-btn').on('click', this.previewDesign.bind(this));
// 重置设计
$('#csd-reset-btn').on('click', this.resetDesign.bind(this));
// 关闭模态框
$('.csd-close-modal').on('click', function() {
$('#csd-preview-modal').hide();
});
},
handleToolClick: function(e) {
var tool = $(e.currentTarget).data('tool');
this.currentTool = tool;
switch(tool) {
case 'text':
this.addTextElement();
break;
case 'image':
this.addImageElement();
break;
case 'shape':
this.addShapeElement();
break;
}
},
addTextElement: function() {
var elementId = 'element_' + Date.now();
var element = {
id: elementId,
type: 'text',
content: '新文本',
styles: {
fontSize: '16px',
color: this.designData.colors.primary,
fontWeight: 'normal',
textAlign: 'left'
},
position: { x: 50, y: 50 }
};
this.designData.elements.push(element);
this.renderElement(element);
this.selectElement(elementId);
},
addImageElement: function() {
// 创建图片上传逻辑
this.openMediaUploader();
},
addShapeElement: function() {
var elementId = 'element_' + Date.now();
var element = {
id: elementId,
type: 'shape',
shapeType: 'rectangle',
styles: {
backgroundColor: this.designData.colors.secondary,
width: '100px',
height: '100px',
borderRadius: '0px'
},
position: { x: 100, y: 100 }
};
this.designData.elements.push(element);
this.renderElement(element);
this.selectElement(elementId);
},
renderElement: function(element) {
var $canvas = $('#csd-design-canvas');
var $element = $('<div class="csd-element"></div>')
.attr('id', element.id)
.css({
position: 'absolute',
left: element.position.x + 'px',
top: element.position.y + 'px'
})
.data('element-data', element);
switch(element.type) {
case 'text':
$element.addClass('csd-text-element')
.text(element.content)
.css(element.styles);
break;
case 'shape':
$element.addClass('csd-shape-element')
.css(element.styles);
break;
}
$element.on('click', this.handleElementClick.bind(this));
$canvas.append($element);
},
handleElementClick: function(e) {
var elementId = $(e.currentTarget).attr('id');
this.selectElement(elementId);
},
selectElement: function(elementId) {
this.selectedElement = elementId;
$('.csd-element').removeClass('selected');
$('#' + elementId).addClass('selected');
this.loadPropertiesForm(elementId);
},
loadPropertiesForm: function(elementId) {
var element = this.getElementById(elementId);
if (!element) return;
var formHtml = '';
switch(element.type) {
case 'text':
formHtml = this.getTextPropertiesForm(element);
break;
case 'shape':
formHtml = this.getShapePropertiesForm(element);
break;
}
$('#csd-properties-form').html(formHtml);
this.bindPropertiesEvents();
},
getTextPropertiesForm: function(element) {
return `
<div class="csd-property-group">
<label>文本内容:</label>
<input type="text" class="csd-property-input"
data-property="content"
value="${element.content}">
</div>
<div class="csd-property-group">
<label>字体大小:</label>
<input type="range" class="csd-property-input"
data-property="fontSize" min="8" max="72"
value="${parseInt(element.styles.fontSize)}">
<span class="csd-property-value">${element.styles.fontSize}</span>
</div>
<div class="csd-property-group">
<label>文字颜色:</label>
<input type="color" class="csd-property-input"
data-property="color"
value="${element.styles.color}">
</div>
`;
},
bindPropertiesEvents: function() {
var self = this;
$('.csd-property-input').on('input change', function() {
var property = $(this).data('property');
var value = $(this).val();
if ($(this).attr('type') === 'range') {
value = value + 'px';
$(this).siblings('.csd-property-value').text(value);
}
self.updateSelectedElement(property, value);
});
},
updateSelectedElement: function(property, value) {
if (!this.selectedElement) return;
var element = this.getElementById(this.selectedElement);
if (!element) return;
if (property === 'content') {
element.content = value;
$('#' + this.selectedElement).text(value);
} else {
element.styles[property] = value;
$('#' + this.selectedElement).css(property, value);
}
},
getElementById: function(elementId) {
return this.designData.elements.find(function(el) {
return el.id === elementId;
});
},
handleColorChange: function(e) {
var colorType = e.target.id.replace('csd-', '').replace('-color', '');
this.designData.colors[colorType] = $(e.target).val();
},
saveDesign: function() {
var designName = $('#csd-design-name').val().trim();
var isPublic = $('#csd-design-public').is(':checked') ? 1 : 0;
if (!designName) {
alert('请输入设计名称');
return;
}
var designData = {
name: designName,
data: this.designData,
is_public: isPublic
};
$.ajax({
url: csd_ajax_url,
type: 'POST',
data: {
action: 'csd_save_design',
nonce: csd_nonce,
user_id: csd_user_id,
design_data: designData
},
beforeSend: function() {
true).text('保存中...');
},
success: function(response) {
if (response.success) {
alert('设计保存成功!');
// 刷新页面显示新设计
location.reload();
} else {
alert('保存失败: ' + response.data);
}
},
error: function() {
alert('网络错误,请重试');
},
complete: function() {
$('#csd-save-btn').prop('disabled', false).text('保存设计');
}
});
},
previewDesign: function() {
var previewHtml = '<div class="csd-preview-container">';
this.designData.elements.forEach(function(element) {
previewHtml += '<div class="csd-preview-element" style="';
previewHtml += 'position:absolute;';
previewHtml += 'left:' + element.position.x + 'px;';
previewHtml += 'top:' + element.position.y + 'px;';
for (var style in element.styles) {
previewHtml += style + ':' + element.styles[style] + ';';
}
previewHtml += '">';
previewHtml += element.content || '';
previewHtml += '</div>';
});
previewHtml += '</div>';
$('#csd-preview-content').html(previewHtml);
$('#csd-preview-modal').show();
},
resetDesign: function() {
if (confirm('确定要重置所有设计吗?')) {
this.designData.elements = [];
$('#csd-design-canvas').html('<div class="csd-default-message">从工具栏选择工具开始设计</div>');
$('#csd-properties-form').empty();
this.selectedElement = null;
}
},
loadColorSettings: function() {
this.designData.colors.primary = $('#csd-primary-color').val();
this.designData.colors.secondary = $('#csd-secondary-color').val();
},
openMediaUploader: function() {
var frame = wp.media({
title: '选择图片',
multiple: false,
library: {
type: 'image'
},
button: {
text: '使用此图片'
}
});
frame.on('select', function() {
var attachment = frame.state().get('selection').first().toJSON();
CSD_Designer.addImageFromMedia(attachment);
});
frame.open();
},
addImageFromMedia: function(attachment) {
var elementId = 'element_' + Date.now();
var element = {
id: elementId,
type: 'image',
url: attachment.url,
alt: attachment.alt,
styles: {
width: '200px',
height: 'auto'
},
position: { x: 150, y: 150 }
};
this.designData.elements.push(element);
var $canvas = $('#csd-design-canvas');
var $img = $('<img>')
.attr('src', attachment.url)
.attr('alt', attachment.alt)
.css({
position: 'absolute',
left: element.position.x + 'px',
top: element.position.y + 'px',
width: element.styles.width
})
.addClass('csd-element csd-image-element')
.attr('id', elementId)
.data('element-data', element);
$img.on('click', this.handleElementClick.bind(this));
$canvas.append($img);
this.selectElement(elementId);
}
};
// 初始化设计器
CSD_Designer.init();
});
## 六、后端数据处理与保存
实现AJAX保存功能:
// 在ClientSelfDesign类中添加以下方法
public function save_design() {
// 验证nonce
if (!wp_verify_nonce($_POST['nonce'], 'csd_save_design_nonce')) {
wp_die('安全验证失败');
}
// 验证用户权限
$user_id = intval($_POST['user_id']);
if ($user_id !== get_current_user_id()) {
wp_die('用户权限错误');
}
// 获取并清理数据
$design_data = json_decode(stripslashes($_POST['design_data']), true);
if (!$design_data) {
wp_send_json_error('无效的设计数据');
}
// 验证设计数量限制
$max_designs = get_option('csd_default_settings')['max_designs_per_user'];
$current_count = $this->get_user_design_count($user_id);
if ($current_count >= $max_designs) {
wp_send_json_error('已达到最大设计数量限制');
}
// 保存到数据库
global $wpdb;
$table_name = $wpdb->prefix . 'csd_designs';
$result = $wpdb->insert(
$table_name,
array(
'user_id' => $user_id,
'design_name' => sanitize_text_field($design_data['name']),
'design_data' => json_encode($design_data['data']),
'design_type' => 'custom',
'is_public' => $design_data['is_public'] ? 1 : 0
),
array('%d', '%s', '%s', '%s', '%d')
);
if ($result) {
// 清除缓存
delete_transient('csd_user_designs_' . $user_id);
wp_send_json_success(array(
'design_id' => $wpdb->insert_id,
'message' => '设计保存成功'
));
} else {
wp_send_json_error('数据库保存失败');
}
}
// 添加获取用户设计的函数
public function get_user_designs($user_id) {
global $wpdb;
$table_name = $wpdb->prefix . 'csd_designs';
$cache_key = 'csd_user_designs_' . $user_id;
$designs = get_transient($cache_key);
if (false === $designs) {
$designs = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM $table_name WHERE user_id = %d ORDER BY created_at DESC",
$user_id
), ARRAY_A);
set_transient($cache_key, $designs, HOUR_IN_SECONDS);
}
return $designs;
}
## 七、管理界面与样式优化
创建管理后台界面:
// 在ClientSelfDesign类中添加以下方法
public function add_admin_menu() {
add_menu_page(
'客户设计管理',
'客户设计',
'manage_options',
'csd-designs',
array($this, 'render_admin_page'),
'dashicons-art',
30
);
add_submenu_page(
'csd-designs',
'设计设置',
'设置',
'manage_options',
'csd-settings',
array($this, 'render_settings_page')
);
}
public function render_admin_page() {
?>
<div class="wrap">
<h1><?php _e('客户设计管理', 'csd'); ?></h1>
<div class="csd-admin-container">
<div class="csd-stats">
<?php
global $wpdb;
$table_name = $wpdb->prefix . 'csd_designs';
$total_designs = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
$total_users = $wpdb->get_var("SELECT COUNT(DISTINCT user_id) FROM $table_name");
$public_designs = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE is_public = 1");
?>
<div class="csd-stat-card">
<h3><?php echo $total_designs; ?></h3>
<p>总设计数</p>
</div>
<div class="csd-stat-card">
<h3><?php echo $total_users; ?></h3>
<p>使用用户数</p>
</div>
<div class="csd-stat-card">
<h3><?php echo $public_designs; ?></h3>
<p>公开设计数</p>
</div>
</div>
<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
$designs = $wpdb->get_results(
"SELECT d.*, u.user_login
FROM $table_name d
LEFT JOIN {$wpdb->users} u ON d.user_id = u.ID
ORDER BY d.created_at DESC
LIMIT 50"
);
foreach ($designs as $design) {
?>
<tr>
<td><?php echo $design->id; ?></td>
<td><?php echo esc_html($design->design_name); ?></td>
<td><?php echo $design->user_login ?: '游客'; ?></td>
<td><?php echo $design->design_type; ?></td>
<td><?php echo $design->is_public ? '是' : '否'; ?></td>
<td><?php echo date('Y-m-d H:i', strtotime($design->created_at)); ?></td>
<td>
<button class="button button-small csd-preview-btn"
data-design-id="<?php echo $design->id; ?>">
预览
</button>
<button class="button button-small button-link-delete csd-delete-btn"
data-design-id="<?php echo $design->id; ?>">
删除
</button>
</td>
</tr>
<?php
}
?>
</tbody>
</table>
</div>
</div>
<?php
}
public function render_settings_page() {
$settings = get_option('csd_default_settings', array());
if ($_SERVER['REQUEST_METHOD'] === 'POST' && check_admin_referer('csd_settings_update')) {
$new_settings = array(
'primary_color' => sanitize_hex_color($_POST['primary_color']),
'secondary_color' => sanitize_hex_color($_POST['secondary_color']),
'max_designs_per_user' => intval($_POST['max_designs_per_user']),
'allow_public_designs' => isset($_POST['allow_public_designs']) ? true : false
);
update_option('csd_default_settings', $new_settings);
$settings = $new_settings;
echo '<div class="notice notice-success"><p>设置已保存</p></div>';
}
?>
<div class="wrap">
<h1><?php _e('设计工具设置', 'csd'); ?></h1>
<form method="post" action="">
<?php wp_nonce_field('csd_settings_update'); ?>
<table class="form-table">
<tr>
<th scope="row">主色调</th>
<td>
<input type="color" name="primary_color"
value="<?php echo esc_attr($settings['primary_color']); ?>">
</td>
</tr>
<tr>
<th scope="row">辅色调</th>
<td>
<input type="color" name="secondary_color"
value="<?php echo esc_attr($settings['secondary_color']); ?>">
</td>
</tr>
<tr>
<th scope="row">用户最大设计数</th>
<td>
<input type="number" name="max_designs_per_user"
value="<?php echo esc_attr($settings['max_designs_per_user']); ?>"
min="1" max="50">
<p class="description">每个用户最多可以保存的设计数量</p>
</td>
</tr>
<tr>
<th scope="row">允许公开设计</th>
<td>
<label>
<input type="checkbox" name="allow_public_designs" value="1"
<?php checked($settings['allow_public_designs']); ?>>
允许用户将设计设为公开
</label>
</td>
</tr>
</table>
<?php submit_button(); ?>
</form>
</div>
<?php
}
## 八、CSS样式文件
创建插件样式文件:
/ 创建文件: assets/css/csd-designer.css /
/ 设计器主容器 /
.csd-designer {
background: #fff;
border: 1px solid #ddd;
border-radius: 4px;
padding: 20px;
margin: 20px 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
}
/ 头部样式 /
.csd-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 2px solid #f0f0f0;
}
.csd-header h2 {
margin: 0;
color: #333;
}
.csd-design-count {
background: #f7f7f7;
padding: 5px 10px;
border-radius: 3px;
font-size: 14px;
color: #666;
}
/ 工具栏样式 /
.csd-toolbar {
background: #f9f9f9;
padding: 15px;
border-radius: 4px;
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.csd-tool-group {
display: flex;
gap: 10px;
}
.csd-tool-btn {
display: flex;
align-items: center;
gap: 5px;
padding: 8px 15px;
background: #fff;
border: 1px solid #ddd;
border-radius: 3px;
cursor: pointer;
transition: all 0.3s ease;
}
.csd-tool-btn:hover {
background: #f0f0f0;
border-color: #999;
}
.csd-tool-btn .dashicons {
font-size: 16px;
width: 16px;
height: 16px;
}
.csd-color-picker {
display: flex;
align-items: center;
gap: 10px;
}
.csd-color-picker label {
font-weight: 600;
color: #555;
}
.csd-color-picker input[type="color"] {
width: 40px;
height: 40px;
border: 2px solid #ddd;
border-radius: 4px;
cursor: pointer;
}
/ 工作区样式 /
.csd-workspace {
display: grid;
grid-template-columns: 3fr 1fr;
gap: 20px;
min-height: 500px;
}
.csd-canvas {
background: #f8f9fa;
border: 2px dashed #dee2e6;
border-radius: 4px;
position: relative;
min-height: 500px;
}
.csd-default-message {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #6c757d;
font-size: 16px;
}
/ 设计元素样式 /
.csd-element {
cursor: move;
user-select: none;
transition: all 0.2s ease;
}
.csd-element.selected {
outline: 2px solid #007cba;
outline-offset: 2px;
}
.csd-text-element {
padding: 5px;
min-width: 50px;
min-height: 20px;
}
.csd-shape-element {
background: #3498db;
}
/ 属性面板样式 /
.csd-properties-panel {
background: #fff;
border: 1px solid #ddd;
border-radius: 4px;
padding: 15px;
}
.csd-properties-panel h3 {
margin-top: 0;
margin-bottom: 15px;
color: #333;
font-size: 16px;
}
.csd-property-group {
margin-bottom: 15px;
}
.csd-property-group label {
display: block;
margin-bottom: 5px;
font-weight: 600;
color: #555;
font-size: 14px;
}
.csd-property-input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 3px;
font-size: 14px;
}
.csd-property-value {
display: inline-block;
margin-left: 10px;
font-size: 14px;
color: #666;
}
/ 底部样式 /
.csd-footer {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.csd-design-info {
display: flex;
gap: 15px;
align-items: center;
}
csd-design-name {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 3px;
width: 200px;
}
.csd-design-info label {
display: flex;
align-items: center;
gap: 5px;
font-size: 14px;
color: #555;
}
.csd-actions {
display: flex;
gap: 10px;
}
/ 模态框样式 /
.csd-modal {
display: none;
position: fixed;
z-index: 1000;
left
