From debef2f8813a1846bb8a7404a33440d4dea83e25 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Fri, 20 Dec 2024 13:43:25 +0800 Subject: [PATCH] 1 --- .../Design/components/NodeConfigModal.tsx | 63 +++++++---------- .../Design/components/NodePanel.tsx | 43 +++++------- .../Workflow/Definition/Design/index.tsx | 11 +-- .../Definition/Design/utils/nodeUtils.ts | 20 +++--- .../src/pages/Workflow/NodeDesign/Design.tsx | 70 +++++++------------ .../src/pages/Workflow/NodeDesign/types.ts | 5 +- 6 files changed, 88 insertions(+), 124 deletions(-) diff --git a/frontend/src/pages/Workflow/Definition/Design/components/NodeConfigModal.tsx b/frontend/src/pages/Workflow/Definition/Design/components/NodeConfigModal.tsx index 5b7f352c..9fb6be2f 100644 --- a/frontend/src/pages/Workflow/Definition/Design/components/NodeConfigModal.tsx +++ b/frontend/src/pages/Workflow/Definition/Design/components/NodeConfigModal.tsx @@ -1,13 +1,13 @@ -import React, { useEffect, ReactNode } from 'react'; -import { Drawer, Form, Input, Select, Button, Space, Divider, Tooltip } from 'antd'; -import { InfoCircleOutlined } from '@ant-design/icons'; -import { Cell } from '@antv/x6'; -import { NodeDefinition } from '../types'; +import React, {useEffect, ReactNode} from 'react'; +import {Drawer, Form, Input, Select, Button, Space, Divider, Tooltip} from 'antd'; +import {InfoCircleOutlined} from '@ant-design/icons'; +import {Cell} from '@antv/x6'; +import {NodeDefinitionResponse} from "@/pages/Workflow/NodeDesign/types"; interface NodeConfigDrawerProps { visible: boolean; node: Cell | null; - nodeDefinition: NodeDefinition | null; + nodeDefinition: NodeDefinitionResponse | null; onOk: (values: any) => void; onCancel: () => void; } @@ -22,11 +22,11 @@ interface SchemaProperty { } const NodeConfigDrawer: React.FC = ({ - visible, - node, - nodeDefinition, - onOk, - onCancel, + visible, + node, + nodeDefinition, + onOk, + onCancel, }) => { const [form] = Form.useForm(); @@ -34,19 +34,8 @@ const NodeConfigDrawer: React.FC = ({ if (visible && node && nodeDefinition) { const currentConfig = node.getProp('config') || {}; if (!currentConfig.name) { - currentConfig.name = nodeDefinition.name; + currentConfig.name = nodeDefinition.nodeName; } - // 从节点定义中获取 delegate 的默认值 - const defaultDelegate = nodeDefinition.graphConfig.configSchema?.properties?.delegate?.default; - // 优先使用当前配置的值,如果没有则使用默认值 - const delegateValue = currentConfig.delegate !== undefined ? currentConfig.delegate : defaultDelegate; - - // 设置表单值,包括 code 和其他配置项 - form.setFieldsValue({ - code: node.getProp('code') === undefined ? nodeDefinition.graphConfig.code : node.getProp('code'), - delegate: delegateValue, // 使用当前值或默认值 - ...currentConfig - }); } }, [visible, node, nodeDefinition, form]); @@ -73,12 +62,12 @@ const NodeConfigDrawer: React.FC = ({ {property.title} {property.description && ( - + )} ), - rules: [{ required, message: `请输入${property.title}` }], + rules: [{required, message: `请输入${property.title}`}], }; switch (property.type) { @@ -99,16 +88,16 @@ const NodeConfigDrawer: React.FC = ({ return ( {property.format === 'textarea' ? ( - + ) : ( - + )} ); case 'number': return ( - + ); case 'boolean': @@ -131,7 +120,7 @@ const NodeConfigDrawer: React.FC = ({ return null; } - const { configSchema } = nodeDefinition.graphConfig; + const {configSchema} = nodeDefinition.graphConfig; console.log('NodeConfigModal - Rendering form items with schema:', configSchema); const formItems: ReactNode[] = []; @@ -155,7 +144,7 @@ const NodeConfigDrawer: React.FC = ({ const renderNodeDetails = () => { if (!nodeDefinition?.graphConfig.details) return null; - const { details } = nodeDefinition.graphConfig; + const {details} = nodeDefinition.graphConfig; return ( <> 节点说明 @@ -188,7 +177,7 @@ const NodeConfigDrawer: React.FC = ({ return ( = ({ initialValues={{}} > - {nodeDefinition?.graphConfig.configSchema && - Object.entries(nodeDefinition.graphConfig.configSchema.properties).map(([key, property]) => { - // 跳过 code 和 delegate 字段的显示 - if (key === 'code' || key === 'delegate') return null; - const required = nodeDefinition.graphConfig.configSchema.required?.includes(key) || false; + {nodeDefinition?.panelVariablesSchema && + Object.entries(nodeDefinition.panelVariablesSchema.properties).map(([key, property]) => { + const required = nodeDefinition.panelVariablesSchema?.properties.required?.includes(key) || false; return renderFormItem(key, property as SchemaProperty, required); })} diff --git a/frontend/src/pages/Workflow/Definition/Design/components/NodePanel.tsx b/frontend/src/pages/Workflow/Definition/Design/components/NodePanel.tsx index 698e2505..ba032eba 100644 --- a/frontend/src/pages/Workflow/Definition/Design/components/NodePanel.tsx +++ b/frontend/src/pages/Workflow/Definition/Design/components/NodePanel.tsx @@ -1,5 +1,5 @@ import React, {useState, useEffect} from 'react'; -import {Card, Collapse, Tooltip, message, Spin} from 'antd'; +import {Card, Collapse, Tooltip, message} from 'antd'; import type {NodeDefinition, NodeCategory} from '../types'; import { PlayCircleOutlined, @@ -13,6 +13,7 @@ import { BranchesOutlined } from '@ant-design/icons'; import {getNodeDefinitionList} from "@/pages/Workflow/Definition/Design/service"; +import {NodeDefinitionResponse} from "@/pages/Workflow/NodeDesign/types"; const {Panel} = Collapse; @@ -53,19 +54,20 @@ const categoryConfig: Record void, - nodeDefinitions?: NodeDefinition[] + onNodeDragStart?: (node: NodeDefinitionResponse, e: React.DragEvent) => void, + nodeDefinitions?: NodeDefinitionResponse[] } const NodePanel: React.FC = ({onNodeDragStart}) => { const [loading, setLoading] = useState(false); - const [nodeDefinitions, setNodeDefinitions] = useState([]); + const [nodeDefinitions, setNodeDefinitions] = useState([]); // 加载节点定义列表 const loadNodeDefinitions = async () => { setLoading(true); try { const data = await getNodeDefinitionList(); + console.log(data) setNodeDefinitions(data); } catch (error) { if (error instanceof Error) { @@ -87,29 +89,29 @@ const NodePanel: React.FC = ({onNodeDragStart}) => { } acc[node.category].push(node); return acc; - }, {} as Record); + }, {} as Record); // 处理节点拖拽开始事件 - const handleDragStart = (node: NodeDefinition, e: React.DragEvent) => { + const handleDragStart = (node: NodeDefinitionResponse, e: React.DragEvent) => { e.dataTransfer.setData('node', JSON.stringify(node)); onNodeDragStart?.(node, e); }; // 渲染节点图标 - const renderNodeIcon = (node: NodeDefinition) => { - const iconName = node.graphConfig.uiSchema.style.icon; + const renderNodeIcon = (node: NodeDefinitionResponse) => { + const iconName = node.uiVariables?.style.icon; // 首先尝试使用配置的图标 let IconComponent = iconMap[iconName]; // 如果没有找到对应的图标,使用节点类型对应的默认图标 if (!IconComponent) { - IconComponent = typeIconMap[node.type] || AppstoreOutlined; + IconComponent = typeIconMap[node.nodeType] || AppstoreOutlined; } return ( = ({onNodeDragStart}) => { ); }; - const getNodeItemStyle = (node: NodeDefinition) => ({ + const getNodeItemStyle = (node: NodeDefinitionResponse) => ({ width: '100%', padding: '10px 12px', - border: `1px solid ${node.graphConfig.uiSchema.style.stroke}`, + border: `1px solid ${node.uiVariables?.style.stroke}`, borderRadius: '6px', cursor: 'move', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '10px', - background: node.graphConfig.uiSchema.style.fill, + background: node.uiVariables?.style.fill, transition: 'all 0.3s', boxShadow: '0 1px 2px rgba(0,0,0,0.05)', '&:hover': { @@ -163,19 +165,6 @@ const NodePanel: React.FC = ({onNodeDragStart}) => { title={
{node.description}
-
-
功能特点:
-
    - {node.graphConfig.details.features.map((feature, index) => ( -
  • {feature}
  • - ))} -
-
} overlayStyle={tooltipStyle} @@ -201,7 +190,7 @@ const NodePanel: React.FC = ({onNodeDragStart}) => { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', - }}>{node.name} + }}>{node.nodeName}({node.nodeType}) diff --git a/frontend/src/pages/Workflow/Definition/Design/index.tsx b/frontend/src/pages/Workflow/Definition/Design/index.tsx index c3f9f015..88d4e04d 100644 --- a/frontend/src/pages/Workflow/Definition/Design/index.tsx +++ b/frontend/src/pages/Workflow/Definition/Design/index.tsx @@ -43,6 +43,7 @@ import { DEFAULT_STYLES, } from './constants'; import './index.less'; +import {NodeDefinitionResponse, NodeDesignDataResponse} from "@/pages/Workflow/NodeDesign/types"; const WorkflowDesign: React.FC = () => { const {id} = useParams<{ id: string }>(); @@ -52,10 +53,10 @@ const WorkflowDesign: React.FC = () => { const minimapContainerRef = useRef(null); const [graph, setGraph] = useState(null); const [selectedNode, setSelectedNode] = useState(null); - const [selectedNodeDefinition, setSelectedNodeDefinition] = useState(null); + const [selectedNodeDefinition, setSelectedNodeDefinition] = useState(null); const [configModalVisible, setConfigModalVisible] = useState(false); const [definitionData, setDefinitionData] = useState(null); - const [nodeDefinitions, setNodeDefinitions] = useState([]); + const [nodeDefinitions, setNodeDefinitions] = useState([]); const [isNodeDefinitionsLoaded, setIsNodeDefinitionsLoaded] = useState(false); const [forceUpdate, setForceUpdate] = useState(false); const [scale, setScale] = useState(1); @@ -571,9 +572,8 @@ const WorkflowDesign: React.FC = () => { // 节点双击事件 graph.on('node:dblclick', ({node}) => { const nodeType = node.getProp('type'); - console.log(nodeType) // 从节点定义列表中找到对应的定义 - const nodeDefinition = nodeDefinitions.find(def => def.type === nodeType); + const nodeDefinition = nodeDefinitions.find(def => def.nodeType === nodeType); if (nodeDefinition) { setSelectedNode(node); setSelectedNodeDefinition(nodeDefinition); @@ -1027,7 +1027,7 @@ const WorkflowDesign: React.FC = () => { }, [graphContainerRef, id, nodeDefinitions, isNodeDefinitionsLoaded]); // 处理节点拖拽开始 - const handleNodeDragStart = (node: NodeDefinition, e: React.DragEvent) => { + const handleNodeDragStart = (node: NodeDefinitionResponse, e: React.DragEvent) => { e.dataTransfer.setData('node', JSON.stringify(node)); }; @@ -1043,6 +1043,7 @@ const WorkflowDesign: React.FC = () => { const node = JSON.parse(nodeData); const {clientX, clientY} = e; const point = graph.clientToLocal({x: clientX, y: clientY}); + console.log("dasdasd", graph, node, nodeDefinitions, point); addNodeToGraph(true, graph, node, nodeDefinitions, point); } catch (error) { console.error('创建节点失败:', error); diff --git a/frontend/src/pages/Workflow/Definition/Design/utils/nodeUtils.ts b/frontend/src/pages/Workflow/Definition/Design/utils/nodeUtils.ts index 71b84ef8..fd773297 100644 --- a/frontend/src/pages/Workflow/Definition/Design/utils/nodeUtils.ts +++ b/frontend/src/pages/Workflow/Definition/Design/utils/nodeUtils.ts @@ -5,7 +5,7 @@ import {convertPortConfig} from '../constants'; * 添加节点到图形 * @param isNew 是否为新节点 * @param graph X6 Graph实例 - * @param workflowDefinitionNode 工作流节点定义 + * @param nodeDefinitions 工作流节点定义 * @param workflowNodeDefinitionList 工作流节点定义列表 * @param position 新节点的位置(可选) * @returns 创建的节点实例 @@ -13,13 +13,13 @@ import {convertPortConfig} from '../constants'; export const addNodeToGraph = ( isNew: boolean, graph: Graph, - workflowDefinitionNode: any, + nodeDefinitions: any, workflowNodeDefinitionList: any, position?: { x: number; y: number } ) => { - let nodeDefinition = workflowNodeDefinitionList.find(def => def.type === workflowDefinitionNode.type); - let uiGraph = isNew ? nodeDefinition.graphConfig.uiSchema : workflowDefinitionNode.graph; - + let nodeDefinition = workflowNodeDefinitionList.find(def => def.nodeType === nodeDefinitions.nodeType); + let uiGraph = isNew ? nodeDefinition.uiVariables : nodeDefinitions.graph; + console.log(uiGraph) // 根据形状类型设置正确的 shape let shape = 'rect'; // 默认使用矩形 if (uiGraph.shape === 'circle') { @@ -41,12 +41,12 @@ export const addNodeToGraph = ( } : {}) }, label: { - text: isNew ? nodeDefinition.name : workflowDefinitionNode.name + text: isNew ? nodeDefinition.nodeName : nodeDefinitions.name }, }, shape, - type: isNew ? nodeDefinition.type : workflowDefinitionNode.type, - code: uiGraph.code, + type: isNew ? nodeDefinition.nodeType : nodeDefinitions.type, + code: uiGraph.nodeCode, ports: convertPortConfig(uiGraph.ports), nodeDefinition: nodeDefinition }; @@ -55,9 +55,9 @@ export const addNodeToGraph = ( if (isNew && position) { // 新节点:使用传入的position Object.assign(nodeConfig, { x: position.x, y: position.y }); - } else if (!isNew && workflowDefinitionNode.graph?.position) { + } else if (!isNew && nodeDefinitions.graph?.position) { // 已有节点:使用后端返回的position - Object.assign(nodeConfig, { position: workflowDefinitionNode.graph.position }); + Object.assign(nodeConfig, { position: nodeDefinitions.graph.position }); } // 设置节点ID(如果有) diff --git a/frontend/src/pages/Workflow/NodeDesign/Design.tsx b/frontend/src/pages/Workflow/NodeDesign/Design.tsx index aa641e92..8a96bf6c 100644 --- a/frontend/src/pages/Workflow/NodeDesign/Design.tsx +++ b/frontend/src/pages/Workflow/NodeDesign/Design.tsx @@ -65,8 +65,7 @@ const FormRenderer: React.FC<{ schema: any; path?: string; readOnly?: boolean; - currentTab?: any; -}> = ({schema, path = '', readOnly = false, currentTab}) => { +}> = ({schema, path = '', readOnly = false}) => { if (!schema || !schema.properties) return null; const renderPortConfig = (portSchema: any, portPath: string) => { if (!portSchema || !portSchema.properties) return null; @@ -86,12 +85,6 @@ const FormRenderer: React.FC<{ required={portSchema.required?.includes('position')} initialValue={'default' in position ? position.default : undefined} style={{marginBottom: 16}} - rules={[ - { - required: portSchema.required?.includes('position'), - message: `请${position.type === 'string' ? '输入' : '选择'}${position.title}` - } - ]} > {readOnly ? ( {position.default || '-'} @@ -119,7 +112,6 @@ const FormRenderer: React.FC<{ schema={attrs} path={`${portPath}.attrs`} readOnly={readOnly} - currentTab={currentTab} /> )} @@ -200,7 +192,6 @@ const FormRenderer: React.FC<{ schema={value} path={fieldPath} readOnly={readOnly} - currentTab={currentTab} /> ); @@ -216,15 +207,9 @@ const FormRenderer: React.FC<{ } tooltip={value.description} - required={currentTab?.schemaKey === 'uiVariables' && schema.required?.includes(key)} + required={schema.required?.includes(key)} initialValue={'default' in value ? value.default : undefined} style={{marginBottom: 16}} - rules={currentTab?.schemaKey === 'uiVariables' ? [ - { - required: schema.required?.includes(key), - message: `请${value.type === 'string' ? '输入' : '选择'}${value.title}` - } - ] : undefined} > {readOnly ? ( {value.default || '-'} @@ -276,7 +261,7 @@ const NodeDesignForm: React.FC = () => { useEffect(() => { const loadNodeDetail = async () => { if (!id) return; - + try { setLoading(true); const data = await service.getNodeDefinition(id); @@ -318,7 +303,7 @@ const NodeDesignForm: React.FC = () => { console.log('选择节点:', node); console.log('当前编辑状态:', isEdit); console.log('当前编辑数据:', editData); - + setSelectedNode(node); // 更新表单数据 form.setFieldsValue({ @@ -341,7 +326,7 @@ const NodeDesignForm: React.FC = () => { try { const values = await form.validateFields(); console.log('Form values:', values); - + // 提取基本信息字段 const baseFields = { nodeType: values['base.nodeType'], @@ -362,7 +347,7 @@ const NodeDesignForm: React.FC = () => { // 处理颜色值转换为十六进制 const processColorValue = (value: any): any => { if (!value || typeof value !== 'object') return value; - + // 如果是 ColorPicker 的值,转换为十六进制 if (value.metaColor) { const { r, g, b } = value.metaColor; @@ -372,7 +357,7 @@ const NodeDesignForm: React.FC = () => { }; return `#${toHex(r)}${toHex(g)}${toHex(b)}`; } - + // 如果是普通对象,递归处理 if (typeof value === 'object') { const result: any = {}; @@ -381,39 +366,39 @@ const NodeDesignForm: React.FC = () => { }); return result; } - + return value; }; // 将扁平的键值对转换为嵌套对象 const convertToNestedObject = (flatObj: any) => { const result: any = {}; - + Object.keys(flatObj).forEach(key => { const parts = key.split('.'); let current = result; - + for (let i = 0; i < parts.length - 1; i++) { current[parts[i]] = current[parts[i]] || {}; current = current[parts[i]]; } - + // 处理颜色值 const value = processColorValue(flatObj[key]); current[parts[parts.length - 1]] = value; }); - + return result; }; - + const saveData = { ...selectedNode, ...baseFields, uiVariables: convertToNestedObject(uiValues) }; - + console.log('Save data:', saveData); - + // 根据是否是编辑模式调用不同的接口 if (isEdit && editData?.id) { await service.updateNodeDefinition(editData.id, saveData); @@ -438,7 +423,7 @@ const NodeDesignForm: React.FC = () => { console.log('当前 schema:', schema); console.log('是否编辑模式:', isEdit); console.log('编辑数据:', editData); - + // 如果是编辑模式且有保存的数据,合并到 schema if (isEdit && editData?.uiVariables) { console.log('开始合并 UI 配置数据'); @@ -446,7 +431,7 @@ const NodeDesignForm: React.FC = () => { // 处理颜色值 const processColorValue = (value: any): string => { if (!value || typeof value !== 'object') return value; - + // 如果是颜色对象结构 if (value.metaColor) { const { r, g, b } = value.metaColor; @@ -457,7 +442,7 @@ const NodeDesignForm: React.FC = () => { }; return `#${toHex(r)}${toHex(g)}${toHex(b)}`; } - + return value; }; @@ -466,7 +451,7 @@ const NodeDesignForm: React.FC = () => { console.log('正在处理 schema:', schemaObj); console.log('UI 数据:', uiData); console.log('当前路径:', parentPath); - + const result = {...schemaObj}; // 颜色字段路径列表 @@ -484,7 +469,7 @@ const NodeDesignForm: React.FC = () => { Object.keys(result.properties).forEach(key => { const currentPath = parentPath ? `${parentPath}.${key}` : key; console.log('处理属性:', currentPath); - + // 处理嵌套对象 if (result.properties[key].type === 'object') { const nestedValue = currentPath.split('.').reduce((obj, key) => obj?.[key], uiData); @@ -492,7 +477,7 @@ const NodeDesignForm: React.FC = () => { if (nestedValue !== undefined) { result.properties[key].default = nestedValue; } - } + } // 处理基本类型 else { const value = currentPath.split('.').reduce((obj, key) => obj?.[key], uiData); @@ -506,11 +491,11 @@ const NodeDesignForm: React.FC = () => { } } } - + // 递归处理嵌套属性 result.properties[key] = mergeUiVariables( - result.properties[key], - uiData, + result.properties[key], + uiData, currentPath ); }); @@ -527,7 +512,7 @@ const NodeDesignForm: React.FC = () => { console.log('合并后的 schema:', mergedSchema); return mergedSchema; } - + console.log('使用原始 schema'); return schema; }; @@ -551,8 +536,8 @@ const NodeDesignForm: React.FC = () => { n.nodeType === editData.nodeType)?.nodeCode || selectedNode?.nodeCode : + activeKey={isEdit && editData?.nodeType ? + nodeDefinitionsDefined.find(n => n.nodeType === editData.nodeType)?.nodeCode || selectedNode?.nodeCode : selectedNode?.nodeCode || '' } onChange={(key) => { @@ -653,7 +638,6 @@ const NodeDesignForm: React.FC = () => { ) : null diff --git a/frontend/src/pages/Workflow/NodeDesign/types.ts b/frontend/src/pages/Workflow/NodeDesign/types.ts index 54abd161..e1c40d6c 100644 --- a/frontend/src/pages/Workflow/NodeDesign/types.ts +++ b/frontend/src/pages/Workflow/NodeDesign/types.ts @@ -71,11 +71,12 @@ export interface UIVariables extends BaseSchema { } // 节点设计数据 -export interface NodeDesignDataResponse extends BaseResponse{ +export interface NodeDesignDataResponse extends BaseResponse { nodeCode: string; nodeName: string; nodeType: string; category: string; + description: string; panelVariablesSchema: NodeVariablesSchema | null; localVariablesSchema: NodeVariablesSchema | null; formVariablesSchema: NodeVariablesSchema | null; @@ -100,6 +101,8 @@ export interface NodeDefinitionResponse extends BaseResponse { nodeCode: string; nodeName: string; nodeType: NodeTypeEnum; + category: string; + description: string; panelVariablesSchema: NodeVariablesSchema | null; uiVariables: UIVariables | null; localVariablesSchema: NodeVariablesSchema | null;