首页 / 应用软件 / 一步步实现,为WordPress打造内嵌的在线流程图绘制与原型设计工具

一步步实现,为WordPress打造内嵌的在线流程图绘制与原型设计工具

一步步实现:为WordPress打造内嵌的在线流程图绘制与原型设计工具

引言:为什么WordPress需要内置流程图与原型设计工具?

在当今数字化时代,可视化沟通已成为网站开发、项目管理和内容创作中不可或缺的一环。无论是网站架构规划、用户流程设计,还是产品原型展示,流程图和原型设计工具都能显著提高工作效率和沟通效果。

然而,大多数WordPress用户目前面临一个共同困境:当需要在文章中插入流程图或原型设计时,他们不得不依赖外部工具(如Lucidchart、Figma或Draw.io)创建图表,然后以图片形式导入WordPress。这种方法存在明显缺陷:无法直接编辑、图片质量损失、缺乏交互性,并且破坏了内容创作的一体化体验。

本文将通过详细的代码实现步骤,展示如何为WordPress打造一个完全内嵌的在线流程图绘制与原型设计工具,让用户无需离开WordPress后台即可创建、编辑和发布专业的可视化图表。

第一部分:项目规划与技术选型

1.1 功能需求分析

在开始编码之前,我们需要明确工具的核心功能:

  1. 基本绘图功能:支持常见流程图元素(矩形、圆形、菱形、箭头等)
  2. 原型设计组件:按钮、输入框、导航栏等UI元素库
  3. 实时协作:支持多用户同时编辑(可选高级功能)
  4. 导出与分享:支持PNG、SVG、JSON等多种格式导出
  5. WordPress集成:与文章编辑器无缝对接,支持短代码嵌入
  6. 版本控制:保存编辑历史,支持版本回退
  7. 响应式设计:确保在不同设备上都能良好工作

1.2 技术架构设计

考虑到开发效率和功能完整性,我们采用混合技术方案:

  • 前端绘图库:使用开源的Draw.io(mxGraph)核心库,这是一个功能强大且成熟的图形绘制库
  • WordPress集成:通过自定义插件方式集成到WordPress
  • 数据存储:使用WordPress自定义数据表结合文章元数据
  • 后端API:REST API处理图表保存、加载和用户权限管理

1.3 开发环境准备

在开始开发前,确保你的环境满足以下要求:

  1. WordPress 5.0+(支持Gutenberg编辑器)
  2. PHP 7.4+
  3. MySQL 5.6+
  4. 基本的Web开发知识(HTML、CSS、JavaScript、PHP)

第二部分:创建WordPress插件基础框架

2.1 初始化插件文件结构

首先,在WordPress的wp-content/plugins/目录下创建插件文件夹wp-flowchart-designer,并建立以下文件结构:

wp-flowchart-designer/
├── wp-flowchart-designer.php      # 主插件文件
├── includes/
│   ├── class-database.php         # 数据库处理类
│   ├── class-shortcode.php        # 短代码处理器
│   ├── class-rest-api.php         # REST API处理器
│   └── class-admin.php            # 后台管理类
├── admin/
│   ├── css/
│   │   └── admin-style.css        # 后台样式
│   └── js/
│       └── admin-script.js        # 后台脚本
├── public/
│   ├── css/
│   │   └── public-style.css       # 前台样式
│   ├── js/
│   │   ├── public-script.js       # 前台脚本
│   │   └── mxgraph/               # mxGraph库文件
│   └── partials/                  # 公共模板部分
├── assets/                        # 静态资源
└── uninstall.php                  # 插件卸载脚本

2.2 编写主插件文件

创建主插件文件wp-flowchart-designer.php

<?php
/**
 * Plugin Name: WordPress流程图与原型设计工具
 * Plugin URI: https://yourwebsite.com/wp-flowchart-designer
 * Description: 为WordPress添加内嵌的在线流程图绘制与原型设计功能
 * Version: 1.0.0
 * Author: 你的名字
 * License: GPL v2 or later
 * Text Domain: wp-flowchart-designer
 */

// 防止直接访问
if (!defined('ABSPATH')) {
    exit;
}

