From 60196889bb685ac94322d398468fada71f23589b Mon Sep 17 00:00:00 2001 From: Kirigaya <1193466151@qq.com> Date: Tue, 1 Jul 2025 00:17:14 +0800 Subject: [PATCH] finish work --- .../components/main-panel/tool/diagram.vue | 127 +++++++++++------- yarn.lock | 39 +++--- 2 files changed, 93 insertions(+), 73 deletions(-) diff --git a/renderer/src/components/main-panel/tool/diagram.vue b/renderer/src/components/main-panel/tool/diagram.vue index db2b7ae..c4968b4 100644 --- a/renderer/src/components/main-panel/tool/diagram.vue +++ b/renderer/src/components/main-panel/tool/diagram.vue @@ -65,6 +65,8 @@ const drawDiagram = async () => { height: 48, labels: [{ text: tool.name || 'Tool' }] })); + + // 默认按照链表进行串联 state.edges = tools.slice(1).map((_, i) => ({ id: `e${i}`, sources: [String(i)], @@ -80,14 +82,19 @@ function renderSvg() { const width = Math.max(...state.nodes.map(n => (n.x || 0) + (n.width || 160)), 400) + 60; const height = Math.max(...state.nodes.map(n => (n.y || 0) + (n.height || 48)), 300) + 60; - d3.select(svgContainer.value).selectAll('*').remove(); - - const svg = d3 - .select(svgContainer.value) - .append('svg') - .attr('width', width) - .attr('height', height) - .style('user-select', 'none'); + // 不再全量清空,只清空 svg 元素 + let svg = d3.select(svgContainer.value).select('svg'); + if (svg.empty()) { + svg = d3 + .select(svgContainer.value) + .append('svg') + .attr('width', width) + .attr('height', height) + .style('user-select', 'none'); + } else { + svg.attr('width', width).attr('height', height); + svg.selectAll('defs').remove(); + } // Arrow marker svg @@ -104,41 +111,65 @@ function renderSvg() { .attr('d', 'M 0 0 L 10 5 L 0 10 z') .attr('fill', 'var(--main-color)'); - // Draw edges + // Draw edges with enter animation + const allSections: { id: string, section: any }[] = []; (state.edges || []).forEach(edge => { const sections = edge.sections || []; - sections.forEach(section => { - svg.append('line') - .attr('x1', section.startPoint.x + 30) - .attr('y1', section.startPoint.y + 30) - .attr('x2', section.endPoint.x + 30) - .attr('y2', section.endPoint.y + 30) - .attr('stroke', 'var(--main-color)') - .attr('stroke-width', 2.5) - .attr('marker-end', 'url(#arrow)'); + sections.forEach((section, idx) => { + allSections.push({ + id: (edge.id || '') + '-' + (section.id || idx), + section + }); }); }); - // Draw nodes - const nodeGroup = svg.selectAll('.node') - .data(state.nodes, d => d.id) - .enter() + const edgeSelection = svg.selectAll('.edge') + .data(allSections, d => d.id); + + edgeSelection.exit().remove(); + + const edgeEnter = edgeSelection.enter() + .append('line') + .attr('class', 'edge') + .attr('x1', d => d.section.startPoint.x + 30) + .attr('y1', d => d.section.startPoint.y + 30) + .attr('x2', d => d.section.endPoint.x + 30) + .attr('y2', d => d.section.endPoint.y + 30) + .attr('stroke', 'var(--main-color)') + .attr('stroke-width', 2.5) + .attr('marker-end', 'url(#arrow)') + .attr('opacity', 0); + + edgeEnter + .transition() + .duration(600) + .attr('opacity', 1); + + edgeSelection.merge(edgeEnter) + .transition() + .duration(600) + .ease(d3.easeCubicInOut) + .attr('x1', d => d.section.startPoint.x + 30) + .attr('y1', d => d.section.startPoint.y + 30) + .attr('x2', d => d.section.endPoint.x + 30) + .attr('y2', d => d.section.endPoint.y + 30); + + // --- 节点动画部分 --- + const nodeGroup = svg.selectAll('.node') + .data(state.nodes, d => d.id); + + nodeGroup.exit().remove(); + + const nodeGroupEnter = nodeGroup.enter() .append('g') .attr('class', 'node') .attr('transform', d => `translate(${(d.x || 0) + 30}, ${(d.y || 0) + 30})`) .style('cursor', 'pointer') - .on('mousedown', function (event, d) { - event.stopPropagation(); - state.draggingNodeId = d.id; - state.offset = { - x: event.offsetX - ((d.x || 0) + 30), - y: event.offsetY - ((d.y || 0) + 30) - }; - }) + .attr('opacity', 0) + .on('mousedown', null) .on('mouseup', function (event, d) { event.stopPropagation(); if (state.selectedNodeId && state.selectedNodeId !== d.id) { - // Add new edge state.edges.push({ id: `e${state.selectedNodeId}_${d.id}_${Date.now()}`, sources: [state.selectedNodeId], @@ -153,7 +184,7 @@ function renderSvg() { state.draggingNodeId = null; }); - nodeGroup.append('rect') + nodeGroupEnter.append('rect') .attr('width', d => d.width) .attr('height', d => d.height) .attr('rx', 16) @@ -162,7 +193,7 @@ function renderSvg() { .attr('stroke', 'var(--main-color)') .attr('stroke-width', 2); - nodeGroup.append('text') + nodeGroupEnter.append('text') .attr('x', d => d.width / 2) .attr('y', d => d.height / 2 + 6) .attr('text-anchor', 'middle') @@ -171,24 +202,18 @@ function renderSvg() { .attr('font-weight', 600) .text(d => d.labels?.[0]?.text || 'Tool'); - // Drag behavior - d3.select(window) - .on('mousemove.diagram', event => { - if (state.draggingNodeId) { - const node = state.nodes.find(n => n.id === state.draggingNodeId); - if (node) { - node.x = event.offsetX - state.offset.x - 30; - node.y = event.offsetY - state.offset.y - 30; - renderSvg(); - } - } - }) - .on('mouseup.diagram', () => { - if (state.draggingNodeId) { - state.draggingNodeId = null; - recomputeLayout().then(renderSvg); - } - }); + // 节点 enter 动画 + nodeGroupEnter + .transition() + .duration(600) + .attr('opacity', 1); + + // 节点 update 动画 + nodeGroup + .transition() + .duration(600) + .ease(d3.easeCubicInOut) + .attr('transform', d => `translate(${(d.x || 0) + 30}, ${(d.y || 0) + 30})`); } onMounted(() => { diff --git a/yarn.lock b/yarn.lock index 7a5a592..a3bb1e8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -492,10 +492,10 @@ resolved "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz" integrity sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg== -"@esbuild/darwin-arm64@0.25.5": +"@esbuild/win32-x64@0.25.5": version "0.25.5" - resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz" - integrity sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ== + resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz" + integrity sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g== "@faker-js/faker@^9.8.0": version "9.8.0" @@ -736,7 +736,7 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@openmcp/renderer@file:/Users/bytedance/projects/openmcp-client/renderer": +"@openmcp/renderer@file:C:\\Users\\K\\project\\openmcp-client\\renderer": version "0.1.0" resolved "file:renderer" dependencies: @@ -764,7 +764,7 @@ vue-router "^4.5.0" xml2js "^0.6.2" -"@openmcp/service@file:/Users/bytedance/projects/openmcp-client/service": +"@openmcp/service@file:C:\\Users\\K\\project\\openmcp-client\\service": version "0.0.1" resolved "file:service" dependencies: @@ -869,10 +869,10 @@ estree-walker "^2.0.2" picomatch "^4.0.2" -"@rollup/rollup-darwin-arm64@4.44.0": +"@rollup/rollup-win32-x64-msvc@4.44.0": version "4.44.0" - resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.0.tgz" - integrity sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA== + resolved "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.0.tgz" + integrity sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ== "@rushstack/node-core-library@5.13.1": version "5.13.1" @@ -1665,10 +1665,10 @@ ora "^8.1.0" semver "^7.6.2" -"@vscode/vsce-sign-darwin-arm64@2.0.5": +"@vscode/vsce-sign-win32-x64@2.0.5": version "2.0.5" - resolved "https://registry.npmjs.org/@vscode/vsce-sign-darwin-arm64/-/vsce-sign-darwin-arm64-2.0.5.tgz" - integrity sha512-z2Q62bk0ptADFz8a0vtPvnm6vxpyP3hIEYMU+i1AWz263Pj8Mc38cm/4sjzxu+LIsAfhe9HzvYNS49lV+KsatQ== + resolved "https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.5.tgz" + integrity sha512-1ixKFGM2FwM+6kQS2ojfY3aAelICxjiCzeg4nTHpkeU1Tfs4RC+lVLrgq5NwcBC7ZLr6UfY3Ct3D6suPeOf7BQ== "@vscode/vsce-sign@^2.0.0": version "2.0.6" @@ -3456,10 +3456,10 @@ es-set-tostringtag@^2.1.0: has-tostringtag "^1.0.2" hasown "^2.0.2" -esbuild-darwin-arm64@0.14.54: +esbuild-windows-64@0.14.54: version "0.14.54" - resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz" - integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw== + resolved "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz" + integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ== esbuild@^0.14.14: version "0.14.54" @@ -3916,11 +3916,6 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@~2.3.2, fsevents@~2.3.3: - version "2.3.3" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" @@ -6985,10 +6980,10 @@ tunnel@0.0.6: resolved "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz" integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== -turbo-darwin-arm64@2.5.4: +turbo-windows-64@2.5.4: version "2.5.4" - resolved "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-2.5.4.tgz" - integrity sha512-2+Nx6LAyuXw2MdXb7pxqle3MYignLvS7OwtsP9SgtSBaMlnNlxl9BovzqdYAgkUW3AsYiQMJ/wBRb7d+xemM5A== + resolved "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-2.5.4.tgz" + integrity sha512-EQUO4SmaCDhO6zYohxIjJpOKRN3wlfU7jMAj3CgcyTPvQR/UFLEKAYHqJOnJtymbQmiiM/ihX6c6W6Uq0yC7mA== turbo@^2.5.3: version "2.5.4"