feat(web): 增加节点分组

This commit is contained in:
v-zhangjc9
2025-07-12 19:09:47 +08:00
parent 528e66c497
commit 60f6b79167
3 changed files with 40 additions and 22 deletions

View File

@@ -1,6 +1,6 @@
import {type Connection, type Edge, getOutgoers, type Node} from '@xyflow/react' import {type Connection, type Edge, getOutgoers, type Node} from '@xyflow/react'
import {find, has, isEmpty, isEqual, lpad, toStr} from 'licia' import {find, has, isEmpty, isEqual, lpad, toStr} from 'licia'
import NodeRegistry from './NodeRegistry.tsx' import {NodeRegistryMap} from './NodeRegistry.tsx'
export class CheckError extends Error { export class CheckError extends Error {
readonly id: string readonly id: string
@@ -83,7 +83,7 @@ export const checkSave: (inputSchema: Record<string, Record<string, any>>, nodes
throw nodeTypeNotFound() throw nodeTypeNotFound()
} }
let nodeType = node.type! let nodeType = node.type!
let nodeDefine = NodeRegistry[nodeType] let nodeDefine = NodeRegistryMap[nodeType]
for (let checker of nodeDefine.checkers) { for (let checker of nodeDefine.checkers) {
let checkResult = checker(nodeId, inputSchema, nodes, edges, data) let checkResult = checker(nodeId, inputSchema, nodes, edges, data)
if (checkResult.error) { if (checkResult.error) {

View File

@@ -1,14 +1,14 @@
import {PlusCircleFilled, RollbackOutlined, SaveFilled} from '@ant-design/icons' import {PlusCircleFilled, RollbackOutlined, SaveFilled} from '@ant-design/icons'
import {Background, BackgroundVariant, Controls, MiniMap, ReactFlow} from '@xyflow/react' import {Background, BackgroundVariant, Controls, MiniMap, ReactFlow} from '@xyflow/react'
import {Button, Dropdown, message, Popconfirm, Space} from 'antd' import {Button, Dropdown, message, Popconfirm, Space} from 'antd'
import {arrToMap, randomId} from 'licia' import {arrToMap, isEqual, randomId, unique} 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'
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 {checkAddConnection, checkAddNode, checkSave} from './FlowChecker.tsx' import {checkAddConnection, checkAddNode, checkSave} from './FlowChecker.tsx'
import NodeRegistry from './NodeRegistry.tsx' import {NodeRegistry, NodeRegistryMap} from './NodeRegistry.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'
@@ -88,7 +88,13 @@ function FlowEditor(props: FlowEditorProps) {
<Space className="toolbar"> <Space className="toolbar">
<Dropdown <Dropdown
menu={{ menu={{
items: Object.keys(NodeRegistry).map(key => ({key: key, label: NodeRegistry[key]!.name})), items: unique(NodeRegistry.map(i => i.group))
.map(group => ({
type: 'group',
label: group,
children: NodeRegistry.filter(i => isEqual(group, i.group))
.map(i => ({key: i.key, label: i.name}))
})),
onClick: ({key}) => { onClick: ({key}) => {
try { try {
if (commonInfo.debug) { if (commonInfo.debug) {
@@ -97,7 +103,7 @@ function FlowEditor(props: FlowEditorProps) {
checkAddNode(key, nodes, edges) checkAddNode(key, nodes, edges)
let nodeId = randomId(10, 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM') let nodeId = randomId(10, 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM')
let define = NodeRegistry[key] let define = NodeRegistryMap[key]
setDataById( setDataById(
nodeId, nodeId,
@@ -171,7 +177,7 @@ function FlowEditor(props: FlowEditorProps) {
} }
}} }}
// @ts-ignore // @ts-ignore
nodeTypes={arrToMap(Object.keys(NodeRegistry), key => NodeRegistry[key]!.component)} nodeTypes={arrToMap(Object.keys(NodeRegistryMap), key => NodeRegistryMap[key]!.component)}
> >
<Controls/> <Controls/>
<MiniMap/> <MiniMap/>

View File

@@ -14,7 +14,7 @@ const inputVariableChecker: NodeChecker = (id, inputSchema, nodes, edges, data)
if (!isEmpty(inputs)) { if (!isEmpty(inputs)) {
let outputVariables = new Set([ let outputVariables = new Set([
...getAllIncomerNodeOutputVariables(id, nodes, edges, data).map(i => `${i.id}.${i.variable}`), ...getAllIncomerNodeOutputVariables(id, nodes, edges, data).map(i => `${i.id}.${i.variable}`),
...Object.keys(inputSchema) ...Object.keys(inputSchema),
]) ])
for (const key of Object.keys(inputs)) { for (const key of Object.keys(inputs)) {
let variable = inputs[key]?.variable ?? '' let variable = inputs[key]?.variable ?? ''
@@ -31,43 +31,55 @@ const inputVariableChecker: NodeChecker = (id, inputSchema, nodes, edges, data)
} }
type NodeDefine = { type NodeDefine = {
key: string,
group: string,
name: string, name: string,
description: string, description: string,
component: any, component: any,
checkers: NodeChecker[], checkers: NodeChecker[],
} }
const NodeRegistry: Record<string, NodeDefine> = { export const NodeRegistry: NodeDefine[] = [
'output-node': { {
name: '输出', key: 'llm-node',
description: '定义输出变量', group: '普通节点',
component: OutputNode,
checkers: [inputVariableChecker],
},
'llm-node': {
name: '大模型', name: '大模型',
description: '使用大模型对话', description: '使用大模型对话',
component: LlmNode, component: LlmNode,
checkers: [inputVariableChecker], checkers: [inputVariableChecker],
}, },
'knowledge-node': { {
key: 'knowledge-node',
group: '普通节点',
name: '知识库', name: '知识库',
description: '', description: '',
component: KnowledgeNode, component: KnowledgeNode,
checkers: [inputVariableChecker], checkers: [inputVariableChecker],
}, },
'code-node': { {
key: 'code-node',
group: '普通节点',
name: '代码执行', name: '代码执行',
description: '执行自定义的处理代码', description: '执行自定义的处理代码',
component: CodeNode, component: CodeNode,
checkers: [inputVariableChecker], checkers: [inputVariableChecker],
}, },
'switch-node': { {
name: '分支节点', key: 'switch-node',
group: '逻辑节点',
name: '分支',
description: '根据不同的情况前往不同的分支', description: '根据不同的情况前往不同的分支',
component: SwitchNode, component: SwitchNode,
checkers: [], checkers: [],
}, },
} {
key: 'output-node',
group: '输出节点',
name: '输出',
description: '定义输出变量',
component: OutputNode,
checkers: [inputVariableChecker],
},
]
export default NodeRegistry export const NodeRegistryMap: Record<string, NodeDefine> = NodeRegistry.reduce((a, v) => ({...a, [v.key]: v}), {})