feat(web): 节点描述和名称直接放在节点数据中
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import {PlusCircleFilled, RollbackOutlined, SaveFilled} from '@ant-design/icons'
|
import {PlusCircleFilled, RollbackOutlined, SaveFilled} from '@ant-design/icons'
|
||||||
import {Background, BackgroundVariant, Controls, type Edge, MiniMap, type Node, ReactFlow} from '@xyflow/react'
|
import {Background, BackgroundVariant, Controls, type Edge, MiniMap, type Node, ReactFlow} from '@xyflow/react'
|
||||||
import {Button, Dropdown, message, Popconfirm, Space} from 'antd'
|
import {Button, Dropdown, message, Popconfirm, Space} from 'antd'
|
||||||
import {arrToMap, find, isEqual, randomId} from 'licia'
|
import {arrToMap, randomId} from 'licia'
|
||||||
import {useEffect} from 'react'
|
import {useEffect} from 'react'
|
||||||
import {useNavigate} from 'react-router'
|
import {useNavigate} from 'react-router'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
@@ -52,33 +52,45 @@ const FlowableDiv = styled.div`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const nodeDefine = [
|
type NodeDefine = {
|
||||||
{
|
key: string,
|
||||||
|
name: string,
|
||||||
|
description: string,
|
||||||
|
component: any,
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodeDefine: Record<string, NodeDefine> = {
|
||||||
|
'output-node': {
|
||||||
key: 'output-node',
|
key: 'output-node',
|
||||||
name: '输出',
|
name: '输出',
|
||||||
|
description: '定义输出变量',
|
||||||
component: OutputNode,
|
component: OutputNode,
|
||||||
},
|
},
|
||||||
{
|
'llm-node': {
|
||||||
key: 'llm-node',
|
key: 'llm-node',
|
||||||
name: '大模型',
|
name: '大模型',
|
||||||
|
description: '使用大模型对话',
|
||||||
component: LlmNode,
|
component: LlmNode,
|
||||||
},
|
},
|
||||||
{
|
'knowledge-node': {
|
||||||
key: 'knowledge-node',
|
key: 'knowledge-node',
|
||||||
name: '知识库',
|
name: '知识库',
|
||||||
|
description: '',
|
||||||
component: KnowledgeNode,
|
component: KnowledgeNode,
|
||||||
},
|
},
|
||||||
{
|
'code-node': {
|
||||||
key: 'code-node',
|
key: 'code-node',
|
||||||
name: '代码执行',
|
name: '代码执行',
|
||||||
|
description: '执行自定义的处理代码',
|
||||||
component: CodeNode,
|
component: CodeNode,
|
||||||
},
|
},
|
||||||
{
|
'switch-node': {
|
||||||
key: 'switch-node',
|
key: 'switch-node',
|
||||||
name: '条件分支',
|
name: '分支节点',
|
||||||
|
description: '根据不同的情况前往不同的分支',
|
||||||
component: SwitchNode,
|
component: SwitchNode,
|
||||||
},
|
},
|
||||||
]
|
}
|
||||||
|
|
||||||
export type GraphData = { nodes: Node[], edges: Edge[], data: any }
|
export type GraphData = { nodes: Node[], edges: Edge[], data: any }
|
||||||
|
|
||||||
@@ -92,7 +104,7 @@ function FlowEditor(props: FlowEditorProps) {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const [messageApi, contextHolder] = message.useMessage()
|
const [messageApi, contextHolder] = message.useMessage()
|
||||||
|
|
||||||
const {data, setData} = useDataStore()
|
const {data, setData, setDataById} = useDataStore()
|
||||||
const {
|
const {
|
||||||
nodes,
|
nodes,
|
||||||
addNode,
|
addNode,
|
||||||
@@ -127,15 +139,29 @@ function FlowEditor(props: FlowEditorProps) {
|
|||||||
<Space className="toolbar">
|
<Space className="toolbar">
|
||||||
<Dropdown
|
<Dropdown
|
||||||
menu={{
|
menu={{
|
||||||
items: nodeDefine.map(def => ({key: def.key, label: def.name})),
|
items: Object.keys(nodeDefine).map(key => ({key: key, label: nodeDefine[key]!.name})),
|
||||||
onClick: ({key}) => {
|
onClick: ({key}) => {
|
||||||
try {
|
try {
|
||||||
if (commonInfo.debug) {
|
if (commonInfo.debug) {
|
||||||
console.info('Add', key, JSON.stringify({nodes, edges, data}))
|
console.info('Add', key, JSON.stringify({nodes, edges, data}))
|
||||||
}
|
}
|
||||||
checkAddNode(key, nodes, edges)
|
checkAddNode(key, nodes, edges)
|
||||||
|
|
||||||
|
let nodeId = randomId(10, 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM')
|
||||||
|
let define = nodeDefine[key]
|
||||||
|
|
||||||
|
setDataById(
|
||||||
|
nodeId,
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
name: define.name,
|
||||||
|
description: define.description,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
addNode({
|
addNode({
|
||||||
id: randomId(10),
|
id: nodeId,
|
||||||
type: key,
|
type: key,
|
||||||
position: {x: 100, y: 100},
|
position: {x: 100, y: 100},
|
||||||
data: {},
|
data: {},
|
||||||
@@ -196,10 +222,7 @@ function FlowEditor(props: FlowEditorProps) {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
nodeTypes={arrToMap(
|
nodeTypes={arrToMap(Object.keys(nodeDefine), key => nodeDefine[key]!.component)}
|
||||||
nodeDefine.map(def => def.key),
|
|
||||||
key => find(nodeDefine, def => isEqual(key, def.key))!.component)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Controls/>
|
<Controls/>
|
||||||
<MiniMap/>
|
<MiniMap/>
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export function inputsFormColumns(
|
|||||||
data: any,
|
data: any,
|
||||||
): Schema[] {
|
): Schema[] {
|
||||||
let inputSchemaVariables: InputFormOptions[] = Object.keys(inputSchema).map(key => ({
|
let inputSchemaVariables: InputFormOptions[] = Object.keys(inputSchema).map(key => ({
|
||||||
label: inputSchema[key]?.label ?? '',
|
label: `${key} (${inputSchema[key]?.label ?? ''})`,
|
||||||
value: key,
|
value: key,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@@ -50,10 +50,14 @@ export function inputsFormColumns(
|
|||||||
let incomerVariables: InputFormOptionsGroup[] = []
|
let incomerVariables: InputFormOptionsGroup[] = []
|
||||||
for (const incomerId of incomerIds) {
|
for (const incomerId of incomerIds) {
|
||||||
let nodeData = data[incomerId] ?? {}
|
let nodeData = data[incomerId] ?? {}
|
||||||
|
let group = incomerId
|
||||||
|
if (has(nodeData, 'node') && has(nodeData.node, 'name')) {
|
||||||
|
group = `${nodeData.node.name} ${incomerId}`
|
||||||
|
}
|
||||||
if (has(nodeData, 'outputs')) {
|
if (has(nodeData, 'outputs')) {
|
||||||
let outputs = nodeData?.outputs ?? []
|
let outputs = nodeData?.outputs ?? []
|
||||||
incomerVariables.push({
|
incomerVariables.push({
|
||||||
group: incomerId,
|
group: group,
|
||||||
variables: Object.keys(outputs).map(key => ({
|
variables: Object.keys(outputs).map(key => ({
|
||||||
value: `${incomerId}.${key}`,
|
value: `${incomerId}.${key}`,
|
||||||
label: key,
|
label: key,
|
||||||
@@ -151,8 +155,6 @@ export function outputsFormColumns(editable: boolean = false, required: boolean
|
|||||||
|
|
||||||
type AmisNodeProps = {
|
type AmisNodeProps = {
|
||||||
nodeProps: NodeProps
|
nodeProps: NodeProps
|
||||||
defaultNodeName: String
|
|
||||||
defaultNodeDescription?: String
|
|
||||||
extraNodeDescription?: JSX.Element
|
extraNodeDescription?: JSX.Element
|
||||||
handler: JSX.Element
|
handler: JSX.Element
|
||||||
columnSchema?: () => Schema[]
|
columnSchema?: () => Schema[]
|
||||||
@@ -187,8 +189,6 @@ export const NormalNodeHandler = () => {
|
|||||||
|
|
||||||
const AmisNode: (props: AmisNodeProps) => JSX.Element = ({
|
const AmisNode: (props: AmisNodeProps) => JSX.Element = ({
|
||||||
nodeProps,
|
nodeProps,
|
||||||
defaultNodeName,
|
|
||||||
defaultNodeDescription,
|
|
||||||
extraNodeDescription,
|
extraNodeDescription,
|
||||||
handler,
|
handler,
|
||||||
columnSchema,
|
columnSchema,
|
||||||
@@ -198,8 +198,8 @@ const AmisNode: (props: AmisNodeProps) => JSX.Element = ({
|
|||||||
const {id} = nodeProps
|
const {id} = nodeProps
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const nodeData = getDataById(id)
|
const nodeData = getDataById(id)
|
||||||
const nodeName = isEmpty(nodeData?.node?.name) ? defaultNodeName : nodeData.node.name
|
const nodeName = nodeData?.node?.name ?? ''
|
||||||
const nodeDescription = isEmpty(nodeData?.node?.description) ? defaultNodeDescription : nodeData.node?.description
|
const nodeDescription = nodeData?.node?.description ?? ''
|
||||||
|
|
||||||
const [editDrawerOpen, setEditDrawerOpen] = useState(false)
|
const [editDrawerOpen, setEditDrawerOpen] = useState(false)
|
||||||
const [editDrawerForm, setEditDrawerForm] = useState<JSX.Element>(<></>)
|
const [editDrawerForm, setEditDrawerForm] = useState<JSX.Element>(<></>)
|
||||||
|
|||||||
@@ -66,8 +66,6 @@ const CodeNode = (props: NodeProps) => {
|
|||||||
return (
|
return (
|
||||||
<AmisNode
|
<AmisNode
|
||||||
nodeProps={props}
|
nodeProps={props}
|
||||||
defaultNodeName="代码执行"
|
|
||||||
defaultNodeDescription="执行自定义的处理代码"
|
|
||||||
columnSchema={columnsSchema}
|
columnSchema={columnsSchema}
|
||||||
handler={<NormalNodeHandler/>}
|
handler={<NormalNodeHandler/>}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -80,8 +80,6 @@ const KnowledgeNode = (props: NodeProps) => {
|
|||||||
return (
|
return (
|
||||||
<AmisNode
|
<AmisNode
|
||||||
nodeProps={props}
|
nodeProps={props}
|
||||||
defaultNodeName="知识库"
|
|
||||||
defaultNodeDescription="查询知识库获取外部知识"
|
|
||||||
columnSchema={columnsSchema}
|
columnSchema={columnsSchema}
|
||||||
handler={<NormalNodeHandler/>}
|
handler={<NormalNodeHandler/>}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -58,8 +58,6 @@ const LlmNode = (props: NodeProps) => {
|
|||||||
return (
|
return (
|
||||||
<AmisNode
|
<AmisNode
|
||||||
nodeProps={props}
|
nodeProps={props}
|
||||||
defaultNodeName="大模型"
|
|
||||||
defaultNodeDescription="使用大模型对话"
|
|
||||||
extraNodeDescription={
|
extraNodeDescription={
|
||||||
nodeData?.model
|
nodeData?.model
|
||||||
? <div className="mt-2 flex justify-between">
|
? <div className="mt-2 flex justify-between">
|
||||||
|
|||||||
@@ -1,15 +1,22 @@
|
|||||||
import type {NodeProps} from '@xyflow/react'
|
import type {NodeProps} from '@xyflow/react'
|
||||||
import React, {useCallback} from 'react'
|
import React, {useCallback} from 'react'
|
||||||
import AmisNode, {EndNodeHandler, outputsFormColumns} from './AmisNode.tsx'
|
import {useContextStore} from '../store/ContextStore.ts'
|
||||||
|
import {useDataStore} from '../store/DataStore.ts'
|
||||||
|
import {useFlowStore} from '../store/FlowStore.ts'
|
||||||
|
import AmisNode, {EndNodeHandler, inputsFormColumns} from './AmisNode.tsx'
|
||||||
|
|
||||||
const OutputNode = (props: NodeProps) => {
|
const OutputNode = (props: NodeProps) => {
|
||||||
const columnsSchema = useCallback(() => outputsFormColumns(true), [props.id])
|
const {getNodes, getEdges} = useFlowStore()
|
||||||
|
const {getData} = useDataStore()
|
||||||
|
const {getInputSchema} = useContextStore()
|
||||||
|
const columnsSchema = useCallback(
|
||||||
|
() => inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()),
|
||||||
|
[props.id],
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AmisNode
|
<AmisNode
|
||||||
nodeProps={props}
|
nodeProps={props}
|
||||||
defaultNodeName="输出节点"
|
|
||||||
defaultNodeDescription="定义输出变量"
|
|
||||||
columnSchema={columnsSchema}
|
columnSchema={columnsSchema}
|
||||||
handler={<EndNodeHandler/>}
|
handler={<EndNodeHandler/>}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ const SwitchNode = (props: NodeProps) => {
|
|||||||
return (
|
return (
|
||||||
<AmisNode
|
<AmisNode
|
||||||
nodeProps={props}
|
nodeProps={props}
|
||||||
defaultNodeName="分支节点"
|
|
||||||
defaultNodeDescription="根据不同的情况前往不同的分支"
|
|
||||||
extraNodeDescription={
|
extraNodeDescription={
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
{cases.map(item => (
|
{cases.map(item => (
|
||||||
|
|||||||
Reference in New Issue
Block a user