/** * drag-editor.js — 拖拽编程画布编辑器页面逻辑 * * 组装:左侧块面板(分类 + 块拖入/点击添加) + 中间画布(DragCanvas) + 右侧参数面板。 * 工具栏:保存 / 校验 / 编译 / 发布。 */ (function () { 'use strict'; var state = { graphId: null, graphName: '', blockDefs: {categories: [], blocks: {}}, canvas: null, editingBlock: null }; function $(sel, root) { return (root || document).querySelector(sel); } function toast(msg, type) { if (window.bricks_toast) { window.bricks_toast(msg, type || 'info'); return; } alert(msg); } function apiFetch(url, data) { var opts = {method: 'POST', headers: {'Content-Type': 'application/json'}}; if (data !== undefined) { opts.body = JSON.stringify(data); } return fetch(url, opts).then(function (r) { return r.json(); }); } function entireUrl(path) { // 宿主注入 entire_url 模板函数时使用;否则原样 if (window.entire_url) { return window.entire_url(path); } return path; } /* ─────────────── 块面板 ─────────────── */ function renderBlockPanel() { var panel = $('#block-panel'); if (!panel) { return; } panel.innerHTML = ''; (state.blockDefs.categories || []).forEach(function (cat) { var sec = document.createElement('div'); sec.className = 'drag-cat'; var title = document.createElement('div'); title.className = 'drag-cat-title'; title.style.borderLeftColor = cat.color; title.textContent = cat.label; sec.appendChild(title); var list = document.createElement('div'); list.className = 'drag-cat-blocks'; Object.keys(state.blockDefs.blocks || {}).forEach(function (type) { var def = state.blockDefs.blocks[type]; if (def.category !== cat.key) { return; } var item = document.createElement('div'); item.className = 'drag-block-item'; item.draggable = true; item.dataset.type = type; item.innerHTML = '' + def.label + ''; item.title = def.description || def.label; // 拖入画布 item.addEventListener('dragstart', function (e) { e.dataTransfer.setData('text/plain', type); }); // 点击添加 item.addEventListener('click', function () { if (state.canvas) { state.canvas.addBlock(type); } }); list.appendChild(item); }); sec.appendChild(list); panel.appendChild(sec); }); } /* ─────────────── 参数面板 ─────────────── */ function renderParamPanel(block) { state.editingBlock = block; var panel = $('#param-panel'); if (!panel) { return; } panel.innerHTML = ''; if (!block) { panel.innerHTML = '
' + escapeHtml(d.content || '') + ''; showValidate([]); } else { out.innerHTML = ''; showValidate(d.errors || []); } }); } function doPublish() { if (!state.graphId) { toast('请先保存画布再发布', 'error'); return; } apiFetch(entireUrl('/drag/api/graph_publish.dspy'), {id: state.graphId}).then(function (r) { if (r.status === 200 && r.data && r.data.success) { toast('已发布到脚本引擎:' + (r.data.script_name || ''), 'success'); } else { toast((r.data && r.data.message) || '发布失败', 'error'); } }); } function escapeHtml(s) { return String(s || '').replace(/&/g, '&').replace(//g, '>'); } /* ─────────────── 初始化 ─────────────── */ function init() { // 画布名称 var nameInput = $('#graph-name'); if (nameInput) { nameInput.addEventListener('change', function () { state.graphName = nameInput.value; }); } // 工具栏按钮 var saveBtn = $('#btn-save'); var valBtn = $('#btn-validate'); var cmpBtn = $('#btn-compile'); var pubBtn = $('#btn-publish'); if (saveBtn) { saveBtn.addEventListener('click', doSave); } if (valBtn) { valBtn.addEventListener('click', doValidate); } if (cmpBtn) { cmpBtn.addEventListener('click', doCompile); } if (pubBtn) { pubBtn.addEventListener('click', doPublish); } // 画布区拖放(从块面板拖入) var stage = $('#canvas-area'); if (stage) { stage.addEventListener('dragover', function (e) { e.preventDefault(); }); stage.addEventListener('drop', function (e) { e.preventDefault(); var type = e.dataTransfer.getData('text/plain'); if (!type || !state.canvas) { return; } var rect = stage.getBoundingClientRect(); state.canvas.addBlock(type, e.clientX - rect.left - 60, e.clientY - rect.top - 20); }); } // 加载块定义 → 初始化画布 apiFetch(entireUrl('/drag/api/block_defs.dspy'), {}).then(function (r) { state.blockDefs = (r && r.data) || {categories: [], blocks: {}}; renderBlockPanel(); var container = $('#canvas-area'); if (!container) { return; } state.canvas = new DragCanvas(container, { blocks: [], connections: [], blockDefs: state.blockDefs, onSelect: function (b) { renderParamPanel(b); }, onChange: function () { /* 可做脏标记 */ } }); // 载入已保存画布 loadGraphIfAny(); }); } function loadGraphIfAny() { // 通过查询参数 ?graph_id=xxx 载入画布 var m = location.search.match(/[?&]graph_id=([^&]+)/); if (!m || !state.canvas) { return; } apiFetch(entireUrl('/drag/api/graph_get.dspy'), {id: decodeURIComponent(m[1])}).then(function (r) { if (r.status === 200 && r.data) { state.graphId = r.data.id; state.graphName = r.data.name || ''; var nameInput = $('#graph-name'); if (nameInput) { nameInput.value = state.graphName; } state.canvas.setGraph({blocks: r.data.blocks || [], connections: r.data.connections || []}); } }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();