1
This commit is contained in:
parent
4b37225a50
commit
c1da34e46b
@ -0,0 +1,142 @@
|
|||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { Form, Input, Select, InputNumber, Switch } from 'antd';
|
||||||
|
import { NodeType, JsonSchema, JsonSchemaProperty } from '../../service';
|
||||||
|
|
||||||
|
interface NodeConfigProps {
|
||||||
|
nodeType: NodeType;
|
||||||
|
form: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NodeConfig: React.FC<NodeConfigProps> = ({ nodeType, form }) => {
|
||||||
|
// 解析配置模式
|
||||||
|
const schema = useMemo(() => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(nodeType.configSchema) as JsonSchema;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解析配置模式失败:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, [nodeType.configSchema]);
|
||||||
|
|
||||||
|
// 解析默认配置
|
||||||
|
const defaultConfig = useMemo(() => {
|
||||||
|
try {
|
||||||
|
return nodeType.defaultConfig ? JSON.parse(nodeType.defaultConfig) : {};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解析默认配置失败:', error);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}, [nodeType.defaultConfig]);
|
||||||
|
|
||||||
|
// 根据属性类型渲染表单控件
|
||||||
|
const renderFormItem = (key: string, property: JsonSchemaProperty) => {
|
||||||
|
const {
|
||||||
|
type,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
minimum,
|
||||||
|
maximum,
|
||||||
|
minLength,
|
||||||
|
maxLength,
|
||||||
|
enum: enumValues,
|
||||||
|
enumNames,
|
||||||
|
pattern,
|
||||||
|
format,
|
||||||
|
} = property;
|
||||||
|
|
||||||
|
let formItem;
|
||||||
|
const rules = [];
|
||||||
|
|
||||||
|
// 添加必填规则
|
||||||
|
if (schema?.required?.includes(key)) {
|
||||||
|
rules.push({ required: true, message: `请输入${title}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加长度限制
|
||||||
|
if (minLength !== undefined) {
|
||||||
|
rules.push({ min: minLength, message: `最少输入${minLength}个字符` });
|
||||||
|
}
|
||||||
|
if (maxLength !== undefined) {
|
||||||
|
rules.push({ max: maxLength, message: `最多输入${maxLength}个字符` });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加数值范围限制
|
||||||
|
if (minimum !== undefined) {
|
||||||
|
rules.push({ min: minimum, message: `不能小于${minimum}` });
|
||||||
|
}
|
||||||
|
if (maximum !== undefined) {
|
||||||
|
rules.push({ max: maximum, message: `不能大于${maximum}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加正则校验
|
||||||
|
if (pattern) {
|
||||||
|
rules.push({ pattern: new RegExp(pattern), message: `格式不正确` });
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'string':
|
||||||
|
if (enumValues) {
|
||||||
|
formItem = (
|
||||||
|
<Select
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
placeholder={`请选择${title}`}
|
||||||
|
options={enumValues.map((value, index) => ({
|
||||||
|
label: enumNames?.[index] || value,
|
||||||
|
value,
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else if (format === 'shell') {
|
||||||
|
formItem = <Input.TextArea rows={6} placeholder={`请输入${title}`} />;
|
||||||
|
} else {
|
||||||
|
formItem = <Input placeholder={`请输入${title}`} />;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'number':
|
||||||
|
formItem = (
|
||||||
|
<InputNumber
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
placeholder={`请输入${title}`}
|
||||||
|
min={minimum}
|
||||||
|
max={maximum}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'boolean':
|
||||||
|
formItem = <Switch checkedChildren="是" unCheckedChildren="否" />;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
formItem = <Input placeholder={`请输入${title}`} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form.Item
|
||||||
|
key={key}
|
||||||
|
label={title}
|
||||||
|
name={key}
|
||||||
|
tooltip={description}
|
||||||
|
rules={rules}
|
||||||
|
initialValue={defaultConfig[key] ?? property.default}
|
||||||
|
>
|
||||||
|
{formItem}
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 渲染表单项
|
||||||
|
const renderFormItems = () => {
|
||||||
|
if (!schema?.properties) return null;
|
||||||
|
|
||||||
|
return Object.entries(schema.properties).map(([key, property]) =>
|
||||||
|
renderFormItem(key, property)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="node-config">
|
||||||
|
{renderFormItems()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NodeConfig;
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
.workflow-toolbar {
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: #fff;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.ant-btn {
|
||||||
|
padding: 4px 8px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #1890ff;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[disabled] {
|
||||||
|
color: #d9d9d9;
|
||||||
|
background: transparent;
|
||||||
|
cursor: not-allowed;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #d9d9d9;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-dangerous {
|
||||||
|
color: #ff4d4f;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #ff7875;
|
||||||
|
background: #fff1f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[disabled] {
|
||||||
|
color: #d9d9d9;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #d9d9d9;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-divider {
|
||||||
|
height: 16px;
|
||||||
|
margin: 0 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,164 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Space, Button, Tooltip, Divider } from 'antd';
|
||||||
|
import {
|
||||||
|
ZoomInOutlined,
|
||||||
|
ZoomOutOutlined,
|
||||||
|
FullscreenOutlined,
|
||||||
|
OneToOneOutlined,
|
||||||
|
SelectOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
UndoOutlined,
|
||||||
|
RedoOutlined,
|
||||||
|
CopyOutlined,
|
||||||
|
SnippetsOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import { Graph } from '@antv/x6';
|
||||||
|
import './index.less';
|
||||||
|
|
||||||
|
interface ToolbarProps {
|
||||||
|
graph: Graph | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Toolbar: React.FC<ToolbarProps> = ({ graph }) => {
|
||||||
|
// 缩放画布
|
||||||
|
const zoom = (delta: number) => {
|
||||||
|
if (!graph) return;
|
||||||
|
const zoom = graph.zoom();
|
||||||
|
graph.zoom(zoom + delta);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 适应画布
|
||||||
|
const fitContent = () => {
|
||||||
|
if (!graph) return;
|
||||||
|
graph.zoomToFit({ padding: 20 });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 实际大小
|
||||||
|
const resetZoom = () => {
|
||||||
|
if (!graph) return;
|
||||||
|
graph.scale(1);
|
||||||
|
graph.centerContent();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 全选
|
||||||
|
const selectAll = () => {
|
||||||
|
if (!graph) return;
|
||||||
|
const nodes = graph.getNodes();
|
||||||
|
const edges = graph.getEdges();
|
||||||
|
graph.resetSelection();
|
||||||
|
graph.select(nodes);
|
||||||
|
graph.select(edges);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除选中
|
||||||
|
const deleteSelected = () => {
|
||||||
|
if (!graph) return;
|
||||||
|
const cells = graph.getSelectedCells();
|
||||||
|
graph.removeCells(cells);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 撤销
|
||||||
|
const undo = () => {
|
||||||
|
if (!graph) return;
|
||||||
|
if (graph.canUndo()) {
|
||||||
|
graph.undo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重做
|
||||||
|
const redo = () => {
|
||||||
|
if (!graph) return;
|
||||||
|
if (graph.canRedo()) {
|
||||||
|
graph.redo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 复制
|
||||||
|
const copy = () => {
|
||||||
|
if (!graph) return;
|
||||||
|
const cells = graph.getSelectedCells();
|
||||||
|
if (cells.length > 0) {
|
||||||
|
graph.copy(cells);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 粘贴
|
||||||
|
const paste = () => {
|
||||||
|
if (!graph) return;
|
||||||
|
if (!graph.isClipboardEmpty()) {
|
||||||
|
const cells = graph.paste({ offset: 20 });
|
||||||
|
graph.cleanSelection();
|
||||||
|
graph.select(cells);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="workflow-toolbar">
|
||||||
|
<Space split={<Divider type="vertical" />}>
|
||||||
|
<Space>
|
||||||
|
<Tooltip title="放大">
|
||||||
|
<Button icon={<ZoomInOutlined />} onClick={() => zoom(0.1)} />
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="缩小">
|
||||||
|
<Button icon={<ZoomOutOutlined />} onClick={() => zoom(-0.1)} />
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="适应画布">
|
||||||
|
<Button icon={<FullscreenOutlined />} onClick={fitContent} />
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="实际大小">
|
||||||
|
<Button icon={<OneToOneOutlined />} onClick={resetZoom} />
|
||||||
|
</Tooltip>
|
||||||
|
</Space>
|
||||||
|
|
||||||
|
<Space>
|
||||||
|
<Tooltip title="全选">
|
||||||
|
<Button icon={<SelectOutlined />} onClick={selectAll} />
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="删除">
|
||||||
|
<Button
|
||||||
|
icon={<DeleteOutlined />}
|
||||||
|
onClick={deleteSelected}
|
||||||
|
danger
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Space>
|
||||||
|
|
||||||
|
<Space>
|
||||||
|
<Tooltip title="撤销">
|
||||||
|
<Button
|
||||||
|
icon={<UndoOutlined />}
|
||||||
|
onClick={undo}
|
||||||
|
disabled={!graph?.canUndo()}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="重做">
|
||||||
|
<Button
|
||||||
|
icon={<RedoOutlined />}
|
||||||
|
onClick={redo}
|
||||||
|
disabled={!graph?.canRedo()}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Space>
|
||||||
|
|
||||||
|
<Space>
|
||||||
|
<Tooltip title="复制">
|
||||||
|
<Button
|
||||||
|
icon={<CopyOutlined />}
|
||||||
|
onClick={copy}
|
||||||
|
disabled={!graph?.getSelectedCells().length}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="粘贴">
|
||||||
|
<Button
|
||||||
|
icon={<SnippetsOutlined />}
|
||||||
|
onClick={paste}
|
||||||
|
disabled={graph?.isClipboardEmpty()}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Space>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Toolbar;
|
||||||
@ -1,17 +1,26 @@
|
|||||||
import React, {useEffect, useRef, useState} from 'react';
|
import React, {useEffect, useRef, useState} from 'react';
|
||||||
import {useNavigate, useParams} from 'react-router-dom';
|
import {useNavigate, useParams} from 'react-router-dom';
|
||||||
import {Button, Card, Layout, message, Space, Spin} from 'antd';
|
import {Button, Card, Layout, message, Space, Spin, Drawer, Form} from 'antd';
|
||||||
import {ArrowLeftOutlined, SaveOutlined} from '@ant-design/icons';
|
import {ArrowLeftOutlined, SaveOutlined} from '@ant-design/icons';
|
||||||
import {getDefinition, updateDefinition} from '../../service';
|
import {getDefinition, updateDefinition} from '../../service';
|
||||||
import {WorkflowDefinition, WorkflowStatus} from '../../../Workflow/types';
|
import {WorkflowDefinition, WorkflowStatus} from '../../../Workflow/types';
|
||||||
import {Graph} from '@antv/x6';
|
import {Graph, Node, Cell} from '@antv/x6';
|
||||||
import '@antv/x6-react-shape';
|
import '@antv/x6-react-shape';
|
||||||
import './index.module.less';
|
import './index.module.less';
|
||||||
import NodePanel from './components/NodePanel';
|
import NodePanel from './components/NodePanel';
|
||||||
|
import NodeConfig from './components/NodeConfig';
|
||||||
|
import Toolbar from './components/Toolbar';
|
||||||
import { NodeType } from './service';
|
import { NodeType } from './service';
|
||||||
|
|
||||||
const {Sider, Content} = Layout;
|
const {Sider, Content} = Layout;
|
||||||
|
|
||||||
|
interface NodeData {
|
||||||
|
type: string;
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
config: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
const FlowDesigner: React.FC = () => {
|
const FlowDesigner: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const {id} = useParams<{ id: string }>();
|
const {id} = useParams<{ id: string }>();
|
||||||
@ -20,6 +29,10 @@ const FlowDesigner: React.FC = () => {
|
|||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const graphRef = useRef<Graph>();
|
const graphRef = useRef<Graph>();
|
||||||
const draggedNodeRef = useRef<NodeType>();
|
const draggedNodeRef = useRef<NodeType>();
|
||||||
|
const [configVisible, setConfigVisible] = useState(false);
|
||||||
|
const [currentNode, setCurrentNode] = useState<Node>();
|
||||||
|
const [currentNodeType, setCurrentNodeType] = useState<NodeType>();
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
// 初始化图形
|
// 初始化图形
|
||||||
const initGraph = () => {
|
const initGraph = () => {
|
||||||
@ -127,6 +140,26 @@ const FlowDesigner: React.FC = () => {
|
|||||||
// 监听画布拖拽事件
|
// 监听画布拖拽事件
|
||||||
containerRef.current.addEventListener('dragover', handleDragOver);
|
containerRef.current.addEventListener('dragover', handleDragOver);
|
||||||
containerRef.current.addEventListener('drop', handleDrop);
|
containerRef.current.addEventListener('drop', handleDrop);
|
||||||
|
|
||||||
|
// 监听节点点击事件
|
||||||
|
graph.on('node:click', ({node}: {node: Node}) => {
|
||||||
|
setCurrentNode(node);
|
||||||
|
const data = node.getData() as NodeData;
|
||||||
|
if (data) {
|
||||||
|
// 获取节点类型
|
||||||
|
const nodeType = draggedNodeRef.current;
|
||||||
|
if (nodeType && nodeType.code === data.type) {
|
||||||
|
setCurrentNodeType(nodeType);
|
||||||
|
}
|
||||||
|
// 设置表单值
|
||||||
|
form.setFieldsValue({
|
||||||
|
name: data.name,
|
||||||
|
description: data.description,
|
||||||
|
...data.config,
|
||||||
|
});
|
||||||
|
setConfigVisible(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理拖拽移动
|
// 处理拖拽移动
|
||||||
@ -166,7 +199,7 @@ const FlowDesigner: React.FC = () => {
|
|||||||
y: position.y - 20, // 节点高度的一半,使节点中心对准鼠标
|
y: position.y - 20, // 节点高度的一半,使节点中心对准鼠标
|
||||||
width: 180,
|
width: 180,
|
||||||
height: 40,
|
height: 40,
|
||||||
label: nodeType.name,
|
shape: 'rect',
|
||||||
attrs: {
|
attrs: {
|
||||||
body: {
|
body: {
|
||||||
fill: '#fff',
|
fill: '#fff',
|
||||||
@ -176,6 +209,7 @@ const FlowDesigner: React.FC = () => {
|
|||||||
ry: 4,
|
ry: 4,
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
|
text: nodeType.name,
|
||||||
fill: '#333',
|
fill: '#333',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
refX: 0.5,
|
refX: 0.5,
|
||||||
@ -224,14 +258,51 @@ const FlowDesigner: React.FC = () => {
|
|||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
type: nodeType.code,
|
type: nodeType.code,
|
||||||
|
name: nodeType.name,
|
||||||
config: {},
|
config: {},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 选中新创建的节点
|
// 选中新创建的节点
|
||||||
const cells = graphRef.current.getSelectedCells();
|
graphRef.current.getNodes().forEach(n => {
|
||||||
cells.forEach(cell => cell.unselect());
|
n.setAttrByPath('body/strokeWidth', 1);
|
||||||
node.select();
|
});
|
||||||
|
node.setAttrByPath('body/strokeWidth', 2);
|
||||||
|
|
||||||
|
// 打开配置抽屉
|
||||||
|
setCurrentNode(node);
|
||||||
|
setCurrentNodeType(nodeType);
|
||||||
|
form.setFieldsValue({
|
||||||
|
name: nodeType.name,
|
||||||
|
});
|
||||||
|
setConfigVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理配置保存
|
||||||
|
const handleConfigSave = async () => {
|
||||||
|
try {
|
||||||
|
const values = await form.validateFields();
|
||||||
|
if (currentNode) {
|
||||||
|
const data = currentNode.getData() as NodeData;
|
||||||
|
const { name, description, ...config } = values;
|
||||||
|
|
||||||
|
// 更新节点数据
|
||||||
|
currentNode.setData({
|
||||||
|
...data,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
config,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新节点标签
|
||||||
|
currentNode.setAttrByPath('label/text', name);
|
||||||
|
|
||||||
|
message.success('配置保存成功');
|
||||||
|
setConfigVisible(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 表单验证失败
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取详情
|
// 获取详情
|
||||||
@ -286,6 +357,9 @@ const FlowDesigner: React.FC = () => {
|
|||||||
containerRef.current.removeEventListener('dragover', handleDragOver);
|
containerRef.current.removeEventListener('dragover', handleDragOver);
|
||||||
containerRef.current.removeEventListener('drop', handleDrop);
|
containerRef.current.removeEventListener('drop', handleDrop);
|
||||||
}
|
}
|
||||||
|
if (graphRef.current) {
|
||||||
|
graphRef.current.dispose();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [detail, containerRef.current]);
|
}, [detail, containerRef.current]);
|
||||||
|
|
||||||
@ -325,10 +399,32 @@ const FlowDesigner: React.FC = () => {
|
|||||||
<Sider width={280} className="workflow-designer-sider">
|
<Sider width={280} className="workflow-designer-sider">
|
||||||
<NodePanel onNodeDragStart={handleNodeDragStart}/>
|
<NodePanel onNodeDragStart={handleNodeDragStart}/>
|
||||||
</Sider>
|
</Sider>
|
||||||
|
<Layout>
|
||||||
|
<Toolbar graph={graphRef.current} />
|
||||||
<Content className="workflow-designer-content">
|
<Content className="workflow-designer-content">
|
||||||
<div ref={containerRef} className="workflow-designer-graph"/>
|
<div ref={containerRef} className="workflow-designer-graph"/>
|
||||||
</Content>
|
</Content>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
</Layout>
|
||||||
|
|
||||||
|
<Drawer
|
||||||
|
title="节点配置"
|
||||||
|
width={400}
|
||||||
|
open={configVisible}
|
||||||
|
onClose={() => setConfigVisible(false)}
|
||||||
|
extra={
|
||||||
|
<Space>
|
||||||
|
<Button onClick={() => setConfigVisible(false)}>取消</Button>
|
||||||
|
<Button type="primary" onClick={handleConfigSave}>
|
||||||
|
确定
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{currentNodeType && (
|
||||||
|
<NodeConfig nodeType={currentNodeType} form={form} />
|
||||||
|
)}
|
||||||
|
</Drawer>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,26 +4,55 @@ export interface NodeExecutor {
|
|||||||
code: string;
|
code: string;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
configSchema: Record<string, any>;
|
configSchema: string;
|
||||||
|
defaultConfig: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JsonSchemaProperty {
|
||||||
|
type: string;
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
minLength?: number;
|
||||||
|
maxLength?: number;
|
||||||
|
minimum?: number;
|
||||||
|
maximum?: number;
|
||||||
|
default?: any;
|
||||||
|
format?: string;
|
||||||
|
pattern?: string;
|
||||||
|
enum?: string[];
|
||||||
|
enumNames?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JsonSchema {
|
||||||
|
type: string;
|
||||||
|
properties: Record<string, JsonSchemaProperty>;
|
||||||
|
required?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NodeType {
|
export interface NodeType {
|
||||||
id: number;
|
id: number;
|
||||||
code: string;
|
code: string;
|
||||||
name: string;
|
name: string;
|
||||||
category: 'TASK' | 'EVENT' | 'GATEWAY';
|
category: 'BASIC' | 'TASK' | 'EVENT' | 'GATEWAY';
|
||||||
description: string;
|
description: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
icon: string;
|
icon: string;
|
||||||
color: string;
|
color: string;
|
||||||
executors: NodeExecutor[];
|
executors: NodeExecutor[];
|
||||||
|
configSchema: string;
|
||||||
|
defaultConfig: string;
|
||||||
createTime: string;
|
createTime: string;
|
||||||
updateTime: string;
|
updateTime: string;
|
||||||
|
version: number;
|
||||||
|
deleted: boolean;
|
||||||
|
createBy: string;
|
||||||
|
updateBy: string;
|
||||||
|
extraData: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NodeTypeQuery {
|
export interface NodeTypeQuery {
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
category?: 'TASK' | 'EVENT' | 'GATEWAY';
|
category?: 'BASIC' | 'TASK' | 'EVENT' | 'GATEWAY';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取节点类型列表
|
// 获取节点类型列表
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user