refactor(web): 优化流程节点的定义和实现

This commit is contained in:
v-zhangjc9
2025-07-10 12:08:13 +08:00
parent 898e20d5d7
commit 5e763637da
10 changed files with 467 additions and 513 deletions

View File

@@ -1,23 +1,12 @@
import {PlusCircleFilled, RollbackOutlined, SaveFilled} from '@ant-design/icons'
import {
Background,
BackgroundVariant,
Controls,
type Edge,
MiniMap,
type Node,
type NodeProps,
ReactFlow,
} from '@xyflow/react'
import type {Schema} from 'amis'
import {Button, Drawer, Dropdown, message, Popconfirm, Space} from 'antd'
import {arrToMap, find, isEqual, isNil, randomId} from 'licia'
import {type JSX, type MemoExoticComponent, useEffect, useState} from 'react'
import {Background, BackgroundVariant, Controls, type Edge, MiniMap, type Node, ReactFlow} from '@xyflow/react'
import {Button, Dropdown, message, Popconfirm, Space} from 'antd'
import {arrToMap, find, isEqual, randomId} from 'licia'
import {useEffect} from 'react'
import {useNavigate} from 'react-router'
import styled from 'styled-components'
import '@xyflow/react/dist/style.css'
import {useShallow} from 'zustand/react/shallow'
import {amisRender, commonInfo, horizontalFormOptions} from '../../util/amis.tsx'
import {commonInfo} from '../../util/amis.tsx'
import {checkAddConnection, checkAddNode, checkSave} from './FlowChecker.tsx'
import CodeNode from './node/CodeNode.tsx'
import KnowledgeNode from './node/KnowledgeNode.tsx'
@@ -29,8 +18,6 @@ import {useDataStore} from './store/DataStore.ts'
import {useFlowStore} from './store/FlowStore.ts'
const FlowableDiv = styled.div`
height: 100%;
.react-flow__node.selectable {
&:focus {
box-shadow: 0 0 20px 1px #e8e8e8;
@@ -65,6 +52,34 @@ const FlowableDiv = styled.div`
}
`
const nodeDefine = [
{
key: 'output-node',
name: '输出',
component: OutputNode,
},
{
key: 'llm-node',
name: '大模型',
component: LlmNode,
},
{
key: 'knowledge-node',
name: '知识库',
component: KnowledgeNode,
},
{
key: 'code-node',
name: '代码执行',
component: CodeNode,
},
{
key: 'switch-node',
name: '条件分支',
component: SwitchNode,
},
]
export type GraphData = { nodes: Node[], edges: Edge[], data: any }
export type FlowEditorProps = {
@@ -76,47 +91,8 @@ export type FlowEditorProps = {
function FlowEditor(props: FlowEditorProps) {
const navigate = useNavigate()
const [messageApi, contextHolder] = message.useMessage()
const [nodeDef] = useState<{
key: string,
name: string,
component: MemoExoticComponent<(props: NodeProps) => JSX.Element>
}[]>([
{
key: 'output-node',
name: '输出',
component: OutputNode,
},
{
key: 'llm-node',
name: '大模型',
component: LlmNode,
},
{
key: 'knowledge-node',
name: '知识库',
component: KnowledgeNode,
},
{
key: 'code-node',
name: '代码执行',
component: CodeNode,
},
{
key: 'switch-node',
name: '条件分支',
component: SwitchNode,
},
])
const [open, setOpen] = useState(false)
const {data, setData, getDataById, setDataById} = useDataStore(
useShallow(state => ({
data: state.data,
setData: state.setData,
getDataById: state.getDataById,
setDataById: state.setDataById,
})),
)
const {data, setData} = useDataStore()
const {
nodes,
addNode,
@@ -126,106 +102,9 @@ function FlowEditor(props: FlowEditorProps) {
setEdges,
onEdgesChange,
onConnect,
} = useFlowStore(
useShallow(state => ({
nodes: state.nodes,
getNodes: state.getNodes,
addNode: state.addNode,
removeNode: state.removeNode,
setNodes: state.setNodes,
onNodesChange: state.onNodesChange,
edges: state.edges,
getEdges: state.getEdges,
setEdges: state.setEdges,
onEdgesChange: state.onEdgesChange,
onConnect: state.onConnect,
})),
)
} = useFlowStore()
const {
setInputSchema,
} = useContextStore()
const [currentNodeForm, setCurrentNodeForm] = useState<JSX.Element>()
const editNode = (id: string, columnSchema?: Schema[]) => {
if (!isNil(columnSchema)) {
setCurrentNodeForm(
amisRender(
{
type: 'wrapper',
size: 'none',
body: [
{
debug: commonInfo.debug,
type: 'form',
...horizontalFormOptions(),
wrapWithPanel: false,
onEvent: {
submitSucc: {
actions: [
{
actionType: 'custom',
// @ts-ignore
script: (context, action, event) => {
setDataById(
id,
{
...context.props.data,
finished: true,
},
)
setOpen(false)
},
},
],
},
},
body: [
...(columnSchema ?? []),
{
type: 'wrapper',
size: 'none',
className: 'space-x-2 text-right',
body: [
{
type: 'action',
label: '取消',
onEvent: {
click: {
actions: [
{
actionType: 'custom',
// @ts-ignore
script: (context, action, event) => {
setOpen(false)
},
},
],
},
},
},
{
type: 'submit',
label: '保存',
level: 'primary',
},
],
},
],
},
],
},
getDataById(id),
),
)
setOpen(true)
}
}
// 用于透传node操作到主流程
const initialNodeHandlers = {
editNode,
}
const {setInputSchema} = useContextStore()
useEffect(() => {
// language=JSON
@@ -236,10 +115,6 @@ function FlowEditor(props: FlowEditorProps) {
let initialNodeData = props.graphData?.data ?? {}
setData(initialNodeData)
for (let node of initialNodes) {
node.data = initialNodeHandlers
}
setNodes(initialNodes)
setEdges(initialEdges)
@@ -247,12 +122,12 @@ function FlowEditor(props: FlowEditorProps) {
}, [props.graphData])
return (
<FlowableDiv>
<FlowableDiv className="h-full w-full">
{contextHolder}
<Space className="toolbar">
<Dropdown
menu={{
items: nodeDef.map(def => ({key: def.key, label: def.name})),
items: nodeDefine.map(def => ({key: def.key, label: def.name})),
onClick: ({key}) => {
try {
if (commonInfo.debug) {
@@ -263,7 +138,7 @@ function FlowEditor(props: FlowEditorProps) {
id: randomId(10),
type: key,
position: {x: 100, y: 100},
data: initialNodeHandlers,
data: {},
})
} catch (e) {
// @ts-ignore
@@ -303,16 +178,6 @@ function FlowEditor(props: FlowEditorProps) {
</Button>
</Space>
<Drawer
title="节点编辑"
open={open}
closeIcon={false}
maskClosable={false}
destroyOnHidden
size="large"
>
{currentNodeForm}
</Drawer>
<ReactFlow
nodes={nodes}
edges={edges}
@@ -332,8 +197,8 @@ function FlowEditor(props: FlowEditorProps) {
}}
// @ts-ignore
nodeTypes={arrToMap(
nodeDef.map(def => def.key),
key => find(nodeDef, def => isEqual(key, def.key))!.component)
nodeDefine.map(def => def.key),
key => find(nodeDefine, def => isEqual(key, def.key))!.component)
}
>
<Controls/>