// 定义插件常量
define('WPFD_VERSION', '1.0.0');
define('WPFD_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('WPFD_PLUGIN_URL', plugin_dir_url(__FILE__));
define('WPFD_PLUGIN_BASENAME', plugin_basename(__FILE__));

// 自动加载类文件
spl_autoload_register(function ($class_name) {
    $prefix = 'WPFD_';
    $base_dir = WPFD_PLUGIN_DIR . 'includes/';
    
    $len = strlen($prefix);
    if (strncmp($prefix, $class_name, $len) !== 0) {
        return;
    }
    
    $relative_class = substr($class_name, $len);
    $file = $base_dir . 'class-' . strtolower(str_replace('_', '-', $relative_class)) . '.php';
    
    if (file_exists($file)) {
        require $file;
    }
});

// 初始化插件
function wpfd_init() {
    // 检查WordPress版本
    if (version_compare(get_bloginfo('version'), '5.0', '<')) {
        add_action('admin_notices', function() {
            echo '<div class="notice notice-error"><p>';
            echo __('WordPress流程图与原型设计工具需要WordPress 5.0或更高版本。', 'wp-flowchart-designer');
            echo '</p></div>';
        });
        return;
    }
    
    // 初始化各个组件
    WPFD_Database::init();
    WPFD_Shortcode::init();
    WPFD_REST_API::init();
    
    if (is_admin()) {
        WPFD_Admin::init();
    }
}
add_action('plugins_loaded', 'wpfd_init');

// 激活插件时创建数据库表
register_activation_hook(__FILE__, ['WPFD_Database', 'create_tables']);

// 卸载插件时清理数据
register_uninstall_hook(__FILE__, ['WPFD_Database', 'drop_tables']);

第三部分:实现数据库层

3.1 创建数据库表

includes/class-database.php中,我们创建存储图表数据的数据表:

<?php
class WPFD_Database {
    
    private static $table_name;
    
    public static function init() {
        global $wpdb;
        self::$table_name = $wpdb->prefix . 'wpfd_diagrams';
    }
    
    public static function create_tables() {
        global $wpdb;
        $charset_collate = $wpdb->get_charset_collate();
        
        $sql = "CREATE TABLE IF NOT EXISTS " . self::$table_name . " (
            id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
            post_id bigint(20) UNSIGNED DEFAULT NULL,
            user_id bigint(20) UNSIGNED NOT NULL,
            title varchar(255) NOT NULL,
            diagram_data longtext NOT NULL,
            diagram_type varchar(50) DEFAULT 'flowchart',
            settings text,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            KEY post_id (post_id),
            KEY user_id (user_id),
            KEY diagram_type (diagram_type)
        ) $charset_collate;";
        
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql);
        
        // 添加版本选项,便于后续升级
        add_option('wpfd_db_version', '1.0');
    }
    
    public static function drop_tables() {
        global $wpdb;
        
        $sql = "DROP TABLE IF EXISTS " . self::$table_name;
        $wpdb->query($sql);
        
        delete_option('wpfd_db_version');
    }
    
    public static function save_diagram($data) {
        global $wpdb;
        
        $defaults = [
            'post_id' => null,
            'user_id' => get_current_user_id(),
            'title' => '未命名图表',
            'diagram_data' => '',
            'diagram_type' => 'flowchart',
            'settings' => '{}'
        ];
        
        $data = wp_parse_args($data, $defaults);
        
        if (isset($data['id']) && $data['id'] > 0) {
            // 更新现有图表
            $wpdb->update(
                self::$table_name,
                [
                    'title' => sanitize_text_field($data['title']),
                    'diagram_data' => wp_json_encode($data['diagram_data']),
                    'diagram_type' => sanitize_text_field($data['diagram_type']),
                    'settings' => wp_json_encode($data['settings']),
                    'updated_at' => current_time('mysql')
                ],
                ['id' => $data['id']],
                ['%s', '%s', '%s', '%s', '%s'],
                ['%d']
            );
            
            return $data['id'];
        } else {
            // 插入新图表
            $wpdb->insert(
                self::$table_name,
                [
                    'post_id' => $data['post_id'],
                    'user_id' => $data['user_id'],
                    'title' => sanitize_text_field($data['title']),
                    'diagram_data' => wp_json_encode($data['diagram_data']),
                    'diagram_type' => sanitize_text_field($data['diagram_type']),
                    'settings' => wp_json_encode($data['settings']),
                    'created_at' => current_time('mysql'),
                    'updated_at' => current_time('mysql')
                ],
                ['%d', '%d', '%s', '%s', '%s', '%s', '%s', '%s']
            );
            
            return $wpdb->insert_id;
        }
    }
    
    public static function get_diagram($id) {
        global $wpdb;
        
        $result = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM " . self::$table_name . " WHERE id = %d",
                $id
            ),
            ARRAY_A
        );
        
        if ($result) {
            $result['diagram_data'] = json_decode($result['diagram_data'], true);
            $result['settings'] = json_decode($result['settings'], true);
        }
        
        return $result;
    }
    
    public static function get_user_diagrams($user_id = null, $limit = 20, $offset = 0) {
        global $wpdb;
        
        if (!$user_id) {
            $user_id = get_current_user_id();
        }
        
        $results = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT * FROM " . self::$table_name . " 
                WHERE user_id = %d 
                ORDER BY updated_at DESC 
                LIMIT %d OFFSET %d",
                $user_id, $limit, $offset
            ),
            ARRAY_A
        );
        
        foreach ($results as &$result) {
            $result['diagram_data'] = json_decode($result['diagram_data'], true);
            $result['settings'] = json_decode($result['settings'], true);
        }
        
        return $results;
    }
    
    public static function delete_diagram($id) {
        global $wpdb;
        
        return $wpdb->delete(
            self::$table_name,
            ['id' => $id],
            ['%d']
        );
    }
}

