feat(web): 完成分支节点动态改变handle
This commit is contained in:
@@ -7,6 +7,7 @@ import {useNavigate} from 'react-router'
|
|||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import '@xyflow/react/dist/style.css'
|
import '@xyflow/react/dist/style.css'
|
||||||
import {commonInfo} from '../../util/amis.tsx'
|
import {commonInfo} from '../../util/amis.tsx'
|
||||||
|
import AddNodeButton from './component/AddNodeButton.tsx'
|
||||||
import {checkAddConnection, checkSave} from './FlowChecker.tsx'
|
import {checkAddConnection, checkSave} from './FlowChecker.tsx'
|
||||||
import {useNodeDrag} from './Helper.tsx'
|
import {useNodeDrag} from './Helper.tsx'
|
||||||
import {NodeRegistryMap} from './NodeRegistry.tsx'
|
import {NodeRegistryMap} from './NodeRegistry.tsx'
|
||||||
@@ -14,7 +15,6 @@ import {useContextStore} from './store/ContextStore.ts'
|
|||||||
import {useDataStore} from './store/DataStore.ts'
|
import {useDataStore} from './store/DataStore.ts'
|
||||||
import {useFlowStore} from './store/FlowStore.ts'
|
import {useFlowStore} from './store/FlowStore.ts'
|
||||||
import {flowDotColor, type FlowEditorProps} from './types.ts'
|
import {flowDotColor, type FlowEditorProps} from './types.ts'
|
||||||
import AddNodeButton from './component/AddNodeButton.tsx'
|
|
||||||
|
|
||||||
const FlowableDiv = styled.div`
|
const FlowableDiv = styled.div`
|
||||||
.react-flow__node.selectable {
|
.react-flow__node.selectable {
|
||||||
@@ -48,10 +48,9 @@ function FlowEditor(props: FlowEditorProps) {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const [messageApi, contextHolder] = message.useMessage()
|
const [messageApi, contextHolder] = message.useMessage()
|
||||||
|
|
||||||
const {data, setData, setDataById} = useDataStore()
|
const {data, setData} = useDataStore()
|
||||||
const {
|
const {
|
||||||
nodes,
|
nodes,
|
||||||
addNode,
|
|
||||||
setNodes,
|
setNodes,
|
||||||
onNodesChange,
|
onNodesChange,
|
||||||
edges,
|
edges,
|
||||||
@@ -109,6 +108,7 @@ function FlowEditor(props: FlowEditorProps) {
|
|||||||
onNodeDragStart={onNodeDragStart}
|
onNodeDragStart={onNodeDragStart}
|
||||||
onNodeDrag={onNodeDrag}
|
onNodeDrag={onNodeDrag}
|
||||||
onNodeDragStop={onNodeDragEnd}
|
onNodeDragStop={onNodeDragEnd}
|
||||||
|
onEdgesDelete={() => console.info('delete')}
|
||||||
>
|
>
|
||||||
<Panel position="top-right">
|
<Panel position="top-right">
|
||||||
<Space className="toolbar">
|
<Space className="toolbar">
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ type AmisNodeProps = {
|
|||||||
nodeProps: NodeProps
|
nodeProps: NodeProps
|
||||||
extraNodeDescription?: JSX.Element
|
extraNodeDescription?: JSX.Element
|
||||||
handler: JSX.Element
|
handler: JSX.Element
|
||||||
columnSchema?: () => Schema[]
|
columnSchema?: Schema[]
|
||||||
resize?: { minWidth: number, minHeight: number }
|
resize?: { minWidth: number, minHeight: number }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ const AmisNode: (props: AmisNodeProps) => JSX.Element = ({
|
|||||||
{
|
{
|
||||||
type: 'divider',
|
type: 'divider',
|
||||||
},
|
},
|
||||||
...(columnSchema?.() ?? []),
|
...(columnSchema ?? []),
|
||||||
{
|
{
|
||||||
type: 'wrapper',
|
type: 'wrapper',
|
||||||
size: 'none',
|
size: 'none',
|
||||||
@@ -221,7 +221,7 @@ const AmisNode: (props: AmisNodeProps) => JSX.Element = ({
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
setEditDrawerOpen(true)
|
setEditDrawerOpen(true)
|
||||||
}, [nodeData])
|
}, [id])
|
||||||
const onRemoveClick = useCallback(() => {
|
const onRemoveClick = useCallback(() => {
|
||||||
removeNode(id)
|
removeNode(id)
|
||||||
removeDataById(id)
|
removeDataById(id)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type {NodeProps} from '@xyflow/react'
|
import type {NodeProps} from '@xyflow/react'
|
||||||
import {Tag} from 'antd'
|
import {Tag} from 'antd'
|
||||||
import React, {useCallback} from 'react'
|
import React, {useMemo} from 'react'
|
||||||
import {useContextStore} from '../store/ContextStore.ts'
|
import {useContextStore} from '../store/ContextStore.ts'
|
||||||
import {useDataStore} from '../store/DataStore.ts'
|
import {useDataStore} from '../store/DataStore.ts'
|
||||||
import {useFlowStore} from '../store/FlowStore.ts'
|
import {useFlowStore} from '../store/FlowStore.ts'
|
||||||
@@ -19,7 +19,7 @@ const CodeNode = (props: NodeProps) => {
|
|||||||
|
|
||||||
const nodeData = getDataById(props.id)
|
const nodeData = getDataById(props.id)
|
||||||
|
|
||||||
const columnsSchema = useCallback(() => [
|
const columnsSchema = useMemo(() => [
|
||||||
...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()),
|
...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()),
|
||||||
{
|
{
|
||||||
type: 'divider',
|
type: 'divider',
|
||||||
@@ -47,18 +47,21 @@ const CodeNode = (props: NodeProps) => {
|
|||||||
},
|
},
|
||||||
...outputsFormColumns(true, true),
|
...outputsFormColumns(true, true),
|
||||||
], [props.id])
|
], [props.id])
|
||||||
|
|
||||||
|
const extraNodeDescription = useMemo(() => {
|
||||||
|
return nodeData?.type
|
||||||
|
? <div className="mt-2 flex justify-between">
|
||||||
|
<span>代码类型</span>
|
||||||
|
<Tag className="m-0" color="blue">{languageMap[nodeData.type]}</Tag>
|
||||||
|
</div>
|
||||||
|
: <></>
|
||||||
|
}, [nodeData])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AmisNode
|
<AmisNode
|
||||||
className={nodeClassName('code')}
|
className={nodeClassName('code')}
|
||||||
nodeProps={props}
|
nodeProps={props}
|
||||||
extraNodeDescription={
|
extraNodeDescription={extraNodeDescription}
|
||||||
nodeData?.type
|
|
||||||
? <div className="mt-2 flex justify-between">
|
|
||||||
<span>代码类型</span>
|
|
||||||
<Tag className="m-0" color="blue">{languageMap[nodeData.type]}</Tag>
|
|
||||||
</div>
|
|
||||||
: <></>
|
|
||||||
}
|
|
||||||
columnSchema={columnsSchema}
|
columnSchema={columnsSchema}
|
||||||
handler={<NormalNodeHandler/>}
|
handler={<NormalNodeHandler/>}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type {NodeProps} from '@xyflow/react'
|
import type {NodeProps} from '@xyflow/react'
|
||||||
import React, {useCallback, useEffect} from 'react'
|
import React, {useEffect, useMemo} from 'react'
|
||||||
import {commonInfo} from '../../../util/amis.tsx'
|
import {commonInfo} from '../../../util/amis.tsx'
|
||||||
import {useContextStore} from '../store/ContextStore.ts'
|
import {useContextStore} from '../store/ContextStore.ts'
|
||||||
import {useDataStore} from '../store/DataStore.ts'
|
import {useDataStore} from '../store/DataStore.ts'
|
||||||
@@ -24,7 +24,7 @@ const KnowledgeNode = (props: NodeProps) => {
|
|||||||
)
|
)
|
||||||
}, [props.id])
|
}, [props.id])
|
||||||
|
|
||||||
const columnsSchema = useCallback(() => [
|
const columnsSchema = useMemo(() => [
|
||||||
...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()),
|
...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()),
|
||||||
{
|
{
|
||||||
type: 'divider',
|
type: 'divider',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type {NodeProps} from '@xyflow/react'
|
import type {NodeProps} from '@xyflow/react'
|
||||||
import {Tag} from 'antd'
|
import {Tag} from 'antd'
|
||||||
import React, {useCallback, useEffect} from 'react'
|
import React, {useEffect, useMemo} from 'react'
|
||||||
import {useContextStore} from '../store/ContextStore.ts'
|
import {useContextStore} from '../store/ContextStore.ts'
|
||||||
import {useDataStore} from '../store/DataStore.ts'
|
import {useDataStore} from '../store/DataStore.ts'
|
||||||
import {useFlowStore} from '../store/FlowStore.ts'
|
import {useFlowStore} from '../store/FlowStore.ts'
|
||||||
@@ -31,7 +31,7 @@ const LlmNode = (props: NodeProps) => {
|
|||||||
)
|
)
|
||||||
}, [props.id])
|
}, [props.id])
|
||||||
|
|
||||||
const columnsSchema = useCallback(() => [
|
const columnsSchema = useMemo(() => [
|
||||||
...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()),
|
...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()),
|
||||||
{
|
{
|
||||||
type: 'divider',
|
type: 'divider',
|
||||||
@@ -55,18 +55,21 @@ const LlmNode = (props: NodeProps) => {
|
|||||||
},
|
},
|
||||||
...outputsFormColumns(false, true),
|
...outputsFormColumns(false, true),
|
||||||
], [props.id])
|
], [props.id])
|
||||||
|
|
||||||
|
const extraNodeDescription = useMemo(() => {
|
||||||
|
return nodeData?.model
|
||||||
|
? <div className="mt-2 flex justify-between">
|
||||||
|
<span>模型名称</span>
|
||||||
|
<Tag className="m-0" color="blue">{modelMap[nodeData.model]}</Tag>
|
||||||
|
</div>
|
||||||
|
: <></>
|
||||||
|
}, [nodeData])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AmisNode
|
<AmisNode
|
||||||
className={nodeClassName('llm')}
|
className={nodeClassName('llm')}
|
||||||
nodeProps={props}
|
nodeProps={props}
|
||||||
extraNodeDescription={
|
extraNodeDescription={extraNodeDescription}
|
||||||
nodeData?.model
|
|
||||||
? <div className="mt-2 flex justify-between">
|
|
||||||
<span>模型名称</span>
|
|
||||||
<Tag className="m-0" color="blue">{modelMap[nodeData.model]}</Tag>
|
|
||||||
</div>
|
|
||||||
: <></>
|
|
||||||
}
|
|
||||||
columnSchema={columnsSchema}
|
columnSchema={columnsSchema}
|
||||||
handler={<NormalNodeHandler/>}
|
handler={<NormalNodeHandler/>}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {Background, BackgroundVariant, type NodeProps} from '@xyflow/react'
|
import {Background, BackgroundVariant, type NodeProps} from '@xyflow/react'
|
||||||
import {classnames} from 'amis'
|
import {classnames} from 'amis'
|
||||||
import React, {useCallback, useEffect} from 'react'
|
import React, {useEffect, useMemo} from 'react'
|
||||||
import AddNodeButton from '../component/AddNodeButton.tsx'
|
import AddNodeButton from '../component/AddNodeButton.tsx'
|
||||||
import {useDataStore} from '../store/DataStore.ts'
|
import {useDataStore} from '../store/DataStore.ts'
|
||||||
import {flowBackgroundColor, flowDotColor} from '../types.ts'
|
import {flowBackgroundColor, flowDotColor} from '../types.ts'
|
||||||
@@ -22,7 +22,7 @@ const LoopNode = (props: NodeProps) => {
|
|||||||
)
|
)
|
||||||
}, [props.id])
|
}, [props.id])
|
||||||
|
|
||||||
const columnsSchema = useCallback(() => [
|
const columnsSchema = useMemo(() => [
|
||||||
{
|
{
|
||||||
type: 'switch',
|
type: 'switch',
|
||||||
name: 'failFast',
|
name: 'failFast',
|
||||||
@@ -41,6 +41,26 @@ const LoopNode = (props: NodeProps) => {
|
|||||||
...outputsFormColumns(false, true),
|
...outputsFormColumns(false, true),
|
||||||
], [props.id])
|
], [props.id])
|
||||||
|
|
||||||
|
const extraNodeDescription = useMemo(() => {
|
||||||
|
return (
|
||||||
|
<div className="nodrag relative w-full h-full" style={{minHeight: '211px'}}>
|
||||||
|
<Background
|
||||||
|
id={`loop-background-${props.id}`}
|
||||||
|
className="rounded-xl"
|
||||||
|
variant={BackgroundVariant.Cross}
|
||||||
|
gap={20}
|
||||||
|
size={3}
|
||||||
|
style={{
|
||||||
|
zIndex: 0,
|
||||||
|
}}
|
||||||
|
color={flowDotColor}
|
||||||
|
bgColor={flowBackgroundColor}
|
||||||
|
/>
|
||||||
|
<AddNodeButton className="mt-2 ml-2" parent={props.id} onlyIcon/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [props.id])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AmisNode
|
<AmisNode
|
||||||
className={classnames('w-full', 'h-full', nodeClassName('loop'))}
|
className={classnames('w-full', 'h-full', nodeClassName('loop'))}
|
||||||
@@ -49,23 +69,7 @@ const LoopNode = (props: NodeProps) => {
|
|||||||
minHeight: '290px',
|
minHeight: '290px',
|
||||||
}}
|
}}
|
||||||
nodeProps={props}
|
nodeProps={props}
|
||||||
extraNodeDescription={
|
extraNodeDescription={extraNodeDescription}
|
||||||
<div className="nodrag relative w-full h-full" style={{minHeight: '211px'}}>
|
|
||||||
<Background
|
|
||||||
id={`loop-background-${props.id}`}
|
|
||||||
className="rounded-xl"
|
|
||||||
variant={BackgroundVariant.Cross}
|
|
||||||
gap={20}
|
|
||||||
size={3}
|
|
||||||
style={{
|
|
||||||
zIndex: 0,
|
|
||||||
}}
|
|
||||||
color={flowDotColor}
|
|
||||||
bgColor={flowBackgroundColor}
|
|
||||||
/>
|
|
||||||
<AddNodeButton className="mt-2 ml-2" parent={props.id} onlyIcon/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
columnSchema={columnsSchema}
|
columnSchema={columnsSchema}
|
||||||
handler={<NormalNodeHandler/>}
|
handler={<NormalNodeHandler/>}
|
||||||
resize={{
|
resize={{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type {NodeProps} from '@xyflow/react'
|
import type {NodeProps} from '@xyflow/react'
|
||||||
import React, {useCallback} from 'react'
|
import React, {useMemo} from 'react'
|
||||||
import {generateAllIncomerOutputVariablesFormOptions} from '../Helper.tsx'
|
import {generateAllIncomerOutputVariablesFormOptions} from '../Helper.tsx'
|
||||||
import {useContextStore} from '../store/ContextStore.ts'
|
import {useContextStore} from '../store/ContextStore.ts'
|
||||||
import {useDataStore} from '../store/DataStore.ts'
|
import {useDataStore} from '../store/DataStore.ts'
|
||||||
@@ -11,7 +11,7 @@ const OutputNode = (props: NodeProps) => {
|
|||||||
const {getData} = useDataStore()
|
const {getData} = useDataStore()
|
||||||
const {getInputSchema} = useContextStore()
|
const {getInputSchema} = useContextStore()
|
||||||
|
|
||||||
const columnsSchema = useCallback(
|
const columnsSchema = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
type: 'select',
|
type: 'select',
|
||||||
|
|||||||
@@ -1,30 +1,24 @@
|
|||||||
import {Handle, type NodeProps, Position} from '@xyflow/react'
|
import {Handle, type NodeProps, Position} from '@xyflow/react'
|
||||||
|
import type {ConditionValue} from 'amis'
|
||||||
import {Tag} from 'antd'
|
import {Tag} from 'antd'
|
||||||
import React, {useCallback} from 'react'
|
import {contain, isEqual} from 'licia'
|
||||||
|
import React, {useMemo} from 'react'
|
||||||
import {generateAllIncomerOutputVariablesConditions} from '../Helper.tsx'
|
import {generateAllIncomerOutputVariablesConditions} from '../Helper.tsx'
|
||||||
import {useContextStore} from '../store/ContextStore.ts'
|
import {useContextStore} from '../store/ContextStore.ts'
|
||||||
import {useDataStore} from '../store/DataStore.ts'
|
import {useDataStore} from '../store/DataStore.ts'
|
||||||
import {useFlowStore} from '../store/FlowStore.ts'
|
import {useFlowStore} from '../store/FlowStore.ts'
|
||||||
import AmisNode, {nodeClassName} from './AmisNode.tsx'
|
import AmisNode, {nodeClassName} from './AmisNode.tsx'
|
||||||
|
|
||||||
const cases = [
|
|
||||||
{
|
|
||||||
index: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
index: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
index: 3,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const SwitchNode = (props: NodeProps) => {
|
const SwitchNode = (props: NodeProps) => {
|
||||||
const {getNodes, getEdges} = useFlowStore()
|
const {getNodes, getEdges, removeEdges} = useFlowStore()
|
||||||
const {getData} = useDataStore()
|
const {getData, getDataById} = useDataStore()
|
||||||
const {getInputSchema} = useContextStore()
|
const {getInputSchema} = useContextStore()
|
||||||
|
|
||||||
const columnsSchema = useCallback(() => [
|
const nodeData = getDataById(props.id)
|
||||||
|
// @ts-ignore
|
||||||
|
const conditions: ConditionValue[] = nodeData?.conditions?.map(c => c.condition) ?? []
|
||||||
|
|
||||||
|
const columnsSchema = useMemo(() => [
|
||||||
{
|
{
|
||||||
type: 'combo',
|
type: 'combo',
|
||||||
name: 'conditions',
|
name: 'conditions',
|
||||||
@@ -45,38 +39,55 @@ const SwitchNode = (props: NodeProps) => {
|
|||||||
getData(),
|
getData(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
], [props.id])
|
], [props.id])
|
||||||
|
|
||||||
|
const extraNodeDescription = useMemo(() => {
|
||||||
|
return (
|
||||||
|
<div className="mt-2">
|
||||||
|
{conditions.map((item, index) => (
|
||||||
|
<div key={item.id} className="mt-1">
|
||||||
|
<Tag className="m-0" color="blue">分支 {index + 1}</Tag>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [nodeData])
|
||||||
|
|
||||||
|
const handler = useMemo(() => {
|
||||||
|
// @ts-ignore
|
||||||
|
const conditions: ConditionValue[] = nodeData?.conditions?.map(c => c.condition) ?? []
|
||||||
|
|
||||||
|
// 移除不该存在的边
|
||||||
|
const conditionIds = conditions.map(c => c.id)
|
||||||
|
const removeEdgeIds = getEdges()
|
||||||
|
.filter(edge => isEqual(edge.source, props.id) && !contain(conditionIds, edge.sourceHandle))
|
||||||
|
.map(edge => edge.id)
|
||||||
|
removeEdges(removeEdgeIds)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Handle type="target" position={Position.Left}/>
|
||||||
|
{conditions.map((item, index) => (
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
position={Position.Right}
|
||||||
|
key={item.id}
|
||||||
|
id={item.id}
|
||||||
|
style={{top: 91 + (26 * index)}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}, [nodeData])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AmisNode
|
<AmisNode
|
||||||
className={nodeClassName('switch')}
|
className={nodeClassName('switch')}
|
||||||
nodeProps={props}
|
nodeProps={props}
|
||||||
extraNodeDescription={
|
extraNodeDescription={extraNodeDescription}
|
||||||
<div className="mt-2">
|
|
||||||
{cases.map(item => (
|
|
||||||
<div key={item.index} className="mt-1">
|
|
||||||
<Tag className="m-0" color="blue">分支 {item.index}</Tag>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
columnSchema={columnsSchema}
|
columnSchema={columnsSchema}
|
||||||
handler={
|
handler={handler}
|
||||||
<>
|
|
||||||
<Handle type="target" position={Position.Left}/>
|
|
||||||
{cases.map((item, index) => (
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Right}
|
|
||||||
key={item.index}
|
|
||||||
id={`${item.index}`}
|
|
||||||
style={{top: 85 + (25 * index)}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type {NodeProps} from '@xyflow/react'
|
import type {NodeProps} from '@xyflow/react'
|
||||||
import {Tag} from 'antd'
|
import {Tag} from 'antd'
|
||||||
import React, {useCallback, useEffect} from 'react'
|
import React, {useEffect, useMemo} from 'react'
|
||||||
import {useContextStore} from '../store/ContextStore.ts'
|
import {useContextStore} from '../store/ContextStore.ts'
|
||||||
import {useDataStore} from '../store/DataStore.ts'
|
import {useDataStore} from '../store/DataStore.ts'
|
||||||
import {useFlowStore} from '../store/FlowStore.ts'
|
import {useFlowStore} from '../store/FlowStore.ts'
|
||||||
@@ -33,7 +33,7 @@ const TemplateNode = (props: NodeProps) => {
|
|||||||
)
|
)
|
||||||
}, [props.id])
|
}, [props.id])
|
||||||
|
|
||||||
const columnsSchema = useCallback(
|
const columnsSchema = useMemo(
|
||||||
() => [
|
() => [
|
||||||
...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()),
|
...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()),
|
||||||
{
|
{
|
||||||
@@ -69,22 +69,22 @@ const TemplateNode = (props: NodeProps) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
...outputsFormColumns(false, true),
|
...outputsFormColumns(false, true),
|
||||||
],
|
], [props.id])
|
||||||
[props.id],
|
|
||||||
)
|
const extraNodeDescription = useMemo(() => {
|
||||||
|
return nodeData?.type
|
||||||
|
? <div className="mt-2 flex justify-between">
|
||||||
|
<span>模板类型</span>
|
||||||
|
<Tag className="m-0" color="blue">{typeMap[nodeData.type]}</Tag>
|
||||||
|
</div>
|
||||||
|
: <></>
|
||||||
|
}, [nodeData])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AmisNode
|
<AmisNode
|
||||||
className={nodeClassName('template')}
|
className={nodeClassName('template')}
|
||||||
nodeProps={props}
|
nodeProps={props}
|
||||||
extraNodeDescription={
|
extraNodeDescription={extraNodeDescription}
|
||||||
nodeData?.type
|
|
||||||
? <div className="mt-2 flex justify-between">
|
|
||||||
<span>模板类型</span>
|
|
||||||
<Tag className="m-0" color="blue">{typeMap[nodeData.type]}</Tag>
|
|
||||||
</div>
|
|
||||||
: <></>
|
|
||||||
}
|
|
||||||
columnSchema={columnsSchema}
|
columnSchema={columnsSchema}
|
||||||
handler={<NormalNodeHandler/>}
|
handler={<NormalNodeHandler/>}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
type OnEdgesChange,
|
type OnEdgesChange,
|
||||||
type OnNodesChange,
|
type OnNodesChange,
|
||||||
} from '@xyflow/react'
|
} from '@xyflow/react'
|
||||||
import {filter, find, isEqual} from 'licia'
|
import {contain, filter, find, isEqual} from 'licia'
|
||||||
import {create} from 'zustand/react'
|
import {create} from 'zustand/react'
|
||||||
|
|
||||||
export const useFlowStore = create<{
|
export const useFlowStore = create<{
|
||||||
@@ -24,6 +24,8 @@ export const useFlowStore = create<{
|
|||||||
edges: Edge[],
|
edges: Edge[],
|
||||||
getEdges: () => Edge[],
|
getEdges: () => Edge[],
|
||||||
onEdgesChange: OnEdgesChange,
|
onEdgesChange: OnEdgesChange,
|
||||||
|
removeEdge: (id: string) => void,
|
||||||
|
removeEdges: (ids: string[]) => void,
|
||||||
setEdges: (edges: Edge[]) => void,
|
setEdges: (edges: Edge[]) => void,
|
||||||
|
|
||||||
onConnect: OnConnect,
|
onConnect: OnConnect,
|
||||||
@@ -61,6 +63,16 @@ export const useFlowStore = create<{
|
|||||||
edges: applyEdgeChanges(changes, get().edges),
|
edges: applyEdgeChanges(changes, get().edges),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
removeEdge: id => {
|
||||||
|
set({
|
||||||
|
edges: filter(get().edges, edge => !isEqual(edge.id, id)),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
removeEdges: ids => {
|
||||||
|
set({
|
||||||
|
edges: filter(get().edges, edge => !contain(ids, edge.id)),
|
||||||
|
})
|
||||||
|
},
|
||||||
setEdges: edges => set({edges}),
|
setEdges: edges => set({edges}),
|
||||||
|
|
||||||
onConnect: connection => {
|
onConnect: connection => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type {GraphData} from '../components/flow/types.ts'
|
|||||||
|
|
||||||
function Test() {
|
function Test() {
|
||||||
// language=JSON
|
// language=JSON
|
||||||
const [graphData] = useState<GraphData>(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": false,\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": 108\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": 100,\n "y": 188\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 108\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "JsUwvjkJCW",\n "type": "switch-node",\n "position": {\n "x": 400,\n "y": 267\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 159\n },\n "selected": true,\n "dragging": false\n }\n ],\n "edges": [\n {\n "source": "bivXSpiLaI",\n "sourceHandle": "source",\n "target": "JsUwvjkJCW",\n "id": "xy-edge__bivXSpiLaIsource-JsUwvjkJCW"\n }\n ],\n "data": {\n "MzEitlOusl": {\n "node": {\n "name": "大模型",\n "description": "使用大模型对话"\n },\n "outputs": {\n "text": {\n "type": "text"\n }\n },\n "model": "qwen3",\n "systemPrompt": "你是个好人",\n "finished": true\n },\n "bivXSpiLaI": {\n "node": {\n "name": "代码执行",\n "description": "执行自定义的处理代码"\n },\n "outputs": {\n "text": {\n "type": "text"\n },\n "condition": {\n "type": "boolean"\n },\n "count": {\n "type": "number"\n },\n "person": {\n "type": "object"\n },\n "words": {\n "type": "array-text"\n },\n "people": {\n "type": "array-object"\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 "JsUwvjkJCW": {\n "node": {\n "name": "分支",\n "description": "根据不同的情况前往不同的分支"\n }\n }\n }\n}'))
|
const [graphData] = useState<GraphData>(JSON.parse('{"nodes":[{"id":"QxNrkChBWQ","type":"loop-node","position":{"x":890,"y":119},"data":{},"measured":{"width":458,"height":368},"selected":false,"dragging":false,"width":458,"height":368,"resizing":false},{"id":"MzEitlOusl","type":"llm-node","position":{"x":47,"y":135},"data":{},"measured":{"width":256,"height":110},"selected":false,"dragging":false,"extent":"parent","parentId":"QxNrkChBWQ"},{"id":"bivXSpiLaI","type":"code-node","position":{"x":100,"y":188},"data":{},"measured":{"width":256,"height":110},"selected":false,"dragging":false},{"id":"JsUwvjkJCW","type":"switch-node","position":{"x":495,"y":219},"data":{},"measured":{"width":256,"height":162},"selected":false,"dragging":false},{"id":"sRWQqqshAE","type":"llm-node","position":{"x":938,"y":551},"data":{},"measured":{"width":256,"height":110},"selected":false,"dragging":false}],"edges":[{"source":"bivXSpiLaI","sourceHandle":"source","target":"JsUwvjkJCW","id":"xy-edge__bivXSpiLaIsource-JsUwvjkJCW"},{"source":"JsUwvjkJCW","sourceHandle":"736a724a5de4","target":"QxNrkChBWQ","targetHandle":"target","id":"xy-edge__JsUwvjkJCW736a724a5de4-QxNrkChBWQtarget"},{"source":"JsUwvjkJCW","sourceHandle":"d4e42668119c","target":"sRWQqqshAE","targetHandle":"target","id":"xy-edge__JsUwvjkJCWd4e42668119c-sRWQqqshAEtarget"}],"data":{"MzEitlOusl":{"node":{"name":"大模型","description":"使用大模型对话"},"outputs":{"text":{"type":"text"}},"model":"qwen3","systemPrompt":"你是个好人","finished":true},"bivXSpiLaI":{"node":{"name":"代码执行","description":"执行自定义的处理代码"},"outputs":{"text":{"type":"text"},"condition":{"type":"boolean"},"count":{"type":"number"},"person":{"type":"object"},"words":{"type":"array-text"},"people":{"type":"array-object"}},"type":"javascript","content":"console.log(\'hello\')","inputs":{"text":{"variable":"MzEitlOusl.text"}},"finished":true},"QxNrkChBWQ":{"node":{"name":"循环","description":"实现循环执行流程"},"finished":true,"outputs":{"output":{"type":"array-object"}}},"JsUwvjkJCW":{"node":{"name":"分支","description":"根据不同的情况前往不同的分支"},"conditions":[{"condition":{"id":"736a724a5de4","conjunction":"and","children":[{"id":"1db9e7a90aae","left":{"type":"field","field":"bivXSpiLaI.text"},"op":"equal","right":"1"},{"id":"98cc5c39eed4","left":{"type":"field","field":"bivXSpiLaI.condition"},"op":"is_true"},{"id":"1cbccc438a64","left":{"type":"field","field":"bivXSpiLaI.text"},"op":"equal","right":"2"}]}},{"condition":{"id":"406bf6637c64","conjunction":"and","children":[{"id":"ea37e95e6d1b","left":{"type":"field","field":"bivXSpiLaI.text"},"op":"equal","right":"3"}]}},{"condition":{"id":"d4e42668119c","conjunction":"and","children":[{"id":"62618f0083ec","left":{"type":"field","field":"bivXSpiLaI.count"},"op":"equal","right":2}]}}],"finished":true},"sRWQqqshAE":{"node":{"name":"大模型","description":"使用大模型对话"},"outputs":{"text":{"type":"text"}},"model":"deepseek","systemPrompt":"Hello","finished":true}}}'))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-screen">
|
<div className="h-screen">
|
||||||
|
|||||||
Reference in New Issue
Block a user