After Width: | Height: | Size: 812 B |
After Width: | Height: | Size: 736 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 743 B |
After Width: | Height: | Size: 745 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 707 B |
After Width: | Height: | Size: 709 B |
After Width: | Height: | Size: 930 B |
After Width: | Height: | Size: 862 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 747 B |
After Width: | Height: | Size: 745 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 896 B |
After Width: | Height: | Size: 897 B |
After Width: | Height: | Size: 692 B |
After Width: | Height: | Size: 693 B |
After Width: | Height: | Size: 885 B |
After Width: | Height: | Size: 825 B |
@ -0,0 +1,51 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* truncateText('ALongText', 4) => 'ALon...' |
||||||
|
* @param {number} limit |
||||||
|
* @param {string} text |
||||||
|
* Each Chinese character is equal to two chars |
||||||
|
*/ |
||||||
|
export default function truncateText(text: string, n: number) { |
||||||
|
const exp = /[\u4E00-\u9FA5]/ |
||||||
|
let res = '' |
||||||
|
let len = text.length |
||||||
|
let chinese = text.match(new RegExp(exp, 'g')) |
||||||
|
if (chinese) { |
||||||
|
len += chinese.length |
||||||
|
} |
||||||
|
if (len > n) { |
||||||
|
let i = 0 |
||||||
|
let acc = 0 |
||||||
|
while (true) { |
||||||
|
let char = text[i] |
||||||
|
if (exp.test(char)) { |
||||||
|
acc += 2 |
||||||
|
} else { |
||||||
|
acc++ |
||||||
|
} |
||||||
|
if (acc > n) break |
||||||
|
res += char |
||||||
|
i++ |
||||||
|
} |
||||||
|
res += '...' |
||||||
|
} else { |
||||||
|
res = text |
||||||
|
} |
||||||
|
return res |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import { defineComponent } from 'vue' |
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
name: "Projects", |
||||||
|
setup() { |
||||||
|
return () => ( |
||||||
|
<div>Projects</div> |
||||||
|
) |
||||||
|
} |
||||||
|
}) |
@ -0,0 +1,67 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
export const ALL_TASK_TYPES:any = { |
||||||
|
SHELL: { |
||||||
|
alias: 'SHELL', |
||||||
|
}, |
||||||
|
SUB_PROCESS: { |
||||||
|
alias: 'SUB_PROCESS', |
||||||
|
}, |
||||||
|
PROCEDURE: { |
||||||
|
alias: 'PROCEDURE', |
||||||
|
}, |
||||||
|
SQL: { |
||||||
|
alias: 'SQL', |
||||||
|
}, |
||||||
|
SPARK: { |
||||||
|
alias: 'SPARK', |
||||||
|
}, |
||||||
|
FLINK: { |
||||||
|
alias: 'FLINK', |
||||||
|
}, |
||||||
|
MR: { |
||||||
|
alias: 'MapReduce', |
||||||
|
}, |
||||||
|
PYTHON: { |
||||||
|
alias: 'PYTHON', |
||||||
|
}, |
||||||
|
DEPENDENT: { |
||||||
|
alias: 'DEPENDENT', |
||||||
|
}, |
||||||
|
HTTP: { |
||||||
|
alias: 'HTTP', |
||||||
|
}, |
||||||
|
DATAX: { |
||||||
|
alias: 'DataX', |
||||||
|
}, |
||||||
|
PIGEON: { |
||||||
|
alias: 'PIGEON', |
||||||
|
}, |
||||||
|
SQOOP: { |
||||||
|
alias: 'SQOOP', |
||||||
|
}, |
||||||
|
CONDITIONS: { |
||||||
|
alias: 'CONDITIONS', |
||||||
|
}, |
||||||
|
SWITCH: { |
||||||
|
alias: 'SWITCH', |
||||||
|
}, |
||||||
|
SEATUNNEL: { |
||||||
|
alias: 'WATERDROP', |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,27 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import { defineComponent } from 'vue' |
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
name: "TaskConfigModal", |
||||||
|
setup() { |
||||||
|
return () => ( |
||||||
|
<div>TaskConfigModal</div> |
||||||
|
) |
||||||
|
} |
||||||
|
}) |
@ -0,0 +1,60 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import { defineComponent, ref, inject } from 'vue' |
||||||
|
import Styles from './dag.module.scss' |
||||||
|
import type { PropType, Ref } from 'vue' |
||||||
|
import type { Dragged } from './dag' |
||||||
|
import { useCanvasInit, useGraphOperations, useCellActive, useCanvasDrop } from './dag-hooks'; |
||||||
|
import { useRoute } from 'vue-router' |
||||||
|
|
||||||
|
const props = { |
||||||
|
dragged: { |
||||||
|
type: Object as PropType<Ref<Dragged>>, |
||||||
|
default: ref({ |
||||||
|
x: 0, |
||||||
|
y: 0, |
||||||
|
type: '' |
||||||
|
}), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
name: "workflow-dag-canvas", |
||||||
|
props, |
||||||
|
setup(props, context) { |
||||||
|
const readonly = inject('readonly', ref(false)); |
||||||
|
const graph = inject('graph', ref()); |
||||||
|
const route = useRoute(); |
||||||
|
const projectCode = route.params.projectCode as string; |
||||||
|
|
||||||
|
const { paper, minimap, container } = useCanvasInit({ readonly, graph }); |
||||||
|
|
||||||
|
// Change the style on cell hover and select
|
||||||
|
useCellActive({ graph }); |
||||||
|
|
||||||
|
// Drop sidebar item in canvas
|
||||||
|
const { onDrop, onDragenter, onDragover, onDragleave } = useCanvasDrop({ readonly, dragged: props.dragged, graph, container, projectCode }); |
||||||
|
|
||||||
|
return () => ( |
||||||
|
<div ref={container} class={Styles.canvas} onDrop={onDrop} onDragenter={onDragenter} onDragover={onDragover} onDragleave={onDragleave}> |
||||||
|
<div ref={paper} class={Styles.paper}></div> |
||||||
|
<div ref={minimap} class={Styles.minimap}></div> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
}) |
@ -0,0 +1,339 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
export const X6_NODE_NAME = 'dag-task' |
||||||
|
export const X6_EDGE_NAME = 'dag-edge' |
||||||
|
export const X6_PORT_OUT_NAME = 'dag-port-out' |
||||||
|
|
||||||
|
const EDGE_COLOR = '#999999' |
||||||
|
const BG_BLUE = '#DFE9F7' |
||||||
|
const BG_WHITE = '#FFFFFF' |
||||||
|
const NODE_BORDER = '#CCCCCC' |
||||||
|
const TITLE = '#333333' |
||||||
|
const STROKE_BLUE = '#288FFF' |
||||||
|
const NODE_SHADOW = 'drop-shadow(3px 3px 4px rgba(0, 0, 0, 0.2))' |
||||||
|
const EDGE_SHADOW = 'drop-shadow(3px 3px 2px rgba(0, 0, 0, 0.2))' |
||||||
|
|
||||||
|
export const PORT = { |
||||||
|
groups: { |
||||||
|
[X6_PORT_OUT_NAME]: { |
||||||
|
position: { |
||||||
|
name: 'absolute', |
||||||
|
args: { |
||||||
|
x: 200, |
||||||
|
y: 24 |
||||||
|
} |
||||||
|
}, |
||||||
|
markup: [ |
||||||
|
{ |
||||||
|
tagName: 'g', |
||||||
|
selector: 'body', |
||||||
|
children: [ |
||||||
|
{ |
||||||
|
tagName: 'circle', |
||||||
|
selector: 'circle-outer' |
||||||
|
}, |
||||||
|
{ |
||||||
|
tagName: 'text', |
||||||
|
selector: 'plus-text' |
||||||
|
}, |
||||||
|
{ |
||||||
|
tagName: 'circle', |
||||||
|
selector: 'circle-inner' |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
], |
||||||
|
attrs: { |
||||||
|
body: { |
||||||
|
magnet: true |
||||||
|
}, |
||||||
|
'plus-text': { |
||||||
|
fontSize: 12, |
||||||
|
fill: NODE_BORDER, |
||||||
|
text: '+', |
||||||
|
textAnchor: 'middle', |
||||||
|
x: 0, |
||||||
|
y: 3 |
||||||
|
}, |
||||||
|
'circle-outer': { |
||||||
|
stroke: NODE_BORDER, |
||||||
|
strokeWidth: 1, |
||||||
|
r: 6, |
||||||
|
fill: BG_WHITE |
||||||
|
}, |
||||||
|
'circle-inner': { |
||||||
|
r: 4, |
||||||
|
fill: 'transparent' |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const PORT_HOVER = { |
||||||
|
groups: { |
||||||
|
[X6_PORT_OUT_NAME]: { |
||||||
|
attrs: { |
||||||
|
'circle-outer': { |
||||||
|
stroke: STROKE_BLUE, |
||||||
|
fill: BG_BLUE, |
||||||
|
r: 8 |
||||||
|
}, |
||||||
|
'circle-inner': { |
||||||
|
fill: STROKE_BLUE, |
||||||
|
r: 6 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const PORT_SELECTED = { |
||||||
|
groups: { |
||||||
|
[X6_PORT_OUT_NAME]: { |
||||||
|
attrs: { |
||||||
|
'plus-text': { |
||||||
|
fill: STROKE_BLUE |
||||||
|
}, |
||||||
|
'circle-outer': { |
||||||
|
stroke: STROKE_BLUE, |
||||||
|
fill: BG_WHITE |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const NODE_STATUS_MARKUP = [{ |
||||||
|
tagName: 'foreignObject', |
||||||
|
selector: 'fo', |
||||||
|
children: [ |
||||||
|
{ |
||||||
|
tagName: 'body', |
||||||
|
selector: 'fo-body', |
||||||
|
ns: 'http://www.w3.org/1999/xhtml', |
||||||
|
children: [{ |
||||||
|
tagName: 'div', |
||||||
|
selector: 'status' |
||||||
|
}] |
||||||
|
} |
||||||
|
] |
||||||
|
}] |
||||||
|
|
||||||
|
export const NODE = { |
||||||
|
width: 220, |
||||||
|
height: 48, |
||||||
|
markup: [ |
||||||
|
{ |
||||||
|
tagName: 'rect', |
||||||
|
selector: 'body', |
||||||
|
className: 'dag-task-body' |
||||||
|
}, |
||||||
|
{ |
||||||
|
tagName: 'image', |
||||||
|
selector: 'image' |
||||||
|
}, |
||||||
|
{ |
||||||
|
tagName: 'text', |
||||||
|
selector: 'title' |
||||||
|
} |
||||||
|
], |
||||||
|
attrs: { |
||||||
|
body: { |
||||||
|
refWidth: '100%', |
||||||
|
refHeight: '100%', |
||||||
|
rx: 6, |
||||||
|
ry: 6, |
||||||
|
pointerEvents: 'visiblePainted', |
||||||
|
fill: BG_WHITE, |
||||||
|
stroke: NODE_BORDER, |
||||||
|
strokeWidth: 1, |
||||||
|
strokeDasharray: 'none', |
||||||
|
filter: 'none' |
||||||
|
}, |
||||||
|
image: { |
||||||
|
width: 30, |
||||||
|
height: 30, |
||||||
|
refX: 12, |
||||||
|
refY: 9 |
||||||
|
}, |
||||||
|
title: { |
||||||
|
refX: 45, |
||||||
|
refY: 18, |
||||||
|
fontFamily: 'Microsoft Yahei', |
||||||
|
fontSize: 12, |
||||||
|
fontWeight: 'bold', |
||||||
|
fill: TITLE, |
||||||
|
strokeWidth: 0 |
||||||
|
}, |
||||||
|
fo: { |
||||||
|
refX: '46%', |
||||||
|
refY: -25, |
||||||
|
width: 18, |
||||||
|
height: 18 |
||||||
|
} |
||||||
|
}, |
||||||
|
ports: { |
||||||
|
...PORT, |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
id: X6_PORT_OUT_NAME, |
||||||
|
group: X6_PORT_OUT_NAME |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const NODE_HOVER = { |
||||||
|
attrs: { |
||||||
|
body: { |
||||||
|
fill: BG_BLUE, |
||||||
|
stroke: STROKE_BLUE, |
||||||
|
strokeDasharray: '5,2' |
||||||
|
}, |
||||||
|
title: { |
||||||
|
fill: STROKE_BLUE |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const NODE_SELECTED = { |
||||||
|
attrs: { |
||||||
|
body: { |
||||||
|
filter: NODE_SHADOW, |
||||||
|
fill: BG_WHITE, |
||||||
|
stroke: STROKE_BLUE, |
||||||
|
strokeDasharray: '5,2', |
||||||
|
strokeWidth: '1.5' |
||||||
|
}, |
||||||
|
title: { |
||||||
|
fill: STROKE_BLUE |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const EDGE = { |
||||||
|
attrs: { |
||||||
|
line: { |
||||||
|
stroke: EDGE_COLOR, |
||||||
|
strokeWidth: 1, |
||||||
|
targetMarker: { |
||||||
|
tagName: 'path', |
||||||
|
fill: EDGE_COLOR, |
||||||
|
strokeWidth: 0, |
||||||
|
d: 'M 6 -3 0 0 6 3 Z' |
||||||
|
}, |
||||||
|
filter: 'none' |
||||||
|
} |
||||||
|
}, |
||||||
|
connector: { |
||||||
|
name: 'rounded' |
||||||
|
}, |
||||||
|
router: { |
||||||
|
name: 'manhattan', |
||||||
|
args: { |
||||||
|
endDirections: ['top', 'bottom', 'left'] |
||||||
|
} |
||||||
|
}, |
||||||
|
defaultLabel: { |
||||||
|
markup: [ |
||||||
|
{ |
||||||
|
tagName: 'rect', |
||||||
|
selector: 'body' |
||||||
|
}, |
||||||
|
{ |
||||||
|
tagName: 'text', |
||||||
|
selector: 'label' |
||||||
|
} |
||||||
|
], |
||||||
|
attrs: { |
||||||
|
label: { |
||||||
|
fill: EDGE_COLOR, |
||||||
|
fontSize: 14, |
||||||
|
textAnchor: 'middle', |
||||||
|
textVerticalAnchor: 'middle', |
||||||
|
pointerEvents: 'none' |
||||||
|
}, |
||||||
|
body: { |
||||||
|
ref: 'label', |
||||||
|
fill: BG_WHITE, |
||||||
|
stroke: EDGE_COLOR, |
||||||
|
strokeWidth: 1, |
||||||
|
rx: 4, |
||||||
|
ry: 4, |
||||||
|
refWidth: '140%', |
||||||
|
refHeight: '140%', |
||||||
|
refX: '-20%', |
||||||
|
refY: '-20%' |
||||||
|
} |
||||||
|
}, |
||||||
|
position: { |
||||||
|
distance: 0.5, |
||||||
|
options: { |
||||||
|
absoluteDistance: true, |
||||||
|
reverseDistance: true |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const EDGE_HOVER = { |
||||||
|
attrs: { |
||||||
|
line: { |
||||||
|
stroke: STROKE_BLUE, |
||||||
|
targetMarker: { |
||||||
|
fill: STROKE_BLUE |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
defaultLabel: { |
||||||
|
attrs: { |
||||||
|
label: { |
||||||
|
fill: STROKE_BLUE |
||||||
|
}, |
||||||
|
body: { |
||||||
|
fill: BG_WHITE, |
||||||
|
stroke: STROKE_BLUE |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const EDGE_SELECTED = { |
||||||
|
attrs: { |
||||||
|
line: { |
||||||
|
stroke: STROKE_BLUE, |
||||||
|
targetMarker: { |
||||||
|
fill: STROKE_BLUE |
||||||
|
}, |
||||||
|
strokeWidth: 2, |
||||||
|
filter: EDGE_SHADOW |
||||||
|
} |
||||||
|
}, |
||||||
|
defaultLabel: { |
||||||
|
attrs: { |
||||||
|
label: { |
||||||
|
fill: STROKE_BLUE |
||||||
|
}, |
||||||
|
body: { |
||||||
|
fill: BG_WHITE, |
||||||
|
stroke: STROKE_BLUE |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import { useCanvasInit } from './use-canvas-init'; |
||||||
|
import { useGraphOperations } from './use-graph-operations'; |
||||||
|
import { useCellActive } from './use-cell-active'; |
||||||
|
import { useSidebarDrag } from './use-sidebar-drag'; |
||||||
|
import { useCanvasDrop } from './use-canvas-drop'; |
||||||
|
import { useNodeSearch } from './use-node-search'; |
||||||
|
|
||||||
|
export { |
||||||
|
useCanvasInit, |
||||||
|
useGraphOperations, |
||||||
|
useCellActive, |
||||||
|
useSidebarDrag, |
||||||
|
useCanvasDrop, |
||||||
|
useNodeSearch, |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import type { PropType, Ref } from 'vue'; |
||||||
|
import type { Dragged } from './dag'; |
||||||
|
import { defineComponent, ref, inject } from 'vue' |
||||||
|
import { ALL_TASK_TYPES } from '../task/config'; |
||||||
|
import { useSidebarDrag } from './dag-hooks'; |
||||||
|
import Styles from './dag.module.scss'; |
||||||
|
|
||||||
|
const props = { |
||||||
|
dragged: { |
||||||
|
type: Object as PropType<Ref<Dragged>>, |
||||||
|
default: ref({ |
||||||
|
x: 0, |
||||||
|
y: 0, |
||||||
|
type: '' |
||||||
|
}), |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
name: "workflow-dag-sidebar", |
||||||
|
props, |
||||||
|
setup(props) { |
||||||
|
const readonly = inject('readonly', ref(false)) |
||||||
|
const dragged = props.dragged; |
||||||
|
const { onDragStart } = useSidebarDrag({ |
||||||
|
readonly, |
||||||
|
dragged |
||||||
|
}); |
||||||
|
const allTaskTypes = Object.keys(ALL_TASK_TYPES).map(type => ({ |
||||||
|
type, |
||||||
|
...ALL_TASK_TYPES[type] |
||||||
|
})); |
||||||
|
|
||||||
|
return () => ( |
||||||
|
<div class={Styles.sidebar}> |
||||||
|
{ |
||||||
|
allTaskTypes.map(task => ( |
||||||
|
<div |
||||||
|
class={Styles.draggable} |
||||||
|
draggable="true" |
||||||
|
onDragstart={(e) => onDragStart(e, task.type)} |
||||||
|
> |
||||||
|
<em class={`${Styles['sidebar-icon']} ${Styles['icon-' + task.type.toLocaleLowerCase()]}`}></em> |
||||||
|
<span>{task.alias}</span> |
||||||
|
</div> |
||||||
|
)) |
||||||
|
} |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
}) |
@ -0,0 +1,180 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import { defineComponent, ref, inject } from 'vue' |
||||||
|
import { useI18n } from 'vue-i18n' |
||||||
|
import Styles from './dag.module.scss' |
||||||
|
import { NTooltip, NIcon, NButton, NSelect } from 'naive-ui'; |
||||||
|
import { SearchOutlined, DownloadOutlined, FullscreenOutlined, FullscreenExitOutlined, InfoCircleOutlined, FormatPainterOutlined } from '@vicons/antd'; |
||||||
|
import { useNodeSearch } from './dag-hooks'; |
||||||
|
import { DataUri } from '@antv/x6' |
||||||
|
import { useFullscreen } from '@vueuse/core'; |
||||||
|
import { useRouter } from 'vue-router'; |
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
name: "workflow-dag-toolbar", |
||||||
|
setup(props, context) { |
||||||
|
const { t } = useI18n(); |
||||||
|
const graph = inject('graph', ref()); |
||||||
|
const router = useRouter(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Node search and navigate |
||||||
|
*/ |
||||||
|
const { |
||||||
|
searchNode, |
||||||
|
getAllNodes, |
||||||
|
allNodes, |
||||||
|
toggleSearchInput, |
||||||
|
searchInputVisible |
||||||
|
} = useNodeSearch({ graph }); |
||||||
|
|
||||||
|
/** |
||||||
|
* Download Workflow Image |
||||||
|
* @param {string} fileName |
||||||
|
* @param {string} bgColor
|
||||||
|
*/ |
||||||
|
const downloadPNG = (options = { fileName: 'dag', bgColor: '#f2f3f7' }) => { |
||||||
|
const { fileName, bgColor } = options; |
||||||
|
graph.value?.toPNG( |
||||||
|
(dataUri: string) => { |
||||||
|
DataUri.downloadDataUri(dataUri, `${fileName}.png`) |
||||||
|
}, |
||||||
|
{ |
||||||
|
padding: { |
||||||
|
top: 50, |
||||||
|
right: 50, |
||||||
|
bottom: 50, |
||||||
|
left: 50 |
||||||
|
}, |
||||||
|
backgroundColor: bgColor |
||||||
|
} |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Toggle fullscreen |
||||||
|
*/ |
||||||
|
const { isFullscreen, toggle } = useFullscreen(); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Open workflow version modal |
||||||
|
*/ |
||||||
|
const openVersionModal = () => { |
||||||
|
//TODO, same as the version popup in the workflow list page
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Open DAG format modal |
||||||
|
*/ |
||||||
|
const openDagFormatModal = () => { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
const onClose = () => { |
||||||
|
router.go(-1) |
||||||
|
} |
||||||
|
|
||||||
|
return () => ( |
||||||
|
<div class={Styles.toolbar}> |
||||||
|
<span class={Styles['workflow-name']}>{t("project.dag.createWorkflow")}</span> |
||||||
|
<div class={Styles['toolbar-right-part']}> |
||||||
|
{/* Search node */} |
||||||
|
<NTooltip v-slots={{ |
||||||
|
trigger: () => ( |
||||||
|
<NButton class={Styles['toolbar-right-item']} strong secondary circle type="info" onClick={toggleSearchInput} v-slots={{ |
||||||
|
icon: () => ( |
||||||
|
<NIcon> |
||||||
|
<SearchOutlined /> |
||||||
|
</NIcon> |
||||||
|
) |
||||||
|
}} /> |
||||||
|
), |
||||||
|
default: () => t('project.dag.search') |
||||||
|
}}> |
||||||
|
</NTooltip> |
||||||
|
<div |
||||||
|
class={`${Styles['toolbar-right-item']} ${Styles['node-selector']} ${searchInputVisible.value ? Styles['visible'] : ''}`} |
||||||
|
> |
||||||
|
<NSelect size="small" options={allNodes.value} onFocus={getAllNodes} onUpdateValue={searchNode} filterable /> |
||||||
|
</div> |
||||||
|
{/* Download workflow PNG */} |
||||||
|
<NTooltip v-slots={{ |
||||||
|
trigger: () => ( |
||||||
|
<NButton class={Styles['toolbar-right-item']} strong secondary circle type="info" onClick={() => downloadPNG()} v-slots={{ |
||||||
|
icon: () => ( |
||||||
|
<NIcon> |
||||||
|
<DownloadOutlined /> |
||||||
|
</NIcon> |
||||||
|
) |
||||||
|
}} /> |
||||||
|
), |
||||||
|
default: () => t('project.dag.download_png') |
||||||
|
}}> |
||||||
|
</NTooltip> |
||||||
|
{/* Toggle fullscreen */} |
||||||
|
<NTooltip v-slots={{ |
||||||
|
trigger: () => ( |
||||||
|
<NButton class={Styles['toolbar-right-item']} strong secondary circle type="info" onClick={toggle} v-slots={{ |
||||||
|
icon: () => ( |
||||||
|
<NIcon> |
||||||
|
{isFullscreen.value ? <FullscreenExitOutlined /> : <FullscreenOutlined />} |
||||||
|
</NIcon> |
||||||
|
) |
||||||
|
}} /> |
||||||
|
), |
||||||
|
default: () => isFullscreen.value ? t('project.dag.fullscreen_close') : t('project.dag.fullscreen_open') |
||||||
|
}}> |
||||||
|
</NTooltip> |
||||||
|
{/* DAG Format */} |
||||||
|
<NTooltip v-slots={{ |
||||||
|
trigger: () => ( |
||||||
|
<NButton class={Styles['toolbar-right-item']} strong secondary circle type="info" onClick={openDagFormatModal} v-slots={{ |
||||||
|
icon: () => ( |
||||||
|
<NIcon> |
||||||
|
<FormatPainterOutlined /> |
||||||
|
</NIcon> |
||||||
|
) |
||||||
|
}} /> |
||||||
|
), |
||||||
|
default: () => t('project.dag.format') |
||||||
|
}}> |
||||||
|
</NTooltip> |
||||||
|
{/* Version info */} |
||||||
|
<NTooltip v-slots={{ |
||||||
|
trigger: () => ( |
||||||
|
<NButton class={Styles['toolbar-right-item']} strong secondary circle type="info" onClick={openVersionModal} v-slots={{ |
||||||
|
icon: () => ( |
||||||
|
<NIcon> |
||||||
|
<InfoCircleOutlined /> |
||||||
|
</NIcon> |
||||||
|
) |
||||||
|
}} /> |
||||||
|
), |
||||||
|
default: () => t('project.dag.workflow_version') |
||||||
|
}}> |
||||||
|
</NTooltip> |
||||||
|
{/* Save workflow */} |
||||||
|
<NButton class={Styles['toolbar-right-item']} type="info" secondary round>{t('project.dag.save')}</NButton> |
||||||
|
{/* Return to previous page */} |
||||||
|
<NButton secondary round onClick={onClose}>{t('project.dag.close')}</NButton> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
}) |
@ -0,0 +1,217 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
$blue: #288fff; |
||||||
|
$blueBg: rgba(40, 143, 255, 0.1); |
||||||
|
$toolbarHeight: 50px; |
||||||
|
|
||||||
|
.dag { |
||||||
|
height: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
.content { |
||||||
|
display: flex; |
||||||
|
height: calc(100% - $toolbarHeight - 20px); |
||||||
|
margin-top: 20px; |
||||||
|
} |
||||||
|
|
||||||
|
.toolbar { |
||||||
|
height: $toolbarHeight; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
padding: 0 20px; |
||||||
|
border: 1px solid var(--n-border-color); |
||||||
|
border-radius: 4px; |
||||||
|
justify-content: space-between; |
||||||
|
} |
||||||
|
|
||||||
|
.canvas { |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
position: relative; |
||||||
|
overflow: hidden; |
||||||
|
display: flex; |
||||||
|
} |
||||||
|
|
||||||
|
.paper { |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
.sidebar { |
||||||
|
width: 190px; |
||||||
|
height: 100%; |
||||||
|
margin-right: 20px; |
||||||
|
} |
||||||
|
|
||||||
|
.workflow-name { |
||||||
|
font-size: 14px; |
||||||
|
} |
||||||
|
|
||||||
|
.draggable { |
||||||
|
display: flex; |
||||||
|
width: 100%; |
||||||
|
height: 32px; |
||||||
|
margin-bottom: 10px; |
||||||
|
align-items: center; |
||||||
|
border: 1px solid var(--n-border-color); |
||||||
|
padding: 0 10px; |
||||||
|
border-radius: 4px; |
||||||
|
transform: translate(0, 0); |
||||||
|
box-sizing: border-box; |
||||||
|
cursor: move; |
||||||
|
font-size: 12px; |
||||||
|
|
||||||
|
.sidebar-icon { |
||||||
|
display: block; |
||||||
|
width: 18px; |
||||||
|
height: 18px; |
||||||
|
background-size: 100% 100%; |
||||||
|
margin-right: 10px; |
||||||
|
&.icon-shell { |
||||||
|
background-image: url("../../../assets/images/task-icons/shell.png"); |
||||||
|
} |
||||||
|
&.icon-sub_process { |
||||||
|
background-image: url("../../../assets/images/task-icons/sub_process.png"); |
||||||
|
} |
||||||
|
&.icon-procedure { |
||||||
|
background-image: url("../../../assets/images/task-icons/procedure.png"); |
||||||
|
} |
||||||
|
&.icon-sql { |
||||||
|
background-image: url("../../../assets/images/task-icons/sql.png"); |
||||||
|
} |
||||||
|
&.icon-flink { |
||||||
|
background-image: url("../../../assets/images/task-icons/flink.png"); |
||||||
|
} |
||||||
|
&.icon-mr { |
||||||
|
background-image: url("../../../assets/images/task-icons/mr.png"); |
||||||
|
} |
||||||
|
&.icon-python { |
||||||
|
background-image: url("../../../assets/images/task-icons/python.png"); |
||||||
|
} |
||||||
|
&.icon-dependent { |
||||||
|
background-image: url("../../../assets/images/task-icons/dependent.png"); |
||||||
|
} |
||||||
|
&.icon-http { |
||||||
|
background-image: url("../../../assets/images/task-icons/http.png"); |
||||||
|
} |
||||||
|
&.icon-datax { |
||||||
|
background-image: url("../../../assets/images/task-icons/datax.png"); |
||||||
|
} |
||||||
|
&.icon-pigeon { |
||||||
|
background-image: url("../../../assets/images/task-icons/pigeon.png"); |
||||||
|
} |
||||||
|
&.icon-sqoop { |
||||||
|
background-image: url("../../../assets/images/task-icons/sqoop.png"); |
||||||
|
} |
||||||
|
&.icon-conditions { |
||||||
|
background-image: url("../../../assets/images/task-icons/conditions.png"); |
||||||
|
} |
||||||
|
&.icon-seatunnel { |
||||||
|
background-image: url("../../../assets/images/task-icons/seatunnel.png"); |
||||||
|
} |
||||||
|
&.icon-spark { |
||||||
|
background-image: url("../../../assets/images/task-icons/spark.png"); |
||||||
|
} |
||||||
|
&.icon-switch { |
||||||
|
background-image: url("../../../assets/images/task-icons/switch.png"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
&:hover { |
||||||
|
color: $blue; |
||||||
|
border: 1px dashed $blue; |
||||||
|
background-color: $blueBg; |
||||||
|
.sidebar-icon { |
||||||
|
&.icon-shell { |
||||||
|
background-image: url("../../../assets/images/task-icons/shell_hover.png"); |
||||||
|
} |
||||||
|
&.icon-sub_process { |
||||||
|
background-image: url("../../../assets/images/task-icons/sub_process_hover.png"); |
||||||
|
} |
||||||
|
&.icon-procedure { |
||||||
|
background-image: url("../../../assets/images/task-icons/procedure_hover.png"); |
||||||
|
} |
||||||
|
&.icon-sql { |
||||||
|
background-image: url("../../../assets/images/task-icons/sql_hover.png"); |
||||||
|
} |
||||||
|
&.icon-flink { |
||||||
|
background-image: url("../../../assets/images/task-icons/flink_hover.png"); |
||||||
|
} |
||||||
|
&.icon-mr { |
||||||
|
background-image: url("../../../assets/images/task-icons/mr_hover.png"); |
||||||
|
} |
||||||
|
&.icon-python { |
||||||
|
background-image: url("../../../assets/images/task-icons/python_hover.png"); |
||||||
|
} |
||||||
|
&.icon-dependent { |
||||||
|
background-image: url("../../../assets/images/task-icons/dependent_hover.png"); |
||||||
|
} |
||||||
|
&.icon-http { |
||||||
|
background-image: url("../../../assets/images/task-icons/http_hover.png"); |
||||||
|
} |
||||||
|
&.icon-datax { |
||||||
|
background-image: url("../../../assets/images/task-icons/datax_hover.png"); |
||||||
|
} |
||||||
|
&.icon-pigeon { |
||||||
|
background-image: url("../../../assets/images/task-icons/pigeon_hover.png"); |
||||||
|
} |
||||||
|
&.icon-sqoop { |
||||||
|
background-image: url("../../../assets/images/task-icons/sqoop_hover.png"); |
||||||
|
} |
||||||
|
&.icon-conditions { |
||||||
|
background-image: url("../../../assets/images/task-icons/conditions_hover.png"); |
||||||
|
} |
||||||
|
&.icon-seatunnel { |
||||||
|
background-image: url("../../../assets/images/task-icons/seatunnel_hover.png"); |
||||||
|
} |
||||||
|
&.icon-spark { |
||||||
|
background-image: url("../../../assets/images/task-icons/spark_hover.png"); |
||||||
|
} |
||||||
|
&.icon-switch { |
||||||
|
background-image: url("../../../assets/images/task-icons/switch_hover.png"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.minimap { |
||||||
|
position: absolute; |
||||||
|
right: 0px; |
||||||
|
bottom: 0px; |
||||||
|
border: dashed 1px #e4e4e4; |
||||||
|
z-index: 9; |
||||||
|
} |
||||||
|
|
||||||
|
.toolbar-right-part { |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
.toolbar-right-item { |
||||||
|
margin-right: 10px; |
||||||
|
} |
||||||
|
.node-selector { |
||||||
|
width: 0; |
||||||
|
overflow: hidden; |
||||||
|
transition: all 0.5s; |
||||||
|
margin-right: 0; |
||||||
|
|
||||||
|
&.visible { |
||||||
|
width: 200px; |
||||||
|
margin-right: 10px; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import type { Graph } from '@antv/x6'; |
||||||
|
import { defineComponent, ref, provide } from 'vue' |
||||||
|
import DagToolbar from './dag-toolbar'; |
||||||
|
import DagCanvas from './dag-canvas'; |
||||||
|
import DagSidebar from './dag-sidebar'; |
||||||
|
import Styles from './dag.module.scss'; |
||||||
|
import "./x6-style.scss"; |
||||||
|
|
||||||
|
|
||||||
|
export interface Dragged { |
||||||
|
x: number; |
||||||
|
y: number; |
||||||
|
type: string; |
||||||
|
} |
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
name: "workflow-dag", |
||||||
|
setup(props, context) { |
||||||
|
|
||||||
|
// Whether the graph can be operated
|
||||||
|
const readonly = ref(false); |
||||||
|
provide('readonly', readonly); |
||||||
|
|
||||||
|
const graph = ref<Graph>(); |
||||||
|
provide('graph', graph); |
||||||
|
|
||||||
|
// The sidebar slots
|
||||||
|
const toolbarSlots = { |
||||||
|
left: context.slots.toolbarLeft, |
||||||
|
right: context.slots.toolbarRight |
||||||
|
} |
||||||
|
|
||||||
|
// The element currently being dragged up
|
||||||
|
const dragged = ref<Dragged>({ |
||||||
|
x: 0, |
||||||
|
y: 0, |
||||||
|
type: '' |
||||||
|
}); |
||||||
|
|
||||||
|
return () => ( |
||||||
|
<div class={Styles.dag}> |
||||||
|
<DagToolbar v-slots={toolbarSlots} /> |
||||||
|
<div class={Styles.content}> |
||||||
|
<DagSidebar dragged={dragged} /> |
||||||
|
<DagCanvas dragged={dragged} /> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
}) |
@ -0,0 +1,40 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import { ref, onMounted, Ref, onUnmounted } from 'vue' |
||||||
|
|
||||||
|
interface Options { |
||||||
|
// readonly: Ref<boolean>;
|
||||||
|
// canvas: Ref<HTMLElement | undefined>;
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Canvas Init |
||||||
|
* 1. Bind the graph to the dom |
||||||
|
* 2. Redraw when the page is resized |
||||||
|
* 3. Register custom graphics |
||||||
|
*/ |
||||||
|
export function useCanvasInit(options: Options) { |
||||||
|
|
||||||
|
// Whether the graph can be operated
|
||||||
|
const { } = options; |
||||||
|
|
||||||
|
|
||||||
|
return { |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import type { Ref } from 'vue'; |
||||||
|
import type { Graph } from '@antv/x6' |
||||||
|
import type { Dragged } from './dag' |
||||||
|
import { genTaskCodeList } from '@/service/modules/task-definition'; |
||||||
|
import { useGraphOperations } from './dag-hooks'; |
||||||
|
|
||||||
|
interface Options { |
||||||
|
readonly: Ref<boolean>; |
||||||
|
graph: Ref<Graph | undefined>; |
||||||
|
container: Ref<HTMLElement | undefined>; |
||||||
|
dragged: Ref<Dragged>; |
||||||
|
projectCode: string; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Drop sidebar item in canvas |
||||||
|
*/ |
||||||
|
export function useCanvasDrop(options: Options) { |
||||||
|
|
||||||
|
const { readonly, graph, container, dragged, projectCode } = options; |
||||||
|
|
||||||
|
const { addNode } = useGraphOperations({ graph }); |
||||||
|
|
||||||
|
const onDrop = (e: DragEvent) => { |
||||||
|
e.stopPropagation(); |
||||||
|
e.preventDefault(); |
||||||
|
if (readonly.value) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (dragged.value && graph.value && container.value && projectCode) { |
||||||
|
const { type, x: eX, y: eY } = dragged.value; |
||||||
|
const { x, y } = graph.value.clientToLocal(e.clientX, e.clientY); |
||||||
|
const genNums = 1; |
||||||
|
genTaskCodeList(genNums, Number(projectCode)) |
||||||
|
.then((res) => { |
||||||
|
const [code] = res |
||||||
|
addNode(code + '', type, { x: x - eX, y: y - eY }) |
||||||
|
// openTaskConfigModel(code, type)
|
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const preventDefault = (e: DragEvent) => { |
||||||
|
e.preventDefault(); |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
onDrop, |
||||||
|
onDragenter: preventDefault, |
||||||
|
onDragover: preventDefault, |
||||||
|
onDragleave: preventDefault, |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,177 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import type { Node } from '@antv/x6'; |
||||||
|
import { ref, onMounted, Ref, onUnmounted } from 'vue' |
||||||
|
import { Graph } from '@antv/x6' |
||||||
|
import { |
||||||
|
NODE, |
||||||
|
EDGE, |
||||||
|
X6_NODE_NAME, |
||||||
|
X6_EDGE_NAME, |
||||||
|
} from './dag-config' |
||||||
|
import { debounce } from 'lodash'; |
||||||
|
|
||||||
|
interface Options { |
||||||
|
readonly: Ref<boolean>; |
||||||
|
graph: Ref<Graph | undefined>; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Canvas Init |
||||||
|
* 1. Bind the graph to the dom |
||||||
|
* 2. Redraw when the page is resized |
||||||
|
* 3. Register custom graphics |
||||||
|
*/ |
||||||
|
export function useCanvasInit(options: Options) { |
||||||
|
|
||||||
|
// Whether the graph can be operated
|
||||||
|
const { readonly, graph } = options; |
||||||
|
|
||||||
|
const paper = ref<HTMLElement>(); // The graph mount HTMLElement
|
||||||
|
const minimap = ref<HTMLElement>(); // The minimap mount HTMLElement
|
||||||
|
const container = ref<HTMLElement>(); // The container of paper and minimap
|
||||||
|
|
||||||
|
/** |
||||||
|
* Graph Init, bind graph to the dom |
||||||
|
*/ |
||||||
|
function graphInit() { |
||||||
|
return new Graph({ |
||||||
|
container: paper.value, |
||||||
|
selecting: { |
||||||
|
enabled: true, |
||||||
|
multiple: true, |
||||||
|
rubberband: true, |
||||||
|
rubberEdge: true, |
||||||
|
movable: true, |
||||||
|
showNodeSelectionBox: false |
||||||
|
}, |
||||||
|
scaling: { |
||||||
|
min: 0.2, |
||||||
|
max: 2 |
||||||
|
}, |
||||||
|
mousewheel: { |
||||||
|
enabled: true, |
||||||
|
modifiers: ['ctrl', 'meta'] |
||||||
|
}, |
||||||
|
scroller: true, |
||||||
|
grid: { |
||||||
|
size: 10, |
||||||
|
visible: true |
||||||
|
}, |
||||||
|
snapline: true, |
||||||
|
minimap: { |
||||||
|
enabled: true, |
||||||
|
container: minimap.value, |
||||||
|
scalable: false, |
||||||
|
width: 200, |
||||||
|
height: 120 |
||||||
|
}, |
||||||
|
interacting: { |
||||||
|
edgeLabelMovable: false, |
||||||
|
nodeMovable: !readonly.value, |
||||||
|
magnetConnectable: !readonly.value |
||||||
|
}, |
||||||
|
connecting: { |
||||||
|
// Whether multiple edges can be created between the same start node and end
|
||||||
|
allowMulti: false, |
||||||
|
// Whether a point is allowed to connect to a blank position on the canvas
|
||||||
|
allowBlank: false, |
||||||
|
// The start node and the end node are the same node
|
||||||
|
allowLoop: false, |
||||||
|
// Whether an edge is allowed to link to another edge
|
||||||
|
allowEdge: false, |
||||||
|
// Whether edges are allowed to link to nodes
|
||||||
|
allowNode: true, |
||||||
|
// Whether to allow edge links to ports
|
||||||
|
allowPort: false, |
||||||
|
// Whether all available ports or nodes are highlighted when you drag the edge
|
||||||
|
highlight: true, |
||||||
|
createEdge() { |
||||||
|
return graph.value?.createEdge({ shape: X6_EDGE_NAME }) |
||||||
|
} |
||||||
|
}, |
||||||
|
highlighting: { |
||||||
|
nodeAvailable: { |
||||||
|
name: 'className', |
||||||
|
args: { |
||||||
|
className: 'available' |
||||||
|
} |
||||||
|
}, |
||||||
|
magnetAvailable: { |
||||||
|
name: 'className', |
||||||
|
args: { |
||||||
|
className: 'available' |
||||||
|
} |
||||||
|
}, |
||||||
|
magnetAdsorbed: { |
||||||
|
name: 'className', |
||||||
|
args: { |
||||||
|
className: 'adsorbed' |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
onMounted(() => { |
||||||
|
graph.value = graphInit(); |
||||||
|
// Make sure the edge starts with node, not port
|
||||||
|
graph.value.on('edge:connected', ({ isNew, edge }) => { |
||||||
|
if (isNew) { |
||||||
|
const sourceNode = edge.getSourceNode() as Node |
||||||
|
edge.setSource(sourceNode) |
||||||
|
} |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
/** |
||||||
|
* Redraw when the page is resized |
||||||
|
*/ |
||||||
|
const paperResize = debounce(() => { |
||||||
|
if (!container.value) return; |
||||||
|
const w = container.value.offsetWidth |
||||||
|
const h = container.value.offsetHeight |
||||||
|
graph.value?.resize(w, h); |
||||||
|
}, 200) |
||||||
|
onMounted(() => { |
||||||
|
window.addEventListener('resize', paperResize) |
||||||
|
}) |
||||||
|
onUnmounted(() => { |
||||||
|
window.removeEventListener('resize', paperResize) |
||||||
|
}) |
||||||
|
|
||||||
|
/** |
||||||
|
* Register custom cells |
||||||
|
*/ |
||||||
|
function registerCustomCells() { |
||||||
|
Graph.unregisterNode(X6_NODE_NAME) |
||||||
|
Graph.unregisterEdge(X6_EDGE_NAME) |
||||||
|
Graph.registerNode(X6_NODE_NAME, { ...NODE }) |
||||||
|
Graph.registerEdge(X6_EDGE_NAME, { ...EDGE }) |
||||||
|
} |
||||||
|
onMounted(() => { |
||||||
|
registerCustomCells() |
||||||
|
}) |
||||||
|
|
||||||
|
return { |
||||||
|
graph, |
||||||
|
paper, |
||||||
|
minimap, |
||||||
|
container |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,154 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import type { Ref } from 'vue' |
||||||
|
import { onMounted, ref } from 'vue'; |
||||||
|
import type { Node, Graph, Edge, Cell } from '@antv/x6' |
||||||
|
import _ from 'lodash'; |
||||||
|
import { |
||||||
|
X6_PORT_OUT_NAME, |
||||||
|
PORT_HOVER, |
||||||
|
PORT_SELECTED, |
||||||
|
PORT, |
||||||
|
NODE, |
||||||
|
NODE_HOVER, |
||||||
|
NODE_SELECTED, |
||||||
|
EDGE, |
||||||
|
EDGE_SELECTED, |
||||||
|
EDGE_HOVER |
||||||
|
} from './dag-config'; |
||||||
|
|
||||||
|
interface Options { |
||||||
|
graph: Ref<Graph | undefined> |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Change the style on cell hover and select |
||||||
|
*/ |
||||||
|
export function useCellActive(options: Options) { |
||||||
|
|
||||||
|
const { graph } = options; |
||||||
|
const hoverCell = ref(); |
||||||
|
|
||||||
|
const isStatusIcon = (tagName: string) => { |
||||||
|
if (!tagName) return false; |
||||||
|
return tagName.toLocaleLowerCase() === 'em' || tagName.toLocaleLowerCase() === 'body' |
||||||
|
} |
||||||
|
|
||||||
|
function setEdgeStyle(edge: Edge) { |
||||||
|
const isHover = edge === hoverCell.value; |
||||||
|
const isSelected = graph.value?.isSelected(edge) |
||||||
|
// TODO
|
||||||
|
// const labelName = this.getEdgeLabelName ? this.getEdgeLabelName(edge) : ''
|
||||||
|
let edgeProps = null |
||||||
|
|
||||||
|
if (isHover) { |
||||||
|
edgeProps = _.merge(_.cloneDeep(EDGE), EDGE_HOVER) |
||||||
|
} else if (isSelected) { |
||||||
|
edgeProps = _.merge(_.cloneDeep(EDGE), EDGE_SELECTED) |
||||||
|
} else { |
||||||
|
edgeProps = _.cloneDeep(EDGE) |
||||||
|
} |
||||||
|
|
||||||
|
edge.setAttrs(edgeProps.attrs) |
||||||
|
edge.setLabels([ |
||||||
|
{ |
||||||
|
..._.merge( |
||||||
|
{ |
||||||
|
attrs: _.cloneDeep(edgeProps.defaultLabel.attrs) |
||||||
|
}, |
||||||
|
// {
|
||||||
|
// attrs: { label: { text: labelName } }
|
||||||
|
// }
|
||||||
|
) |
||||||
|
} |
||||||
|
]) |
||||||
|
} |
||||||
|
|
||||||
|
function setNodeStyle(node: Node) { |
||||||
|
const isHover = node === hoverCell.value |
||||||
|
const isSelected = graph.value?.isSelected(node) |
||||||
|
const portHover = _.cloneDeep(PORT_HOVER.groups[X6_PORT_OUT_NAME].attrs) |
||||||
|
const portSelected = _.cloneDeep(PORT_SELECTED.groups[X6_PORT_OUT_NAME].attrs) |
||||||
|
const portDefault = _.cloneDeep(PORT.groups[X6_PORT_OUT_NAME].attrs) |
||||||
|
const nodeHover = _.merge(_.cloneDeep(NODE.attrs), NODE_HOVER.attrs) |
||||||
|
const nodeSelected = _.merge(_.cloneDeep(NODE.attrs), NODE_SELECTED.attrs) |
||||||
|
|
||||||
|
let img = null |
||||||
|
let nodeAttrs = null |
||||||
|
let portAttrs = null |
||||||
|
|
||||||
|
if (isHover || isSelected) { |
||||||
|
img = `/src/assets/images/task-icons/${node.data.taskType.toLocaleLowerCase()}_hover.png` |
||||||
|
if (isHover) { |
||||||
|
nodeAttrs = nodeHover |
||||||
|
portAttrs = _.merge(portDefault, portHover) |
||||||
|
} else { |
||||||
|
nodeAttrs = nodeSelected |
||||||
|
portAttrs = _.merge(portDefault, portSelected) |
||||||
|
} |
||||||
|
} else { |
||||||
|
img = `/src/assets/images/task-icons/${node.data.taskType.toLocaleLowerCase()}.png` |
||||||
|
nodeAttrs = NODE.attrs |
||||||
|
portAttrs = portDefault |
||||||
|
} |
||||||
|
node.setAttrByPath('image/xlink:href', img) |
||||||
|
node.setAttrs(nodeAttrs) |
||||||
|
node.setPortProp( |
||||||
|
X6_PORT_OUT_NAME, |
||||||
|
'attrs', |
||||||
|
portAttrs |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
function updateCellStyle(cell: Cell) { |
||||||
|
if (cell.isEdge()) { |
||||||
|
setEdgeStyle(cell) |
||||||
|
} else if (cell.isNode()) { |
||||||
|
setNodeStyle(cell) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
onMounted(() => { |
||||||
|
if (graph.value) { |
||||||
|
// hover
|
||||||
|
graph.value.on('cell:mouseenter', (data) => { |
||||||
|
const { cell, e } = data |
||||||
|
if (!isStatusIcon(e.target.tagName)) { |
||||||
|
hoverCell.value = cell |
||||||
|
updateCellStyle(cell) |
||||||
|
} |
||||||
|
}) |
||||||
|
graph.value.on('cell:mouseleave', ({ cell }) => { |
||||||
|
hoverCell.value = undefined |
||||||
|
updateCellStyle(cell) |
||||||
|
}) |
||||||
|
|
||||||
|
// select
|
||||||
|
graph.value.on('cell:selected', ({ cell }) => { |
||||||
|
updateCellStyle(cell) |
||||||
|
}) |
||||||
|
graph.value.on('cell:unselected', ({ cell }) => { |
||||||
|
updateCellStyle(cell) |
||||||
|
}) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
return { |
||||||
|
hoverCell |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,157 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import type { Ref } from 'vue' |
||||||
|
import type { Node, Graph, Edge } from '@antv/x6' |
||||||
|
import { |
||||||
|
X6_NODE_NAME, |
||||||
|
X6_EDGE_NAME, |
||||||
|
} from './dag-config' |
||||||
|
import { ALL_TASK_TYPES } from '../task/config'; |
||||||
|
import utils from '@/utils'; |
||||||
|
|
||||||
|
interface Options { |
||||||
|
graph: Ref<Graph | undefined> |
||||||
|
} |
||||||
|
|
||||||
|
type Coordinate = { x: number; y: number; } |
||||||
|
|
||||||
|
/** |
||||||
|
* Expose some graph operation methods |
||||||
|
* @param {Options} options |
||||||
|
*/ |
||||||
|
export function useGraphOperations(options: Options) { |
||||||
|
|
||||||
|
const { graph } = options; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Build edge metadata |
||||||
|
* @param {string} sourceId |
||||||
|
* @param {string} targetId |
||||||
|
* @param {string} label |
||||||
|
*/ |
||||||
|
function buildEdgeMetadata(sourceId: string, targetId: string, label: string = ''): Edge.Metadata { |
||||||
|
return { |
||||||
|
shape: X6_EDGE_NAME, |
||||||
|
source: { |
||||||
|
cell: sourceId |
||||||
|
}, |
||||||
|
target: { |
||||||
|
cell: targetId |
||||||
|
}, |
||||||
|
labels: label ? [label] : undefined |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Build node metadata |
||||||
|
* @param {string} id |
||||||
|
* @param {string} taskType |
||||||
|
* @param {Coordinate} coordinate Default is { x: 100, y: 100 } |
||||||
|
*/ |
||||||
|
function buildNodeMetadata(id: string, type: string, taskName: string, coordinate: Coordinate = { x: 100, y: 100 }): Node.Metadata { |
||||||
|
const truncation = taskName ? utils.truncateText(taskName, 18) : id; |
||||||
|
return { |
||||||
|
id: id, |
||||||
|
shape: X6_NODE_NAME, |
||||||
|
x: coordinate.x, |
||||||
|
y: coordinate.y, |
||||||
|
data: { |
||||||
|
taskType: type, |
||||||
|
taskName: taskName || id |
||||||
|
}, |
||||||
|
attrs: { |
||||||
|
image: { |
||||||
|
// Use href instead of xlink:href, you may lose the icon when downloadPNG
|
||||||
|
'xlink:href': `/src/assets/images/task-icons/${type.toLocaleLowerCase()}.png` |
||||||
|
}, |
||||||
|
title: { |
||||||
|
text: truncation |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add a node to the graph |
||||||
|
* @param {string} id |
||||||
|
* @param {string} taskType |
||||||
|
* @param {Coordinate} coordinate Default is { x: 100, y: 100 } |
||||||
|
*/ |
||||||
|
function addNode(id: string, type: string, coordinate: Coordinate = { x: 100, y: 100 }) { |
||||||
|
if (!ALL_TASK_TYPES[type]) { |
||||||
|
console.warn(`taskType:${type} is invalid!`) |
||||||
|
return |
||||||
|
} |
||||||
|
const node = buildNodeMetadata(id, type, '', coordinate) |
||||||
|
graph.value?.addNode(node) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set node name by id |
||||||
|
* @param {string} id |
||||||
|
* @param {string} name |
||||||
|
*/ |
||||||
|
function setNodeName(id: string, newName: string) { |
||||||
|
const node = graph.value?.getCellById(id) |
||||||
|
if (node) { |
||||||
|
const truncation = utils.truncateText(newName, 18) |
||||||
|
node.attr('title/text', truncation) |
||||||
|
node.setData({ taskName: newName }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get nodes |
||||||
|
*/ |
||||||
|
function getNodes() { |
||||||
|
const nodes = graph.value?.getNodes() |
||||||
|
if (!nodes) return [] |
||||||
|
return nodes.map((node) => { |
||||||
|
const position = node.getPosition() |
||||||
|
const data = node.getData() |
||||||
|
return { |
||||||
|
code: node.id, |
||||||
|
position: position, |
||||||
|
name: data.taskName, |
||||||
|
type: data.taskType |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Navigate to cell |
||||||
|
* @param {string} code |
||||||
|
*/ |
||||||
|
function navigateTo(code: string) { |
||||||
|
if (!graph.value) return; |
||||||
|
const cell = graph.value.getCellById(code) |
||||||
|
graph.value.scrollToCell(cell, { animation: { duration: 600 } }) |
||||||
|
graph.value.cleanSelection() |
||||||
|
graph.value.select(cell) |
||||||
|
}; |
||||||
|
|
||||||
|
return { |
||||||
|
buildEdgeMetadata, |
||||||
|
buildNodeMetadata, |
||||||
|
addNode, |
||||||
|
setNodeName, |
||||||
|
getNodes, |
||||||
|
navigateTo, |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import type { Graph } from '@antv/x6'; |
||||||
|
import { ref, Ref } from 'vue' |
||||||
|
import { useGraphOperations } from './dag-hooks'; |
||||||
|
|
||||||
|
|
||||||
|
interface Options { |
||||||
|
graph: Ref<Graph | undefined>; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Node search and navigate |
||||||
|
*/ |
||||||
|
export function useNodeSearch(options: Options) { |
||||||
|
|
||||||
|
const { graph } = options; |
||||||
|
|
||||||
|
const searchInputVisible = ref(false); |
||||||
|
const allNodes = ref<any>([]); |
||||||
|
const toggleSearchInput = () => { |
||||||
|
searchInputVisible.value = !searchInputVisible.value; |
||||||
|
} |
||||||
|
const { getNodes, navigateTo } = useGraphOperations({ graph }); |
||||||
|
const searchNode = (val: string) => { |
||||||
|
navigateTo(val) |
||||||
|
} |
||||||
|
const getAllNodes = () => { |
||||||
|
const nodes = getNodes(); |
||||||
|
allNodes.value = nodes.map(node => { |
||||||
|
return { |
||||||
|
label: node.name, |
||||||
|
value: node.code |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
searchNode, |
||||||
|
getAllNodes, |
||||||
|
allNodes, |
||||||
|
toggleSearchInput, |
||||||
|
searchInputVisible, |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import type { Ref } from 'vue'; |
||||||
|
import type { Dragged } from './dag'; |
||||||
|
|
||||||
|
interface Options { |
||||||
|
readonly: Ref<boolean>; |
||||||
|
dragged: Ref<Dragged>; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sidebar drag |
||||||
|
*/ |
||||||
|
export function useSidebarDrag(options: Options) { |
||||||
|
|
||||||
|
const { readonly, dragged } = options; |
||||||
|
|
||||||
|
const onDragStart = (e: DragEvent, type: string) => { |
||||||
|
if (readonly.value) { |
||||||
|
e.preventDefault() |
||||||
|
return |
||||||
|
} |
||||||
|
dragged.value = { |
||||||
|
x: e.offsetX, |
||||||
|
y: e.offsetY, |
||||||
|
type: type |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
onDragStart |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
.container{ |
||||||
|
width: 100%; |
||||||
|
box-sizing: border-box; |
||||||
|
height: calc(100vh - 100px); |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import { defineComponent } from 'vue' |
||||||
|
import Dag from './dag'; |
||||||
|
import { NCard } from 'naive-ui'; |
||||||
|
import styles from './workflow-definition-create.module.scss'; |
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
name: "WorkflowDefinitionCreate", |
||||||
|
setup() { |
||||||
|
|
||||||
|
const slots = { |
||||||
|
toolbarLeft: () => <span>left-operations</span>, |
||||||
|
toolbarRight: () => <span>right-operations</span> |
||||||
|
}; |
||||||
|
|
||||||
|
return () => ( |
||||||
|
<NCard class={styles.container}> |
||||||
|
<Dag v-slots={slots} /> |
||||||
|
</NCard> |
||||||
|
) |
||||||
|
} |
||||||
|
}) |
@ -0,0 +1,27 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import { defineComponent } from 'vue' |
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
name: "WorkflowDefinitionDetails", |
||||||
|
setup() { |
||||||
|
return () => ( |
||||||
|
<div>WorkflowDefinitionDetails</div> |
||||||
|
) |
||||||
|
} |
||||||
|
}) |
@ -0,0 +1,27 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import { defineComponent } from 'vue' |
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
name: "WorkflowDefinitionList", |
||||||
|
setup() { |
||||||
|
return () => ( |
||||||
|
<div>WorkflowDefinitionList</div> |
||||||
|
) |
||||||
|
} |
||||||
|
}) |
@ -0,0 +1,27 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import { defineComponent } from 'vue' |
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
name: "WorkflowInstanceDetails", |
||||||
|
setup() { |
||||||
|
return () => ( |
||||||
|
<div>WorkflowInstanceDetails</div> |
||||||
|
) |
||||||
|
} |
||||||
|
}) |
@ -0,0 +1,27 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
import { defineComponent } from 'vue' |
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
name: "WorkflowInstanceList", |
||||||
|
setup() { |
||||||
|
return () => ( |
||||||
|
<div>WorkflowInstanceList</div> |
||||||
|
) |
||||||
|
} |
||||||
|
}) |
@ -0,0 +1,35 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0 |
||||||
|
* (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
$STROKE_BLUE: #288fff; |
||||||
|
$BG_WHITE: #ffffff; |
||||||
|
|
||||||
|
.x6-node[data-shape="dag-task"] { |
||||||
|
&.available { |
||||||
|
.dag-task-body { |
||||||
|
stroke: $STROKE_BLUE; |
||||||
|
stroke-width: 1; |
||||||
|
stroke-dasharray: 5, 2; |
||||||
|
} |
||||||
|
&.adsorbed { |
||||||
|
.dag-task-body { |
||||||
|
stroke-width: 3; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|