第四部分:集成Draw.io编辑器

4.1 引入mxGraph库

由于Draw.io基于mxGraph库,我们需要将其集成到插件中。可以从GitHub获取mxGraph库:

  1. 访问 https://github.com/jgraph/mxgraph
  2. 下载最新版本
  3. javascript目录复制到public/js/mxgraph/

4.2 创建编辑器界面

admin/js/admin-script.js中,我们创建编辑器初始化代码:

(function($) {
    'use strict';
    
    // 全局编辑器实例
    var wpfdEditor = null;
    
    // 初始化流程图编辑器
    function initFlowchartEditor(containerId, initialData) {
        // 检查mxGraph库是否已加载
        if (typeof mxClient === 'undefined') {
            console.error('mxGraph库未加载');
            return;
        }
        
        // 创建编辑器容器
        var container = document.getElementById(containerId);
        if (!container) {
            console.error('找不到容器: ' + containerId);
            return;
        }
        
        // 禁用右键菜单
        mxEvent.disableContextMenu(container);
        
        // 创建图形模型
        var model = new mxGraphModel();
        var graph = new mxGraph(container, model);
        
        // 配置图形
        graph.setConnectable(true);
        graph.setMultigraph(false);
        graph.setAllowDanglingEdges(false);
        graph.setDropEnabled(true);
        graph.setPanning(true);
        graph.setTooltips(true);
        
        // 启用缩放
        new mxKeyHandler(graph);
        new mxRubberband(graph);
        
        // 设置样式
        var style = graph.getStylesheet().getDefaultVertexStyle();
        style[mxConstants.STYLE_FONTFAMILY] = 'Helvetica, Arial, sans-serif';
        style[mxConstants.STYLE_FONTSIZE] = '12';
        style[mxConstants.STYLE_STROKECOLOR] = '#2D3748';
        style[mxConstants.STYLE_FILLCOLOR] = '#EDF2F7';
        
        // 加载初始数据
        if (initialData && initialData.xml) {
            try {
                var doc = mxUtils.parseXml(initialData.xml);
                var codec = new mxCodec(doc);
                codec.decode(doc.documentElement, graph.getModel());
            } catch (e) {
                console.error('解析图表数据失败:', e);
            }
        }
        
        // 保存编辑器实例
        wpfdEditor = {
            graph: graph,
            model: model,
            container: container
        };
        
        return wpfdEditor;
    }
    
    // 获取图表数据
    function getDiagramData() {
        if (!wpfdEditor) {
            return null;
        }
        
        var encoder = new mxCodec();
        var node = encoder.encode(wpfdEditor.model);
        
        return {
            xml: mxUtils.getXml(node),
            bounds: wpfdEditor.graph.getGraphBounds(),
            cells: wpfdEditor.model.getDescendants(wpfdEditor.model.getRoot())
        };
    }
    
    // 导出为图片
    function exportAsImage(format, bgColor) {
        if (!wpfdEditor) {
            return null;
        }
        
        var bounds = wpfdEditor.graph.getGraphBounds();
        var scale = wpfdEditor.graph.view.scale;
        
        // 创建临时canvas
        var canvas = document.createElement('canvas');
        canvas.width = bounds.width * scale;
        canvas.height = bounds.height * scale;
        
        var imgExport = new mxImageExport();
        var ctx = canvas.getContext('2d');
        
        // 设置背景色
        if (bgColor) {
            ctx.fillStyle = bgColor;
            ctx.fillRect(0, 0, canvas.width, canvas.height);
        }
        
        // 导出图形
        imgExport.drawState(wpfdEditor.graph.getView().getState(wpfdEditor.model.getRoot()), ctx);
        
        return canvas.toDataURL('image/' + format);
    }
    
    // 添加形状到工具栏
    function addShapeToToolbar(toolbarId, shapeConfig) {
        var toolbar = document.getElementById(toolbarId);
        if (!toolbar) return;
        
        var button = document.createElement('button');
        button.type = 'button';
        button.className = 'wpfd-toolbar-btn';
        button.innerHTML = shapeConfig.icon || shapeConfig.label;
        button.title = shapeConfig.label;
        
        button.addEventListener('click', function() {
            if (!wpfdEditor) return;
            
            wpfdEditor.graph.stopEditing();
            
            var parent = wpfdEditor.graph.getDefaultParent();
            wpfdEditor.model.beginUpdate();
            
            try {
                var vertex = wpfdEditor.graph.insertVertex(
                    parent,
                    null,
                    shapeConfig.label,
                    20, 20,
                    shapeConfig.width || 120,
                    shapeConfig.height || 60,
                    shapeConfig.style || ''
                );
                
                // 将新形状置于视图中心
                var geo = wpfdEditor.model.getGeometry(vertex);
                var bounds = wpfdEditor.graph.getGraphBounds();
                
                geo.x = Math.max(20, (bounds.width - geo.width) / 2);
                geo.y = Math.max(20, (bounds.height - geo.height) / 2);
                
                wpfdEditor.graph.setSelectionCell(vertex);
            } finally {
                wpfdEditor.model.endUpdate();
            }
        });
        
        toolbar.appendChild(button);
    }
    
    // 初始化工具栏
    function initToolbar(toolbarId) {
        // 预定义形状
        var shapes = [
            { label: '开始/结束', style: 'shape=ellipse', width: 100, height: 60 },
            { label: '过程', style: '', width: 120, height: 60 },
            { label: '判断', style: 'shape=rhombus', width: 100, height: 80 },
            { label: '数据', style: 'shape=parallelogram', width: 120, height: 60 },
            { label: '文档', style: 'shape=document', width: 100, height: 80 },
            { label: '子流程', style: 'shape=process;perimeter=rectanglePerimeter', width: 140, height: 80 }
        ];
        
        shapes.forEach(function(shape) {
            addShapeToToolbar(toolbarId, shape);
        });
    }
    
    // WordPress集成
    $(document).ready(function() {
        // 初始化编辑器
        if ($('#wpfd-editor-container').length) {
            var initialData = window.wpfdInitialData || {};
            wpfdEditor = initFlowchartEditor('wpfd-editor-container', initialData);
            

('#wpfd-toolbar').length) {

            initToolbar('wpfd-toolbar');
        }
    }
    
    // 保存图表
    $('#wpfd-save-diagram').on('click', function() {
        var diagramData = getDiagramData();
        var title = $('#wpfd-diagram-title').val() || '未命名图表';
        
        if (!diagramData) {
            alert('无法获取图表数据');
            return;
        }
        
        // 显示保存中状态
        var $button = $(this);
        var originalText = $button.text();
        $button.text('保存中...').prop('disabled', true);
        
        // 发送保存请求
        $.ajax({
            url: wpfd_ajax.ajax_url,
            type: 'POST',
            data: {
                action: 'wpfd_save_diagram',
                nonce: wpfd_ajax.nonce,
                title: title,
                diagram_data: JSON.stringify(diagramData),
                diagram_type: 'flowchart'
            },
            success: function(response) {
                if (response.success) {
                    alert('图表保存成功!');
                    if (response.data.redirect) {
                        window.location.href = response.data.redirect;
                    }
                } else {
                    alert('保存失败: ' + response.data.message);
                }
            },
            error: function() {
                alert('保存请求失败,请检查网络连接');
            },
            complete: function() {
                $button.text(originalText).prop('disabled', false);
            }
        });
    });
    
    // 导出功能
    $('.wpfd-export-btn').on('click', function() {
        var format = $(this).data('format');
        var imageData = exportAsImage(format, '#ffffff');
        
        if (imageData) {
            // 创建下载链接
            var link = document.createElement('a');
            link.download = 'diagram.' + format;
            link.href = imageData;
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }
    });
    
    // 插入到文章
    $('#wpfd-insert-to-post').on('click', function() {
        var diagramData = getDiagramData();
        
        if (!diagramData) {
            alert('无法获取图表数据');
            return;
        }
        
        // 生成短代码
        var shortcode = '[wpfd_diagram id="' + wpfd_ajax.diagram_id + '"]';
        
        // 发送到编辑器
        if (typeof wp !== 'undefined' && wp.media && wp.media.editor) {
            wp.media.editor.insert(shortcode);
        } else {
            // 备用方案:复制到剪贴板
            var tempInput = document.createElement('input');
            tempInput.value = shortcode;
            document.body.appendChild(tempInput);
            tempInput.select();
            document.execCommand('copy');
            document.body.removeChild(tempInput);
            alert('短代码已复制到剪贴板');
        }
    });
});

// 暴露公共API
window.WPFD_Editor = {
    init: initFlowchartEditor,
    getData: getDiagramData,
    exportImage: exportAsImage,
    addShape: addShapeToToolbar
};

})(jQuery);


