sage/wwwroot/tmp/dag.html
2025-11-03 15:34:22 +08:00

157 lines
4.8 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>DAG Editor</title>
<style>
body { font-family: Arial; margin: 0; overflow: hidden; }
#toolbar { padding: 10px; background: #eee; border-bottom: 1px solid #ccc; }
#canvas { position: relative; width: 100%; height: calc(100vh - 40px); background: #fafafa; }
.node {
position: absolute; padding: 10px 15px;
background: #fff; border: 2px solid #333; border-radius: 6px;
cursor: move; user-select: none; text-align: center;
}
.start { background: #d1ffd1; border-color: green; }
.end { background: #ffd1d1; border-color: red; }
.selected { border-color: blue; }
svg { position: absolute; top: 0; left: 0; pointer-events: none; }
line, path { stroke: #333; stroke-width: 2; marker-end: url(#arrow); }
</style>
</head>
<body>
<div id="toolbar">
<button onclick="addNode('normal')">添加节点</button>
<button onclick="addNode('start')">添加开始节点</button>
<button onclick="addNode('end')">添加结束节点</button>
<button onclick="deleteSelected()">删除选中</button>
<button onclick="exportData()">导出流程</button>
</div>
<div id="canvas">
<svg id="edges"></svg>
</div>
<script>
let canvas = document.getElementById('canvas');
let edgesSVG = document.getElementById('edges');
let nodes = [];
let edges = [];
let selectedNode = null;
let linkingStart = null;
// 添加箭头标记
let defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
let marker = document.createElementNS("http://www.w3.org/2000/svg", "marker");
marker.setAttribute("id", "arrow");
marker.setAttribute("markerWidth", "10");
marker.setAttribute("markerHeight", "10");
marker.setAttribute("refX", "10");
marker.setAttribute("refY", "3");
marker.setAttribute("orient", "auto");
let arrowPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
arrowPath.setAttribute("d", "M0,0 L10,3 L0,6 Z");
arrowPath.setAttribute("fill", "#333");
marker.appendChild(arrowPath);
defs.appendChild(marker);
edgesSVG.appendChild(defs);
function addNode(type) {
let div = document.createElement('div');
div.className = 'node ' + (type === 'start' ? 'start' : type === 'end' ? 'end' : '');
div.style.left = "100px";
div.style.top = "100px";
div.contentEditable = true;
div.innerText = type === 'start' ? "开始" : type === 'end' ? "结束" : "节点";
div.onclick = (e) => {
e.stopPropagation();
if (linkingStart && linkingStart !== div) {
addEdge(linkingStart, div);
linkingStart.classList.remove("selected");
linkingStart = null;
} else {
if (selectedNode) selectedNode.classList.remove("selected");
div.classList.add("selected");
selectedNode = div;
linkingStart = div; // 准备连线
}
};
// 拖拽
div.onmousedown = (e) => {
let offsetX = e.offsetX, offsetY = e.offsetY;
function move(ev) {
div.style.left = (ev.pageX - offsetX) + "px";
div.style.top = (ev.pageY - offsetY) + "px";
updateEdges();
}
function up() {
document.removeEventListener('mousemove', move);
document.removeEventListener('mouseup', up);
}
document.addEventListener('mousemove', move);
document.addEventListener('mouseup', up);
};
canvas.appendChild(div);
nodes.push(div);
}
function addEdge(from, to) {
let edge = {from, to};
edges.push(edge);
updateEdges();
}
function updateEdges() {
edgesSVG.innerHTML = "<defs>"+defs.innerHTML+"</defs>";
edges.forEach(e => {
let x1 = e.from.offsetLeft + e.from.offsetWidth/2;
let y1 = e.from.offsetTop + e.from.offsetHeight/2;
let x2 = e.to.offsetLeft + e.to.offsetWidth/2;
let y2 = e.to.offsetTop + e.to.offsetHeight/2;
let path = document.createElementNS("http://www.w3.org/2000/svg", "line");
path.setAttribute("x1", x1);
path.setAttribute("y1", y1);
path.setAttribute("x2", x2);
path.setAttribute("y2", y2);
path.setAttribute("stroke", "#333");
path.setAttribute("stroke-width", "2");
path.setAttribute("marker-end", "url(#arrow)");
path.onclick = (ev) => {
ev.stopPropagation();
edges = edges.filter(ed => ed !== e);
updateEdges();
}
edgesSVG.appendChild(path);
});
}
function deleteSelected() {
if (selectedNode) {
edges = edges.filter(e => e.from !== selectedNode && e.to !== selectedNode);
canvas.removeChild(selectedNode);
nodes = nodes.filter(n => n !== selectedNode);
selectedNode = null;
updateEdges();
}
}
function exportData() {
let data = {
nodes: nodes.map(n => ({
id: n.innerText,
type: n.classList.contains('start') ? 'start' : n.classList.contains('end') ? 'end' : 'normal',
x: n.offsetLeft, y: n.offsetTop
})),
edges: edges.map(e => ({ from: e.from.innerText, to: e.to.innerText }))
};
console.log(JSON.stringify(data, null, 2));
alert("导出数据已打印到控制台");
}
</script>
</body>
</html>