From 6f7f7cea67d83ff385be37092ac2b025be5cc9dd Mon Sep 17 00:00:00 2001 From: v-zhangjc9 Date: Wed, 25 Jun 2025 17:54:43 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20=E5=A2=9E=E5=8A=A0=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/src/pages/ai/flow/FlowEditor.tsx | 118 ++---------------- .../src/pages/ai/flow/node/AmisNode.tsx | 3 +- .../src/pages/ai/flow/node/CodeNode.tsx | 73 +++++++++++ .../src/pages/ai/flow/node/KnowledgeNode.tsx | 1 - .../client/src/pages/ai/flow/node/LlmNode.tsx | 2 +- 5 files changed, 84 insertions(+), 113 deletions(-) create mode 100644 service-web/client/src/pages/ai/flow/node/CodeNode.tsx diff --git a/service-web/client/src/pages/ai/flow/FlowEditor.tsx b/service-web/client/src/pages/ai/flow/FlowEditor.tsx index 9db0218..dc9f8aa 100644 --- a/service-web/client/src/pages/ai/flow/FlowEditor.tsx +++ b/service-web/client/src/pages/ai/flow/FlowEditor.tsx @@ -19,6 +19,7 @@ import {type JSX, useState} from 'react' import styled from 'styled-components' import '@xyflow/react/dist/style.css' import {amisRender, commonInfo, horizontalFormOptions} from '../../../util/amis.tsx' +import CodeNode from './node/CodeNode.tsx' import EndNode from './node/EndNode.tsx' import KnowledgeNode from './node/KnowledgeNode.tsx' import LlmNode from './node/LlmNode.tsx' @@ -90,6 +91,11 @@ function FlowEditor() { name: '知识库', component: KnowledgeNode, }, + { + key: 'code-amis-node', + name: '代码执行', + component: CodeNode, + }, ]) const [open, setOpen] = useState(false) @@ -195,7 +201,6 @@ function FlowEditor() { if (!targetNode) { throw new Error('连线目标节点未找到') } - console.log(sourceNode, targetNode, connection) // 禁止短路整个流程 if (isEqual('start-amis-node', sourceNode.type) && isEqual('end-amis-node', targetNode.type)) { throw new Error('开始节点不能直连结束节点') @@ -229,115 +234,7 @@ function FlowEditor() { useMount(() => { // language=JSON - let initialData = JSON.parse(`{ - "nodes": [ - { - "id": "BMFP3Eov94", - "type": "start-amis-node", - "position": { - "x": 8, - "y": 272 - }, - "data": {}, - "measured": { - "width": 256, - "height": 75 - }, - "selected": false, - "dragging": false - }, - { - "id": "PYK8LjduQ1", - "type": "end-amis-node", - "position": { - "x": 736, - "y": 317 - }, - "data": {}, - "measured": { - "width": 256, - "height": 75 - }, - "selected": true, - "dragging": false - }, - { - "id": "nCm-ij5I6o", - "type": "llm-amis-node", - "position": { - "x": 438, - "y": 113 - }, - "data": {}, - "measured": { - "width": 256, - "height": 75 - }, - "selected": false, - "dragging": false - }, - { - "id": "9RIg65O0YQ", - "type": "knowledge-amis-node", - "position": { - "x": 352, - "y": 387 - }, - "data": {}, - "measured": { - "width": 256, - "height": 75 - }, - "selected": false, - "dragging": false - } - ], - "edges": [ - { - "source": "BMFP3Eov94", - "target": "9RIg65O0YQ", - "id": "xy-edge__BMFP3Eov94-9RIg65O0YQ" - }, - { - "source": "9RIg65O0YQ", - "target": "nCm-ij5I6o", - "id": "xy-edge__9RIg65O0YQ-nCm-ij5I6o" - }, - { - "source": "nCm-ij5I6o", - "target": "PYK8LjduQ1", - "id": "xy-edge__nCm-ij5I6o-PYK8LjduQ1" - } - ], - "data": { - "BMFP3Eov94": { - "inputs": { - "name": { - "type": "text" - }, - "description": { - "type": "text", - "description": "文件描述" - } - } - }, - "nCm-ij5I6o": { - "model": "qwen3", - "outputs": { - "text": { - "type": "string" - } - }, - "systemPrompt": "你是个沙雕" - }, - "9RIg65O0YQ": { - "count": 3, - "score": 0.75, - "knowledgeId": 3585368238960640, - "query": "hello world" - } - } - }`) + let initialData = JSON.parse('{\n "nodes": [\n {\n "id": "BMFP3Eov94",\n "type": "start-amis-node",\n "position": {\n "x": 8,\n "y": 272\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "PYK8LjduQ1",\n "type": "end-amis-node",\n "position": {\n "x": 1439.5556937134281,\n "y": 282.2797340760818\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "nCm-ij5I6o",\n "type": "llm-amis-node",\n "position": {\n "x": 902.6781018665707,\n "y": 115.31234529524048\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "9RIg65O0YQ",\n "type": "knowledge-amis-node",\n "position": {\n "x": 338,\n "y": 287\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "2vTyjP0Gu9",\n "type": "code-amis-node",\n "position": {\n "x": 1086.6322978498904,\n "y": 371.3061114283591\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": true,\n "dragging": false\n },\n {\n "id": "s9VfZpvb4P",\n "type": "llm-amis-node",\n "position": {\n "x": 700.0944461714178,\n "y": 369.84258971430364\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n }\n ],\n "edges": [\n {\n "source": "BMFP3Eov94",\n "target": "9RIg65O0YQ",\n "id": "xy-edge__BMFP3Eov94-9RIg65O0YQ"\n },\n {\n "source": "9RIg65O0YQ",\n "target": "nCm-ij5I6o",\n "id": "xy-edge__9RIg65O0YQ-nCm-ij5I6o"\n },\n {\n "source": "nCm-ij5I6o",\n "target": "PYK8LjduQ1",\n "id": "xy-edge__nCm-ij5I6o-PYK8LjduQ1"\n },\n {\n "source": "s9VfZpvb4P",\n "target": "2vTyjP0Gu9",\n "id": "xy-edge__s9VfZpvb4P-2vTyjP0Gu9"\n },\n {\n "source": "9RIg65O0YQ",\n "target": "s9VfZpvb4P",\n "id": "xy-edge__9RIg65O0YQ-s9VfZpvb4P"\n },\n {\n "source": "2vTyjP0Gu9",\n "target": "PYK8LjduQ1",\n "id": "xy-edge__2vTyjP0Gu9-PYK8LjduQ1"\n }\n ],\n "data": {\n "BMFP3Eov94": {\n "inputs": {\n "name": {\n "type": "text"\n },\n "description": {\n "type": "text",\n "description": "文件描述"\n }\n }\n },\n "nCm-ij5I6o": {\n "model": "qwen3",\n "outputs": {\n "text": {\n "type": "string"\n }\n },\n "systemPrompt": "你是个沙雕"\n },\n "9RIg65O0YQ": {\n "count": 3,\n "score": 0.75,\n "knowledgeId": 3585368238960640,\n "query": "hello world"\n },\n "2vTyjP0Gu9": {\n "type": "python",\n "content": "code=\'hello\'\\nprint(code)"\n },\n "s9VfZpvb4P": {\n "model": "qwen3",\n "outputs": {\n "text": {\n "type": "string"\n }\n },\n "systemPrompt": "你是个聪明人"\n }\n }\n}') let initialNodes = initialData['nodes'] ?? [] let initialEdges = initialData['edges'] ?? [] @@ -426,6 +323,7 @@ function FlowEditor() { nodeDef.map(def => def.key), key => find(nodeDef, def => isEqual(key, def.key))!.component) } + fitView > diff --git a/service-web/client/src/pages/ai/flow/node/AmisNode.tsx b/service-web/client/src/pages/ai/flow/node/AmisNode.tsx index c5a4dc8..3ef611b 100644 --- a/service-web/client/src/pages/ai/flow/node/AmisNode.tsx +++ b/service-web/client/src/pages/ai/flow/node/AmisNode.tsx @@ -8,7 +8,7 @@ import {horizontalFormOptions} from '../../../../util/amis.tsx' export type AmisNodeType = 'normal' | 'start' | 'end' -export function outputsFormColumns(editable: boolean = false, preload?: any): Schema[] { +export function outputsFormColumns(editable: boolean = false, required: boolean = false, preload?: any): Schema[] { return [ { disabled: !editable, @@ -22,6 +22,7 @@ export function outputsFormColumns(editable: boolean = false, preload?: any): Sc ...horizontalFormOptions(), label: '参数名称', }, + required: required, valueItems: [ { ...horizontalFormOptions(), diff --git a/service-web/client/src/pages/ai/flow/node/CodeNode.tsx b/service-web/client/src/pages/ai/flow/node/CodeNode.tsx new file mode 100644 index 0000000..cfef347 --- /dev/null +++ b/service-web/client/src/pages/ai/flow/node/CodeNode.tsx @@ -0,0 +1,73 @@ +import type {NodeProps} from '@xyflow/react' +import {horizontalFormOptions} from '../../../../util/amis.tsx' +import AmisNode, {outputsFormColumns} from './AmisNode.tsx' + +const CodeNode = (props: NodeProps) => AmisNode( + props, + 'normal', + '代码执行', + '执行自定义的处理代码', + undefined, + [ + { + type: 'input-kvs', + name: 'inputs', + label: '输入变量', + addButtonText: '新增输入', + draggable: false, + keyItem: { + ...horizontalFormOptions(), + label: '参数名称', + }, + valueItems: [ + { + ...horizontalFormOptions(), + type: 'select', + name: 'type', + label: '变量', + required: true, + options: [], + }, + ], + }, + { + type: 'divider', + }, + { + type: 'select', + name: 'type', + label: '代码类型', + required: true, + options: [ + { + value: 'javascript', + label: 'JavaScript', + }, + { + value: 'python', + label: 'Python', + }, + { + value: 'lua', + label: 'Lua', + }, + ], + }, + { + type: 'editor', + required: true, + label: '代码内容', + name: 'content', + language: '${type}', + options: { + wordWrap: 'bounded', + }, + }, + { + type: 'divider', + }, + ...outputsFormColumns(true, true, {result: {type: 'string'}}), + ], +) + +export default CodeNode \ No newline at end of file diff --git a/service-web/client/src/pages/ai/flow/node/KnowledgeNode.tsx b/service-web/client/src/pages/ai/flow/node/KnowledgeNode.tsx index ff39dc1..0a58a44 100644 --- a/service-web/client/src/pages/ai/flow/node/KnowledgeNode.tsx +++ b/service-web/client/src/pages/ai/flow/node/KnowledgeNode.tsx @@ -2,7 +2,6 @@ import type {NodeProps} from '@xyflow/react' import {commonInfo} from '../../../../util/amis.tsx' import AmisNode from './AmisNode.tsx' -// @ts-ignore const KnowledgeNode = (props: NodeProps) => AmisNode( props, 'normal', diff --git a/service-web/client/src/pages/ai/flow/node/LlmNode.tsx b/service-web/client/src/pages/ai/flow/node/LlmNode.tsx index 09768d5..8f25d47 100644 --- a/service-web/client/src/pages/ai/flow/node/LlmNode.tsx +++ b/service-web/client/src/pages/ai/flow/node/LlmNode.tsx @@ -34,7 +34,7 @@ const LlmNode = (props: NodeProps) => AmisNode( { type: 'divider', }, - ...outputsFormColumns(false, {text: {type: 'string'}}), + ...outputsFormColumns(false, true, {text: {type: 'string'}}), ], )