### 4.3 创建编辑器CSS样式

在`admin/css/admin-style.css`中添加编辑器样式:

/ 流程图编辑器主容器 /
.wpfd-editor-wrapper {

display: flex;
flex-direction: column;
height: 800px;
border: 1px solid #ccd0d4;
border-radius: 4px;
overflow: hidden;
background: #f8f9fa;

}

/ 编辑器工具栏 /
.wpfd-editor-toolbar {

background: #fff;
border-bottom: 1px solid #ccd0d4;
padding: 10px;
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
min-height: 60px;

}

.wpfd-toolbar-group {

display: flex;
align-items: center;
gap: 4px;
padding: 0 10px;
border-right: 1px solid #e0e0e0;

}

.wpfd-toolbar-group:last-child {

border-right: none;

}

.wpfd-toolbar-btn {

background: #f0f0f0;
border: 1px solid #ddd;
border-radius: 3px;
padding: 6px 12px;
cursor: pointer;
font-size: 13px;
color: #333;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 5px;

}

.wpfd-toolbar-btn:hover {

background: #e0e0e0;
border-color: #ccc;

}

.wpfd-toolbar-btn.active {

background: #007cba;
color: white;
border-color: #007cba;

}

.wpfd-toolbar-btn i {

font-size: 16px;

}

