1
This commit is contained in:
parent
9c9ff6a21a
commit
0f5acb22dc
@ -1,15 +1,15 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, {useState, useEffect} from 'react';
|
||||||
import { PageContainer } from '@ant-design/pro-layout';
|
import {PageContainer} from '@ant-design/pro-layout';
|
||||||
import { Button, Card, Form, Input, InputNumber, Select, Switch, Space, Menu, Tabs, Row, Col, message } from 'antd';
|
import {Button, Card, Form, Input, InputNumber, Select, Switch, Space, Menu, Tabs, Row, Col, message} from 'antd';
|
||||||
import type { NodeDesignData } from './types';
|
import type {NodeDesignData} from './types';
|
||||||
import * as service from './service';
|
import * as service from './service';
|
||||||
|
|
||||||
// Tab 配置
|
// Tab 配置
|
||||||
const TAB_CONFIG = [
|
const TAB_CONFIG = [
|
||||||
{ key: 'panel', label: '属性(预览)', schemaKey: 'panelVariablesSchema', readonly: true },
|
{key: 'panel', label: '属性(预览)', schemaKey: 'panelVariablesSchema', readonly: true},
|
||||||
{ key: 'local', label: '环境变量(预览)', schemaKey: 'localVariablesSchema', readonly: true },
|
{key: 'local', label: '环境变量(预览)', schemaKey: 'localVariablesSchema', readonly: true},
|
||||||
{ key: 'form', label: '表单(预览)', schemaKey: 'formVariablesSchema', readonly: true },
|
{key: 'form', label: '表单(预览)', schemaKey: 'formVariablesSchema', readonly: true},
|
||||||
{ key: 'ui', label: 'UI配置', schemaKey: 'uiVariables', readonly: false }
|
{key: 'ui', label: 'UI配置', schemaKey: 'uiVariables', readonly: false}
|
||||||
];
|
];
|
||||||
|
|
||||||
// 渲染具体的表单控件
|
// 渲染具体的表单控件
|
||||||
@ -35,9 +35,9 @@ const renderField = (schema: any) => {
|
|||||||
return <Input {...commonProps} />;
|
return <Input {...commonProps} />;
|
||||||
case 'integer':
|
case 'integer':
|
||||||
case 'number':
|
case 'number':
|
||||||
return <InputNumber {...commonProps} style={{ width: '100%' }} />;
|
return <InputNumber {...commonProps} style={{width: '100%'}}/>;
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
return <Switch />;
|
return <Switch/>;
|
||||||
default:
|
default:
|
||||||
return <Input {...commonProps} />;
|
return <Input {...commonProps} />;
|
||||||
}
|
}
|
||||||
@ -48,29 +48,29 @@ const FormRenderer: React.FC<{
|
|||||||
schema: any;
|
schema: any;
|
||||||
path?: string;
|
path?: string;
|
||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
}> = ({ schema, path = '', readOnly = false }) => {
|
}> = ({schema, path = '', readOnly = false}) => {
|
||||||
if (!schema || !schema.properties) return null;
|
if (!schema || !schema.properties) return null;
|
||||||
const renderPortConfig = (portSchema: any, portPath: string) => {
|
const renderPortConfig = (portSchema: any, portPath: string) => {
|
||||||
if (!portSchema || !portSchema.properties) return null;
|
if (!portSchema || !portSchema.properties) return null;
|
||||||
|
|
||||||
const { position, attrs } = portSchema.properties;
|
const {position, attrs} = portSchema.properties;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* 先渲染端口位置 */}
|
{/* 先渲染端口位置 */}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={`${portPath}.position`}
|
name={`${portPath}.position`}
|
||||||
label={
|
label={
|
||||||
<span style={{ fontSize: '14px' }}>
|
<span style={{fontSize: '14px'}}>
|
||||||
{position.title}
|
{position.title}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
tooltip={position.description}
|
tooltip={position.description}
|
||||||
required={portSchema.required?.includes('position')}
|
required={portSchema.required?.includes('position')}
|
||||||
initialValue={'default' in position ? position.default : undefined}
|
initialValue={'default' in position ? position.default : undefined}
|
||||||
style={{ marginBottom: 16 }}
|
style={{marginBottom: 16}}
|
||||||
>
|
>
|
||||||
{readOnly ? (
|
{readOnly ? (
|
||||||
<span style={{ color: '#666' }}>{position.default || '-'}</span>
|
<span style={{color: '#666'}}>{position.default || '-'}</span>
|
||||||
) : (
|
) : (
|
||||||
renderField(position)
|
renderField(position)
|
||||||
)}
|
)}
|
||||||
@ -81,14 +81,14 @@ const FormRenderer: React.FC<{
|
|||||||
<Card
|
<Card
|
||||||
title={attrs.title}
|
title={attrs.title}
|
||||||
size="small"
|
size="small"
|
||||||
style={{
|
style={{
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
boxShadow: '0 1px 2px rgba(0,0,0,0.03)',
|
boxShadow: '0 1px 2px rgba(0,0,0,0.03)',
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
border: '1px solid #f0f0f0'
|
border: '1px solid #f0f0f0'
|
||||||
}}
|
}}
|
||||||
styles={{
|
styles={{
|
||||||
body: { padding: '16px' }
|
body: {padding: '16px'}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormRenderer
|
<FormRenderer
|
||||||
@ -103,7 +103,7 @@ const FormRenderer: React.FC<{
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: '8px 0' }}>
|
<div style={{padding: '8px 0'}}>
|
||||||
{Object.entries(schema.properties).map(([key, value]: [string, any]) => {
|
{Object.entries(schema.properties).map(([key, value]: [string, any]) => {
|
||||||
const fieldPath = path ? `${path}.${key}` : key;
|
const fieldPath = path ? `${path}.${key}` : key;
|
||||||
if (value.type === 'object') {
|
if (value.type === 'object') {
|
||||||
@ -111,21 +111,21 @@ const FormRenderer: React.FC<{
|
|||||||
if (key === 'groups' && path.endsWith('ports')) {
|
if (key === 'groups' && path.endsWith('ports')) {
|
||||||
const inPort = value.properties?.in;
|
const inPort = value.properties?.in;
|
||||||
const outPort = value.properties?.out;
|
const outPort = value.properties?.out;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row key={fieldPath} gutter={16}>
|
<Row key={fieldPath} gutter={16}>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Card
|
<Card
|
||||||
title={inPort.title}
|
title={inPort.title}
|
||||||
size="small"
|
size="small"
|
||||||
style={{
|
style={{
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
boxShadow: '0 1px 2px rgba(0,0,0,0.03)',
|
boxShadow: '0 1px 2px rgba(0,0,0,0.03)',
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
border: '1px solid #f0f0f0'
|
border: '1px solid #f0f0f0'
|
||||||
}}
|
}}
|
||||||
styles={{
|
styles={{
|
||||||
body: { padding: '16px' }
|
body: {padding: '16px'}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{renderPortConfig(inPort, `${fieldPath}.in`)}
|
{renderPortConfig(inPort, `${fieldPath}.in`)}
|
||||||
@ -135,14 +135,14 @@ const FormRenderer: React.FC<{
|
|||||||
<Card
|
<Card
|
||||||
title={outPort.title}
|
title={outPort.title}
|
||||||
size="small"
|
size="small"
|
||||||
style={{
|
style={{
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
boxShadow: '0 1px 2px rgba(0,0,0,0.03)',
|
boxShadow: '0 1px 2px rgba(0,0,0,0.03)',
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
border: '1px solid #f0f0f0'
|
border: '1px solid #f0f0f0'
|
||||||
}}
|
}}
|
||||||
styles={{
|
styles={{
|
||||||
body: { padding: '16px' }
|
body: {padding: '16px'}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{renderPortConfig(outPort, `${fieldPath}.out`)}
|
{renderPortConfig(outPort, `${fieldPath}.out`)}
|
||||||
@ -151,24 +151,24 @@ const FormRenderer: React.FC<{
|
|||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
key={fieldPath}
|
key={fieldPath}
|
||||||
title={
|
title={
|
||||||
<span style={{ fontSize: '14px', fontWeight: 500 }}>
|
<span style={{fontSize: '14px', fontWeight: 500}}>
|
||||||
{value.title}
|
{value.title}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
size="small"
|
size="small"
|
||||||
style={{
|
style={{
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
boxShadow: '0 1px 2px rgba(0,0,0,0.03)',
|
boxShadow: '0 1px 2px rgba(0,0,0,0.03)',
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
border: '1px solid #f0f0f0'
|
border: '1px solid #f0f0f0'
|
||||||
}}
|
}}
|
||||||
styles={{
|
styles={{
|
||||||
body: { padding: '16px' }
|
body: {padding: '16px'}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormRenderer
|
<FormRenderer
|
||||||
@ -185,17 +185,17 @@ const FormRenderer: React.FC<{
|
|||||||
key={fieldPath}
|
key={fieldPath}
|
||||||
name={fieldPath}
|
name={fieldPath}
|
||||||
label={
|
label={
|
||||||
<span style={{ fontSize: '14px' }}>
|
<span style={{fontSize: '14px'}}>
|
||||||
{value.title}
|
{value.title}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
tooltip={value.description}
|
tooltip={value.description}
|
||||||
required={schema.required?.includes(key)}
|
required={schema.required?.includes(key)}
|
||||||
initialValue={'default' in value ? value.default : undefined}
|
initialValue={'default' in value ? value.default : undefined}
|
||||||
style={{ marginBottom: 16 }}
|
style={{marginBottom: 16}}
|
||||||
>
|
>
|
||||||
{readOnly ? (
|
{readOnly ? (
|
||||||
<span style={{ color: '#666' }}>{value.default || '-'}</span>
|
<span style={{color: '#666'}}>{value.default || '-'}</span>
|
||||||
) : (
|
) : (
|
||||||
renderField(value)
|
renderField(value)
|
||||||
)}
|
)}
|
||||||
@ -246,7 +246,14 @@ const NodeDesignForm: React.FC = () => {
|
|||||||
// 处理节点选择
|
// 处理节点选择
|
||||||
const handleNodeSelect = (node: NodeDesignData) => {
|
const handleNodeSelect = (node: NodeDesignData) => {
|
||||||
setSelectedNode(node);
|
setSelectedNode(node);
|
||||||
form.resetFields();
|
// 更新表单数据
|
||||||
|
form.setFieldsValue({
|
||||||
|
'base.nodeType': node.nodeCode, // 使用 nodeCode 作为节点类型
|
||||||
|
'base.nodeCode': node.nodeCode,
|
||||||
|
'base.nodeName': node.nodeName,
|
||||||
|
'base.category': node.category,
|
||||||
|
'base.description': node.description
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理 Tab 切换
|
// 处理 Tab 切换
|
||||||
@ -259,11 +266,23 @@ const NodeDesignForm: React.FC = () => {
|
|||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
try {
|
try {
|
||||||
const values = await form.validateFields();
|
const values = await form.validateFields();
|
||||||
console.log('Form values:', values);
|
const { base, ...otherValues } = values;
|
||||||
|
|
||||||
|
const saveData = {
|
||||||
|
...selectedNode,
|
||||||
|
nodeType: base.nodeType,
|
||||||
|
nodeCode: base.nodeCode,
|
||||||
|
nodeName: base.nodeName,
|
||||||
|
category: base.category,
|
||||||
|
description: base.description,
|
||||||
|
uiVariables: otherValues // 其他表单数据
|
||||||
|
};
|
||||||
|
|
||||||
|
await service.saveNodeDefinition(saveData);
|
||||||
message.success('保存成功');
|
message.success('保存成功');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('表单验证失败:', error);
|
console.error('保存失败:', error);
|
||||||
message.error('表单验证失败');
|
message.error('保存失败');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -290,7 +309,7 @@ const NodeDesignForm: React.FC = () => {
|
|||||||
],
|
],
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ display: 'flex', gap: '24px', padding: '24px' }}>
|
<div style={{display: 'flex', gap: '24px', padding: '24px'}}>
|
||||||
<Tabs
|
<Tabs
|
||||||
tabPosition="left"
|
tabPosition="left"
|
||||||
type="card"
|
type="card"
|
||||||
@ -304,11 +323,11 @@ const NodeDesignForm: React.FC = () => {
|
|||||||
items={nodeDefinitionsDefined.map(node => ({
|
items={nodeDefinitionsDefined.map(node => ({
|
||||||
key: node.nodeCode,
|
key: node.nodeCode,
|
||||||
label: (
|
label: (
|
||||||
<div style={{ padding: '4px 0' }}>
|
<div style={{padding: '4px 0'}}>
|
||||||
<div style={{ fontSize: '14px', fontWeight: 500 }}>
|
<div style={{fontSize: '14px', fontWeight: 500}}>
|
||||||
{node.nodeName}
|
{node.nodeName}
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: '12px',
|
fontSize: '12px',
|
||||||
color: '#666',
|
color: '#666',
|
||||||
marginTop: '4px'
|
marginTop: '4px'
|
||||||
@ -319,7 +338,7 @@ const NodeDesignForm: React.FC = () => {
|
|||||||
)
|
)
|
||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
<Card
|
<Card
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
}}
|
}}
|
||||||
@ -329,18 +348,63 @@ const NodeDesignForm: React.FC = () => {
|
|||||||
layout="vertical"
|
layout="vertical"
|
||||||
key={`${selectedNode?.nodeCode}-${activeTab}`}
|
key={`${selectedNode?.nodeCode}-${activeTab}`}
|
||||||
>
|
>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={12}>
|
||||||
|
<Form.Item
|
||||||
|
name="base.nodeType"
|
||||||
|
label="节点类型"
|
||||||
|
rules={[{required: true}]}
|
||||||
|
>
|
||||||
|
<Input disabled/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Form.Item
|
||||||
|
name="base.nodeCode"
|
||||||
|
label="节点编码"
|
||||||
|
rules={[{required: true}]}
|
||||||
|
>
|
||||||
|
<Input disabled/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={12}>
|
||||||
|
<Form.Item
|
||||||
|
name="base.nodeName"
|
||||||
|
label="节点名称"
|
||||||
|
rules={[{required: true}]}
|
||||||
|
>
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Form.Item
|
||||||
|
name="base.category"
|
||||||
|
label="节点类别"
|
||||||
|
>
|
||||||
|
<Input disabled/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Form.Item
|
||||||
|
name="base.description"
|
||||||
|
label="节点描述"
|
||||||
|
>
|
||||||
|
<Input.TextArea rows={4}/>
|
||||||
|
</Form.Item>
|
||||||
<Tabs
|
<Tabs
|
||||||
activeKey={activeTab}
|
activeKey={activeTab}
|
||||||
onChange={handleTabChange}
|
onChange={handleTabChange}
|
||||||
items={getAvailableTabs(selectedNode).map(tab => ({
|
items={getAvailableTabs(selectedNode).map(tab => ({
|
||||||
key: tab.key,
|
key: tab.key,
|
||||||
label: (
|
label: (
|
||||||
<span style={{ fontSize: '14px' }}>
|
<span style={{fontSize: '14px'}}>
|
||||||
{tab.label}
|
{tab.label}
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
children: tab.key === activeTab ? (
|
children: tab.key === activeTab ? (
|
||||||
<div style={{
|
<div style={{
|
||||||
padding: '16px 0',
|
padding: '16px 0',
|
||||||
minHeight: '400px'
|
minHeight: '400px'
|
||||||
}}>
|
}}>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user