|
@@ -1,6 +1,6 @@
|
|
import React from 'react';
|
|
import React from 'react';
|
|
-import { Graph, Addon, Path, Cell, Model, Node } from '@antv/x6';
|
|
|
|
-import { Drawer, Modal, Table } from 'antd';
|
|
|
|
|
|
+import { Graph, Addon, Path, Cell, Model, Node, Markup } from '@antv/x6';
|
|
|
|
+import { Drawer, Modal, Table, Tooltip } from 'antd';
|
|
import type { ColumnsType } from 'antd/es/table';
|
|
import type { ColumnsType } from 'antd/es/table';
|
|
import AlgoNode from './AlgoNode';
|
|
import AlgoNode from './AlgoNode';
|
|
import ToolBar from './ToolBar';
|
|
import ToolBar from './ToolBar';
|
|
@@ -8,6 +8,7 @@ import DatasourceNodeInfo from './DatasourceNodeInfo';
|
|
import ScriptNodeInfo from './ScriptNodeInfo';
|
|
import ScriptNodeInfo from './ScriptNodeInfo';
|
|
import OutputNodeInfo from './OutputNodeInfo';
|
|
import OutputNodeInfo from './OutputNodeInfo';
|
|
import ContextMenuView from './ContextMenu';
|
|
import ContextMenuView from './ContextMenu';
|
|
|
|
+import ReactDOM from 'react-dom';
|
|
|
|
|
|
// 侧边栏UI组件
|
|
// 侧边栏UI组件
|
|
const { Stencil } = Addon;
|
|
const { Stencil } = Addon;
|
|
@@ -16,7 +17,7 @@ const { Stencil } = Addon;
|
|
interface NodeStatus {
|
|
interface NodeStatus {
|
|
id: string;
|
|
id: string;
|
|
status: 'default' | 'success' | 'failed' | 'running';
|
|
status: 'default' | 'success' | 'failed' | 'running';
|
|
- type: 'script' | 'datasource';
|
|
|
|
|
|
+ type: 'script' | 'datasource' | 'outputsource';
|
|
label?: string;
|
|
label?: string;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -28,18 +29,14 @@ const dataSourceNodes = [
|
|
height: 80,
|
|
height: 80,
|
|
width: 180,
|
|
width: 180,
|
|
data: {
|
|
data: {
|
|
- label: 'DataSource',
|
|
|
|
|
|
+ label: 'InputSource',
|
|
status: 'default',
|
|
status: 'default',
|
|
type: 'datasource'
|
|
type: 'datasource'
|
|
},
|
|
},
|
|
ports: {
|
|
ports: {
|
|
items: [{ id: 'bottomPort', group: 'bottom' }]
|
|
items: [{ id: 'bottomPort', group: 'bottom' }]
|
|
}
|
|
}
|
|
- }
|
|
|
|
-];
|
|
|
|
-
|
|
|
|
-// 输出源节点
|
|
|
|
-const outputSourceNodes = [
|
|
|
|
|
|
+ },
|
|
{
|
|
{
|
|
id: '10',
|
|
id: '10',
|
|
shape: 'dag-node',
|
|
shape: 'dag-node',
|
|
@@ -53,61 +50,22 @@ const outputSourceNodes = [
|
|
ports: {
|
|
ports: {
|
|
items: [{ id: 'topPort', group: 'top' }]
|
|
items: [{ id: 'topPort', group: 'top' }]
|
|
}
|
|
}
|
|
- }
|
|
|
|
-]
|
|
|
|
-
|
|
|
|
-/* // 数据处理节点
|
|
|
|
-const dataHandleNodes = [
|
|
|
|
- {
|
|
|
|
- id: '1',
|
|
|
|
- shape: 'dag-node',
|
|
|
|
- data: {
|
|
|
|
- label: '数据处理节点',
|
|
|
|
- status: 'default',
|
|
|
|
- type: 'script'
|
|
|
|
- },
|
|
|
|
- ports: {
|
|
|
|
- items: [
|
|
|
|
- { id: 'topPort', group: 'top' },
|
|
|
|
- { id: 'bottomPort', group: 'bottom' }
|
|
|
|
- ]
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
];
|
|
];
|
|
-
|
|
|
|
-// 其它节点
|
|
|
|
-const otherScriptNodes = [
|
|
|
|
- {
|
|
|
|
- id: '2',
|
|
|
|
- shape: 'dag-node',
|
|
|
|
- data: {
|
|
|
|
- label: '其它节点',
|
|
|
|
- status: 'default',
|
|
|
|
- type: 'script'
|
|
|
|
- },
|
|
|
|
- ports: {
|
|
|
|
- items: [
|
|
|
|
- { id: 'topPort', group: 'top' },
|
|
|
|
- { id: 'bottomPort', group: 'bottom' }
|
|
|
|
- ]
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-]; */
|
|
|
|
-
|
|
|
|
// 用户自定义脚本节点
|
|
// 用户自定义脚本节点
|
|
const customScriptNodes = [
|
|
const customScriptNodes = [
|
|
{
|
|
{
|
|
id: '5',
|
|
id: '5',
|
|
shape: 'dag-node',
|
|
shape: 'dag-node',
|
|
data: {
|
|
data: {
|
|
- label: 'SQL',
|
|
|
|
|
|
+ label: 'sql',
|
|
status: 'default',
|
|
status: 'default',
|
|
type: 'script'
|
|
type: 'script'
|
|
},
|
|
},
|
|
ports: {
|
|
ports: {
|
|
items: [
|
|
items: [
|
|
- { id: 'topPort', group: 'top' },
|
|
|
|
- { id: 'bottomPort', group: 'bottom' }
|
|
|
|
|
|
+ // { id: 'topPort', group: 'top' },
|
|
|
|
+ // { id: 'bottomPort', group: 'bottom' }
|
|
]
|
|
]
|
|
}
|
|
}
|
|
},
|
|
},
|
|
@@ -115,14 +73,14 @@ const customScriptNodes = [
|
|
id: '6',
|
|
id: '6',
|
|
shape: 'dag-node',
|
|
shape: 'dag-node',
|
|
data: {
|
|
data: {
|
|
- label: 'PySpark',
|
|
|
|
|
|
+ label: 'pyspark',
|
|
status: 'default',
|
|
status: 'default',
|
|
type: 'script'
|
|
type: 'script'
|
|
},
|
|
},
|
|
ports: {
|
|
ports: {
|
|
items: [
|
|
items: [
|
|
- { id: 'topPort', group: 'top' },
|
|
|
|
- { id: 'bottomPort', group: 'bottom' }
|
|
|
|
|
|
+ // { id: 'topPort', group: 'top' },
|
|
|
|
+ // { id: 'bottomPort', group: 'bottom' }
|
|
]
|
|
]
|
|
}
|
|
}
|
|
},
|
|
},
|
|
@@ -130,14 +88,14 @@ const customScriptNodes = [
|
|
id: '7',
|
|
id: '7',
|
|
shape: 'dag-node',
|
|
shape: 'dag-node',
|
|
data: {
|
|
data: {
|
|
- label: 'Python',
|
|
|
|
|
|
+ label: 'python',
|
|
status: 'default',
|
|
status: 'default',
|
|
type: 'script'
|
|
type: 'script'
|
|
},
|
|
},
|
|
ports: {
|
|
ports: {
|
|
items: [
|
|
items: [
|
|
- { id: 'topPort', group: 'top' },
|
|
|
|
- { id: 'bottomPort', group: 'bottom' }
|
|
|
|
|
|
+ // { id: 'topPort', group: 'top' },
|
|
|
|
+ // { id: 'bottomPort', group: 'bottom' }
|
|
]
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -151,29 +109,30 @@ Graph.registerNode(
|
|
width: 180,
|
|
width: 180,
|
|
height: 36,
|
|
height: 36,
|
|
component: <AlgoNode />,
|
|
component: <AlgoNode />,
|
|
|
|
+ portMarkup: [Markup.getForeignObjectMarkup()],
|
|
ports: {
|
|
ports: {
|
|
groups: {
|
|
groups: {
|
|
top: {
|
|
top: {
|
|
position: 'top',
|
|
position: 'top',
|
|
attrs: {
|
|
attrs: {
|
|
- circle: {
|
|
|
|
- r: 4,
|
|
|
|
- magnet: true,
|
|
|
|
- stroke: '#C2C8D5',
|
|
|
|
- strokeWidth: 1,
|
|
|
|
- fill: '#fff'
|
|
|
|
|
|
+ fo: {
|
|
|
|
+ width: 10,
|
|
|
|
+ height: 10,
|
|
|
|
+ x: -5,
|
|
|
|
+ y: -5,
|
|
|
|
+ magnet: 'true',
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
},
|
|
bottom: {
|
|
bottom: {
|
|
position: 'bottom',
|
|
position: 'bottom',
|
|
attrs: {
|
|
attrs: {
|
|
- circle: {
|
|
|
|
- r: 4,
|
|
|
|
- magnet: true,
|
|
|
|
- stroke: '#C2C8D5',
|
|
|
|
- strokeWidth: 1,
|
|
|
|
- fill: '#fff'
|
|
|
|
|
|
+ fo: {
|
|
|
|
+ width: 10,
|
|
|
|
+ height: 10,
|
|
|
|
+ x: -5,
|
|
|
|
+ y: -5,
|
|
|
|
+ magnet: 'true',
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -221,9 +180,6 @@ Graph.registerConnector(
|
|
true
|
|
true
|
|
);
|
|
);
|
|
|
|
|
|
-// 存储边
|
|
|
|
-const dagEdges: Array<any> = [];
|
|
|
|
-
|
|
|
|
const columns: ColumnsType<any> = [
|
|
const columns: ColumnsType<any> = [
|
|
{ title: 'pord_order_id', dataIndex: 'pord_order_id', key: 'pord_order_id' },
|
|
{ title: 'pord_order_id', dataIndex: 'pord_order_id', key: 'pord_order_id' },
|
|
{
|
|
{
|
|
@@ -408,16 +364,16 @@ export default class Dag extends React.Component<any, any> {
|
|
// 验证
|
|
// 验证
|
|
validateMagnet({ magnet }) {
|
|
validateMagnet({ magnet }) {
|
|
return magnet.getAttribute('port-group') !== 'top';
|
|
return magnet.getAttribute('port-group') !== 'top';
|
|
|
|
+
|
|
},
|
|
},
|
|
- validateConnection({ targetPort, sourceCell, targetCell }) {
|
|
|
|
- const jugeEdge = dagEdges.find(
|
|
|
|
- (item: any) =>
|
|
|
|
- item?.source === sourceCell?.id && item?.target === targetCell?.id
|
|
|
|
- );
|
|
|
|
- if (jugeEdge) {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- return targetPort === 'topPort';
|
|
|
|
|
|
+ validateConnection({ targetPort, sourceCell, targetCell, targetMagnet }) {
|
|
|
|
+ if (!targetCell) return false
|
|
|
|
+ const incomingEdges = this.getIncomingEdges(targetCell)
|
|
|
|
+ const existPorts = incomingEdges?.map(edge => {
|
|
|
|
+ return edge.getTargetPortId()
|
|
|
|
+ })
|
|
|
|
+ const jugeExist = existPorts?.indexOf(targetPort as any)
|
|
|
|
+ return targetMagnet?.getAttribute('port-group') === 'top' && (jugeExist === undefined || jugeExist === -1);
|
|
},
|
|
},
|
|
createEdge() {
|
|
createEdge() {
|
|
return graph.createEdge({
|
|
return graph.createEdge({
|
|
@@ -444,7 +400,22 @@ export default class Dag extends React.Component<any, any> {
|
|
keyboard: {
|
|
keyboard: {
|
|
enabled: true,
|
|
enabled: true,
|
|
global: true
|
|
global: true
|
|
- }
|
|
|
|
|
|
+ },
|
|
|
|
+ onPortRendered(args) {
|
|
|
|
+ const selectors = args.contentSelectors
|
|
|
|
+ const port = args.port
|
|
|
|
+ const container = selectors && selectors.foContent
|
|
|
|
+ if (container) {
|
|
|
|
+ ReactDOM.render(
|
|
|
|
+ (
|
|
|
|
+ <Tooltip title={port.id}>
|
|
|
|
+ <div className="my-port" />
|
|
|
|
+ </Tooltip>
|
|
|
|
+ ) as any,
|
|
|
|
+ container as HTMLElement,
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
+ },
|
|
});
|
|
});
|
|
|
|
|
|
// 删除节点
|
|
// 删除节点
|
|
@@ -456,12 +427,6 @@ export default class Dag extends React.Component<any, any> {
|
|
|
|
|
|
// 监听边连接
|
|
// 监听边连接
|
|
graph.on('edge:connected', ({ edge }) => {
|
|
graph.on('edge:connected', ({ edge }) => {
|
|
- dagEdges.push({
|
|
|
|
- source: edge.getSourceCell()?.id,
|
|
|
|
- target: edge.getTargetCell()?.id
|
|
|
|
- });
|
|
|
|
- const targetNodeData = edge.getTargetCell()?.data;
|
|
|
|
- targetNodeData.inputNumber += 1;
|
|
|
|
edge.attr({
|
|
edge.attr({
|
|
line: {
|
|
line: {
|
|
strokeDasharray: ''
|
|
strokeDasharray: ''
|
|
@@ -469,24 +434,6 @@ export default class Dag extends React.Component<any, any> {
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
- graph.on('edge:removed', ({ edge }) => {
|
|
|
|
- const sourceNodeId = (edge as any).store.data.source.cell;
|
|
|
|
- const targetNodeId = (edge as any).store.data.target.cell;
|
|
|
|
-
|
|
|
|
- const index = dagEdges.indexOf({
|
|
|
|
- source: sourceNodeId,
|
|
|
|
- target: targetNodeId
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- const targetNode = this.state.dagGraph.getCellById(targetNodeId);
|
|
|
|
-
|
|
|
|
- if (targetNode) {
|
|
|
|
- targetNode.data.inputNumber -= 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- dagEdges.splice(index, 1);
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
// 监听状态改变
|
|
// 监听状态改变
|
|
graph.on('node:change:data', ({ node }) => {
|
|
graph.on('node:change:data', ({ node }) => {
|
|
const edges = graph.getIncomingEdges(node);
|
|
const edges = graph.getIncomingEdges(node);
|
|
@@ -557,10 +504,6 @@ export default class Dag extends React.Component<any, any> {
|
|
name: 'dataSource',
|
|
name: 'dataSource',
|
|
title: '数据源'
|
|
title: '数据源'
|
|
},
|
|
},
|
|
- {
|
|
|
|
- name: 'outputSource',
|
|
|
|
- title: '输出源'
|
|
|
|
- },
|
|
|
|
{
|
|
{
|
|
name: 'dataHandle',
|
|
name: 'dataHandle',
|
|
title: '数据处理',
|
|
title: '数据处理',
|
|
@@ -589,26 +532,11 @@ export default class Dag extends React.Component<any, any> {
|
|
|
|
|
|
// 将可拖拽项加载到侧边栏中
|
|
// 将可拖拽项加载到侧边栏中
|
|
stencil.load(dataSourceNodes, 'dataSource');
|
|
stencil.load(dataSourceNodes, 'dataSource');
|
|
- stencil.load(outputSourceNodes, 'outputSource')
|
|
|
|
- // stencil.load(dataHandleNodes, 'dataHandle');
|
|
|
|
- // stencil.load(otherScriptNodes, 'otherScript');
|
|
|
|
stencil.load(customScriptNodes, 'customScript');
|
|
stencil.load(customScriptNodes, 'customScript');
|
|
stencil.resizeGroup('dataSource', {
|
|
stencil.resizeGroup('dataSource', {
|
|
width: 200,
|
|
width: 200,
|
|
- height: dataSourceNodes.length * 120
|
|
|
|
- });
|
|
|
|
- stencil.resizeGroup('outputSource', {
|
|
|
|
- width: 200,
|
|
|
|
- height: outputSourceNodes.length * 120
|
|
|
|
|
|
+ height: dataSourceNodes.length * 100
|
|
});
|
|
});
|
|
- /* stencil.resizeGroup('dataHandle', {
|
|
|
|
- width: 200,
|
|
|
|
- height: dataHandleNodes.length * 60
|
|
|
|
- });
|
|
|
|
- stencil.resizeGroup('otherScript', {
|
|
|
|
- width: 200,
|
|
|
|
- height: otherScriptNodes.length * 60
|
|
|
|
- }); */
|
|
|
|
stencil.resizeGroup('customScript', {
|
|
stencil.resizeGroup('customScript', {
|
|
width: 200,
|
|
width: 200,
|
|
height: customScriptNodes.length * 60
|
|
height: customScriptNodes.length * 60
|
|
@@ -698,7 +626,7 @@ export default class Dag extends React.Component<any, any> {
|
|
)}
|
|
)}
|
|
{/* 脚本节点 */}
|
|
{/* 脚本节点 */}
|
|
{this.state.selectedNodeData?.type === 'script' && (
|
|
{this.state.selectedNodeData?.type === 'script' && (
|
|
- <ScriptNodeInfo nodeInfo={this.state.selectedNodeData} />
|
|
|
|
|
|
+ <ScriptNodeInfo nodeInfo={this.state.selectedNodeData} graph={this.state.dagGraph} />
|
|
)}
|
|
)}
|
|
{/* 输出源节点 */}
|
|
{/* 输出源节点 */}
|
|
{this.state.selectedNodeData?.type === 'outputsource' && (
|
|
{this.state.selectedNodeData?.type === 'outputsource' && (
|
|
@@ -731,7 +659,7 @@ function initNodeData(newNode: Node<Node.Properties>) {
|
|
newNode.data.paramText = undefined;
|
|
newNode.data.paramText = undefined;
|
|
newNode.data.nodeName = undefined;
|
|
newNode.data.nodeName = undefined;
|
|
newNode.data.scriptText = '';
|
|
newNode.data.scriptText = '';
|
|
- newNode.data.outputData = undefined;
|
|
|
|
|
|
+ newNode.data.outputData = [];
|
|
newNode.data.inputNumber = 0;
|
|
newNode.data.inputNumber = 0;
|
|
newNode.data.packageData = undefined;
|
|
newNode.data.packageData = undefined;
|
|
newNode.data.status = 'undone';
|
|
newNode.data.status = 'undone';
|