文章目录[隐藏]
WordPress高级教程:开发内部知识库与文档协作中心
引言:WordPress的无限可能性
WordPress作为全球最流行的内容管理系统,早已超越了简单的博客平台定位。根据W3Techs的数据,截至2023年,WordPress占据了全球网站市场份额的43%,其中超过三分之一的顶级网站使用WordPress。这一成功不仅源于其用户友好的界面和丰富的插件生态,更得益于其高度可扩展的架构设计。
本教程将深入探讨如何通过WordPress的二次开发,构建一个功能完善的内部知识库与文档协作中心,并集成常用互联网小工具功能。我们将从基础架构设计开始,逐步深入到高级功能实现,最终打造一个集知识管理、团队协作和工具集成为一体的企业级平台。
第一部分:知识库系统架构设计
1.1 知识库内容模型设计
一个高效的知识库系统需要精心设计的内容架构。在WordPress中,我们可以通过自定义文章类型(Custom Post Types)和分类法(Taxonomies)来构建专业的知识库结构。
// 注册知识库自定义文章类型
function register_knowledgebase_post_type() {
$labels = array(
'name' => '知识库文章',
'singular_name' => '知识库文章',
'menu_name' => '知识库',
'add_new' => '添加新文章',
'add_new_item' => '添加新知识库文章',
'edit_item' => '编辑知识库文章',
'new_item' => '新知识库文章',
'view_item' => '查看知识库文章',
'search_items' => '搜索知识库文章',
'not_found' => '未找到知识库文章',
'not_found_in_trash' => '回收站中未找到知识库文章'
);
$args = array(
'labels' => $labels,
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => array('slug' => 'knowledgebase'),
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => 5,
'menu_icon' => 'dashicons-book',
'supports' => array('title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments', 'revisions', 'custom-fields'),
'show_in_rest' => true, // 启用Gutenberg编辑器支持
);
register_post_type('knowledgebase', $args);
}
add_action('init', 'register_knowledgebase_post_type');
// 注册知识库分类法
function register_knowledgebase_taxonomies() {
// 主分类
register_taxonomy(
'kb_category',
'knowledgebase',
array(
'label' => '知识分类',
'rewrite' => array('slug' => 'kb-category'),
'hierarchical' => true,
'show_admin_column' => true,
'show_in_rest' => true,
)
);
// 标签
register_taxonomy(
'kb_tag',
'knowledgebase',
array(
'label' => '知识标签',
'rewrite' => array('slug' => 'kb-tag'),
'hierarchical' => false,
'show_admin_column' => true,
'show_in_rest' => true,
)
);
}
add_action('init', 'register_knowledgebase_taxonomies');
1.2 高级权限管理系统
内部知识库需要精细的权限控制。我们可以扩展WordPress的角色和权限系统,实现基于团队、部门和用户级别的访问控制。
// 添加自定义用户角色和权限
function add_knowledgebase_roles() {
// 知识库编辑者角色
add_role('kb_editor', '知识库编辑者', array(
'read' => true,
'edit_knowledgebase' => true,
'edit_published_knowledgebase' => true,
'publish_knowledgebase' => true,
'delete_knowledgebase' => true,
'delete_published_knowledgebase' => true,
'upload_files' => true,
'read_knowledgebase' => true,
));
// 知识库审核者角色
add_role('kb_reviewer', '知识库审核者', array(
'read' => true,
'edit_knowledgebase' => true,
'edit_others_knowledgebase' => true,
'edit_published_knowledgebase' => true,
'publish_knowledgebase' => true,
'read_private_knowledgebase' => true,
'delete_knowledgebase' => true,
'delete_published_knowledgebase' => true,
'delete_private_knowledgebase' => true,
'delete_others_knowledgebase' => true,
'manage_knowledgebase_categories' => true,
));
}
add_action('init', 'add_knowledgebase_roles');
// 扩展用户权限
function add_knowledgebase_capabilities() {
$roles = array('administrator', 'editor', 'kb_editor', 'kb_reviewer');
foreach ($roles as $role_name) {
$role = get_role($role_name);
if ($role) {
$role->add_cap('read_knowledgebase');
$role->add_cap('edit_knowledgebase');
$role->add_cap('edit_knowledgebases');
$role->add_cap('edit_others_knowledgebases');
$role->add_cap('publish_knowledgebases');
$role->add_cap('read_private_knowledgebases');
}
}
}
add_action('admin_init', 'add_knowledgebase_capabilities');
第二部分:文档协作功能实现
2.1 实时协作编辑器集成
现代文档协作中心需要支持多人实时编辑。我们可以集成开源协作编辑器,如CKEditor 5或Quill,实现类似Google Docs的协作体验。
// 集成协作编辑器
function enqueue_collaborative_editor() {
global $post;
// 仅在知识库文章编辑页面加载
if (is_admin() && isset($post) && $post->post_type === 'knowledgebase') {
// 加载CKEditor 5
wp_enqueue_script('ckeditor5', 'https://cdn.ckeditor.com/ckeditor5/36.0.1/super-build/ckeditor.js', array(), '36.0.1', true);
// 加载协作插件
wp_enqueue_script('collab-editor', get_template_directory_uri() . '/js/collab-editor.js', array('ckeditor5', 'jquery'), '1.0.0', true);
// 传递必要参数
wp_localize_script('collab-editor', 'collabConfig', array(
'postId' => $post->ID,
'userId' => get_current_user_id(),
'userName' => wp_get_current_user()->display_name,
'nonce' => wp_create_nonce('collab_editor_nonce'),
'apiUrl' => rest_url('collab/v1/'),
));
}
}
add_action('admin_enqueue_scripts', 'enqueue_collaborative_editor');
// 创建REST API端点处理实时协作
function register_collaboration_api() {
register_rest_route('collab/v1', '/update/(?P<id>d+)', array(
'methods' => 'POST',
'callback' => 'handle_collaborative_update',
'permission_callback' => function() {
return current_user_can('edit_knowledgebase');
},
'args' => array(
'id' => array(
'validate_callback' => function($param, $request, $key) {
return is_numeric($param);
}
),
),
));
register_rest_route('collab/v1', '/presence/(?P<id>d+)', array(
'methods' => 'GET',
'callback' => 'get_active_users',
'permission_callback' => function() {
return current_user_can('read_knowledgebase');
},
));
}
add_action('rest_api_init', 'register_collaboration_api');
function handle_collaborative_update(WP_REST_Request $request) {
$post_id = $request->get_param('id');
$content = $request->get_param('content');
$user_id = get_current_user_id();
// 验证权限
if (!current_user_can('edit_post', $post_id)) {
return new WP_Error('rest_forbidden', '您没有编辑此文章的权限', array('status' => 403));
}
// 更新文章内容
wp_update_post(array(
'ID' => $post_id,
'post_content' => wp_kses_post($content)
));
// 记录协作历史
add_post_meta($post_id, '_collab_history', array(
'user_id' => $user_id,
'timestamp' => current_time('mysql'),
'action' => 'edit'
));
return new WP_REST_Response(array(
'success' => true,
'message' => '内容已更新'
), 200);
}
2.2 版本控制与历史记录
完善的文档协作需要完整的版本控制功能。我们可以扩展WordPress的修订版本系统,提供更强大的版本管理。
// 增强版本控制功能
class Enhanced_Revision_Manager {
private $max_revisions = 50;
public function __construct() {
// 设置最大修订版本数
add_filter('wp_revisions_to_keep', array($this, 'set_max_revisions'), 10, 2);
// 添加版本比较功能
add_action('add_meta_boxes', array($this, 'add_revision_comparison_meta_box'));
// 添加版本标记功能
add_action('post_submitbox_misc_actions', array($this, 'add_version_note_field'));
add_action('save_post', array($this, 'save_version_note'));
}
public function set_max_revisions($num, $post) {
if ($post->post_type === 'knowledgebase') {
return $this->max_revisions;
}
return $num;
}
public function add_revision_comparison_meta_box() {
add_meta_box(
'revision-comparison',
'版本比较',
array($this, 'render_revision_comparison'),
'knowledgebase',
'side',
'default'
);
}
public function render_revision_comparison($post) {
$revisions = wp_get_post_revisions($post->ID);
if (count($revisions) > 1) {
echo '<div class="revision-comparison">';
echo '<select id="revision-from">';
echo '<option value="">选择旧版本</option>';
foreach ($revisions as $revision) {
$author = get_userdata($revision->post_author);
$date = date_i18n('Y-m-d H:i', strtotime($revision->post_modified));
echo sprintf(
'<option value="%d">%s - %s</option>',
$revision->ID,
$date,
$author->display_name
);
}
echo '</select>';
echo '<select id="revision-to">';
echo '<option value="">选择新版本</option>';
foreach ($revisions as $revision) {
$author = get_userdata($revision->post_author);
$date = date_i18n('Y-m-d H:i', strtotime($revision->post_modified));
echo sprintf(
'<option value="%d">%s - %s</option>',
$revision->ID,
$date,
$author->display_name
);
}
echo '</select>';
echo '<button id="compare-revisions" class="button">比较版本</button>';
echo '<div id="revision-diff-result" style="margin-top:10px;"></div>';
echo '</div>';
// 添加比较脚本
wp_enqueue_script('revision-diff', get_template_directory_uri() . '/js/revision-diff.js', array('jquery'), '1.0.0', true);
} else {
echo '<p>暂无历史版本</p>';
}
}
public function add_version_note_field($post) {
if ($post->post_type !== 'knowledgebase') return;
echo '<div class="misc-pub-section">';
echo '<label for="version-note">版本说明:</label>';
echo '<input type="text" id="version-note" name="version_note" value="" placeholder="简要说明本次修改内容" style="width:100%;margin-top:5px;">';
echo '</div>';
}
public function save_version_note($post_id) {
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if (!current_user_can('edit_post', $post_id)) return;
if (!isset($_POST['version_note'])) return;
$version_note = sanitize_text_field($_POST['version_note']);
if (!empty($version_note)) {
// 获取最新修订版本
$revisions = wp_get_post_revisions($post_id);
$latest_revision = reset($revisions);
if ($latest_revision) {
update_metadata('post', $latest_revision->ID, '_version_note', $version_note);
}
}
}
}
new Enhanced_Revision_Manager();
第三部分:常用互联网小工具集成
3.1 代码片段管理器
对于技术团队,代码片段管理是知识库的重要功能。我们可以创建一个专门的代码片段管理器。
// 代码片段管理器
class Code_Snippet_Manager {
public function __construct() {
// 注册代码片段文章类型
add_action('init', array($this, 'register_code_snippet_post_type'));
// 添加上传代码文件支持
add_filter('upload_mimes', array($this, 'add_code_mime_types'));
// 添加代码高亮
add_action('wp_enqueue_scripts', array($this, 'enqueue_code_highlight'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_code_highlight'));
// 添加快捷码
add_shortcode('code_snippet', array($this, 'code_snippet_shortcode'));
}
public function register_code_snippet_post_type() {
$args = array(
'public' => true,
'label' => '代码片段',
'menu_icon' => 'dashicons-editor-code',
'supports' => array('title', 'editor', 'author', 'custom-fields'),
'show_in_rest' => true,
'taxonomies' => array('post_tag', 'category'),
'has_archive' => true,
);
register_post_type('code_snippet', $args);
}
public function add_code_mime_types($mimes) {
$mimes['js'] = 'text/javascript';
$mimes['php'] = 'text/php';
$mimes['py'] = 'text/x-python';
$mimes['java'] = 'text/x-java';
$mimes['cpp'] = 'text/x-c++';
$mimes['c'] = 'text/x-c';
$mimes['sql'] = 'text/x-sql';
$mimes['json'] = 'application/json';
$mimes['xml'] = 'application/xml';
return $mimes;
}
public function enqueue_code_highlight() {
// 加载Prism.js代码高亮库
wp_enqueue_style('prism-css', 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css');
wp_enqueue_script('prism-js', 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js', array(), '1.29.0', true);
// 加载语言支持
$languages = array('php', 'javascript', 'python', 'java', 'cpp', 'sql', 'json', 'bash');
foreach ($languages as $lang) {
wp_enqueue_script("prism-$lang", "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-$lang.min.js", array('prism-js'), '1.29.0', true);
}
}
public function code_snippet_shortcode($atts) {
$atts = shortcode_atts(array(
'id' => 0,
'language' => 'php',
'title' => '',
'linenumbers' => 'true',
'download' => 'false'
), $atts);
$snippet_id = intval($atts['id']);
if ($snippet_id <= 0) {
return '<p>错误:未指定代码片段ID</p>';
}
$snippet = get_post($snippet_id);
if (!$snippet || $snippet->post_type !== 'code_snippet') {
return '<p>错误:代码片段不存在</p>';
}
$language = sanitize_text_field($atts['language']);
$title = sanitize_text_field($atts['title']);
$show_line_numbers = $atts['linenumbers'] === 'true';
$show_download = $atts['download'] === 'true';
$output = '<div class="code-snippet-container">';
if (!empty($title)) {
$output .= '<div class="code-snippet-header">';
$output .= '<h4>' . esc_html($title) . '</h4>';
if ($show_download) {
$output .= '<a href="' . get_permalink($snippet_id) . '?download=1" class="download-snippet" download>下载代码</a>';
}
$output .= '</div>';
}
$output .= '<pre class="' . ($show_line_numbers ? 'line-numbers' : '') . '">';
$output .= '<code class="language-' . esc_attr($language) . '">';
$output .= htmlspecialchars($snippet->post_content);
$output .= '</code></pre>';
// 添加代码信息
$output .= '<div class="code-snippet-meta">';
$output .= '<span>语言: ' . esc_html($language) . '</span>';
$output .= '<span>作者: ' . get_the_author_meta('display_name', $snippet->post_author) . '</span>';
$output .= '<span>更新: ' . get_the_modified_date('', $snippet_id) . '</span>';
$output .= '</div>';
$output .= '</div>';
return $output;
}
}
new Code_Snippet_Manager();
// 代码片段下载处理
function handle_code_snippet_download() {
if (isset($_GET['download']) && is_singular('code_snippet')) {
$post_id = get_the_ID();
$post = get_post($post_id);
if ($post && $post->post_type === 'code_snippet') {
$filename = sanitize_title($post->post_title) . '.txt';
$content = $post->post_content;
header('Content-Type: text/plain');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Length: ' . strlen($content));
echo $content;
exit;
}
}
}
add_action('template_redirect', 'handle_code_snippet_download');
### 3.2 API测试工具集成
为开发团队集成API测试工具,方便在知识库中直接测试和调试API接口。
// API测试工具
class API_Test_Tool {
public function __construct() {
// 注册API测试文章类型
add_action('init', array($this, 'register_api_test_post_type'));
// 添加快捷码
add_shortcode('api_tester', array($this, 'api_tester_shortcode'));
// 添加REST API端点处理代理请求
add_action('rest_api_init', array($this, 'register_api_proxy_endpoint'));
// 加载前端资源
add_action('wp_enqueue_scripts', array($this, 'enqueue_api_tester_assets'));
}
public function register_api_test_post_type() {
$args = array(
'public' => true,
'label' => 'API文档',
'menu_icon' => 'dashicons-rest-api',
'supports' => array('title', 'editor', 'custom-fields'),
'show_in_rest' => true,
'has_archive' => true,
);
register_post_type('api_doc', $args);
}
public function api_tester_shortcode($atts) {
$atts = shortcode_atts(array(
'id' => 0,
'title' => 'API测试工具',
'default_url' => '',
'default_method' => 'GET',
'default_headers' => '{"Content-Type": "application/json"}',
'default_body' => '{}'
), $atts);
$api_doc_id = intval($atts['id']);
$api_data = array();
if ($api_doc_id > 0) {
$api_doc = get_post($api_doc_id);
if ($api_doc && $api_doc->post_type === 'api_doc') {
$api_data = array(
'endpoint' => get_post_meta($api_doc_id, '_api_endpoint', true),
'method' => get_post_meta($api_doc_id, '_api_method', true) ?: 'GET',
'headers' => get_post_meta($api_doc_id, '_api_headers', true) ?: '{"Content-Type": "application/json"}',
'body' => get_post_meta($api_doc_id, '_api_body', true) ?: '{}',
'description' => $api_doc->post_content
);
}
}
ob_start();
?>
<div class="api-tester-container" data-doc-id="<?php echo esc_attr($api_doc_id); ?>">
<div class="api-tester-header">
<h3><?php echo esc_html($atts['title']); ?></h3>
<div class="api-tester-actions">
<button class="btn-run-api">发送请求</button>
<button class="btn-save-api">保存配置</button>
<button class="btn-clear-api">清空</button>
</div>
</div>
<div class="api-tester-form">
<div class="form-group">
<label>请求方法:</label>
<select class="api-method">
<option value="GET" <?php selected($api_data['method'] ?? $atts['default_method'], 'GET'); ?>>GET</option>
<option value="POST" <?php selected($api_data['method'] ?? $atts['default_method'], 'POST'); ?>>POST</option>
<option value="PUT" <?php selected($api_data['method'] ?? $atts['default_method'], 'PUT'); ?>>PUT</option>
<option value="DELETE" <?php selected($api_data['method'] ?? $atts['default_method'], 'DELETE'); ?>>DELETE</option>
<option value="PATCH" <?php selected($api_data['method'] ?? $atts['default_method'], 'PATCH'); ?>>PATCH</option>
</select>
<label class="url-label">请求URL:</label>
<input type="text" class="api-url"
value="<?php echo esc_attr($api_data['endpoint'] ?? $atts['default_url']); ?>"
placeholder="https://api.example.com/endpoint">
</div>
<div class="form-group">
<label>请求头:</label>
<textarea class="api-headers" rows="4"><?php
echo esc_textarea($api_data['headers'] ?? $atts['default_headers']);
?></textarea>
</div>
<div class="form-group">
<label>请求体:</label>
<textarea class="api-body" rows="8"><?php
echo esc_textarea($api_data['body'] ?? $atts['default_body']);
?></textarea>
</div>
<?php if (!empty($api_data['description'])): ?>
<div class="api-description">
<h4>API说明:</h4>
<div><?php echo wp_kses_post($api_data['description']); ?></div>
</div>
<?php endif; ?>
</div>
<div class="api-tester-response">
<div class="response-header">
<h4>响应结果</h4>
<div class="response-status">
<span class="status-label">状态:</span>
<span class="status-code">-</span>
<span class="status-time">时间: <span class="time-value">-</span>ms</span>
</div>
</div>
<div class="response-body">
<pre><code class="language-json">等待请求...</code></pre>
</div>
</div>
</div>
<?php
return ob_get_clean();
}
public function register_api_proxy_endpoint() {
register_rest_route('api-tester/v1', '/proxy', array(
'methods' => 'POST',
'callback' => array($this, 'handle_api_proxy_request'),
'permission_callback' => function() {
// 仅限登录用户使用
return is_user_logged_in();
},
));
}
public function handle_api_proxy_request(WP_REST_Request $request) {
$url = sanitize_url($request->get_param('url'));
$method = sanitize_text_field($request->get_param('method'));
$headers = $request->get_param('headers');
$body = $request->get_param('body');
// 验证URL
if (empty($url) || !filter_var($url, FILTER_VALIDATE_URL)) {
return new WP_Error('invalid_url', '无效的URL地址', array('status' => 400));
}
// 解析headers
$headers_array = array();
if (!empty($headers)) {
$headers_data = json_decode($headers, true);
if (is_array($headers_data)) {
foreach ($headers_data as $key => $value) {
$headers_array[sanitize_text_field($key)] = sanitize_text_field($value);
}
}
}
// 发送请求
$start_time = microtime(true);
$args = array(
'method' => $method,
'headers' => $headers_array,
'timeout' => 30,
'sslverify' => false,
);
if (in_array($method, array('POST', 'PUT', 'PATCH')) && !empty($body)) {
$args['body'] = $body;
}
$response = wp_remote_request($url, $args);
$end_time = microtime(true);
$response_time = round(($end_time - $start_time) * 1000, 2);
if (is_wp_error($response)) {
return array(
'success' => false,
'error' => $response->get_error_message(),
'response_time' => $response_time
);
}
$response_code = wp_remote_retrieve_response_code($response);
$response_body = wp_remote_retrieve_body($response);
$response_headers = wp_remote_retrieve_headers($response);
// 尝试解析JSON响应
$json_response = json_decode($response_body);
if (json_last_error() === JSON_ERROR_NONE) {
$formatted_body = json_encode($json_response, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
} else {
$formatted_body = $response_body;
}
return array(
'success' => true,
'status_code' => $response_code,
'headers' => $response_headers,
'body' => $formatted_body,
'response_time' => $response_time,
'size' => strlen($response_body)
);
}
public function enqueue_api_tester_assets() {
wp_enqueue_style('api-tester-css', get_template_directory_uri() . '/css/api-tester.css');
wp_enqueue_script('api-tester-js', get_template_directory_uri() . '/js/api-tester.js', array('jquery'), '1.0.0', true);
wp_localize_script('api-tester-js', 'apiTesterConfig', array(
'ajax_url' => admin_url('admin-ajax.php'),
'rest_url' => rest_url('api-tester/v1/'),
'nonce' => wp_create_nonce('api_tester_nonce')
));
}
}
new API_Test_Tool();
### 3.3 数据可视化工具
集成数据可视化工具,支持在知识库中直接创建和展示图表。
// 数据可视化工具
class Data_Visualization_Tool {
public function __construct() {
// 注册图表文章类型
add_action('init', array($this, 'register_chart_post_type'));
// 添加快捷码
add_shortcode('chart', array($this, 'chart_shortcode'));
// 添加图表编辑器元框
add_action('add_meta_boxes', array($this, 'add_chart_editor_meta_box'));
add_action('save_post_chart', array($this, 'save_chart_data'));
// 加载图表库
add_action('wp_enqueue_scripts', array($this, 'enqueue_chart_library'));
}
public function register_chart_post_type() {
$args = array(
'public' => true,
'label' => '数据图表',
'menu_icon' => 'dashicons-chart-line',
'supports' => array('title', 'author'),
'show_in_rest' => true,
'has_archive' => true,
);
register_post_type('chart', $args);
}
public function add_chart_editor_meta_box() {
add_meta_box(
'chart-editor',
'图表编辑器',
array($this, 'render_chart_editor'),
'chart',
'normal',
'high'
);
}
public function render_chart_editor($post) {
wp_nonce_field('save_chart_data', 'chart_data_nonce');
$chart_type = get_post_meta($post->ID, '_chart_type', true) ?: 'line';
$chart_data = get_post_meta($post->ID, '_chart_data', true) ?: '{}';
$chart_options = get_post_meta($post->ID, '_chart_options', true) ?: '{}';
?>
<div class="chart-editor-container">
<div class="chart-settings">
<div class="setting-group">
<label for="chart-type">图表类型:</label>
<select id="chart-type" name="chart_type">
<option value="line" <?php selected($chart_type, 'line'); ?>>折线图</option>
<option value="bar" <?php selected($chart_type, 'bar'); ?>>柱状图</option>
<option value="pie" <?php selected($chart_type, 'pie'); ?>>饼图</option>
<option value="doughnut" <?php selected($chart_type, 'doughnut'); ?>>环形图</option>
<option value="radar" <?php selected($chart_type, 'radar'); ?>>雷达图</option>
<option value="scatter" <?php selected($chart_type, 'scatter'); ?>>散点图</option>
</select>
</div>
<div class="setting-group">
<label for="chart-data">图表数据 (JSON格式):</label>
<textarea id="chart-data" name="chart_data" rows="10"><?php echo esc_textarea($chart_data); ?></textarea>
<p class="description">示例: {"labels": ["一月", "二月", "三月"], "datasets": [{"label": "销售额", "data": [65, 59, 80]}]}</p>
</div>
<div class="setting-group">
<label for="chart-options">图表选项 (JSON格式):</label>
<textarea id="chart-options" name="chart_options" rows="10"><?php echo esc_textarea($chart_options); ?></textarea>
<p class="description">配置图表显示选项,如颜色、标题等</p>
</div>
</div>
<div class="chart-preview">
<h4>预览:</h4>
<canvas id="chart-preview-canvas" width="400" height="300"></canvas>
</div>
</div>
<style>
.chart-editor-container {
display: flex;
gap: 20px;
}
.chart-settings {
flex: 1;
}
.chart-preview {
flex: 1;
}
.setting-group {
margin-bottom: 15px;
}
.setting-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.setting-group textarea,
.setting-group select {
width: 100%;
padding: 8px;
}
</style>
<script>
jQuery(document).ready(function($) {
function updateChartPreview() {
var type = $('#chart-type').val();
var data = $('#chart-data').val();
var options = $('#chart-options').val();
try {
var chartData = JSON.parse(data);
var chartOptions = JSON.parse(options);
var ctx = document.getElementById('chart-preview-canvas').getContext('2d');
// 销毁现有图表
if (window.previewChart) {
window.previewChart.destroy();
}
// 创建新图表
window.previewChart = new Chart(ctx, {
type: type,
data: chartData,
options: chartOptions
});
} catch (e) {
console.error('图表数据解析错误:', e);
}
}
// 监听输入变化
$('#chart-type, #chart-data, #chart-options').on('change keyup', function() {
updateChartPreview();
});
// 初始预览
updateChartPreview();
});
</script>
<?php
}
public function save_chart_data($post_id) {
if (!isset($_POST['chart_data_nonce']) ||
!wp_verify_nonce($_POST['chart_data_nonce'], 'save_chart_data')) {
return;
}
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if (!current_user_can('edit_post', $post_id)) return;
if (isset($_POST['chart_type'])) {
update_post_meta($post_id, '_chart_type', sanitize_text_field($_POST['chart_type']));
}
if (isset($_POST['chart_data'])) {
// 验证JSON格式
$chart_data = stripslashes($_POST['chart_data']);
json_decode($chart_data);
if (json_last_error() === JSON_ERROR_NONE) {
update_post_meta($
