Перейти к основному содержимому
claw
am
tools
Инструменты
Документы
Daoark
EN
Главная
Главная
/
Инструменты для изображений
›
Главная
manifest.json
"']/g, function(c) { return {'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]; }); } // 解析 DSL function parseDSL(text) { var lines = text.split('\n'); var nodes = {}; // id -> {id, type, label} var edges = []; // {from, to, label} for (var i = 0; i < lines.length; i++) { var line = lines[i].trim(); if (!line) continue; if (line.indexOf('->') >= 0) { // 边 var m = line.match(/^(\S+)\s*->\s*(\S+)\s*(?:\[(.+?)\])?/); if (m) { edges.push({ from: m[1], to: m[2], label: m[3] || '' }); if (!nodes[m[1]]) nodes[m[1]] = { id: m[1], type: 'proc', label: m[1] }; if (!nodes[m[2]]) nodes[m[2]] = { id: m[2], type: 'proc', label: m[2] }; } } else { // 节点 var parts = line.split(/\s+/); if (parts.length >= 3) { nodes[parts[0]] = { id: parts[0], type: parts[1], label: parts.slice(2).join(' ') }; } else if (parts.length >= 1) { if (!nodes[parts[0]]) nodes[parts[0]] = { id: parts[0], type: 'proc', label: parts[0] }; } } } return { nodes: nodes, edges: edges }; } // 简化布局:按 BFS 层级放置 function layout(graph) { var ids = Object.keys(graph.nodes); if (ids.length === 0) return { levels: {}, positions: {} }; // 找根(无入度的节点) var indeg = {}; for (var i = 0; i < ids.length; i++) indeg[ids[i]] = 0; for (var j = 0; j < graph.edges.length; j++) indeg[graph.edges[j].to]++; var roots = []; for (var k in indeg) if (indeg[k] === 0) roots.push(k); if (roots.length === 0) roots = [ids[0]]; // BFS var levels = {}; var queue = roots.map(function(r) { return { id: r, level: 0 }; }); var visited = {}; var maxLevel = 0; while (queue.length > 0) { var cur = queue.shift(); if (visited[cur.id]) continue; visited[cur.id] = true; if (!levels[cur.level]) levels[cur.level] = []; levels[cur.level].push(cur.id); if (cur.level > maxLevel) maxLevel = cur.level; for (var e = 0; e < graph.edges.length; e++) { if (graph.edges[e].from === cur.id) queue.push({ id: graph.edges[e].to, level: cur.level + 1 }); } } // 孤儿节点放最后 for (var l = 0; l < ids.length; l++) if (!visited[ids[l]]) { if (!levels[maxLevel + 1]) levels[maxLevel + 1] = []; levels[maxLevel + 1].push(ids[l]); } // 计算位置 var positions = {}; var rowH = 80; for (var lev = 0; lev <= maxLevel + 1; lev++) { if (!levels[lev]) continue; var row = levels[lev]; var colW = Math.min(180, 1000 / row.length); for (var m = 0; m < row.length; m++) { positions[row[m]] = { x: 50 + lev * 180, y: 50 + m * rowH }; } } return { levels: levels, positions: positions, maxLevel: maxLevel }; } // 节点形状 function renderNode(x, y, w, h, type, label) { var fill = '#e0f2fe'; var stroke = '#0284c7'; var s = ''; if (type === 'start' || type === 'end') { s = '
'; } else if (type === 'cond') { var pts = (x - w / 2) + ',' + y + ' ' + x + ',' + (y - h / 2) + ' ' + (x + w / 2) + ',' + y + ' ' + x + ',' + (y + h / 2); s = '
'; } else if (type === 'io') { var off = 15; var pts = (x - w / 2 + off) + ',' + (y - h / 2) + ' ' + (x + w / 2) + ',' + (y - h / 2) + ' ' + (x + w / 2 - off) + ',' + (y + h / 2) + ' ' + (x - w / 2) + ',' + (y + h / 2); s = '
'; } else { s = '
'; } // 文字换行 var lines = (label.match(/.{1,8}/g) || [label]); var ty = y - (lines.length - 1) * 7; for (var i = 0; i < lines.length; i++) { s += '
' + escape(lines[i]) + '
'; } return s; } function render(graph) { var lay = layout(graph); var maxY = 0; for (var id in lay.positions) if (lay.positions[id].y > maxY) maxY = lay.positions[id].y; var W = 50 + (lay.maxLevel + 2) * 180; var H = maxY + 80; var svg = '
'; // 边 for (var i = 0; i < graph.edges.length; i++) { var e = graph.edges[i]; var f = lay.positions[e.from]; var t = lay.positions[e.to]; if (!f || !t) continue; // 边起点终点(节点边缘) var fx = f.x + 60, fy = f.y; var tx = t.x - 60, ty = t.y; svg += '
'; if (e.label) { var mx = (fx + tx) / 2, my = (fy + ty) / 2; svg += '
'; svg += '
' + escape(e.label) + '
'; } } svg += '
'; // 节点 for (var nid in graph.nodes) { var n = graph.nodes[nid]; var p = lay.positions[nid]; if (!p) continue; var w = Math.max(80, n.label.length * 12 + 20); if (w > 160) w = 160; svg += renderNode(p.x, p.y, w, 40, n.type, n.label); } svg += '
'; return svg; } var currentSVG = ''; function doProcess() { try { var text = getVal('text-a'); if (!text) { showError('请输入 DSL'); return; } var graph = parseDSL(text); if (Object.keys(graph.nodes).length === 0) { showError('无有效节点'); return; } currentSVG = render(graph); showResult(currentSVG, '节点 ' + Object.keys(graph.nodes).length + ', 边 ' + graph.edges.length); } catch (e) { showError('生成失败: ' + (e.message || e)); } } $('btn-main').addEventListener('click', function(e) { e.preventDefault(); doProcess(); }); $('btn-download').addEventListener('click', function() { if (!currentSVG) return; var blob = new Blob([currentSVG], { type: 'image/svg+xml' }); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = 'flowchart.svg'; a.click(); URL.revokeObjectURL(url); }); $('btn-clear').addEventListener('click', function() { var inputs = document.querySelectorAll('input,textarea,select'); for (var i = 0; i < inputs.length; i++) { if (inputs[i].tagName === 'SELECT') inputs[i].selectedIndex = 0; else inputs[i].value = inputs[i].defaultValue; } var ra = $('result-area'); ra.hidden = true; ra.style.display = 'none'; var em = $('error-msg'); em.hidden = true; em.style.display = 'none'; $('btn-download').style.display = 'none'; }); })();