/ 编辑器主区域 /
.wpfd-editor-main {

display: flex;
flex: 1;
overflow: hidden;

}

/ 左侧形状面板 /
.wpfd-shapes-panel {

width: 200px;
background: #fff;
border-right: 1px solid #ccd0d4;
padding: 15px;
overflow-y: auto;

}

.wpfd-shapes-category {

margin-bottom: 20px;

}

.wpfd-shapes-category h4 {

margin: 0 0 10px 0;
font-size: 13px;
color: #666;
text-transform: uppercase;
font-weight: 600;

}

.wpfd-shapes-list {

display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8px;

}

.wpfd-shape-item {

background: #f8f9fa;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 10px;
cursor: move;
text-align: center;
font-size: 12px;
transition: all 0.2s;

}

.wpfd-shape-item:hover {

background: #e9ecef;
border-color: #007cba;

}

/ 画布区域 /
.wpfd-canvas-container {

flex: 1;
position: relative;
overflow: hidden;
background: #fff;

}

wpfd-editor-container {

width: 100%;
height: 100%;
min-height: 600px;

}

/ 右侧属性面板 /
.wpfd-properties-panel {

width: 300px;
background: #fff;
border-left: 1px solid #ccd0d4;
padding: 15px;
overflow-y: auto;

}

.wpfd-property-group {

margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #eee;

}

.wpfd-property-group:last-child {

border-bottom: none;
margin-bottom: 0;

}

.wpfd-property-group h4 {

margin: 0 0 12px 0;
font-size: 14px;
color: #333;
font-weight: 600;

}

.wpfd-property-item {

margin-bottom: 10px;

}

.wpfd-property-item label {

display: block;
margin-bottom: 5px;
font-size: 13px;
color: #555;

}

