mirror of
https://github.com/whyour/qinglong.git
synced 2025-12-16 17:35:37 +08:00
fix flowgram style
This commit is contained in:
parent
326ea992e9
commit
cff9c1cf68
26952
pnpm-lock.yaml
26952
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
|
@ -591,7 +591,7 @@
|
||||||
"是": "Yes",
|
"是": "Yes",
|
||||||
"否": "No",
|
"否": "No",
|
||||||
"共": "Total",
|
"共": "Total",
|
||||||
"项": "items"
|
"项": "items",
|
||||||
"日志名称": "Log Name",
|
"日志名称": "Log Name",
|
||||||
"自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成": "Custom log folder name to distinguish logs from different tasks. Leave blank to auto-generate",
|
"自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成": "Custom log folder name to distinguish logs from different tasks. Leave blank to auto-generate",
|
||||||
"自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成。支持绝对路径如 /dev/null": "Custom log folder name to distinguish logs from different tasks. Leave blank to auto-generate. Supports absolute paths like /dev/null",
|
"自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成。支持绝对路径如 /dev/null": "Custom log folder name to distinguish logs from different tasks. Leave blank to auto-generate. Supports absolute paths like /dev/null",
|
||||||
|
|
|
||||||
|
|
@ -591,7 +591,7 @@
|
||||||
"是": "是",
|
"是": "是",
|
||||||
"否": "否",
|
"否": "否",
|
||||||
"共": "共",
|
"共": "共",
|
||||||
"项": "项"
|
"项": "项",
|
||||||
"日志名称": "日志名称",
|
"日志名称": "日志名称",
|
||||||
"自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成": "自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成",
|
"自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成": "自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成",
|
||||||
"自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成。支持绝对路径如 /dev/null": "自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成。支持绝对路径如 /dev/null",
|
"自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成。支持绝对路径如 /dev/null": "自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成。支持绝对路径如 /dev/null",
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { Modal, Form, Input, message } from 'antd';
|
||||||
import { request } from '@/utils/http';
|
import { request } from '@/utils/http';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import { FreeLayoutEditor } from '@flowgram.ai/free-layout-editor';
|
import { FreeLayoutEditor } from '@flowgram.ai/free-layout-editor';
|
||||||
import '@flowgram.ai/free-layout-editor/dist/index.css';
|
import '@flowgram.ai/free-layout-editor/index.css';
|
||||||
|
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -252,7 +252,7 @@ const Scenario = () => {
|
||||||
title: intl.get('操作'),
|
title: intl.get('操作'),
|
||||||
key: 'action',
|
key: 'action',
|
||||||
width: 100,
|
width: 100,
|
||||||
fixed: 'right',
|
fixed: 'right' as const,
|
||||||
render: (_: any, record: any) => (
|
render: (_: any, record: any) => (
|
||||||
<Dropdown menu={getActionsMenu(record)} trigger={['click']}>
|
<Dropdown menu={getActionsMenu(record)} trigger={['click']}>
|
||||||
<Button type="link" icon={<EllipsisOutlined />} />
|
<Button type="link" icon={<EllipsisOutlined />} />
|
||||||
|
|
@ -296,7 +296,7 @@ const Scenario = () => {
|
||||||
setIsModalVisible(false);
|
setIsModalVisible(false);
|
||||||
setSelectedScenario(null);
|
setSelectedScenario(null);
|
||||||
}}
|
}}
|
||||||
onSuccess={() => {
|
onOk={() => {
|
||||||
setIsModalVisible(false);
|
setIsModalVisible(false);
|
||||||
setSelectedScenario(null);
|
setSelectedScenario(null);
|
||||||
fetchScenarios();
|
fetchScenarios();
|
||||||
|
|
|
||||||
|
|
@ -1,471 +0,0 @@
|
||||||
import React, { useState, useEffect, useCallback } from 'react';
|
|
||||||
import { Modal, Form, Input, message, Button, Drawer, Select, InputNumber, Space } from 'antd';
|
|
||||||
import { request } from '@/utils/http';
|
|
||||||
import intl from 'react-intl-universal';
|
|
||||||
import ReactFlow, {
|
|
||||||
Node,
|
|
||||||
Edge,
|
|
||||||
Controls,
|
|
||||||
Background,
|
|
||||||
applyNodeChanges,
|
|
||||||
applyEdgeChanges,
|
|
||||||
addEdge,
|
|
||||||
NodeChange,
|
|
||||||
EdgeChange,
|
|
||||||
Connection,
|
|
||||||
BackgroundVariant,
|
|
||||||
} from 'reactflow';
|
|
||||||
import 'reactflow/dist/style.css';
|
|
||||||
import {
|
|
||||||
NodeRenderers,
|
|
||||||
NodeTemplates,
|
|
||||||
createNode,
|
|
||||||
WorkflowGraph,
|
|
||||||
} from './nodeTypes';
|
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
|
||||||
|
|
||||||
const { TextArea } = Input;
|
|
||||||
const { Option } = Select;
|
|
||||||
|
|
||||||
interface ScenarioModalProps {
|
|
||||||
visible: boolean;
|
|
||||||
scenario: any;
|
|
||||||
onCancel: () => void;
|
|
||||||
onSuccess: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const VisualWorkflowModal: React.FC<ScenarioModalProps> = ({
|
|
||||||
visible,
|
|
||||||
scenario,
|
|
||||||
onCancel,
|
|
||||||
onSuccess,
|
|
||||||
}) => {
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [nodes, setNodes] = useState<Node[]>([]);
|
|
||||||
const [edges, setEdges] = useState<Edge[]>([]);
|
|
||||||
const [drawerVisible, setDrawerVisible] = useState(false);
|
|
||||||
const [selectedNode, setSelectedNode] = useState<Node | null>(null);
|
|
||||||
const [nodeConfigForm] = Form.useForm();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (visible) {
|
|
||||||
if (scenario) {
|
|
||||||
form.setFieldsValue({
|
|
||||||
name: scenario.name,
|
|
||||||
description: scenario.description || '',
|
|
||||||
});
|
|
||||||
|
|
||||||
if (scenario.workflowGraph) {
|
|
||||||
setNodes(scenario.workflowGraph.nodes || []);
|
|
||||||
setEdges(scenario.workflowGraph.edges || []);
|
|
||||||
} else {
|
|
||||||
initializeDefaultWorkflow();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
form.resetFields();
|
|
||||||
initializeDefaultWorkflow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [visible, scenario, form]);
|
|
||||||
|
|
||||||
const initializeDefaultWorkflow = () => {
|
|
||||||
const triggerNode = createNode('trigger', { x: 250, y: 100 }, {
|
|
||||||
label: intl.get('时间触发'),
|
|
||||||
triggerType: 'time',
|
|
||||||
config: { schedule: '0 0 * * *' },
|
|
||||||
});
|
|
||||||
|
|
||||||
setNodes([triggerNode]);
|
|
||||||
setEdges([]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onNodesChange = useCallback(
|
|
||||||
(changes: NodeChange[]) => setNodes((nds) => applyNodeChanges(changes, nds)),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onEdgesChange = useCallback(
|
|
||||||
(changes: EdgeChange[]) => setEdges((eds) => applyEdgeChanges(changes, eds)),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onConnect = useCallback(
|
|
||||||
(connection: Connection) => setEdges((eds) => addEdge(connection, eds)),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleAddNode = (type: string, template: any) => {
|
|
||||||
const newNode = createNode(type, { x: Math.random() * 400 + 100, y: Math.random() * 300 + 100 }, template);
|
|
||||||
setNodes((nds) => [...nds, newNode]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleNodeClick = (_event: React.MouseEvent, node: Node) => {
|
|
||||||
setSelectedNode(node);
|
|
||||||
nodeConfigForm.setFieldsValue(node.data);
|
|
||||||
setDrawerVisible(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleNodeConfigSave = async () => {
|
|
||||||
try {
|
|
||||||
const values = await nodeConfigForm.validateFields();
|
|
||||||
setNodes((nds) =>
|
|
||||||
nds.map((node) =>
|
|
||||||
node.id === selectedNode?.id
|
|
||||||
? { ...node, data: { ...node.data, ...values } }
|
|
||||||
: node
|
|
||||||
)
|
|
||||||
);
|
|
||||||
setDrawerVisible(false);
|
|
||||||
message.success(intl.get('配置已保存'));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('节点配置验证失败:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
try {
|
|
||||||
const values = await form.validateFields();
|
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
const workflowGraph: WorkflowGraph = {
|
|
||||||
nodes: nodes.map(node => ({
|
|
||||||
id: node.id,
|
|
||||||
type: node.type || 'default',
|
|
||||||
position: node.position,
|
|
||||||
data: node.data,
|
|
||||||
})),
|
|
||||||
edges: edges.map(edge => ({
|
|
||||||
id: edge.id,
|
|
||||||
source: edge.source,
|
|
||||||
target: edge.target,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
|
|
||||||
const endpoint = scenario ? '/api/scenarios' : '/api/scenarios';
|
|
||||||
const method = scenario ? 'put' : 'post';
|
|
||||||
const payload = {
|
|
||||||
...values,
|
|
||||||
workflowGraph,
|
|
||||||
...(scenario ? { id: scenario.id } : {}),
|
|
||||||
};
|
|
||||||
|
|
||||||
const { code } = await request[method](endpoint, payload);
|
|
||||||
if (code === 200) {
|
|
||||||
message.success(
|
|
||||||
scenario ? intl.get('更新成功') : intl.get('创建成功'),
|
|
||||||
);
|
|
||||||
onSuccess();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to save scenario:', error);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const nodeTypes = {
|
|
||||||
trigger: ({ data }: any) => NodeRenderers.trigger({ data, id: '', type: 'trigger', position: { x: 0, y: 0 } }),
|
|
||||||
condition: ({ data }: any) => NodeRenderers.condition({ data, id: '', type: 'condition', position: { x: 0, y: 0 } }),
|
|
||||||
action: ({ data }: any) => NodeRenderers.action({ data, id: '', type: 'action', position: { x: 0, y: 0 } }),
|
|
||||||
control: ({ data }: any) => NodeRenderers.control({ data, id: '', type: 'control', position: { x: 0, y: 0 } }),
|
|
||||||
logic_gate: ({ data }: any) => NodeRenderers.logic_gate({ data, id: '', type: 'logic_gate', position: { x: 0, y: 0 } }),
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderNodeConfig = () => {
|
|
||||||
if (!selectedNode) return null;
|
|
||||||
|
|
||||||
switch (selectedNode.type) {
|
|
||||||
case 'trigger':
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Form.Item name="label" label={intl.get('名称')} rules={[{ required: true }]}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="triggerType" label={intl.get('触发类型')} rules={[{ required: true }]}>
|
|
||||||
<Select>
|
|
||||||
<Option value="time">{intl.get('时间触发')}</Option>
|
|
||||||
<Option value="webhook">Webhook</Option>
|
|
||||||
<Option value="variable">{intl.get('变量监听')}</Option>
|
|
||||||
<Option value="task_status">{intl.get('任务状态')}</Option>
|
|
||||||
<Option value="system_event">{intl.get('系统事件')}</Option>
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item noStyle shouldUpdate={(prev, curr) => prev.triggerType !== curr.triggerType}>
|
|
||||||
{({ getFieldValue }) => {
|
|
||||||
const triggerType = getFieldValue('triggerType');
|
|
||||||
if (triggerType === 'time') {
|
|
||||||
return (
|
|
||||||
<Form.Item name={['config', 'schedule']} label="Cron 表达式">
|
|
||||||
<Input placeholder="0 0 * * *" />
|
|
||||||
</Form.Item>
|
|
||||||
);
|
|
||||||
} else if (triggerType === 'variable') {
|
|
||||||
return (
|
|
||||||
<Form.Item name={['config', 'watchPath']} label={intl.get('监听路径')}>
|
|
||||||
<Input placeholder="/path/to/watch" />
|
|
||||||
</Form.Item>
|
|
||||||
);
|
|
||||||
} else if (triggerType === 'system_event') {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Form.Item name={['config', 'eventType']} label={intl.get('事件类型')}>
|
|
||||||
<Select>
|
|
||||||
<Option value="disk_space">{intl.get('磁盘空间')}</Option>
|
|
||||||
<Option value="memory">{intl.get('内存使用')}</Option>
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name={['config', 'threshold']} label={intl.get('阈值')}>
|
|
||||||
<InputNumber min={0} max={100} addonAfter="%" />
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}}
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
case 'condition':
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Form.Item name="label" label={intl.get('名称')} rules={[{ required: true }]}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="field" label={intl.get('字段名')} rules={[{ required: true }]}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="operator" label={intl.get('操作符')} rules={[{ required: true }]}>
|
|
||||||
<Select>
|
|
||||||
<Option value="equals">=</Option>
|
|
||||||
<Option value="not_equals">!=</Option>
|
|
||||||
<Option value="greater_than">></Option>
|
|
||||||
<Option value="less_than"><</Option>
|
|
||||||
<Option value="contains">{intl.get('包含')}</Option>
|
|
||||||
<Option value="not_contains">{intl.get('不包含')}</Option>
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="value" label={intl.get('值')} rules={[{ required: true }]}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
case 'action':
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Form.Item name="label" label={intl.get('名称')} rules={[{ required: true }]}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="actionType" label={intl.get('动作类型')} rules={[{ required: true }]}>
|
|
||||||
<Select>
|
|
||||||
<Option value="run_task">{intl.get('运行任务')}</Option>
|
|
||||||
<Option value="set_variable">{intl.get('设置变量')}</Option>
|
|
||||||
<Option value="execute_command">{intl.get('执行命令')}</Option>
|
|
||||||
<Option value="send_notification">{intl.get('发送通知')}</Option>
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item noStyle shouldUpdate={(prev, curr) => prev.actionType !== curr.actionType}>
|
|
||||||
{({ getFieldValue }) => {
|
|
||||||
const actionType = getFieldValue('actionType');
|
|
||||||
if (actionType === 'run_task') {
|
|
||||||
return (
|
|
||||||
<Form.Item name="cronId" label={intl.get('任务 ID')}>
|
|
||||||
<InputNumber min={1} style={{ width: '100%' }} />
|
|
||||||
</Form.Item>
|
|
||||||
);
|
|
||||||
} else if (actionType === 'set_variable') {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Form.Item name="name" label={intl.get('变量名')}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="value" label={intl.get('变量值')}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
} else if (actionType === 'execute_command') {
|
|
||||||
return (
|
|
||||||
<Form.Item name="command" label={intl.get('命令')}>
|
|
||||||
<TextArea rows={3} />
|
|
||||||
</Form.Item>
|
|
||||||
);
|
|
||||||
} else if (actionType === 'send_notification') {
|
|
||||||
return (
|
|
||||||
<Form.Item name="message" label={intl.get('消息')}>
|
|
||||||
<TextArea rows={3} />
|
|
||||||
</Form.Item>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}}
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
case 'control':
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Form.Item name="label" label={intl.get('名称')} rules={[{ required: true }]}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="controlType" label={intl.get('控制类型')} rules={[{ required: true }]}>
|
|
||||||
<Select>
|
|
||||||
<Option value="delay">{intl.get('延迟执行')}</Option>
|
|
||||||
<Option value="retry">{intl.get('重试策略')}</Option>
|
|
||||||
<Option value="circuit_breaker">{intl.get('熔断器')}</Option>
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item noStyle shouldUpdate={(prev, curr) => prev.controlType !== curr.controlType}>
|
|
||||||
{({ getFieldValue }) => {
|
|
||||||
const controlType = getFieldValue('controlType');
|
|
||||||
if (controlType === 'delay') {
|
|
||||||
return (
|
|
||||||
<Form.Item name="delaySeconds" label={intl.get('延迟时间')}>
|
|
||||||
<InputNumber min={1} addonAfter={intl.get('秒')} />
|
|
||||||
</Form.Item>
|
|
||||||
);
|
|
||||||
} else if (controlType === 'retry') {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Form.Item name="maxRetries" label={intl.get('最大重试次数')}>
|
|
||||||
<InputNumber min={1} max={10} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="retryDelay" label={intl.get('重试延迟')}>
|
|
||||||
<InputNumber min={1} addonAfter={intl.get('秒')} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="backoffMultiplier" label={intl.get('退避倍数')}>
|
|
||||||
<InputNumber min={1} step={0.5} />
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
} else if (controlType === 'circuit_breaker') {
|
|
||||||
return (
|
|
||||||
<Form.Item name="failureThreshold" label={intl.get('失败熔断阈值')}>
|
|
||||||
<InputNumber min={1} />
|
|
||||||
</Form.Item>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}}
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title={scenario ? intl.get('编辑场景') : intl.get('新建场景')}
|
|
||||||
open={visible}
|
|
||||||
onCancel={onCancel}
|
|
||||||
onOk={handleSubmit}
|
|
||||||
confirmLoading={loading}
|
|
||||||
width={1400}
|
|
||||||
destroyOnClose
|
|
||||||
style={{ top: 20 }}
|
|
||||||
>
|
|
||||||
<Form form={form} layout="vertical">
|
|
||||||
<Form.Item
|
|
||||||
name="name"
|
|
||||||
label={intl.get('名称')}
|
|
||||||
rules={[{ required: true }]}
|
|
||||||
>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item name="description" label={intl.get('描述')}>
|
|
||||||
<TextArea rows={2} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item label={intl.get('工作流设计')}>
|
|
||||||
<div style={{ marginBottom: 8 }}>
|
|
||||||
<Space>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
onClick={() => handleAddNode('trigger', NodeTemplates.triggers.time)}
|
|
||||||
>
|
|
||||||
{intl.get('触发器')}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
onClick={() => handleAddNode('condition', NodeTemplates.conditions.equals)}
|
|
||||||
>
|
|
||||||
{intl.get('条件')}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
onClick={() => handleAddNode('action', NodeTemplates.actions.run_task)}
|
|
||||||
>
|
|
||||||
{intl.get('动作')}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
onClick={() => handleAddNode('control', NodeTemplates.controls.delay)}
|
|
||||||
>
|
|
||||||
{intl.get('控制流')}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
onClick={() => handleAddNode('logic_gate', NodeTemplates.logic_gates.and)}
|
|
||||||
>
|
|
||||||
{intl.get('逻辑门')}
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
border: '1px solid #d9d9d9',
|
|
||||||
borderRadius: 4,
|
|
||||||
height: 500,
|
|
||||||
overflow: 'hidden',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ReactFlow
|
|
||||||
nodes={nodes}
|
|
||||||
edges={edges}
|
|
||||||
onNodesChange={onNodesChange}
|
|
||||||
onEdgesChange={onEdgesChange}
|
|
||||||
onConnect={onConnect}
|
|
||||||
onNodeClick={handleNodeClick}
|
|
||||||
nodeTypes={nodeTypes}
|
|
||||||
fitView
|
|
||||||
>
|
|
||||||
<Controls />
|
|
||||||
<Background variant={BackgroundVariant.Dots} gap={12} size={1} />
|
|
||||||
</ReactFlow>
|
|
||||||
</div>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
|
|
||||||
<Drawer
|
|
||||||
title={intl.get('节点配置')}
|
|
||||||
placement="right"
|
|
||||||
onClose={() => setDrawerVisible(false)}
|
|
||||||
open={drawerVisible}
|
|
||||||
width={400}
|
|
||||||
extra={
|
|
||||||
<Button type="primary" onClick={handleNodeConfigSave}>
|
|
||||||
{intl.get('保存')}
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Form form={nodeConfigForm} layout="vertical">
|
|
||||||
{renderNodeConfig()}
|
|
||||||
</Form>
|
|
||||||
</Drawer>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default VisualWorkflowModal;
|
|
||||||
Loading…
Reference in New Issue
Block a user