From 35c5150a1f559c186439e90c1a4d297f0c57691e Mon Sep 17 00:00:00 2001 From: v-zhangjc9 Date: Tue, 15 Jul 2025 17:32:23 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20=E5=85=A5=E5=8F=82=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/src/components/flow/Helper.tsx | 30 +++++++++++++++++-- .../src/components/flow/node/AmisNode.tsx | 24 ++++----------- .../src/components/flow/node/SwitchNode.tsx | 26 +++++++++++++++- .../client/src/components/flow/types.ts | 12 ++++++++ service-web/client/src/pages/Test.tsx | 2 +- .../client/src/pages/ai/task/InputSchema.tsx | 8 +++++ .../ai/task/template/FlowTaskTemplateEdit.tsx | 4 +-- .../template/FlowTaskTemplateFlowEdit.tsx | 13 ++++---- 8 files changed, 88 insertions(+), 31 deletions(-) diff --git a/service-web/client/src/components/flow/Helper.tsx b/service-web/client/src/components/flow/Helper.tsx index 9eeafba..3413f31 100644 --- a/service-web/client/src/components/flow/Helper.tsx +++ b/service-web/client/src/components/flow/Helper.tsx @@ -3,8 +3,9 @@ import type {Option} from 'amis/lib/Schema' import {find, has, isEqual, max, min, unique} from 'licia' import {type DependencyList, type MouseEvent as ReactMouseEvent, useCallback, useRef} from 'react' import Queue from 'yocto-queue' +import {originTypeMap} from '../../pages/ai/task/InputSchema.tsx' import {useFlowStore} from './store/FlowStore.ts' -import type {OutputVariable} from './types.ts' +import {type OutputVariable} from './types.ts' export const getAllIncomerNodeById: (id: string, nodes: Node[], edges: Edge[]) => string[] = (id, nodes, edges) => { let queue = new Queue() @@ -24,6 +25,7 @@ export const getAllIncomerNodeOutputVariables: (id: string, inputSchema: Record< let inputSchemaVariables: OutputVariable[] = Object.keys(inputSchema).map(key => ({ group: '流程入参', name: `${key}${inputSchema[key]?.label ? ` (${inputSchema[key].label})` : ''}`, + type: originTypeMap[inputSchema[key]?.type ?? ''], variable: key, })) @@ -53,6 +55,7 @@ export const getAllIncomerNodeOutputVariables: (id: string, inputSchema: Record< incomerVariables.push({ group: group, name: key, + type: outputs[key].type, variable: `${incomerId}.${key}`, }) } @@ -65,13 +68,15 @@ export const getAllIncomerNodeOutputVariables: (id: string, inputSchema: Record< { group: '循环入参', name: 'loopIndex (当前迭代索引)', + type: 'number', variable: 'loopIndex', - }, + } as OutputVariable, { group: '循环入参', name: 'loopItem (当前迭代对象)', + type: 'object', variable: 'loopItem', - }, + } as OutputVariable, ] : []), ...incomerVariables, ] @@ -95,6 +100,25 @@ export const generateAllIncomerOutputVariablesFormOptions: (id: string, inputSch })) } +export const generateAllIncomerOutputVariablesConditions: (id: string, inputSchema: Record>, nodes: Node[], edges: Edge[], data: any) => Option[] = (id, inputSchema, nodes, edges, data) => { + let optionMap: Record = {} + for (const item of getAllIncomerNodeOutputVariables(id, inputSchema, nodes, edges, data)) { + if (!optionMap[item.group]) { + optionMap[item.group] = [] + } + optionMap[item.group].push({ + label: item.name, + type: item.type, + name: item.variable, + }) + } + return Object.keys(optionMap) + .map(key => ({ + label: key, + children: optionMap[key], + })) +} + // 处理循环节点的边界问题 export const useNodeDrag = (deps: DependencyList) => { const currentPosition = useRef({x: 0, y: 0} as { x: number, y: number }) diff --git a/service-web/client/src/components/flow/node/AmisNode.tsx b/service-web/client/src/components/flow/node/AmisNode.tsx index 36e1548..a4227b7 100644 --- a/service-web/client/src/components/flow/node/AmisNode.tsx +++ b/service-web/client/src/components/flow/node/AmisNode.tsx @@ -8,6 +8,7 @@ import {amisRender, commonInfo, horizontalFormOptions} from '../../../util/amis. import {generateAllIncomerOutputVariablesFormOptions} from '../Helper.tsx' import {useDataStore} from '../store/DataStore.ts' import {useFlowStore} from '../store/FlowStore.ts' +import {OutputVariableTypeMap} from '../types.ts' export function inputsFormColumns( nodeId: string, @@ -70,24 +71,11 @@ export function outputsFormColumns(editable: boolean = false, required: boolean label: '参数', required: true, selectFirst: true, - options: [ - { - label: '文本', - value: 'string', - }, - { - label: '数字', - value: 'number', - }, - { - label: '文本数组', - value: 'array-string', - }, - { - label: '对象数组', - value: 'array-object', - }, - ], + options: Object.keys(OutputVariableTypeMap).map(key => ({ + // @ts-ignore + label: OutputVariableTypeMap[key], + value: key, + })), }, ], }, diff --git a/service-web/client/src/components/flow/node/SwitchNode.tsx b/service-web/client/src/components/flow/node/SwitchNode.tsx index 60a1ab3..92977ab 100644 --- a/service-web/client/src/components/flow/node/SwitchNode.tsx +++ b/service-web/client/src/components/flow/node/SwitchNode.tsx @@ -1,6 +1,10 @@ import {Handle, type NodeProps, Position} from '@xyflow/react' import {Tag} from 'antd' -import React from 'react' +import React, {useCallback} from 'react' +import {generateAllIncomerOutputVariablesConditions} from '../Helper.tsx' +import {useContextStore} from '../store/ContextStore.ts' +import {useDataStore} from '../store/DataStore.ts' +import {useFlowStore} from '../store/FlowStore.ts' import AmisNode, {nodeClassName} from './AmisNode.tsx' const cases = [ @@ -16,6 +20,25 @@ const cases = [ ] const SwitchNode = (props: NodeProps) => { + const {getNodes, getEdges} = useFlowStore() + const {getData} = useDataStore() + const {getInputSchema} = useContextStore() + + const columnsSchema = useCallback(() => [ + { + type: 'condition-builder', + name: 'condition', + label: '判断条件', + fields: generateAllIncomerOutputVariablesConditions( + props.id, + getInputSchema(), + getNodes(), + getEdges(), + getData(), + ), + }, + ], [props.id]) + return ( { ))} } + columnSchema={columnsSchema} handler={ <> diff --git a/service-web/client/src/components/flow/types.ts b/service-web/client/src/components/flow/types.ts index 18ee66a..f009df1 100644 --- a/service-web/client/src/components/flow/types.ts +++ b/service-web/client/src/components/flow/types.ts @@ -29,6 +29,17 @@ export type FlowEditorProps = { onGraphDataChange: (graphData: GraphData) => void, } +export type OutputVariableType = 'text' | 'boolean' | 'number' | 'object' | 'array-text' | 'array-object' + +export const OutputVariableTypeMap: Record = { + 'text': '文本', + 'boolean': '布尔值', + 'number': '数字', + 'object': '对象', + 'array-text': '文本数组', + 'array-object': '对象数组', +} + export type NodeDefine = { key: string, group: string, @@ -42,5 +53,6 @@ export type NodeDefine = { export type OutputVariable = { group: string, name: string | undefined, + type: OutputVariableType, variable: string, } diff --git a/service-web/client/src/pages/Test.tsx b/service-web/client/src/pages/Test.tsx index dd5e7d1..d93cf7a 100644 --- a/service-web/client/src/pages/Test.tsx +++ b/service-web/client/src/pages/Test.tsx @@ -4,7 +4,7 @@ import type {GraphData} from '../components/flow/types.ts' function Test() { // language=JSON - const [graphData] = useState(JSON.parse('{\n "nodes": [\n {\n "id": "QxNrkChBWQ",\n "type": "loop-node",\n "position": {\n "x": 742,\n "y": 119\n },\n "data": {},\n "measured": {\n "width": 458,\n "height": 368\n },\n "selected": true,\n "dragging": false,\n "width": 458,\n "height": 368,\n "resizing": false\n },\n {\n "id": "MzEitlOusl",\n "type": "llm-node",\n "position": {\n "x": 47,\n "y": 135\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 110\n },\n "selected": false,\n "dragging": false,\n "extent": "parent",\n "parentId": "QxNrkChBWQ"\n },\n {\n "id": "bivXSpiLaI",\n "type": "code-node",\n "position": {\n "x": 381,\n "y": 181\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 110\n },\n "selected": false,\n "dragging": false\n }\n ],\n "edges": [],\n "data": {\n "MzEitlOusl": {\n "node": {\n "name": "大模型",\n "description": "使用大模型对话"\n },\n "outputs": {\n "text": {\n "type": "string"\n }\n },\n "model": "qwen3",\n "systemPrompt": "你是个好人",\n "finished": true\n },\n "bivXSpiLaI": {\n "node": {\n "name": "代码执行",\n "description": "执行自定义的处理代码"\n },\n "outputs": {\n "result": {\n "type": "string"\n }\n },\n "type": "javascript",\n "content": "console.log(\'hello\')",\n "inputs": {\n "text": {\n "variable": "MzEitlOusl.text"\n }\n },\n "finished": true\n },\n "QxNrkChBWQ": {\n "node": {\n "name": "循环",\n "description": "实现循环执行流程"\n },\n "finished": true\n }\n }\n}')) + const [graphData] = useState(JSON.parse('{\n "nodes": [\n {\n "id": "QxNrkChBWQ",\n "type": "loop-node",\n "position": {\n "x": 742,\n "y": 119\n },\n "data": {},\n "measured": {\n "width": 458,\n "height": 368\n },\n "selected": true,\n "dragging": false,\n "width": 458,\n "height": 368,\n "resizing": false\n },\n {\n "id": "MzEitlOusl",\n "type": "llm-node",\n "position": {\n "x": 47,\n "y": 135\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 110\n },\n "selected": false,\n "dragging": false,\n "extent": "parent",\n "parentId": "QxNrkChBWQ"\n },\n {\n "id": "bivXSpiLaI",\n "type": "code-node",\n "position": {\n "x": 381,\n "y": 181\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 110\n },\n "selected": false,\n "dragging": false\n }\n ],\n "edges": [],\n "data": {\n "MzEitlOusl": {\n "node": {\n "name": "大模型",\n "description": "使用大模型对话"\n },\n "outputs": {\n "text": {\n "type": "string"\n }\n },\n "model": "qwen3",\n "systemPrompt": "你是个好人",\n "finished": true\n },\n "bivXSpiLaI": {\n "node": {\n "name": "代码执行",\n "description": "执行自定义的处理代码"\n },\n "outputs": {\n "result": {\n "type": "text"\n }\n },\n "type": "javascript",\n "content": "console.log(\'hello\')",\n "inputs": {\n "text": {\n "variable": "MzEitlOusl.text"\n }\n },\n "finished": true\n },\n "QxNrkChBWQ": {\n "node": {\n "name": "循环",\n "description": "实现循环执行流程"\n },\n "finished": true\n }\n }\n}')) return (
diff --git a/service-web/client/src/pages/ai/task/InputSchema.tsx b/service-web/client/src/pages/ai/task/InputSchema.tsx index 4c7092c..37c51f3 100644 --- a/service-web/client/src/pages/ai/task/InputSchema.tsx +++ b/service-web/client/src/pages/ai/task/InputSchema.tsx @@ -1,4 +1,5 @@ import type {Schema} from 'amis' +import type {OutputVariableType} from '../../../components/flow/types.ts' import {commonInfo, formInputFileStaticColumns} from '../../../util/amis.tsx' export const typeMap: Record = { @@ -8,6 +9,13 @@ export const typeMap: Record = { files: '文件', } +export const originTypeMap: Record = { + text: 'text', + textarea: 'text', + number: 'number', + files: 'array-text', +} + export type InputField = { type: string label: string diff --git a/service-web/client/src/pages/ai/task/template/FlowTaskTemplateEdit.tsx b/service-web/client/src/pages/ai/task/template/FlowTaskTemplateEdit.tsx index 4107f80..7cfe7cb 100644 --- a/service-web/client/src/pages/ai/task/template/FlowTaskTemplateEdit.tsx +++ b/service-web/client/src/pages/ai/task/template/FlowTaskTemplateEdit.tsx @@ -73,8 +73,8 @@ const FlowTaskTemplateEdit: React.FC = () => { navigate(-1) }, }, - ] - } + ], + }, }, body: [ { diff --git a/service-web/client/src/pages/ai/task/template/FlowTaskTemplateFlowEdit.tsx b/service-web/client/src/pages/ai/task/template/FlowTaskTemplateFlowEdit.tsx index f59172e..6f6c147 100644 --- a/service-web/client/src/pages/ai/task/template/FlowTaskTemplateFlowEdit.tsx +++ b/service-web/client/src/pages/ai/task/template/FlowTaskTemplateFlowEdit.tsx @@ -3,7 +3,8 @@ import axios from 'axios' import React, {useState} from 'react' import {useNavigate, useParams} from 'react-router' import styled from 'styled-components' -import FlowEditor, {type GraphData} from '../../../../components/flow/FlowEditor.tsx' +import FlowEditor from '../../../../components/flow/FlowEditor.tsx' +import type {GraphData} from '../../../../components/flow/types.ts' import {commonInfo} from '../../../../util/amis.tsx' const FlowTaskTemplateFlowEditDiv = styled.div` @@ -19,8 +20,8 @@ const FlowTaskTemplateFlowEdit: React.FC = () => { let {data} = await axios.get( `${commonInfo.baseAiUrl}/flow_task/template/detail/${template_id}`, { - headers: commonInfo.authorizationHeaders - } + headers: commonInfo.authorizationHeaders, + }, ) setInputSchema(data?.data?.inputSchema) setGraphData(data?.data?.flowGraph) @@ -36,11 +37,11 @@ const FlowTaskTemplateFlowEdit: React.FC = () => { `${commonInfo.baseAiUrl}/flow_task/template/update_flow_graph`, { id: template_id, - graph: data + graph: data, }, { - headers: commonInfo.authorizationHeaders - } + headers: commonInfo.authorizationHeaders, + }, ) navigate(-1) }}