import React, {useState, useEffect} from 'react'; import {PageContainer} from '@ant-design/pro-layout'; import {Button, Card, Form, Input, InputNumber, Select, Switch, Space, Menu, Tabs, Row, Col, message, ColorPicker} from 'antd'; import type {NodeDesignDataResponse} from './types'; import * as service from './service'; // Tab 配置 const TAB_CONFIG = [ {key: 'panel', label: '属性(预览)', schemaKey: 'panelVariablesSchema', readonly: true}, {key: 'local', label: '环境变量(预览)', schemaKey: 'localVariablesSchema', readonly: true}, {key: 'form', label: '表单(预览)', schemaKey: 'formVariablesSchema', readonly: true}, {key: 'ui', label: 'UI配置', schemaKey: 'uiVariables', readonly: false} ]; // 渲染具体的表单控件 const renderField = (schema: any, fieldPath: string) => { const commonProps = { placeholder: `请输入${schema.title}`, }; // 颜色字段路径列表 const colorFields = [ 'ports.groups.in.attrs.circle.fill', 'ports.groups.in.attrs.circle.stroke', 'ports.groups.out.attrs.circle.fill', 'ports.groups.out.attrs.circle.stroke', 'style.fill', 'style.stroke', 'style.iconColor' ]; // 检查是否是颜色字段 if (colorFields.some(path => fieldPath.endsWith(path))) { return ; } if (schema.enum) { return ( ({ label: schema.enumNames?.[index] || value, value }))} /> ); } switch (schema.type) { case 'string': return ; case 'integer': case 'number': return ; case 'boolean': return ; default: return ; } }; // 表单渲染器组件 const FormRenderer: React.FC<{ schema: any; path?: string; readOnly?: boolean; }> = ({schema, path = '', readOnly = false}) => { if (!schema || !schema.properties) return null; const renderPortConfig = (portSchema: any, portPath: string) => { if (!portSchema || !portSchema.properties) return null; const {position, attrs} = portSchema.properties; return ( <> {/* 先渲染端口位置 */} {position.title} } tooltip={position.description} required={portSchema.required?.includes('position')} initialValue={'default' in position ? position.default : undefined} style={{marginBottom: 16}} > {readOnly ? ( {position.default || '-'} ) : ( renderField(position, `${portPath}.position`) )} {/* 再渲染端口属性 */} {attrs && ( )} > ); }; return ( {Object.entries(schema.properties).map(([key, value]: [string, any]) => { const fieldPath = path ? `${path}.${key}` : key; if (value.type === 'object') { // 特殊处理端口组配置 if (key === 'groups' && path.endsWith('ports')) { const inPort = value.properties?.in; const outPort = value.properties?.out; return ( {renderPortConfig(inPort, `${fieldPath}.in`)} {renderPortConfig(outPort, `${fieldPath}.out`)} ); } return ( {value.title} } size="small" style={{ marginBottom: 16, boxShadow: '0 1px 2px rgba(0,0,0,0.03)', borderRadius: '8px', border: '1px solid #f0f0f0' }} styles={{ body: {padding: '16px'} }} > ); } return ( {value.title} } tooltip={value.description} required={schema.required?.includes(key)} initialValue={'default' in value ? value.default : undefined} style={{marginBottom: 16}} > {readOnly ? ( {value.default || '-'} ) : ( renderField(value, fieldPath) )} ); })} ); }; const NodeDesignForm: React.FC = () => { const [nodeDefinitionsDefined, setNodeDefinitionsDefined] = useState([]); const [loading, setLoading] = useState(false); const [selectedNode, setSelectedNode] = useState(null); const [activeTab, setActiveTab] = useState('panel'); const [form] = Form.useForm(); // 加载节点定义数据 useEffect(() => { const loadNodeDefinitions = async () => { try { setLoading(true); const data = await service.getNodeDefinitionsDefined(); setNodeDefinitionsDefined(data); // 自动选中第一个节点 if (data && data.length > 0) { handleNodeSelect(data[0]); } } catch (error) { console.error('加载节点定义失败:', error); message.error('加载节点定义失败'); } finally { setLoading(false); } }; loadNodeDefinitions(); }, []); // 获取当前节点可用的 Tab 列表 const getAvailableTabs = (node: NodeDesignDataResponse | null) => { if (!node) return []; return TAB_CONFIG.filter(tab => { const value = node[tab.schemaKey as keyof NodeDesignDataResponse]; return value !== null && value !== undefined; }); }; // 处理节点选择 const handleNodeSelect = (node: NodeDesignDataResponse) => { setSelectedNode(node); // 更新表单数据 form.setFieldsValue({ 'base.nodeType': node.nodeCode, // 使用 nodeCode 作为节点类型 'base.nodeCode': node.nodeCode, 'base.nodeName': node.nodeName, 'base.category': node.category, 'base.description': node.description }); }; // 处理 Tab 切换 const handleTabChange = (key: string) => { setActiveTab(key); // 不需要重置表单 }; // 处理保存 const handleSave = async () => { try { const values = await form.validateFields(); console.log('Form values:', values); // 提取基本信息字段 const baseFields = { nodeType: values['base.nodeType'], nodeCode: values['base.nodeCode'], nodeName: values['base.nodeName'], category: values['base.category'], description: values['base.description'] }; // 移除基本信息字段,处理 UI 配置 const uiValues = {...values}; delete uiValues['base.nodeType']; delete uiValues['base.nodeCode']; delete uiValues['base.nodeName']; delete uiValues['base.category']; delete uiValues['base.description']; // 将扁平的键值对转换为嵌套对象 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]]; } current[parts[parts.length - 1]] = flatObj[key]; }); return result; }; const saveData = { ...selectedNode, ...baseFields, uiVariables: convertToNestedObject(uiValues) }; console.log('Save data:', saveData); await service.saveNodeDefinition(saveData); message.success('保存成功'); } catch (error) { console.error('保存失败:', error); message.error('保存失败'); } }; // 获取当前 schema const getCurrentSchema = () => { if (!selectedNode) return null; const currentTab = TAB_CONFIG.find(tab => tab.key === activeTab); if (!currentTab) return null; return selectedNode[currentTab.schemaKey as keyof NodeDesignDataResponse]; }; return ( 保存 ], }} > { const node = nodeDefinitionsDefined.find(n => n.nodeCode === key); if (node) { handleNodeSelect(node); } }} items={nodeDefinitionsDefined.map(node => ({ key: node.nodeCode, label: ( {node.nodeName} {node.nodeCode} ) }))} /> ({ key: tab.key, label: ( {tab.label} ), children: tab.key === activeTab ? ( ) : null }))} /> ); }; export default NodeDesignForm;