.wpfd-property-item input[type="text"],
.wpfd-property-item input[type="number"],
.wpfd-property-item select {

width: 100%;
padding: 6px 8px;
border: 1px solid #ddd;
border-radius: 3px;
font-size: 13px;

}

.wpfd-property-item input[type="color"] {

width: 100%;
height: 35px;
padding: 2px;
border: 1px solid #ddd;
border-radius: 3px;

}

/ 编辑器底部状态栏 /
.wpfd-editor-statusbar {

background: #fff;
border-top: 1px solid #ccd0d4;
padding: 8px 15px;
font-size: 12px;
color: #666;
display: flex;
justify-content: space-between;
align-items: center;

}

/ 响应式设计 /
@media (max-width: 1200px) {

.wpfd-editor-wrapper {
    height: 600px;
}

.wpfd-shapes-panel {
    width: 180px;
}

.wpfd-properties-panel {
    width: 250px;
}

}

@media (max-width: 768px) {

.wpfd-editor-main {
    flex-direction: column;
}

.wpfd-shapes-panel {
    width: 100%;
    height: 150px;
    border-right: none;
    border-bottom: 1px solid #ccd0d4;
}

.wpfd-properties-panel {
    width: 100%;
    height: 200px;
    border-left: none;
    border-top: 1px solid #ccd0d4;
}

.wpfd-shapes-list {
    grid-template-columns: repeat(4, 1fr);
}

}

/ 图标字体 /
@font-face {

font-family: 'wpfd-icons';
src: url('../fonts/wpfd-icons.woff2') format('woff2'),
     url('../fonts/wpfd-icons.woff') format('woff');
font-weight: normal;
font-style: normal;

}

.wpfd-icon {

font-family: 'wpfd-icons';
speak: never;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;

}

.wpfd-icon-rectangle:before { content: "e900"; }
.wpfd-icon-circle:before { content: "e901"; }
.wpfd-icon-diamond:before { content: "e902"; }
.wpfd-icon-arrow:before { content: "e903"; }
.wpfd-icon-text:before { content: "e904"; }
.wpfd-icon-line:before { content: "e905"; }
.wpfd-icon-save:before { content: "e906"; }
.wpfd-icon-export:before { content: "e907"; }
.wpfd-icon-undo:before { content: "e908"; }
.wpfd-icon-redo:before { content: "e909"; }
.wpfd-icon-zoom-in:before { content: "e90a"; }
.wpfd-icon-zoom-out:before { content: "e90b"; }


## 第五部分:实现REST API接口

### 5.1 创建REST API处理器

在`includes/class-rest-api.php`中实现API接口:

