in seatunnel-engine/seatunnel-engine-ui/src/components/directed-acyclic-graph/index.tsx [131:362]
setup(props) {
let focusedId = 0
let graph: Graph
watch(
() => props.focusedId,
() => {
if (!graph || focusedId === props.focusedId) return
if (props.focusedId) {
// const cell = graph.getCellById('node-' + props.focusedId)
// if (cell) {
// cell.trigger('click')
// }
graph.select('node-' + props.focusedId)
} else {
graph.select('node-0')
// graph.trigger('blank:click')
}
}
)
onMounted(() => {
graph = new Graph({
container: document.getElementById('container')!,
panning: {
enabled: true,
eventTypes: ['leftMouseDown', 'mouseWheel']
},
mousewheel: {
enabled: true,
modifiers: 'ctrl',
factor: 1.1,
maxScale: 1.5,
minScale: 0.5
},
highlighting: {
magnetAdsorbed: {
name: 'stroke',
args: {
attrs: {
fill: '#fff',
stroke: '#31d0c6',
strokeWidth: 4
}
}
}
},
connecting: {
snap: true,
allowBlank: false,
allowLoop: false,
highlight: true,
connector: 'algo-connector',
connectionPoint: 'anchor',
anchor: 'center',
validateMagnet({ magnet }) {
return magnet.getAttribute('port-group') !== 'left'
},
createEdge() {
return graph.createEdge({
shape: 'dag-edge',
attrs: {
line: {
strokeDasharray: '5 5'
}
},
zIndex: -1
})
}
}
})
graph.use(
new Selection({
multiple: false,
rubberEdge: true,
rubberNode: true,
modifiers: 'shift',
rubberband: true
})
)
graph.on('edge:connected', ({ edge }) => {
edge.attr({
line: {
strokeDasharray: ''
}
})
})
graph.on('node:change:data', ({ node }) => {
const edges = graph.getIncomingEdges(node)
const { status } = node.getData() as NodeStatus
edges?.forEach((edge) => {
if (status === 'RUNNING') {
edge.attr('line/strokeDasharray', 5)
edge.attr('line/style/animation', 'running-line 30s infinite linear')
} else {
edge.attr('line/strokeDasharray', '')
edge.attr('line/style/animation', '')
}
})
})
graph.on('node:click', ({ node }) => {
const { id } = node.getData() as NodeStatus
focusedId = id
const vertex = props?.job?.jobDag?.vertexInfoMap?.find((item) => item.vertexId === id)
props.onNodeClick(vertex)
})
graph.on('blank:click', () => {
props.onNodeClick()
})
const init = () => {
const matrix = [] as Vertex[][]
const items: Cell.Metadata[] = []
const offsetY = 140
const offsetX = nodeWidth + 200
const processed = [] as Vertex[]
const vertexs = props?.job?.jobDag?.vertexInfoMap || []
const edgeMap = props?.job?.jobDag?.pipelineEdges || {}
let zIndex = 0
for (const pipelineId of Object.keys(edgeMap)) {
const edges = edgeMap[pipelineId]
const row = [] as Vertex[]
matrix.push(row)
for (const edge of edges) {
items.push({
id: `edge-${pipelineId}-${edge.inputVertexId}-${edge.targetVertexId}`,
shape: 'dag-edge',
source: {
cell: `node-${edge.inputVertexId}`,
port: `node-${edge.inputVertexId}-right`
},
target: {
cell: `node-${edge.targetVertexId}`,
port: `node-${edge.targetVertexId}-left`
},
zIndex: zIndex++
})
const input = vertexs.find((item) => item.vertexId === Number(edge.inputVertexId))
if (input && !processed.includes(input)) {
row.push(input)
processed.push(input)
}
const target = vertexs.find((item) => item.vertexId === Number(edge.targetVertexId))
if (target && !processed.includes(target)) {
row.push(target)
processed.push(target)
}
}
}
matrix.forEach((row) => {
row.sort((a, b) => {
if (a.type === 'source') {
return -1
} else if (b.type === 'sink') {
return 1
} else {
return 0
}
})
})
type Port = { id: string; group: string }
matrix.forEach((row, rowNumber) => {
row.forEach((item, colNumber) => {
const data: NodeStatus = {
id: item.vertexId,
label: item.vertexName,
status: props?.job?.jobStatus
}
const id = 'node-' + item.vertexId
const ports = [] as Port[]
if (colNumber !== 0) {
ports.push({
id: `${id}-left`,
group: 'left'
})
}
if (colNumber !== row.length - 1) {
ports.push({
id: `${id}-right`,
group: 'right'
})
}
items.push({
id,
shape: 'dag-node',
x: colNumber * offsetX,
y: rowNumber * offsetY,
data,
ports
})
})
})
const cells: Cell[] = []
items.forEach((item) => {
if (item.shape === 'dag-node') {
cells.push(graph.createNode(item))
} else {
cells.push(graph.createEdge(item))
}
})
graph.resetCells(cells)
}
// 显示节点状态
const showNodeStatus = async (statusList: NodeStatus[][]) => {
const status = statusList[Math.floor(Math.random() * statusList.length)]
status?.forEach((item) => {
const { id, status } = item
const node = graph.getCellById(`node-${id}`)
const data = node.getData() as NodeStatus
node.setData({
...data,
status
})
})
if (!status) return
setTimeout(() => {
showNodeStatus(statusList)
}, 5000)
}
setTimeout(() => {
init()
graph.centerContent()
}, 500)
})
return () => <div id="container" style="height: 600px" />
}