257 lines
7.9 KiB
TypeScript
257 lines
7.9 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
||
import { Modal, Form, Input, Tabs, Card, Button, Space, message } from 'antd';
|
||
import { SaveOutlined, ReloadOutlined } from '@ant-design/icons';
|
||
import type { FlowNode, FlowNodeData } from '../types';
|
||
|
||
interface NodeConfigModalProps {
|
||
visible: boolean;
|
||
node: FlowNode | null;
|
||
onCancel: () => void;
|
||
onOk: (nodeId: string, updatedData: Partial<FlowNodeData>) => void;
|
||
}
|
||
|
||
const { TextArea } = Input;
|
||
|
||
const NodeConfigModal: React.FC<NodeConfigModalProps> = ({
|
||
visible,
|
||
node,
|
||
onCancel,
|
||
onOk
|
||
}) => {
|
||
const [form] = Form.useForm();
|
||
const [loading, setLoading] = useState(false);
|
||
const [activeTab, setActiveTab] = useState('basic');
|
||
|
||
// 重置表单数据
|
||
useEffect(() => {
|
||
if (visible && node) {
|
||
form.setFieldsValue({
|
||
label: node.data.label,
|
||
description: node.data.nodeDefinition?.description || '',
|
||
// 基础配置
|
||
timeout: node.data.configs?.timeout || 3600,
|
||
retryCount: node.data.configs?.retryCount || 0,
|
||
priority: node.data.configs?.priority || 'normal',
|
||
// 输入映射
|
||
inputMapping: JSON.stringify(node.data.inputMapping || {}, null, 2),
|
||
// 输出映射
|
||
outputMapping: JSON.stringify(node.data.outputMapping || {}, null, 2),
|
||
});
|
||
} else {
|
||
form.resetFields();
|
||
}
|
||
}, [visible, node, form]);
|
||
|
||
const handleSubmit = async () => {
|
||
if (!node) return;
|
||
|
||
try {
|
||
const values = await form.validateFields();
|
||
setLoading(true);
|
||
|
||
// 解析JSON字符串
|
||
let inputMapping = {};
|
||
let outputMapping = {};
|
||
|
||
try {
|
||
inputMapping = values.inputMapping ? JSON.parse(values.inputMapping) : {};
|
||
} catch (error) {
|
||
message.error('输入映射JSON格式错误');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
outputMapping = values.outputMapping ? JSON.parse(values.outputMapping) : {};
|
||
} catch (error) {
|
||
message.error('输出映射JSON格式错误');
|
||
return;
|
||
}
|
||
|
||
const updatedData: Partial<FlowNodeData> = {
|
||
label: values.label,
|
||
configs: {
|
||
...node.data.configs,
|
||
timeout: values.timeout,
|
||
retryCount: values.retryCount,
|
||
priority: values.priority,
|
||
},
|
||
inputMapping,
|
||
outputMapping,
|
||
};
|
||
|
||
onOk(node.id, updatedData);
|
||
message.success('节点配置保存成功');
|
||
onCancel();
|
||
} catch (error) {
|
||
console.error('保存节点配置失败:', error);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
const handleReset = () => {
|
||
form.resetFields();
|
||
if (node) {
|
||
form.setFieldsValue({
|
||
label: node.data.label,
|
||
description: node.data.nodeDefinition?.description || '',
|
||
timeout: 3600,
|
||
retryCount: 0,
|
||
priority: 'normal',
|
||
inputMapping: '{}',
|
||
outputMapping: '{}',
|
||
});
|
||
}
|
||
};
|
||
|
||
const renderBasicConfig = () => (
|
||
<Card size="small" title="基本信息">
|
||
<Form.Item
|
||
label="节点名称"
|
||
name="label"
|
||
rules={[{ required: true, message: '请输入节点名称' }]}
|
||
>
|
||
<Input placeholder="请输入节点名称" />
|
||
</Form.Item>
|
||
|
||
<Form.Item label="节点描述" name="description">
|
||
<TextArea
|
||
placeholder="请输入节点描述"
|
||
rows={3}
|
||
maxLength={500}
|
||
showCount
|
||
/>
|
||
</Form.Item>
|
||
|
||
<Form.Item
|
||
label="超时时间(秒)"
|
||
name="timeout"
|
||
rules={[{ required: true, message: '请输入超时时间' }]}
|
||
>
|
||
<Input type="number" min={1} placeholder="3600" />
|
||
</Form.Item>
|
||
|
||
<Form.Item
|
||
label="重试次数"
|
||
name="retryCount"
|
||
>
|
||
<Input type="number" min={0} max={10} placeholder="0" />
|
||
</Form.Item>
|
||
|
||
<Form.Item
|
||
label="优先级"
|
||
name="priority"
|
||
>
|
||
<Input placeholder="normal" />
|
||
</Form.Item>
|
||
</Card>
|
||
);
|
||
|
||
const renderInputMapping = () => (
|
||
<Card size="small" title="输入参数映射">
|
||
<Form.Item
|
||
label="输入映射配置"
|
||
name="inputMapping"
|
||
extra="请输入有效的JSON格式,用于配置节点的输入参数映射"
|
||
>
|
||
<TextArea
|
||
placeholder={`示例:
|
||
{
|
||
"param1": "\${workflow.variable1}",
|
||
"param2": "固定值",
|
||
"param3": "\${previous.output.result}"
|
||
}`}
|
||
rows={12}
|
||
style={{ fontFamily: 'Monaco, Consolas, monospace' }}
|
||
/>
|
||
</Form.Item>
|
||
</Card>
|
||
);
|
||
|
||
const renderOutputMapping = () => (
|
||
<Card size="small" title="输出参数映射">
|
||
<Form.Item
|
||
label="输出映射配置"
|
||
name="outputMapping"
|
||
extra="请输入有效的JSON格式,用于配置节点的输出参数映射"
|
||
>
|
||
<TextArea
|
||
placeholder={`示例:
|
||
{
|
||
"result": "\${task.output.result}",
|
||
"status": "\${task.status}",
|
||
"message": "\${task.output.message}"
|
||
}`}
|
||
rows={12}
|
||
style={{ fontFamily: 'Monaco, Consolas, monospace' }}
|
||
/>
|
||
</Form.Item>
|
||
</Card>
|
||
);
|
||
|
||
return (
|
||
<Modal
|
||
title={`配置节点: ${node?.data?.label || '未知节点'}`}
|
||
open={visible}
|
||
onCancel={onCancel}
|
||
width={800}
|
||
style={{ top: 20 }}
|
||
footer={
|
||
<Space>
|
||
<Button onClick={onCancel}>取消</Button>
|
||
<Button
|
||
icon={<ReloadOutlined />}
|
||
onClick={handleReset}
|
||
>
|
||
重置
|
||
</Button>
|
||
<Button
|
||
type="primary"
|
||
loading={loading}
|
||
icon={<SaveOutlined />}
|
||
onClick={handleSubmit}
|
||
>
|
||
保存配置
|
||
</Button>
|
||
</Space>
|
||
}
|
||
>
|
||
<Form
|
||
form={form}
|
||
layout="vertical"
|
||
initialValues={{
|
||
timeout: 3600,
|
||
retryCount: 0,
|
||
priority: 'normal',
|
||
inputMapping: '{}',
|
||
outputMapping: '{}'
|
||
}}
|
||
>
|
||
<Tabs
|
||
activeKey={activeTab}
|
||
onChange={setActiveTab}
|
||
items={[
|
||
{
|
||
key: 'basic',
|
||
label: '基本配置',
|
||
children: renderBasicConfig()
|
||
},
|
||
{
|
||
key: 'input',
|
||
label: '输入映射',
|
||
children: renderInputMapping()
|
||
},
|
||
{
|
||
key: 'output',
|
||
label: '输出映射',
|
||
children: renderOutputMapping()
|
||
}
|
||
]}
|
||
/>
|
||
</Form>
|
||
</Modal>
|
||
);
|
||
};
|
||
|
||
export default NodeConfigModal;
|