From 3afdff0a051bcbfaf53360cd88878a6441c80988 Mon Sep 17 00:00:00 2001 From: v-zhangjc9 Date: Mon, 7 Jul 2025 19:50:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20=E5=AE=8C=E6=88=90=E5=89=8D?= =?UTF-8?q?=E5=BA=8F=E8=8A=82=E7=82=B9=E8=BE=93=E5=87=BA=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/src/components/flow/FlowEditor.tsx | 10 +++- .../src/components/flow/node/AmisNode.tsx | 60 ++++++++++++------- .../src/components/flow/store/ContextStore.ts | 11 ++++ .../src/components/flow/store/FlowStore.ts | 20 ++++++- .../template/FlowTaskTemplateFlowEdit.tsx | 14 ++--- 5 files changed, 82 insertions(+), 33 deletions(-) create mode 100644 service-web/client/src/components/flow/store/ContextStore.ts diff --git a/service-web/client/src/components/flow/FlowEditor.tsx b/service-web/client/src/components/flow/FlowEditor.tsx index 6ec8e14..30e907b 100644 --- a/service-web/client/src/components/flow/FlowEditor.tsx +++ b/service-web/client/src/components/flow/FlowEditor.tsx @@ -24,6 +24,7 @@ import KnowledgeNode from './node/KnowledgeNode.tsx' import LlmNode from './node/LlmNode.tsx' import OutputNode from './node/OutputNode.tsx' import SwitchNode from './node/SwitchNode.tsx' +import {useContextStore} from './store/ContextStore.ts' import {useDataStore} from './store/DataStore.ts' import {useFlowStore} from './store/FlowStore.ts' @@ -67,7 +68,7 @@ const FlowableDiv = styled.div` export type GraphData = { nodes: Node[], edges: Edge[], data: any } export type FlowEditorProps = { - // inputSchema: Record>, + inputSchema: Record>, graphData: GraphData, onGraphDataChange: (graphData: GraphData) => void, } @@ -141,6 +142,10 @@ function FlowEditor(props: FlowEditorProps) { })), ) + const { + setInputSchema, + } = useContextStore() + const [currentNodeForm, setCurrentNodeForm] = useState() const editNode = (id: string, columnSchema?: Schema[]) => { if (!isNil(columnSchema)) { @@ -219,7 +224,6 @@ function FlowEditor(props: FlowEditorProps) { // 用于透传node操作到主流程 const initialNodeHandlers = { - // getInputSchema: () => props.inputSchema, editNode, } @@ -238,6 +242,8 @@ function FlowEditor(props: FlowEditorProps) { } setNodes(initialNodes) setEdges(initialEdges) + + setInputSchema(props.inputSchema) }, [props.graphData]) return ( diff --git a/service-web/client/src/components/flow/node/AmisNode.tsx b/service-web/client/src/components/flow/node/AmisNode.tsx index 311414e..99c1faa 100644 --- a/service-web/client/src/components/flow/node/AmisNode.tsx +++ b/service-web/client/src/components/flow/node/AmisNode.tsx @@ -1,27 +1,33 @@ import {CopyFilled, DeleteFilled, EditFilled} from '@ant-design/icons' -import { - type Edge, - Handle, - type HandleProps, - type Node, - type NodeProps, - Position, - useNodeConnections, -} from '@xyflow/react' +import {Handle, type HandleProps, type Node, type NodeProps, Position, useNodeConnections} from '@xyflow/react' import type {Schema} from 'amis' import {Button, Card} from 'antd' -import {isEmpty, isEqual, isNil} from 'licia' +import {has, isEmpty, isEqual, isNil} from 'licia' import {type JSX} from 'react' import styled from 'styled-components' import {horizontalFormOptions} from '../../../util/amis.tsx' +import {useContextStore} from '../store/ContextStore.ts' import {useDataStore} from '../store/DataStore.ts' import {useFlowStore} from '../store/FlowStore.ts' export type AmisNodeType = 'normal' | 'start' | 'end' export function inputsFormColumns(props: ColumnsSchemaProps): Schema[] { - console.log('props', props) - + let incomers = props.getAllIncomerNodeById(props.nodeId) + let groups = [] + for (const incomer of incomers) { + let data = props.getNodeDataById(incomer.id) + if (has(data, 'outputs')) { + let outputs = data?.outputs ?? [] + groups.push({ + label: incomer.id, + children: Object.keys(outputs).map(key => ({ + key: `${incomer.id}.${key}`, + label: key, + })), + }) + } + } return [ { type: 'input-kvs', @@ -40,7 +46,17 @@ export function inputsFormColumns(props: ColumnsSchemaProps): Schema[] { name: 'type', label: '变量', required: true, - options: [], + selectMode: 'group', + options: [ + { + label: '流程参数', + children: Object.keys(props.inputSchema).map(key => ({ + label: props.inputSchema[key]?.label ?? '', + value: key, + })), + }, + ...groups, + ], }, ], }, @@ -108,10 +124,11 @@ export const LimitHandler = (props: HandleProps & { limit: number }) => { type ColumnsSchemaProps = { // getInputSchema: () => Record>, + inputSchema: Record>, nodeId: string, nodeData: any, - getNodes: () => Node[], - getEdges: () => Edge[], + getNodeDataById: (id: string) => any, + getAllIncomerNodeById: (id: string) => Node[], } type AmisNodeProps = { @@ -145,10 +162,10 @@ const AmisNode: (props: AmisNodeProps) => JSX.Element = ({ }) => { const { removeNode, - getNodes, - getEdges, + getAllIncomerNodeById, } = useFlowStore() const {getDataById} = useDataStore() + const {inputSchema} = useContextStore() const {id, data} = nodeProps const {editNode} = data @@ -199,14 +216,11 @@ const AmisNode: (props: AmisNodeProps) => JSX.Element = ({ }, ...( columnSchema?.({ - // @ts-ignore - // getInputSchema, + inputSchema, nodeId: id, nodeData, - // @ts-ignore - getNodes, - // @ts-ignore - getEdges, + getNodeDataById: getDataById, + getAllIncomerNodeById, }) ?? [] ), ], diff --git a/service-web/client/src/components/flow/store/ContextStore.ts b/service-web/client/src/components/flow/store/ContextStore.ts new file mode 100644 index 0000000..1e64f50 --- /dev/null +++ b/service-web/client/src/components/flow/store/ContextStore.ts @@ -0,0 +1,11 @@ +import {create} from 'zustand/react' + +export type ContextStoreState = { + inputSchema: Record>, + setInputSchema: (inputSchema: Record>) => void, +} + +export const useContextStore = create((set) => ({ + inputSchema: {}, + setInputSchema: (inputSchema: Record>) => set({inputSchema}), +})) \ No newline at end of file diff --git a/service-web/client/src/components/flow/store/FlowStore.ts b/service-web/client/src/components/flow/store/FlowStore.ts index 9630681..abc7b4a 100644 --- a/service-web/client/src/components/flow/store/FlowStore.ts +++ b/service-web/client/src/components/flow/store/FlowStore.ts @@ -3,12 +3,14 @@ import { applyEdgeChanges, applyNodeChanges, type Edge, + getIncomers, type Node, type OnConnect, type OnEdgesChange, type OnNodesChange, } from '@xyflow/react' -import {filter, find, isEqual} from 'licia' +import {filter, find, isEqual, unique} from 'licia' +import Queue from 'yocto-queue' import {create} from 'zustand/react' export type FlowStoreState = { @@ -16,6 +18,7 @@ export type FlowStoreState = { getNodes: () => Node[], onNodesChange: OnNodesChange, getNodeById: (id: string) => Node | undefined, + getAllIncomerNodeById: (id: string) => Node[], addNode: (node: Node) => void, removeNode: (id: string) => void, setNodes: (nodes: Node[]) => void, @@ -37,6 +40,21 @@ export const useFlowStore = create((set, get) => ({ }) }, getNodeById: (id: string) => find(get().nodes, node => isEqual(node.id, id)), + getAllIncomerNodeById: (id: string) => { + let nodes = get().nodes + let edges = get().edges + let queue = new Queue() + queue.enqueue(find(nodes, node => isEqual(node.id, id))!) + let result: Node[] = [] + while (queue.size !== 0) { + let currentNode = queue.dequeue()! + for (const incomer of getIncomers(currentNode, nodes, edges)) { + result.push(incomer) + queue.enqueue(incomer) + } + } + return unique(result, (a, b) => isEqual(a.id, b.id)) + }, addNode: node => set({nodes: get().nodes.concat(node)}), removeNode: id => { set({ 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 3b109cb..f59172e 100644 --- a/service-web/client/src/pages/ai/task/template/FlowTaskTemplateFlowEdit.tsx +++ b/service-web/client/src/pages/ai/task/template/FlowTaskTemplateFlowEdit.tsx @@ -1,10 +1,10 @@ -import React, {useState} from 'react' -import styled from 'styled-components' -import {useNavigate, useParams} from 'react-router' import {useMount} from 'ahooks' import axios from 'axios' -import {commonInfo} from '../../../../util/amis.tsx' +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 {commonInfo} from '../../../../util/amis.tsx' const FlowTaskTemplateFlowEditDiv = styled.div` ` @@ -12,7 +12,7 @@ const FlowTaskTemplateFlowEditDiv = styled.div` const FlowTaskTemplateFlowEdit: React.FC = () => { const navigate = useNavigate() const {template_id} = useParams() - // const [inputSchema, setInputSchema] = useState>>({}) + const [inputSchema, setInputSchema] = useState>>({}) const [graphData, setGraphData] = useState({nodes: [], edges: [], data: {}}) useMount(async () => { @@ -22,14 +22,14 @@ const FlowTaskTemplateFlowEdit: React.FC = () => { headers: commonInfo.authorizationHeaders } ) - // setInputSchema(data?.data?.inputSchema) + setInputSchema(data?.data?.inputSchema) setGraphData(data?.data?.flowGraph) }) return ( { await axios.post(