refactor(web): 优化节点展现

This commit is contained in:
v-zhangjc9
2025-07-10 12:44:37 +08:00
parent 5e763637da
commit f707a0d2b5
7 changed files with 202 additions and 145 deletions

View File

@@ -104,14 +104,13 @@ export function inputsFormColumns(
] ]
} }
export function outputsFormColumns(editable: boolean = false, required: boolean = false, preload?: any): Schema[] { export function outputsFormColumns(editable: boolean = false, required: boolean = false): Schema[] {
return [ return [
{ {
disabled: !editable, disabled: !editable,
type: 'input-kvs', type: 'input-kvs',
name: 'outputs', name: 'outputs',
label: '输出变量', label: '输出变量',
value: preload,
addButtonText: '新增输出', addButtonText: '新增输出',
draggable: false, draggable: false,
keyItem: { keyItem: {
@@ -155,7 +154,7 @@ type AmisNodeProps = {
nodeProps: NodeProps nodeProps: NodeProps
defaultNodeName: String defaultNodeName: String
defaultNodeDescription?: String defaultNodeDescription?: String
extraNodeDescription?: (nodeData: any) => JSX.Element extraNodeDescription?: JSX.Element
handler: JSX.Element handler: JSX.Element
columnSchema?: () => Schema[] columnSchema?: () => Schema[]
} }
@@ -338,7 +337,7 @@ const AmisNode: (props: AmisNodeProps) => JSX.Element = ({
> >
<div className="card-description p-2 text-secondary text-sm"> <div className="card-description p-2 text-secondary text-sm">
{nodeDescription} {nodeDescription}
{extraNodeDescription?.(nodeData)} {extraNodeDescription}
</div> </div>
</Card> </Card>
{handler} {handler}

View File

@@ -1,5 +1,5 @@
import type {NodeProps} from '@xyflow/react' import type {NodeProps} from '@xyflow/react'
import React from 'react' import React, {useCallback, useEffect} 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'
@@ -7,53 +7,68 @@ import AmisNode, {inputsFormColumns, NormalNodeHandler, outputsFormColumns} from
const CodeNode = (props: NodeProps) => { const CodeNode = (props: NodeProps) => {
const {getNodes, getEdges} = useFlowStore() const {getNodes, getEdges} = useFlowStore()
const {getData} = useDataStore() const {getData, mergeDataById} = useDataStore()
const {getInputSchema} = useContextStore() const {getInputSchema} = useContextStore()
useEffect(() => {
mergeDataById(
props.id,
{
outputs: {
result: {
type: 'string',
},
},
},
)
}, [props.id])
const columnsSchema = useCallback(() => [
...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()),
{
type: 'divider',
},
{
type: 'select',
name: 'type',
label: '代码类型',
required: true,
options: [
{
value: 'javascript',
label: 'JavaScript',
},
{
value: 'python',
label: 'Python',
},
{
value: 'lua',
label: 'Lua',
},
],
},
{
type: 'editor',
required: true,
label: '代码内容',
name: 'content',
language: '${type}',
options: {
wordWrap: 'bounded',
},
},
{
type: 'divider',
},
...outputsFormColumns(true, true),
], [props.id])
return ( return (
<AmisNode <AmisNode
nodeProps={props} nodeProps={props}
defaultNodeName="代码执行" defaultNodeName="代码执行"
defaultNodeDescription="执行自定义的处理代码" defaultNodeDescription="执行自定义的处理代码"
columnSchema={() => [ columnSchema={columnsSchema}
...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()),
{
type: 'divider',
},
{
type: 'select',
name: 'type',
label: '代码类型',
required: true,
options: [
{
value: 'javascript',
label: 'JavaScript',
},
{
value: 'python',
label: 'Python',
},
{
value: 'lua',
label: 'Lua',
},
],
},
{
type: 'editor',
required: true,
label: '代码内容',
name: 'content',
language: '${type}',
options: {
wordWrap: 'bounded',
},
},
{
type: 'divider',
},
...outputsFormColumns(true, true, {result: {type: 'string'}}),
]}
handler={<NormalNodeHandler/>} handler={<NormalNodeHandler/>}
/> />
) )

View File

@@ -1,5 +1,5 @@
import type {NodeProps} from '@xyflow/react' import type {NodeProps} from '@xyflow/react'
import React from 'react' import React, {useCallback, useEffect} 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'
@@ -8,66 +8,81 @@ import AmisNode, {inputsFormColumns, NormalNodeHandler, outputsFormColumns} from
const KnowledgeNode = (props: NodeProps) => { const KnowledgeNode = (props: NodeProps) => {
const {getNodes, getEdges} = useFlowStore() const {getNodes, getEdges} = useFlowStore()
const {getData} = useDataStore() const {getData, mergeDataById} = useDataStore()
const {getInputSchema} = useContextStore() const {getInputSchema} = useContextStore()
useEffect(() => {
mergeDataById(
props.id,
{
outputs: {
result: {
type: 'array-string',
},
},
},
)
}, [props.id])
const columnsSchema = useCallback(() => [
...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()),
{
type: 'divider',
},
{
type: 'select',
name: 'knowledgeId',
label: '知识库',
required: true,
options: [],
source: {
method: 'get',
url: `${commonInfo.baseAiUrl}/knowledge/list`,
// @ts-ignore
adaptor: (payload, response, api, context) => {
return {
...payload,
data: {
items: payload.data.items.map((item: any) => ({value: item['id'], label: item['name']})),
},
}
},
},
},
{
type: 'input-text',
name: 'query',
label: '查询文本',
required: true,
},
{
type: 'input-range',
name: 'count',
label: '返回数量',
required: true,
value: 3,
max: 10,
},
{
type: 'input-range',
name: 'score',
label: '匹配阀值',
required: true,
value: 0.6,
max: 1,
step: 0.05,
},
{
type: 'divider',
},
...outputsFormColumns(false, true),
], [props.id])
return ( return (
<AmisNode <AmisNode
nodeProps={props} nodeProps={props}
defaultNodeName="知识库" defaultNodeName="知识库"
defaultNodeDescription="查询知识库获取外部知识" defaultNodeDescription="查询知识库获取外部知识"
columnSchema={() => [ columnSchema={columnsSchema}
...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()),
{
type: 'divider',
},
{
type: 'select',
name: 'knowledgeId',
label: '知识库',
required: true,
options: [],
source: {
method: 'get',
url: `${commonInfo.baseAiUrl}/knowledge/list`,
// @ts-ignore
adaptor: (payload, response, api, context) => {
return {
...payload,
data: {
items: payload.data.items.map((item: any) => ({value: item['id'], label: item['name']})),
},
}
},
},
},
{
type: 'input-text',
name: 'query',
label: '查询文本',
required: true,
},
{
type: 'input-range',
name: 'count',
label: '返回数量',
required: true,
value: 3,
max: 10,
},
{
type: 'input-range',
name: 'score',
label: '匹配阀值',
required: true,
value: 0.6,
max: 1,
step: 0.05,
},
{
type: 'divider',
},
...outputsFormColumns(false, true, {result: {type: 'array-string'}}),
]}
handler={<NormalNodeHandler/>} handler={<NormalNodeHandler/>}
/> />
) )

View File

@@ -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 from 'react' import React, {useCallback, useEffect, useState} 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'
@@ -13,46 +13,63 @@ const modelMap: Record<string, string> = {
const LlmNode = (props: NodeProps) => { const LlmNode = (props: NodeProps) => {
const {getNodes, getEdges} = useFlowStore() const {getNodes, getEdges} = useFlowStore()
const {getData} = useDataStore() const {getData, mergeDataById, getDataById} = useDataStore()
const {getInputSchema} = useContextStore() const {getInputSchema} = useContextStore()
const [nodeData, setNodeData] = useState<any>()
useEffect(() => {
mergeDataById(
props.id,
{
outputs: {
text: {
type: 'string',
},
},
},
)
setNodeData(getDataById(props.id))
}, [props.id])
const columnsSchema = useCallback(() => [
...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()),
{
type: 'divider',
},
{
type: 'select',
name: 'model',
label: '大模型',
required: true,
selectFirst: true,
options: Object.keys(modelMap).map(key => ({label: modelMap[key], value: key})),
},
{
type: 'textarea',
name: 'systemPrompt',
label: '系统提示词',
required: true,
},
{
type: 'divider',
},
...outputsFormColumns(false, true),
], [props.id])
return ( return (
<AmisNode <AmisNode
nodeProps={props} nodeProps={props}
defaultNodeName="大模型" defaultNodeName="大模型"
defaultNodeDescription="使用大模型对话" defaultNodeDescription="使用大模型对话"
extraNodeDescription={nodeData => { extraNodeDescription={
const model = nodeData?.model as string | undefined nodeData?.model
return model
? <div className="mt-2 flex justify-between"> ? <div className="mt-2 flex justify-between">
<span></span> <span></span>
<Tag className="m-0" color="blue">{modelMap[model]}</Tag> <Tag className="m-0" color="blue">{modelMap[nodeData.model]}</Tag>
</div> </div>
: <></> : <></>
}} }
columnSchema={() => [ columnSchema={columnsSchema}
...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()),
{
type: 'divider',
},
{
type: 'select',
name: 'model',
label: '大模型',
required: true,
selectFirst: true,
options: Object.keys(modelMap).map(key => ({label: modelMap[key], value: key})),
},
{
type: 'textarea',
name: 'systemPrompt',
label: '系统提示词',
required: true,
},
{
type: 'divider',
},
...outputsFormColumns(false, true, {text: {type: 'string'}}),
]}
handler={<NormalNodeHandler/>} handler={<NormalNodeHandler/>}
/> />
) )

View File

@@ -1,14 +1,16 @@
import type {NodeProps} from '@xyflow/react' import type {NodeProps} from '@xyflow/react'
import React from 'react' import React, {useCallback} from 'react'
import AmisNode, {EndNodeHandler, outputsFormColumns} from './AmisNode.tsx' import AmisNode, {EndNodeHandler, outputsFormColumns} from './AmisNode.tsx'
const OutputNode = (props: NodeProps) => { const OutputNode = (props: NodeProps) => {
const columnsSchema = useCallback(() => outputsFormColumns(true), [props.id])
return ( return (
<AmisNode <AmisNode
nodeProps={props} nodeProps={props}
defaultNodeName="输出节点" defaultNodeName="输出节点"
defaultNodeDescription="定义输出变量" defaultNodeDescription="定义输出变量"
columnSchema={() => outputsFormColumns(true)} columnSchema={columnsSchema}
handler={<EndNodeHandler/>} handler={<EndNodeHandler/>}
/> />
) )

View File

@@ -21,17 +21,15 @@ const SwitchNode = (props: NodeProps) => {
nodeProps={props} nodeProps={props}
defaultNodeName="分支节点" defaultNodeName="分支节点"
defaultNodeDescription="根据不同的情况前往不同的分支" defaultNodeDescription="根据不同的情况前往不同的分支"
extraNodeDescription={() => { extraNodeDescription={
return ( <div className="mt-2">
<div className="mt-2"> {cases.map(item => (
{cases.map(item => ( <div key={item.index} className="mt-1">
<div key={item.index} className="mt-1"> <Tag className="m-0" color="blue"> {item.index}</Tag>
<Tag className="m-0" color="blue"> {item.index}</Tag> </div>
</div> ))}
))} </div>
</div> }
)
}}
handler={ handler={
<> <>
<Handle type="target" position={Position.Left}/> <Handle type="target" position={Position.Left}/>

View File

@@ -6,6 +6,7 @@ export type DataStoreState = {
setData: (data: Record<string, any>) => void, setData: (data: Record<string, any>) => void,
getDataById: (id: string) => any, getDataById: (id: string) => any,
setDataById: (id: string, data: any) => void, setDataById: (id: string, data: any) => void,
mergeDataById: (id: string, data: any) => void,
} }
export const useDataStore = create<DataStoreState>((set, get) => ({ export const useDataStore = create<DataStoreState>((set, get) => ({
@@ -22,4 +23,14 @@ export const useDataStore = create<DataStoreState>((set, get) => ({
data: updateData, data: updateData,
}) })
}, },
mergeDataById: (id, data) => {
let updateData = get().data
updateData[id] = {
...(updateData[id] ?? {}),
...data,
}
set({
data: updateData,
})
},
})) }))