feat(web): 增加模板节点

This commit is contained in:
v-zhangjc9
2025-07-12 22:55:19 +08:00
parent 02b2d44ccc
commit b0d41e0d88
5 changed files with 184 additions and 99 deletions

View File

@@ -2,14 +2,12 @@ import {CopyFilled, DeleteFilled, EditFilled} from '@ant-design/icons'
import {type Edge, Handle, type Node, type NodeProps, Position} from '@xyflow/react'
import type {Schema} from 'amis'
import {Button, Card, Drawer} from 'antd'
import {has, isEmpty} from 'licia'
import {type JSX, useCallback, useState} from 'react'
import styled from 'styled-components'
import {amisRender, commonInfo, horizontalFormOptions} from '../../../util/amis.tsx'
import {getAllIncomerNodeById} from '../Helper.tsx'
import {generateAllIncomerOutputVariablesFormOptions} from '../Helper.tsx'
import {useDataStore} from '../store/DataStore.ts'
import {useFlowStore} from '../store/FlowStore.ts'
import type {InputFormOptions, InputFormOptionsGroup} from '../types.ts'
export function inputsFormColumns(
nodeId: string,
@@ -18,41 +16,6 @@ export function inputsFormColumns(
edges: Edge[],
data: any,
): Schema[] {
let inputSchemaVariables: InputFormOptions[] = Object.keys(inputSchema).map(key => ({
label: `${key} (${inputSchema[key]?.label ?? ''})`,
value: key,
}))
let incomerIds = getAllIncomerNodeById(nodeId, nodes, edges)
let incomerVariables: InputFormOptionsGroup[] = []
for (const incomerId of incomerIds) {
let nodeData = data[incomerId] ?? {}
let group = incomerId
if (has(nodeData, 'node') && has(nodeData.node, 'name')) {
group = `${nodeData.node.name} ${incomerId}`
}
if (has(nodeData, 'outputs')) {
let outputs = nodeData?.outputs ?? []
incomerVariables.push({
group: group,
variables: Object.keys(outputs).map(key => ({
value: `${incomerId}.${key}`,
label: key,
})),
})
}
}
let inputVariables = [
...(isEmpty(inputSchemaVariables) ? [] : [
{
group: '流程入参',
variables: inputSchemaVariables,
},
]),
...incomerVariables,
]
return [
{
type: 'input-kvs',
@@ -72,12 +35,13 @@ export function inputsFormColumns(
label: '变量',
required: true,
selectMode: 'group',
options: [
...inputVariables.map(item => ({
label: item.group,
children: item.variables,
})),
],
options: generateAllIncomerOutputVariablesFormOptions(
nodeId,
inputSchema,
nodes,
edges,
data,
),
},
],
},

View File

@@ -1,59 +1,31 @@
import type {NodeProps} from '@xyflow/react'
import {Tag} from 'antd'
import React, {useCallback} from 'react'
import {generateAllIncomerOutputVariablesFormOptions} from '../Helper.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 typeMap: Record<string, string> = {
markdown: 'Markdown',
json: 'JSON',
'template-markdown': 'Markdown 模板',
'template-rich-text': '富文本模板',
}
import AmisNode, {EndNodeHandler} from './AmisNode.tsx'
const OutputNode = (props: NodeProps) => {
const {getNodes, getEdges} = useFlowStore()
const {getData, getDataById} = useDataStore()
const {getData} = useDataStore()
const {getInputSchema} = useContextStore()
const nodeData = getDataById(props.id)
const columnsSchema = useCallback(
() => [
...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()),
{
type: 'divider',
},
{
type: 'select',
name: 'type',
label: '输出类型',
name: 'output',
label: '输出变量',
required: true,
selectFirst: true,
options: Object.keys(typeMap).map(key => ({label: typeMap[key], value: key})),
},
{
visibleOn: 'type === \'template-markdown\'',
type: 'editor',
required: true,
label: '模板内容',
name: 'template',
language: 'markdown',
options: {
wordWrap: 'bounded',
},
},
{
visibleOn: 'type === \'template-rich-text\'',
type: 'input-rich-text',
required: true,
name: 'template',
label: '模板内容',
options: {
min_height: 500,
},
selectMode: 'group',
options: generateAllIncomerOutputVariablesFormOptions(
props.id,
getInputSchema(),
getNodes(),
getEdges(),
getData(),
),
},
],
[props.id],
@@ -62,14 +34,6 @@ const OutputNode = (props: NodeProps) => {
return (
<AmisNode
nodeProps={props}
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}
handler={<EndNodeHandler/>}
/>

View File

@@ -0,0 +1,79 @@
import type {NodeProps} from '@xyflow/react'
import {Tag} from 'antd'
import React, {useCallback} from 'react'
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 typeMap: Record<string, string> = {
default: '默认',
json: 'JSON',
'template-markdown': 'Markdown',
'template-rich-text': '富文本',
}
const TemplateNode = (props: NodeProps) => {
const {getNodes, getEdges} = useFlowStore()
const {getData, getDataById} = useDataStore()
const {getInputSchema} = useContextStore()
const nodeData = getDataById(props.id)
const columnsSchema = useCallback(
() => [
...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()),
{
type: 'divider',
},
{
type: 'select',
name: 'type',
label: '模板类型',
required: true,
selectFirst: true,
options: Object.keys(typeMap).map(key => ({label: typeMap[key], value: key})),
},
{
visibleOn: 'type === \'template-markdown\'',
type: 'editor',
required: true,
label: '模板内容',
name: 'template',
language: 'markdown',
options: {
wordWrap: 'bounded',
},
},
{
visibleOn: 'type === \'template-rich-text\'',
type: 'input-rich-text',
required: true,
name: 'template',
label: '模板内容',
options: {
min_height: 500,
},
},
],
[props.id],
)
return (
<AmisNode
nodeProps={props}
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}
handler={<EndNodeHandler/>}
/>
)
}
export default React.memo(TemplateNode)