| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- /**
- * Append 拼接节点组件
- * 用于拼接多个字符串片段
- *
- * 特点:
- * - 没有执行端口(纯数据节点)
- * - 左边:动态数量的输入端口(文本片段 + 变量引用)
- * - 右边:一个输出端口(拼接结果)
- * - 蓝色主题
- */
- import { useState, useEffect } from 'react';
- import { parseEchoValue } from '../echo/echo-node.js';
- import { NodeRenderer } from '../node-renderer.jsx';
- import './append-node.css';
- export function AppendNode(props) {
- const { node, connections, onPortMouseDown, onPortMouseUp } = props;
- const [segments, setSegments] = useState([]);
-
- console.log(`🎨 [AppendNode] 渲染 Append 节点:`, { id: node.id, x: node.x, y: node.y, value: node.data?.value });
-
- // 解析 value 获取片段
- useEffect(() => {
- const value = node.data?.value || '';
- const parsed = parseEchoValue(value);
- console.log(` 📊 [AppendNode useEffect] 更新 segments:`, parsed);
- setSegments(parsed);
- }, [node.data?.value]);
-
- // 检查端口是否已连接
- const isPortConnected = (portId) => {
- if (!connections || !Array.isArray(connections)) return false;
- return connections.some(conn =>
- (conn.target === node.id && conn.targetPort === portId) ||
- (conn.source === node.id && conn.sourcePort === portId)
- );
- };
-
- // 数据端口
- const inputPorts = node.inputs || [];
- const outputPort = node.outputs?.[0];
-
- console.log(` 🔌 [AppendNode] 端口信息 - inputPorts:`, inputPorts.length, `outputPort:`, !!outputPort);
-
- // 自定义内容渲染
- const renderCustomBody = () => {
- console.log(` 🎨 [AppendNode renderCustomBody] 开始渲染,segments 数量: ${segments.length}, inputPorts 数量: ${inputPorts.length}`);
-
- return (
- <div className="Blueprint-append-node-content">
- {/* 左侧:输入片段 */}
- <div className="Blueprint-append-inputs">
- {segments.map((segment, index) => {
- const port = inputPorts.find(p => p.segmentIndex === index);
- console.log(` 🔍 渲染 segment[${index}]:`, segment, `找到的 port:`, port);
-
- if (!port) {
- console.log(` ⚠️ segment[${index}] 没有对应的 port,跳过渲染`);
- return null;
- }
-
- const connected = isPortConnected(port.id);
- const portClass = 'port-string';
-
- // 文本片段:空心圆 + 输入框(既可输入也可连接)
- if (segment.type === 'text') {
- console.log(` ✅ 渲染 segment[${index}] (text) - 空心圆 + 输入框`);
- return (
- <div key={index} className="Blueprint-append-segment">
- <div className="Blueprint-append-text-segment">
- <div
- className={`Blueprint-node-port input ${portClass} ${connected ? 'connected' : 'disconnected'}`}
- data-port-type="data"
- onMouseDown={(e) => {
- e.stopPropagation();
- onPortMouseDown?.(node.id, port.id);
- }}
- onMouseUp={(e) => {
- e.stopPropagation();
- onPortMouseUp?.(node.id, port.id);
- }}
- title="文本输入(可连接变量)"
- >
- <div className="Blueprint-node-port-dot hollow"></div>
- </div>
- <input
- type="text"
- className="Blueprint-append-text-input"
- defaultValue={segment.content}
- placeholder="文本"
- onClick={(e) => e.stopPropagation()}
- onMouseDown={(e) => e.stopPropagation()}
- onChange={(e) => {
- // TODO: 更新 value 字段
- const newValue = e.target.value;
- // 需要重新组合整个 value
- }}
- />
- </div>
- </div>
- );
- }
-
- // 变量引用:实心圆 + 标签(专门连接变量)
- if (segment.type === 'variable') {
- console.log(` ✅ 渲染 segment[${index}] (variable) - 实心圆 + 标签`);
- return (
- <div key={index} className="Blueprint-append-segment">
- <div className="Blueprint-append-variable-segment">
- <div
- className={`Blueprint-node-port input ${portClass} ${connected ? 'connected' : 'disconnected'}`}
- data-port-type="data"
- onMouseDown={(e) => {
- e.stopPropagation();
- onPortMouseDown?.(node.id, port.id);
- }}
- onMouseUp={(e) => {
- e.stopPropagation();
- onPortMouseUp?.(node.id, port.id);
- }}
- title={`变量: ${segment.content}`}
- >
- <div className="Blueprint-node-port-dot solid"></div>
- <span className="Blueprint-node-port-label">{segment.content}</span>
- </div>
- </div>
- </div>
- );
- }
-
- return null;
- })}
- </div>
-
- {/* 右侧:输出端口 */}
- {outputPort && (
- <div className="Blueprint-append-output">
- <div
- className={`Blueprint-node-port output port-string ${isPortConnected(outputPort.id) ? 'connected' : 'disconnected'}`}
- data-port-type="data"
- onMouseDown={(e) => {
- e.stopPropagation();
- onPortMouseDown?.(node.id, outputPort.id);
- }}
- onMouseUp={(e) => {
- e.stopPropagation();
- onPortMouseUp?.(node.id, outputPort.id);
- }}
- title="拼接结果"
- >
- <span className="Blueprint-node-port-label">Result</span>
- <div className="Blueprint-node-port-dot solid"></div>
- </div>
- </div>
- )}
- </div>
- );
- };
-
- return (
- <NodeRenderer
- {...props}
- className="Blueprint-append-node"
- headerText="Append"
- layoutType="append-custom"
- renderCustomBody={renderCustomBody}
- />
- );
- }
|