From 5abfcdfbe8c282f2389a79753b2217526010328e Mon Sep 17 00:00:00 2001 From: zhaoying Date: Mon, 2 Mar 2026 17:07:29 +0800 Subject: [PATCH] feat(web): add unknown node --- web/src/assets/images/workflow/unknown.svg | 26 +++++++ web/src/i18n/en.ts | 1 + web/src/i18n/zh.ts | 7 +- .../Workflow/components/Properties/index.tsx | 73 ++++++++++++++++--- web/src/views/Workflow/constant.ts | 5 ++ .../views/Workflow/hooks/useWorkflowGraph.ts | 4 +- 6 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 web/src/assets/images/workflow/unknown.svg diff --git a/web/src/assets/images/workflow/unknown.svg b/web/src/assets/images/workflow/unknown.svg new file mode 100644 index 00000000..4c8198dd --- /dev/null +++ b/web/src/assets/images/workflow/unknown.svg @@ -0,0 +1,26 @@ + + + 未知节点 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index 9df6d018..fe38734d 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -1980,6 +1980,7 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re evolutionAndGovernance: 'Evolution & Governance', self_optimization: 'Self Optimization', process_evolution: 'Process Evolution', + unknown: 'Unknown Node', clickToConfigure: 'Click to configure node parameters', nodeProperties: 'Node Properties', diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index 3fe37ea8..19ff6d53 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -1977,6 +1977,7 @@ export const zh = { evolutionAndGovernance: '演化与治理', self_optimization: '自我优化', process_evolution: '流程演化', + unknown: '未知节点', clickToConfigure: '点击配置节点参数', nodeProperties: '节点属性', @@ -2164,6 +2165,9 @@ export const zh = { output_variables: '输出变量', refreshTip: '同步函数签名至代码', }, + unknown: { + replaceNodeType: '替换节点' + }, name: '键', type: '类型', value: '值', @@ -2195,7 +2199,8 @@ export const zh = { iteration: '迭代', input_cycle_vars: '初始循环变量', output_cycle_vars: '最终循环变量', - } + }, + sureReplace: '确认替换', }, emotionEngine: { emotionEngineConfig: '情感引擎配置', diff --git a/web/src/views/Workflow/components/Properties/index.tsx b/web/src/views/Workflow/components/Properties/index.tsx index e96b1757..76fc9ad0 100644 --- a/web/src/views/Workflow/components/Properties/index.tsx +++ b/web/src/views/Workflow/components/Properties/index.tsx @@ -1,14 +1,14 @@ /* * @Author: ZhaoYing * @Date: 2026-02-03 15:39:59 - * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-02-11 12:07:06 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-03-02 17:06:41 */ import { type FC, useEffect, useState, useMemo } from "react"; import clsx from 'clsx' import { useTranslation } from 'react-i18next' import { Graph, Node } from '@antv/x6'; -import { Form, Input, Select, InputNumber, Switch, Divider, Space } from 'antd' +import { Form, Input, Select, InputNumber, Switch, Divider, Space, Button } from 'antd' import { CaretDownOutlined, CaretRightOutlined } from '@ant-design/icons'; import type { NodeConfig, NodeProperties, ChatVariable } from '../../types' @@ -36,6 +36,7 @@ import Editor, { type LexicalEditorProps } from "../Editor"; import RbSlider from './RbSlider' import JinjaRender from './JinjaRender' import CodeExecution from './CodeExecution' +import { nodeLibrary } from '../../constant'; /** * Props for Properties component @@ -69,7 +70,8 @@ interface PropertiesProps { const Properties: FC = ({ selectedNode, graphRef, - chatVariables + chatVariables, + blankClick }) => { const { t } = useTranslation() const [form] = Form.useForm(); @@ -80,9 +82,8 @@ const Properties: FC = ({ useEffect(() => { if (selectedNode?.getData()?.id) { setOutputCollapsed(true) - } else { - form.resetFields() } + form.resetFields() }, [selectedNode?.getData()?.id]) useEffect(() => { @@ -94,7 +95,7 @@ const Properties: FC = ({ initialValue[key] = config[key].defaultValue } }) - + form.setFieldsValue({ type, id: selectedNode.id, @@ -380,6 +381,41 @@ const Properties: FC = ({ } } console.log('variableList', variableList, currentNodeVariables) + const handleSureReplace = () => { + const { replaceNode } = values; + const nodeLibraryConfig = [...nodeLibrary] + .flatMap(category => category.nodes) + .find(n => n.type === replaceNode) + + if (replaceNode && nodeLibraryConfig) { + // Preserve existing config values when switching node types + const currentData = selectedNode?.data || {}; + const currentConfig = currentData.config || {}; + const newConfig = nodeLibraryConfig.config || {}; + + // Merge configs: keep existing values for matching keys, add new keys from template + const mergedConfig: Record = {}; + Object.keys(newConfig).forEach(key => { + if (currentConfig[key] && currentConfig[key].defaultValue !== undefined) { + // Preserve existing value if it exists + mergedConfig[key] = { + ...newConfig[key], + defaultValue: currentConfig[key].defaultValue + }; + } else { + // Use new config template + mergedConfig[key] = { ...newConfig[key] }; + } + }); + + selectedNode?.setData({ + ...currentData, + ...nodeLibraryConfig, + config: mergedConfig + }) + blankClick() + } + } return (
@@ -399,8 +435,27 @@ const Properties: FC = ({ - - {selectedNode?.data?.type === 'http-request' + {selectedNode?.data?.type === 'unknown' + ? <> + +