<?php
class WPFD_REST_API {


public static function init() {
    add_action('rest_api_init', [__CLASS__, 'register_routes']);
}

public static function register_routes() {
    // 图表CRUD接口
    register_rest_route('wpfd/v1', '/diagrams', [
        [
            'methods' => 'GET',
            'callback' => [__CLASS__, 'get_diagrams'],
            'permission_callback' => [__CLASS__, 'check_permission'],
            'args' => [
                'per_page' => [
                    'default' => 20,
                    'validate_callback' => function($param) {
                        return is_numeric($param) && $param > 0 && $param <= 100;
                    }
                ],
                'page' => [
                    'default' => 1,
                    'validate_callback' => function($param) {
                        return is_numeric($param) && $param > 0;
                    }
                ]
            ]
        ],
        [
            'methods' => 'POST',
            'callback' => [__CLASS__, 'create_diagram'],
            'permission_callback' => [__CLASS__, 'check_permission']
        ]
    ]);
    
    register_rest_route('wpfd/v1', '/diagrams/(?P<id>d+)', [
        [
            'methods' => 'GET',
            'callback' => [__CLASS__, 'get_diagram'],
            'permission_callback' => [__CLASS__, 'check_permission']
        ],
        [
            'methods' => 'PUT',
            'callback' => [__CLASS__, 'update_diagram'],
            'permission_callback' => [__CLASS__, 'check_permission']
        ],
        [
            'methods' => 'DELETE',
            'callback' => [__CLASS__, 'delete_diagram'],
            'permission_callback' => [__CLASS__, 'check_permission']
        ]
    ]);
    
    // 导出接口
    register_rest_route('wpfd/v1', '/export/(?P<id>d+)', [
        [
            'methods' => 'GET',
            'callback' => [__CLASS__, 'export_diagram'],
            'permission_callback' => [__CLASS__, 'check_permission'],
            'args' => [
                'format' => [
                    'default' => 'png',
                    'validate_callback' => function($param) {
                        return in_array($param, ['png', 'jpg', 'svg', 'pdf']);
                    }
                ]
            ]
        ]
    ]);
    
    // 搜索接口
    register_rest_route('wpfd/v1', '/search', [
        [
            'methods' => 'GET',
            'callback' => [__CLASS__, 'search_diagrams'],
            'permission_callback' => [__CLASS__, 'check_permission']
        ]
    ]);
}

public static function check_permission($request) {
    // 检查用户是否登录
    if (!is_user_logged_in()) {
        return new WP_Error('rest_forbidden', __('请先登录'), ['status' => 401]);
    }
    
    // 检查用户权限
    $method = $request->get_method();
    $user_id = get_current_user_id();
    
    // 对于GET请求,允许所有登录用户访问
    if ($method === 'GET') {
        return true;
    }
    
    // 对于其他请求,需要编辑权限
    if (!current_user_can('edit_posts')) {
        return new WP_Error('rest_forbidden', __('权限不足'), ['status' => 403]);
    }
    
    return true;
}

public static function get_diagrams($request) {
    $params = $request->get_params();
    $per_page = intval($params['per_page']);
    $page = intval($params['page']);
    $offset = ($page - 1) * $per_page;
    
    global $wpdb;
    $table_name = $wpdb->prefix . 'wpfd_diagrams';
    $user_id = get_current_user_id();
    
    // 获取图表总数
    $total = $wpdb->get_var(
        $wpdb->prepare(
            "SELECT COUNT(*) FROM {$table_name} WHERE user_id = %d",
            $user_id
        )
    );
    
    // 获取图表列表
    $results = $wpdb->get_results(
        $wpdb->prepare(
            "SELECT id, title, diagram_type, created_at, updated_at 
             FROM {$table_name} 
             WHERE user_id = %d 
             ORDER BY updated_at DESC 
             LIMIT %d OFFSET %d",
            $user_id, $per_page, $offset
        ),
        ARRAY_A
    );
    
    // 准备响应数据
    $diagrams = [];
    foreach ($results as $row) {
        $diagrams[] = [
            'id' => intval($row['id']),
            'title' => $row['title'],
            'type' => $row['diagram_type'],
            'created_at' => $row['created_at'],
            'updated_at' => $row['updated_at'],
            'edit_url' => admin_url('admin.php?page=wpfd-edit&id=' . $row['id'])
        ];
    }
    
    $response = new WP_REST_Response($diagrams);
    $response->header('X-WP-Total', $total);
    $response->header('X-WP-TotalPages', ceil($total / $per_page));
    
    return $response;
}

public static function get_diagram($request) {
    $id = intval($request['id']);
    $diagram = WPFD_Database::get_diagram($id);
    
    if (!$diagram) {
        return new WP_Error('not_found', __('图表不存在'), ['status' => 404]);
    }
    
    // 检查权限:只能访问自己的图表
    if ($diagram['user_id'] != get_current_user_id() && !current_user_can('manage_options')) {
        return new WP_Error('forbidden', __('无权访问此图表'), ['status' => 403]);
    }
    
    return rest_ensure_response($diagram);
}

public static function create_diagram($request) {
    $data = $request->get_json_params();
    
    // 验证数据
    if (empty($data['title'])) {
        return new WP_Error('invalid_data', __('标题不能为空'), ['status' => 400]);
    }
    
    if (empty($data['diagram_data'])) {
        return new WP_Error('invalid_data', __('图表数据不能为空'), ['status' => 400]);
    }
    
    // 准备保存数据
    $save_data = [
        'title' => sanitize_text_field($data['title']),
        'diagram_data' => $data['diagram_data'],
        'diagram_type' => isset($data['diagram_type']) ? sanitize_text_field($data['diagram_type']) : 'flowchart',
        'settings' => isset($data['settings']) ? $data['settings'] : []
    ];
    
    // 如果有post_id,关联到文章
    if (isset($data['post_id']) && $data['post_id
本文来自网络,不代表柔性供应链服务中心立场,转载请注明出处:https://mall.org.cn/5232.html

EXCHANGES®作者

上一篇
下一篇

为您推荐

发表回复

联系我们

联系我们

18559313275

在线咨询: QQ交谈

邮箱: vip@exchanges.center

工作时间:周一至周五,9:00-17:30,节假日休息
返回顶部