feat(web): 节点描述和名称直接放在节点数据中

This commit is contained in:
v-zhangjc9
2025-07-10 16:34:29 +08:00
parent 333da7ef88
commit fad190567b
7 changed files with 58 additions and 36 deletions

View File

@@ -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/>

View File

@@ -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>(<></>)

View File

@@ -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/>}
/> />

View File

@@ -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/>}
/> />

View File

@@ -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">

View File

@@ -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/>}
/> />

View File

@@ -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 => (