* refactor of dolphin-scheduler-ui/dag * copy task feature and code optimization * fix review & ut bugs * add newline at end of file * add license header Co-authored-by: chenxiwei <cxwbeta@139.com> Co-authored-by: chenxiwei <chenxiwei_yewu@cmss.chinamobile.com>2.0.7-release
@ -0,0 +1,52 @@ |
|||||||
|
/* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
.dag-canvas { |
||||||
|
display: flex; |
||||||
|
overflow: hidden; |
||||||
|
height: calc(100% - 50px); |
||||||
|
padding: 10px 0 0 0; |
||||||
|
box-sizing: border-box; |
||||||
|
|
||||||
|
.dag-container { |
||||||
|
height: 100%; |
||||||
|
flex: 1; |
||||||
|
overflow: hidden; |
||||||
|
position: relative; |
||||||
|
|
||||||
|
.paper { |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
.minimap { |
||||||
|
position: absolute; |
||||||
|
width: 300px; |
||||||
|
height: 200px; |
||||||
|
right: 10px; |
||||||
|
bottom: 10px; |
||||||
|
border: dashed 1px #e4e4e4; |
||||||
|
z-index: 9; |
||||||
|
} |
||||||
|
|
||||||
|
.context-menu{ |
||||||
|
position: absolute; |
||||||
|
left: 100px; |
||||||
|
top: 100px; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,739 @@ |
|||||||
|
/* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
<template> |
||||||
|
<div class="dag-canvas"> |
||||||
|
<dag-taskbar @on-drag-start="_onDragStart" /> |
||||||
|
<div |
||||||
|
class="dag-container" |
||||||
|
ref="container" |
||||||
|
@dragenter.prevent |
||||||
|
@dragover.prevent |
||||||
|
@dragleave.prevent |
||||||
|
@drop.stop.prevent="_onDrop" |
||||||
|
> |
||||||
|
<div ref="paper" class="paper"></div> |
||||||
|
<div ref="minimap" class="minimap"></div> |
||||||
|
<context-menu ref="contextMenu" /> |
||||||
|
<status-menu ref="statusMenu" /> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import { Graph, DataUri } from '@antv/x6' |
||||||
|
import dagTaskbar from './taskbar.vue' |
||||||
|
import contextMenu from './contextMenu.vue' |
||||||
|
import statusMenu from './statusMenu.vue' |
||||||
|
import { |
||||||
|
NODE_PROPS, |
||||||
|
EDGE_PROPS, |
||||||
|
PORT_PROPS, |
||||||
|
X6_NODE_NAME, |
||||||
|
X6_PORT_OUT_NAME, |
||||||
|
X6_PORT_IN_NAME, |
||||||
|
X6_EDGE_NAME, |
||||||
|
NODE_HIGHLIGHT_PROPS, |
||||||
|
PORT_HIGHLIGHT_PROPS, |
||||||
|
EDGE_HIGHLIGHT_PROPS |
||||||
|
} from './x6-helper' |
||||||
|
import { DagreLayout } from '@antv/layout' |
||||||
|
import { tasksType } from '../config' |
||||||
|
import _ from 'lodash' |
||||||
|
import { mapActions, mapMutations } from 'vuex' |
||||||
|
|
||||||
|
export default { |
||||||
|
name: 'dag-canvas', |
||||||
|
data () { |
||||||
|
return { |
||||||
|
graph: null, |
||||||
|
// Used to calculate the context menu location |
||||||
|
originalScrollPosition: { |
||||||
|
left: 0, |
||||||
|
top: 0 |
||||||
|
}, |
||||||
|
editable: true, |
||||||
|
dragging: { |
||||||
|
// Distance from the mouse to the top-left corner of the dragging element |
||||||
|
x: 0, |
||||||
|
y: 0, |
||||||
|
type: '' |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
provide () { |
||||||
|
return { |
||||||
|
dagCanvas: this |
||||||
|
} |
||||||
|
}, |
||||||
|
inject: ['dagChart'], |
||||||
|
components: { |
||||||
|
dagTaskbar, |
||||||
|
contextMenu, |
||||||
|
statusMenu |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
...mapActions('dag', ['genTaskCodeList']), |
||||||
|
...mapMutations('dag', ['removeTask']), |
||||||
|
/** |
||||||
|
* Recalculate the paper width and height |
||||||
|
*/ |
||||||
|
paperResize () { |
||||||
|
const w = this.$el.offsetWidth |
||||||
|
const h = this.$el.offsetHeight |
||||||
|
this.graph.resize(w, h) |
||||||
|
}, |
||||||
|
/** |
||||||
|
* Init graph |
||||||
|
* This will be called in the dag-chart mounted event |
||||||
|
* @param {boolean} uneditable |
||||||
|
*/ |
||||||
|
graphInit (editable) { |
||||||
|
const self = this |
||||||
|
this.editable = !!editable |
||||||
|
const paper = this.$refs.paper |
||||||
|
const minimap = this.$refs.minimap |
||||||
|
const graph = (this.graph = new Graph({ |
||||||
|
container: paper, |
||||||
|
selecting: { |
||||||
|
enabled: true, |
||||||
|
multiple: true, |
||||||
|
rubberband: true, |
||||||
|
rubberEdge: true, |
||||||
|
movable: true, |
||||||
|
showNodeSelectionBox: false |
||||||
|
}, |
||||||
|
scroller: true, |
||||||
|
grid: { |
||||||
|
size: 10, |
||||||
|
visible: true |
||||||
|
}, |
||||||
|
snapline: true, |
||||||
|
minimap: { |
||||||
|
enabled: true, |
||||||
|
container: minimap |
||||||
|
}, |
||||||
|
interacting: { |
||||||
|
edgeLabelMovable: false, |
||||||
|
nodeMovable: !!editable, |
||||||
|
magnetConnectable: !!editable |
||||||
|
}, |
||||||
|
connecting: { |
||||||
|
snap: { |
||||||
|
radius: 30 |
||||||
|
}, |
||||||
|
// 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: true, |
||||||
|
// Whether all available ports or nodes are highlighted when you drag the edge |
||||||
|
highlight: true, |
||||||
|
createEdge () { |
||||||
|
return graph.createEdge({ shape: X6_EDGE_NAME }) |
||||||
|
}, |
||||||
|
validateMagnet ({ magnet }) { |
||||||
|
return magnet.getAttribute('port-group') !== X6_PORT_IN_NAME |
||||||
|
}, |
||||||
|
validateConnection (data) { |
||||||
|
const { sourceCell, targetCell, sourceMagnet, targetMagnet } = data |
||||||
|
// Connections can only be created from the output link post |
||||||
|
if ( |
||||||
|
!sourceMagnet || |
||||||
|
sourceMagnet.getAttribute('port-group') !== X6_PORT_OUT_NAME |
||||||
|
) { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
// Can only be connected to the input link post |
||||||
|
if ( |
||||||
|
!targetMagnet || |
||||||
|
targetMagnet.getAttribute('port-group') !== X6_PORT_IN_NAME |
||||||
|
) { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
if ( |
||||||
|
sourceCell && |
||||||
|
targetCell && |
||||||
|
sourceCell.isNode() && |
||||||
|
targetCell.isNode() |
||||||
|
) { |
||||||
|
const edgeData = { |
||||||
|
sourceId: Number(sourceCell.id), |
||||||
|
targetId: Number(targetCell.id) |
||||||
|
} |
||||||
|
if (!self.dagChart.edgeIsValid(edgeData)) { |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return true |
||||||
|
} |
||||||
|
}, |
||||||
|
highlighting: { |
||||||
|
nodeAvailable: { |
||||||
|
name: 'className', |
||||||
|
args: { |
||||||
|
className: 'available' |
||||||
|
} |
||||||
|
}, |
||||||
|
magnetAvailable: { |
||||||
|
name: 'className', |
||||||
|
args: { |
||||||
|
className: 'available' |
||||||
|
} |
||||||
|
}, |
||||||
|
magnetAdsorbed: { |
||||||
|
name: 'className', |
||||||
|
args: { |
||||||
|
className: 'adsorbed' |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
})) |
||||||
|
// TODO will be deleted |
||||||
|
window._graph = graph |
||||||
|
this.registerX6Shape() |
||||||
|
this.bindGraphEvent() |
||||||
|
this.originalScrollPosition = graph.getScrollbarPosition() |
||||||
|
}, |
||||||
|
/** |
||||||
|
* Register custom shapes |
||||||
|
*/ |
||||||
|
registerX6Shape () { |
||||||
|
Graph.unregisterNode(X6_NODE_NAME) |
||||||
|
Graph.unregisterEdge(X6_EDGE_NAME) |
||||||
|
Graph.registerNode(X6_NODE_NAME, { ...NODE_PROPS }) |
||||||
|
Graph.registerEdge(X6_EDGE_NAME, { ...EDGE_PROPS }) |
||||||
|
}, |
||||||
|
/** |
||||||
|
* Bind grap event |
||||||
|
*/ |
||||||
|
bindGraphEvent () { |
||||||
|
// nodes and edges hover |
||||||
|
this.graph.on('cell:mouseenter', (data) => { |
||||||
|
const { cell, e } = data |
||||||
|
const { left: cL, top: cT } = |
||||||
|
this.$refs.container.getBoundingClientRect() |
||||||
|
const cX = e.clientX - cL |
||||||
|
const cY = e.clientY - cT |
||||||
|
const isStatusIcon = (tagName) => |
||||||
|
tagName && tagName.toLocaleLowerCase() === 'i' |
||||||
|
if (isStatusIcon(e.target.tagName)) { |
||||||
|
this.$refs.statusMenu.show(cX, cY) |
||||||
|
this.$refs.statusMenu.setCurrentTask({ |
||||||
|
name: cell.data.taskName, |
||||||
|
type: cell.data.taskType, |
||||||
|
code: Number(cell.id) |
||||||
|
}) |
||||||
|
} else { |
||||||
|
this.setHighlight(cell) |
||||||
|
} |
||||||
|
}) |
||||||
|
this.graph.on('cell:mouseleave', ({ cell }) => { |
||||||
|
if (!this.graph.isSelected(cell)) { |
||||||
|
this.resetHighlight(cell) |
||||||
|
} |
||||||
|
this.$refs.statusMenu.hide() |
||||||
|
}) |
||||||
|
// select |
||||||
|
this.graph.on('cell:selected', ({ cell }) => { |
||||||
|
this.setHighlight(cell) |
||||||
|
}) |
||||||
|
this.graph.on('cell:unselected', ({ cell }) => { |
||||||
|
if (!this.graph.isSelected(cell)) { |
||||||
|
this.resetHighlight(cell) |
||||||
|
} |
||||||
|
}) |
||||||
|
// right click |
||||||
|
this.graph.on('node:contextmenu', ({ x, y, cell }) => { |
||||||
|
const { left, top } = this.graph.getScrollbarPosition() |
||||||
|
const o = this.originalScrollPosition |
||||||
|
this.$refs.contextMenu.show(x + (o.left - left), y + (o.top - top)) |
||||||
|
this.$refs.contextMenu.setCurrentTask({ |
||||||
|
name: cell.data.taskName, |
||||||
|
type: cell.data.taskType, |
||||||
|
code: Number(cell.id) |
||||||
|
}) |
||||||
|
}) |
||||||
|
// node double click |
||||||
|
this.graph.on('node:dblclick', ({ cell }) => { |
||||||
|
this.dagChart.openFormModel(Number(cell.id), cell.data.taskType) |
||||||
|
}) |
||||||
|
// create edge label |
||||||
|
this.graph.on('edge:dblclick', ({ cell }) => { |
||||||
|
const labelName = this.getEdgeLabelName(cell) |
||||||
|
this.dagChart.$refs.edgeEditModel.show({ |
||||||
|
id: cell.id, |
||||||
|
label: labelName |
||||||
|
}) |
||||||
|
}) |
||||||
|
}, |
||||||
|
/** |
||||||
|
* @param {Edge|string} edge |
||||||
|
*/ |
||||||
|
getEdgeLabelName (edge) { |
||||||
|
if (typeof edge === 'string') edge = this.graph.getCellById(edge) |
||||||
|
const labels = edge.getLabels() |
||||||
|
const labelName = _.get(labels, ['0', 'attrs', 'label', 'text'], '') |
||||||
|
return labelName |
||||||
|
}, |
||||||
|
/** |
||||||
|
* Set edge label by id |
||||||
|
* @param {string} id |
||||||
|
* @param {string} label |
||||||
|
*/ |
||||||
|
setEdgeLabel (id, label) { |
||||||
|
const edge = this.graph.getCellById(id) |
||||||
|
edge.setLabels(label) |
||||||
|
if (this.graph.isSelected(edge)) { |
||||||
|
this.setEdgeHighlight(edge) |
||||||
|
} |
||||||
|
}, |
||||||
|
/** |
||||||
|
* @param {number} limit |
||||||
|
* @param {string} text |
||||||
|
* Each Chinese character is equal to two chars |
||||||
|
*/ |
||||||
|
truncateText (text, n) { |
||||||
|
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 |
||||||
|
}, |
||||||
|
/** |
||||||
|
* Set node name by id |
||||||
|
* @param {string|number} id |
||||||
|
* @param {string} name |
||||||
|
*/ |
||||||
|
setNodeName (id, name) { |
||||||
|
id += '' |
||||||
|
const node = this.graph.getCellById(id) |
||||||
|
if (node) { |
||||||
|
const truncation = this.truncateText(name, 18) |
||||||
|
node.attr('title/text', truncation) |
||||||
|
node.setData({ taskName: name }) |
||||||
|
} |
||||||
|
}, |
||||||
|
/** |
||||||
|
* Set node highlight |
||||||
|
* @param {Node} node |
||||||
|
*/ |
||||||
|
setNodeHighlight (node) { |
||||||
|
const url = require(`../images/task-icos/${node.data.taskType.toLocaleLowerCase()}_hover.png`) |
||||||
|
node.setAttrs(NODE_HIGHLIGHT_PROPS.attrs) |
||||||
|
node.setAttrByPath('image/xlink:href', url) |
||||||
|
node.setPortProp( |
||||||
|
X6_PORT_OUT_NAME, |
||||||
|
'attrs', |
||||||
|
PORT_HIGHLIGHT_PROPS[X6_PORT_OUT_NAME].attrs |
||||||
|
) |
||||||
|
}, |
||||||
|
/** |
||||||
|
* Reset node style |
||||||
|
* @param {Node} node |
||||||
|
*/ |
||||||
|
resetNodeStyle (node) { |
||||||
|
const url = require(`../images/task-icos/${node.data.taskType.toLocaleLowerCase()}.png`) |
||||||
|
node.setAttrs(NODE_PROPS.attrs) |
||||||
|
node.setAttrByPath('image/xlink:href', url) |
||||||
|
node.setPortProp( |
||||||
|
X6_PORT_OUT_NAME, |
||||||
|
'attrs', |
||||||
|
PORT_PROPS.groups[X6_PORT_OUT_NAME].attrs |
||||||
|
) |
||||||
|
}, |
||||||
|
/** |
||||||
|
* Set edge highlight |
||||||
|
* @param {Edge} edge |
||||||
|
*/ |
||||||
|
setEdgeHighlight (edge) { |
||||||
|
edge.setAttrs(EDGE_HIGHLIGHT_PROPS.attrs) |
||||||
|
const labelName = this.getEdgeLabelName(edge) |
||||||
|
if (labelName) { |
||||||
|
edge.setLabels([ |
||||||
|
_.merge(EDGE_HIGHLIGHT_PROPS.defaultLabel, { |
||||||
|
attrs: { |
||||||
|
label: { |
||||||
|
text: labelName |
||||||
|
} |
||||||
|
} |
||||||
|
}) |
||||||
|
]) |
||||||
|
} |
||||||
|
}, |
||||||
|
/** |
||||||
|
* Reset edge style |
||||||
|
* @param {Edge} edge |
||||||
|
*/ |
||||||
|
resetEdgeStyle (edge) { |
||||||
|
edge.setAttrs(EDGE_PROPS.attrs) |
||||||
|
const labelName = this.getEdgeLabelName(edge) |
||||||
|
if (labelName) { |
||||||
|
edge.setLabels([ |
||||||
|
{ |
||||||
|
attrs: { |
||||||
|
label: { |
||||||
|
fill: EDGE_PROPS.defaultLabel.attrs.label.fill, |
||||||
|
text: labelName |
||||||
|
}, |
||||||
|
body: { |
||||||
|
stroke: EDGE_PROPS.defaultLabel.attrs.body.stroke |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
]) |
||||||
|
} |
||||||
|
}, |
||||||
|
/** |
||||||
|
* Set cell highlight |
||||||
|
* @param {Cell} cell |
||||||
|
*/ |
||||||
|
setHighlight (cell) { |
||||||
|
if (cell.isEdge()) { |
||||||
|
this.setEdgeHighlight(cell) |
||||||
|
} else if (cell.isNode()) { |
||||||
|
this.setNodeHighlight(cell) |
||||||
|
} |
||||||
|
}, |
||||||
|
/** |
||||||
|
* Reset cell highlight |
||||||
|
* @param {Cell} cell |
||||||
|
*/ |
||||||
|
resetHighlight (cell) { |
||||||
|
if (cell.isEdge()) { |
||||||
|
this.resetEdgeStyle(cell) |
||||||
|
} else if (cell.isNode()) { |
||||||
|
this.resetNodeStyle(cell) |
||||||
|
} |
||||||
|
}, |
||||||
|
/** |
||||||
|
* Convert the graph to JSON |
||||||
|
* @return {{cells:Cell[]}} |
||||||
|
*/ |
||||||
|
toJSON () { |
||||||
|
return this.graph.toJSON() |
||||||
|
}, |
||||||
|
/** |
||||||
|
* Generate graph with JSON |
||||||
|
*/ |
||||||
|
fromJSON (json) { |
||||||
|
this.graph.fromJSON(json) |
||||||
|
}, |
||||||
|
/** |
||||||
|
* getNodes |
||||||
|
* @return {Node[]} |
||||||
|
*/ |
||||||
|
// interface Node { |
||||||
|
// id: number; |
||||||
|
// position: {x:number;y:number}; |
||||||
|
// data: {taskType:string;taskName:string;} |
||||||
|
// } |
||||||
|
getNodes () { |
||||||
|
const nodes = this.graph.getNodes() |
||||||
|
return nodes.map((node) => { |
||||||
|
const position = node.getPosition() |
||||||
|
const data = node.getData() |
||||||
|
return { |
||||||
|
id: Number(node.id), |
||||||
|
position: position, |
||||||
|
data: data |
||||||
|
} |
||||||
|
}) |
||||||
|
}, |
||||||
|
/** |
||||||
|
* getEdges |
||||||
|
* @return {Edge[]} Edge is inherited from the Cell |
||||||
|
*/ |
||||||
|
// interface Edge { |
||||||
|
// label: string; |
||||||
|
// sourceId: number; |
||||||
|
// targetId: number; |
||||||
|
// } |
||||||
|
getEdges () { |
||||||
|
const edges = this.graph.getEdges() |
||||||
|
return edges.map((edge) => { |
||||||
|
const labelData = edge.getLabelAt(0) |
||||||
|
return { |
||||||
|
label: _.get(labelData, ['attrs', 'label', 'text'], ''), |
||||||
|
sourceId: Number(edge.getSourceCellId()), |
||||||
|
targetId: Number(edge.getTargetCellId()) |
||||||
|
} |
||||||
|
}) |
||||||
|
}, |
||||||
|
/** |
||||||
|
* downloadPNG |
||||||
|
* @param {string} filename |
||||||
|
*/ |
||||||
|
downloadPNG (fileName = 'chart') { |
||||||
|
this.graph.toPNG( |
||||||
|
(dataUri) => { |
||||||
|
DataUri.downloadDataUri(dataUri, `${fileName}.png`) |
||||||
|
}, |
||||||
|
{ |
||||||
|
padding: { |
||||||
|
top: 50, |
||||||
|
right: 50, |
||||||
|
bottom: 50, |
||||||
|
left: 50 |
||||||
|
}, |
||||||
|
backgroundColor: '#f2f3f7' |
||||||
|
} |
||||||
|
) |
||||||
|
}, |
||||||
|
/** |
||||||
|
* format |
||||||
|
* @desc Auto layout use @antv/layout |
||||||
|
*/ |
||||||
|
format () { |
||||||
|
const dagreLayout = new DagreLayout({ |
||||||
|
type: 'dagre', |
||||||
|
rankdir: 'LR', |
||||||
|
align: 'UL', |
||||||
|
// Calculate the node spacing based on the edge label length |
||||||
|
ranksepFunc: (d) => { |
||||||
|
const edges = this.graph.getOutgoingEdges(d.id) |
||||||
|
let max = 0 |
||||||
|
if (edges && edges.length > 0) { |
||||||
|
edges.forEach((edge) => { |
||||||
|
const edgeView = this.graph.findViewByCell(edge) |
||||||
|
const labelWidth = +edgeView.findAttr( |
||||||
|
'width', |
||||||
|
_.get(edgeView, ['labelSelectors', '0', 'body'], null) |
||||||
|
) |
||||||
|
max = Math.max(max, labelWidth) |
||||||
|
}) |
||||||
|
} |
||||||
|
return 50 + max |
||||||
|
}, |
||||||
|
nodesep: 50, |
||||||
|
controlPoints: true |
||||||
|
}) |
||||||
|
const json = this.toJSON() |
||||||
|
const nodes = json.cells.filter((cell) => cell.shape === X6_NODE_NAME) |
||||||
|
const edges = json.cells.filter((cell) => cell.shape === X6_EDGE_NAME) |
||||||
|
const newModel = dagreLayout.layout({ |
||||||
|
nodes: nodes, |
||||||
|
edges: edges |
||||||
|
}) |
||||||
|
this.fromJSON(newModel) |
||||||
|
}, |
||||||
|
/** |
||||||
|
* add a node to the graph |
||||||
|
* @param {string|number} id |
||||||
|
* @param {string} taskType |
||||||
|
* @param {{x:number;y:number}} coordinate Default is { x: 100, y: 100 } |
||||||
|
*/ |
||||||
|
addNode (id, taskType, coordinate = { x: 100, y: 100 }) { |
||||||
|
id += '' |
||||||
|
if (!tasksType[taskType]) { |
||||||
|
console.warn(`taskType:${taskType} is invalid!`) |
||||||
|
return |
||||||
|
} |
||||||
|
const node = this.genNodeJSON(id, taskType, '', coordinate) |
||||||
|
this.graph.addNode(node) |
||||||
|
}, |
||||||
|
/** |
||||||
|
* generate node json |
||||||
|
* @param {number|string} id |
||||||
|
* @param {string} taskType |
||||||
|
* @param {{x:number;y:number}} coordinate Default is { x: 100, y: 100 } |
||||||
|
*/ |
||||||
|
genNodeJSON (id, taskType, taskName, coordinate = { x: 100, y: 100 }) { |
||||||
|
id += '' |
||||||
|
const url = require(`../images/task-icos/${taskType.toLocaleLowerCase()}.png`) |
||||||
|
const truncation = taskName ? this.truncateText(taskName, 18) : id |
||||||
|
return { |
||||||
|
id: id, |
||||||
|
shape: X6_NODE_NAME, |
||||||
|
x: coordinate.x, |
||||||
|
y: coordinate.y, |
||||||
|
data: { |
||||||
|
taskType: taskType, |
||||||
|
taskName: taskName |
||||||
|
}, |
||||||
|
attrs: { |
||||||
|
image: { |
||||||
|
// Use href instead of xlink:href, you may lose the icon when downloadPNG |
||||||
|
'xlink:href': url |
||||||
|
}, |
||||||
|
title: { |
||||||
|
text: truncation |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
/** |
||||||
|
* generate edge json |
||||||
|
* @param {number|string} sourceId |
||||||
|
* @param {number|string} targetId |
||||||
|
* @param {string} label |
||||||
|
*/ |
||||||
|
genEdgeJSON (sourceId, targetId, label = '') { |
||||||
|
sourceId += '' |
||||||
|
targetId += '' |
||||||
|
return { |
||||||
|
shape: X6_EDGE_NAME, |
||||||
|
source: { |
||||||
|
cell: sourceId, |
||||||
|
port: X6_PORT_OUT_NAME |
||||||
|
}, |
||||||
|
target: { |
||||||
|
cell: targetId, |
||||||
|
port: X6_PORT_IN_NAME |
||||||
|
}, |
||||||
|
labels: label ? [label] : undefined |
||||||
|
} |
||||||
|
}, |
||||||
|
/** |
||||||
|
* remove a node |
||||||
|
* @param {string|number} id NodeId |
||||||
|
*/ |
||||||
|
removeNode (id) { |
||||||
|
id += '' |
||||||
|
this.graph.removeNode(id) |
||||||
|
this.removeTask(id) |
||||||
|
}, |
||||||
|
/** |
||||||
|
* remove an edge |
||||||
|
* @param {string|number} id EdgeId |
||||||
|
*/ |
||||||
|
removeEdge (id) { |
||||||
|
id += '' |
||||||
|
this.graph.removeEdge(id) |
||||||
|
}, |
||||||
|
/** |
||||||
|
* remove multiple cells |
||||||
|
* @param {Cell[]} cells |
||||||
|
*/ |
||||||
|
removeCells (cells) { |
||||||
|
this.graph.removeCells(cells) |
||||||
|
cells.forEach((cell) => { |
||||||
|
if (cell.isNode()) { |
||||||
|
this.removeTask(cell.id) |
||||||
|
} |
||||||
|
}) |
||||||
|
}, |
||||||
|
/** |
||||||
|
* Gets the current selections |
||||||
|
* @return {Cell[]} |
||||||
|
*/ |
||||||
|
getSelections () { |
||||||
|
return this.graph.getSelectedCells() |
||||||
|
}, |
||||||
|
/** |
||||||
|
* Lock scroller |
||||||
|
*/ |
||||||
|
lockScroller () { |
||||||
|
this.graph.lockScroller() |
||||||
|
}, |
||||||
|
/** |
||||||
|
* Unlock scroller |
||||||
|
*/ |
||||||
|
unlockScroller () { |
||||||
|
this.graph.unlockScroller() |
||||||
|
}, |
||||||
|
/** |
||||||
|
* Drag && Drop Event |
||||||
|
*/ |
||||||
|
_onDragStart (e, taskType) { |
||||||
|
if (!this.editable) { |
||||||
|
e.preventDefault() |
||||||
|
return |
||||||
|
} |
||||||
|
this.dragging = { |
||||||
|
x: e.offsetX, |
||||||
|
y: e.offsetY, |
||||||
|
type: taskType.name |
||||||
|
} |
||||||
|
}, |
||||||
|
calcGraphCoordinate (mClientX, mClientY) { |
||||||
|
// Distance from the mouse to the top-left corner of the container; |
||||||
|
const { left: cX, top: cY } = |
||||||
|
this.$refs.container.getBoundingClientRect() |
||||||
|
const mouseX = mClientX - cX |
||||||
|
const mouseY = mClientY - cY |
||||||
|
|
||||||
|
// The distance that paper has been scrolled |
||||||
|
const { left: sLeft, top: sTop } = this.graph.getScrollbarPosition() |
||||||
|
const { left: oLeft, top: oTop } = this.originalScrollPosition |
||||||
|
const scrollX = sLeft - oLeft |
||||||
|
const scrollY = sTop - oTop |
||||||
|
|
||||||
|
// Distance from the mouse to the top-left corner of the dragging element; |
||||||
|
const { x: eX, y: eY } = this.dragging |
||||||
|
|
||||||
|
return { |
||||||
|
x: mouseX + scrollX - eX, |
||||||
|
y: mouseY + scrollY - eY |
||||||
|
} |
||||||
|
}, |
||||||
|
_onDrop (e) { |
||||||
|
const { type } = this.dragging |
||||||
|
const { x, y } = this.calcGraphCoordinate(e.clientX, e.clientY) |
||||||
|
this.genTaskCodeList({ |
||||||
|
genNum: 1 |
||||||
|
}) |
||||||
|
.then((res) => { |
||||||
|
const [code] = res |
||||||
|
this.addNode(code, type, { x, y }) |
||||||
|
this.dagChart.openFormModel(code, type) |
||||||
|
}) |
||||||
|
.catch((err) => { |
||||||
|
console.error(err) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style lang="scss" scoped> |
||||||
|
@import "./canvas"; |
||||||
|
</style> |
||||||
|
|
||||||
|
<style lang="scss"> |
||||||
|
@import "./x6-style"; |
||||||
|
</style> |
@ -0,0 +1,155 @@ |
|||||||
|
/* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
<template> |
||||||
|
<div |
||||||
|
class="dag-context-menu" |
||||||
|
v-show="visible" |
||||||
|
:style="{ |
||||||
|
left: `${left}px`, |
||||||
|
top: `${top}px`, |
||||||
|
}" |
||||||
|
> |
||||||
|
<menu-item :disabled="!startAvailable" @on-click="onStart"> |
||||||
|
{{ $t("Start") }} |
||||||
|
</menu-item> |
||||||
|
<menu-item :disabled="readOnly" @on-click="onEdit"> |
||||||
|
{{ $t("Edit") }} |
||||||
|
</menu-item> |
||||||
|
<menu-item :disabled="readOnly" @on-click="onCopy"> |
||||||
|
{{ $t("Copy") }} |
||||||
|
</menu-item> |
||||||
|
<menu-item :disabled="readOnly" @on-click="onDelete"> |
||||||
|
{{ $t("Delete") }} |
||||||
|
</menu-item> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import { mapState, mapActions, mapMutations } from 'vuex' |
||||||
|
import { findComponentDownward, uuid } from '@/module/util/' |
||||||
|
import MenuItem from './menuItem.vue' |
||||||
|
|
||||||
|
export default { |
||||||
|
name: 'dag-context-menu', |
||||||
|
inject: ['dagChart', 'dagCanvas'], |
||||||
|
components: { |
||||||
|
MenuItem |
||||||
|
}, |
||||||
|
data () { |
||||||
|
return { |
||||||
|
visible: false, |
||||||
|
left: 0, |
||||||
|
top: 0, |
||||||
|
canvasRef: null, |
||||||
|
currentTask: { |
||||||
|
code: 0, |
||||||
|
name: '', |
||||||
|
type: '' |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
computed: { |
||||||
|
...mapState('dag', ['isDetails', 'releaseState', 'tasks']), |
||||||
|
startAvailable () { |
||||||
|
return ( |
||||||
|
this.$route.name === 'projects-definition-details' && |
||||||
|
this.releaseState !== 'NOT_RELEASE' |
||||||
|
) |
||||||
|
}, |
||||||
|
readOnly () { |
||||||
|
return this.isDetails |
||||||
|
} |
||||||
|
}, |
||||||
|
mounted () { |
||||||
|
document.addEventListener('click', (e) => { |
||||||
|
this.hide() |
||||||
|
}) |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
...mapActions('dag', ['genTaskCodeList']), |
||||||
|
...mapMutations('dag', ['addTask']), |
||||||
|
getDagCanvasRef () { |
||||||
|
if (this.canvasRef) { |
||||||
|
return this.canvasRef |
||||||
|
} else { |
||||||
|
const canvas = findComponentDownward(this.dagChart, 'dag-canvas') |
||||||
|
this.canvasRef = canvas |
||||||
|
return canvas |
||||||
|
} |
||||||
|
}, |
||||||
|
setCurrentTask (task) { |
||||||
|
this.currentTask = { ...this.currentTask, ...task } |
||||||
|
}, |
||||||
|
onStart () { |
||||||
|
this.dagChart.startRunning(this.currentTask.name) |
||||||
|
}, |
||||||
|
onEdit () { |
||||||
|
this.dagChart.openFormModel(this.currentTask.code, this.currentTask.type) |
||||||
|
}, |
||||||
|
onCopy () { |
||||||
|
const nodes = this.dagCanvas.getNodes() |
||||||
|
const targetNode = nodes.find( |
||||||
|
(node) => node.id === this.currentTask.code |
||||||
|
) |
||||||
|
const targetTask = this.tasks.find( |
||||||
|
(task) => task.code === this.currentTask.code |
||||||
|
) |
||||||
|
|
||||||
|
if (!targetNode || !targetTask) return |
||||||
|
|
||||||
|
this.genTaskCodeList({ |
||||||
|
genNum: 1 |
||||||
|
}) |
||||||
|
.then((res) => { |
||||||
|
const [code] = res |
||||||
|
const taskName = uuid(targetTask.name + '_') |
||||||
|
const task = { |
||||||
|
...targetTask, |
||||||
|
code, |
||||||
|
name: taskName |
||||||
|
} |
||||||
|
this.dagCanvas.addNode(code, this.currentTask.type, { |
||||||
|
x: targetNode.position.x + 100, |
||||||
|
y: targetNode.position.y + 100 |
||||||
|
}) |
||||||
|
this.addTask(task) |
||||||
|
this.dagCanvas.setNodeName(code, taskName) |
||||||
|
}) |
||||||
|
.catch((err) => { |
||||||
|
console.error(err) |
||||||
|
}) |
||||||
|
}, |
||||||
|
onDelete () { |
||||||
|
this.dagCanvas.removeNode(this.currentTask.code) |
||||||
|
}, |
||||||
|
show (x = 0, y = 0) { |
||||||
|
this.dagCanvas.lockScroller() |
||||||
|
this.visible = true |
||||||
|
this.left = x + 10 |
||||||
|
this.top = y + 10 |
||||||
|
}, |
||||||
|
hide () { |
||||||
|
this.dagCanvas.unlockScroller() |
||||||
|
this.visible = false |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style lang="scss" scoped> |
||||||
|
@import "./contextMenu"; |
||||||
|
</style> |
@ -0,0 +1,50 @@ |
|||||||
|
/* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
<template> |
||||||
|
<div |
||||||
|
class="draggable-box" |
||||||
|
ref="draggable" |
||||||
|
draggable="true" |
||||||
|
@dragstart="onDragstart" |
||||||
|
@drag="onDrag" |
||||||
|
@dragend="onDragend" |
||||||
|
> |
||||||
|
<slot></slot> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
export default { |
||||||
|
name: 'draggable-box', |
||||||
|
data () { |
||||||
|
return { |
||||||
|
tmp: null |
||||||
|
} |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
onDragstart (e) { |
||||||
|
this.$emit('onDragstart', e) |
||||||
|
}, |
||||||
|
onDrag (e) { |
||||||
|
this.$emit('onDrag', e) |
||||||
|
}, |
||||||
|
onDragend (e) { |
||||||
|
this.$emit('onDragend', e) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
@ -0,0 +1,114 @@ |
|||||||
|
/* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
<template> |
||||||
|
<el-drawer |
||||||
|
:visible.sync="drawerVisible" |
||||||
|
:wrapperClosable="false" |
||||||
|
size="" |
||||||
|
:with-header="false" |
||||||
|
> |
||||||
|
<div class="form-model-wrapper"> |
||||||
|
<div class="title-box"> |
||||||
|
<span class="name">{{ $t("Current connection settings") }}</span> |
||||||
|
</div> |
||||||
|
<div class="content-box"> |
||||||
|
<div class="form-model"> |
||||||
|
<!-- Node name --> |
||||||
|
<div class="clearfix list"> |
||||||
|
<div class="text-box"> |
||||||
|
<span>{{ $t("Connection name") }}</span> |
||||||
|
</div> |
||||||
|
<div class="cont-box"> |
||||||
|
<label class="label-box"> |
||||||
|
<el-input |
||||||
|
type="text" |
||||||
|
size="small" |
||||||
|
v-model="label" |
||||||
|
:disabled="isDetails" |
||||||
|
:placeholder="$t('Please enter name')" |
||||||
|
maxlength="100" |
||||||
|
> |
||||||
|
</el-input> |
||||||
|
</label> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="bottom-box"> |
||||||
|
<div class="submit" style="background: #fff"> |
||||||
|
<el-button type="text" size="small" @click="cancel()"> |
||||||
|
{{ $t("Cancel") }} |
||||||
|
</el-button> |
||||||
|
<el-button |
||||||
|
type="primary" |
||||||
|
size="small" |
||||||
|
@click="ok()" |
||||||
|
:disabled="isDetails" |
||||||
|
>{{ $t("Confirm add") }} |
||||||
|
</el-button> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</el-drawer> |
||||||
|
</template> |
||||||
|
<script> |
||||||
|
import disabledState from '@/module/mixin/disabledState' |
||||||
|
import { mapState } from 'vuex' |
||||||
|
import { findComponentDownward } from '@/module/util/' |
||||||
|
|
||||||
|
export default { |
||||||
|
name: 'edge-edit-model', |
||||||
|
data () { |
||||||
|
return { |
||||||
|
id: '', |
||||||
|
label: '', |
||||||
|
drawerVisible: false |
||||||
|
} |
||||||
|
}, |
||||||
|
inject: ['dagChart'], |
||||||
|
mixins: [disabledState], |
||||||
|
computed: { |
||||||
|
...mapState('dag', ['isDetails']) |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
getDagCanvasRef () { |
||||||
|
if (this.canvasRef) { |
||||||
|
return this.canvasRef |
||||||
|
} else { |
||||||
|
const canvas = findComponentDownward(this.dagChart, 'dag-canvas') |
||||||
|
this.canvasRef = canvas |
||||||
|
return canvas |
||||||
|
} |
||||||
|
}, |
||||||
|
show ({ id, label }) { |
||||||
|
this.id = id |
||||||
|
this.label = label |
||||||
|
this.drawerVisible = true |
||||||
|
}, |
||||||
|
cancel () { |
||||||
|
this.drawerVisible = false |
||||||
|
this.id = '' |
||||||
|
this.label = '' |
||||||
|
}, |
||||||
|
ok () { |
||||||
|
const canvas = this.getDagCanvasRef() |
||||||
|
canvas.setEdgeLabel(this.id, this.label) |
||||||
|
this.cancel() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
@ -0,0 +1,42 @@ |
|||||||
|
/* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
<template> |
||||||
|
<div class="menu-item" :class="disabled ? 'disabled' : ''" @click="onClick"> |
||||||
|
<slot></slot> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
export default { |
||||||
|
name: 'dag-context-menu-item', |
||||||
|
props: { |
||||||
|
disabled: { |
||||||
|
type: Boolean, |
||||||
|
default: false |
||||||
|
} |
||||||
|
}, |
||||||
|
data () { |
||||||
|
return {} |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
onClick (e) { |
||||||
|
if (this.disabled) return |
||||||
|
this.$emit('on-click', e) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
@ -0,0 +1,16 @@ |
|||||||
|
/* |
||||||
|
* 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. |
||||||
|
*/ |
@ -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. |
||||||
|
*/ |
||||||
|
<template> |
||||||
|
<div |
||||||
|
class="dag-status-menu" |
||||||
|
v-show="visible" |
||||||
|
:style="{ |
||||||
|
left: `${left}px`, |
||||||
|
top: `${top}px`, |
||||||
|
}" |
||||||
|
> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import { mapState } from 'vuex' |
||||||
|
|
||||||
|
export default { |
||||||
|
name: 'dag-status-menu', |
||||||
|
inject: ['dagChart', 'dagCanvas'], |
||||||
|
data () { |
||||||
|
return { |
||||||
|
visible: false, |
||||||
|
left: 0, |
||||||
|
top: 0, |
||||||
|
currentTask: { |
||||||
|
code: 0, |
||||||
|
name: '', |
||||||
|
type: '' |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
computed: { |
||||||
|
...mapState('dag', ['tasks']) |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
setCurrentTask (task) { |
||||||
|
this.currentTask = { ...this.currentTask, ...task } |
||||||
|
}, |
||||||
|
show (x = 0, y = 0) { |
||||||
|
this.dagCanvas.lockScroller() |
||||||
|
this.visible = true |
||||||
|
this.left = x |
||||||
|
this.top = y |
||||||
|
}, |
||||||
|
hide () { |
||||||
|
this.dagCanvas.unlockScroller() |
||||||
|
this.visible = false |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style lang="scss" scoped> |
||||||
|
@import "./contextMenu"; |
||||||
|
</style> |
@ -0,0 +1,167 @@ |
|||||||
|
/* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
.dag-taskbar { |
||||||
|
width: 190px; |
||||||
|
height: 100%; |
||||||
|
background-color: #fff; |
||||||
|
margin-right: 20px; |
||||||
|
|
||||||
|
.taskbar-title { |
||||||
|
display: flex; |
||||||
|
border-bottom: dashed 1px #e5e5e5; |
||||||
|
height: 42px; |
||||||
|
padding: 0 20px; |
||||||
|
align-items: center; |
||||||
|
|
||||||
|
h4 { |
||||||
|
color: #666; |
||||||
|
font-size: 14px; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.tasks { |
||||||
|
width: 100%; |
||||||
|
padding: 10px 20px; |
||||||
|
display: flex; |
||||||
|
flex-wrap: wrap; |
||||||
|
box-sizing: border-box; |
||||||
|
max-height: calc(100% - 42px); |
||||||
|
overflow: auto; |
||||||
|
|
||||||
|
.draggable-box { |
||||||
|
cursor: move; |
||||||
|
width: 100%; |
||||||
|
height: 32px; |
||||||
|
margin-bottom: 10px; |
||||||
|
|
||||||
|
.task-item { |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
border: 1px dashed #e4e4e4; |
||||||
|
padding: 0 10px; |
||||||
|
border-radius: 4px; |
||||||
|
|
||||||
|
em { |
||||||
|
margin-right: 10px; |
||||||
|
display: block; |
||||||
|
width: 18px; |
||||||
|
height: 18px; |
||||||
|
background-size: 100% 100%; |
||||||
|
&.icos-shell { |
||||||
|
background-image: url("../images/task-icos/shell.png"); |
||||||
|
} |
||||||
|
&.icos-sub_process { |
||||||
|
background-image: url("../images/task-icos/sub_process.png"); |
||||||
|
} |
||||||
|
&.icos-procedure { |
||||||
|
background-image: url("../images/task-icos/procedure.png"); |
||||||
|
} |
||||||
|
&.icos-sql { |
||||||
|
background-image: url("../images/task-icos/sql.png"); |
||||||
|
} |
||||||
|
&.icos-flink { |
||||||
|
background-image: url("../images/task-icos/flink.png"); |
||||||
|
} |
||||||
|
&.icos-mr { |
||||||
|
background-image: url("../images/task-icos/mr.png"); |
||||||
|
} |
||||||
|
&.icos-python { |
||||||
|
background-image: url("../images/task-icos/python.png"); |
||||||
|
} |
||||||
|
&.icos-dependent { |
||||||
|
background-image: url("../images/task-icos/dependent.png"); |
||||||
|
} |
||||||
|
&.icos-http { |
||||||
|
background-image: url("../images/task-icos/http.png"); |
||||||
|
} |
||||||
|
&.icos-datax { |
||||||
|
background-image: url("../images/task-icos/datax.png"); |
||||||
|
} |
||||||
|
&.icos-sqoop { |
||||||
|
background-image: url("../images/task-icos/sqoop.png"); |
||||||
|
} |
||||||
|
&.icos-conditions { |
||||||
|
background-image: url("../images/task-icos/conditions.png"); |
||||||
|
} |
||||||
|
&.icos-waterdrop { |
||||||
|
background-image: url("../images/task-icos/waterdrop.png"); |
||||||
|
} |
||||||
|
&.icos-spark { |
||||||
|
background-image: url("../images/task-icos/spark.png"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
span { |
||||||
|
font-size: 12px; |
||||||
|
} |
||||||
|
|
||||||
|
&:hover { |
||||||
|
color: #288fff; |
||||||
|
border: 1px dashed #288fff; |
||||||
|
background-color: rgba(40, 143, 255, 0.1); |
||||||
|
|
||||||
|
em { |
||||||
|
&.icos-shell { |
||||||
|
background-image: url("../images/task-icos/shell_hover.png"); |
||||||
|
} |
||||||
|
&.icos-sub_process { |
||||||
|
background-image: url("../images/task-icos/sub_process_hover.png"); |
||||||
|
} |
||||||
|
&.icos-procedure { |
||||||
|
background-image: url("../images/task-icos/procedure_hover.png"); |
||||||
|
} |
||||||
|
&.icos-sql { |
||||||
|
background-image: url("../images/task-icos/sql_hover.png"); |
||||||
|
} |
||||||
|
&.icos-flink { |
||||||
|
background-image: url("../images/task-icos/flink_hover.png"); |
||||||
|
} |
||||||
|
&.icos-mr { |
||||||
|
background-image: url("../images/task-icos/mr_hover.png"); |
||||||
|
} |
||||||
|
&.icos-python { |
||||||
|
background-image: url("../images/task-icos/python_hover.png"); |
||||||
|
} |
||||||
|
&.icos-dependent { |
||||||
|
background-image: url("../images/task-icos/dependent_hover.png"); |
||||||
|
} |
||||||
|
&.icos-http { |
||||||
|
background-image: url("../images/task-icos/http_hover.png"); |
||||||
|
} |
||||||
|
&.icos-datax { |
||||||
|
background-image: url("../images/task-icos/datax_hover.png"); |
||||||
|
} |
||||||
|
&.icos-sqoop { |
||||||
|
background-image: url("../images/task-icos/sqoop_hover.png"); |
||||||
|
} |
||||||
|
&.icos-conditions { |
||||||
|
background-image: url("../images/task-icos/conditions_hover.png"); |
||||||
|
} |
||||||
|
&.icos-waterdrop { |
||||||
|
background-image: url("../images/task-icos/waterdrop_hover.png"); |
||||||
|
} |
||||||
|
&.icos-spark { |
||||||
|
background-image: url("../images/task-icos/spark_hover.png"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,64 @@ |
|||||||
|
/* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
<template> |
||||||
|
<div class="dag-taskbar"> |
||||||
|
<div class="taskbar-title"> |
||||||
|
<h4>{{$t('Toolbar')}}</h4> |
||||||
|
</div> |
||||||
|
<div class="tasks"> |
||||||
|
<template v-for="taskType in tasksTypeList"> |
||||||
|
<draggable-box |
||||||
|
:key="taskType.name" |
||||||
|
@onDragstart="(e) => $emit('on-drag-start', e, taskType)" |
||||||
|
> |
||||||
|
<div class="task-item"> |
||||||
|
<em :class="`icos-${taskType.name.toLocaleLowerCase()}`"></em> |
||||||
|
<span>{{ taskType.name }}</span> |
||||||
|
</div> |
||||||
|
</draggable-box> |
||||||
|
</template> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import draggableBox from './draggableBox.vue' |
||||||
|
import { tasksType } from '../config.js' |
||||||
|
|
||||||
|
export default { |
||||||
|
name: 'dag-taskbar', |
||||||
|
components: { |
||||||
|
draggableBox |
||||||
|
}, |
||||||
|
data () { |
||||||
|
const tasksTypeList = Object.keys(tasksType).map((type) => { |
||||||
|
return { |
||||||
|
name: type, |
||||||
|
desc: tasksType[type].desc |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
return { |
||||||
|
tasksTypeList |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style lang="scss" scoped> |
||||||
|
@import "./taskbar"; |
||||||
|
</style> |
@ -0,0 +1,113 @@ |
|||||||
|
/* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
.dag-toolbar { |
||||||
|
width: 100%; |
||||||
|
height: 50px; |
||||||
|
background-color: #fff; |
||||||
|
padding: 0 20px; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
box-sizing: border-box; |
||||||
|
|
||||||
|
h3 { |
||||||
|
font-size: 14px; |
||||||
|
font-weight: bold; |
||||||
|
margin: 0 10px 0 0; |
||||||
|
} |
||||||
|
|
||||||
|
.transparent { |
||||||
|
width: 1px; |
||||||
|
height: 1px; |
||||||
|
opacity: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.toolbar-operation { |
||||||
|
font-size: 18px; |
||||||
|
cursor: pointer; |
||||||
|
margin-left: 20px; |
||||||
|
color: #666666; |
||||||
|
&:hover { |
||||||
|
color: #288fff; |
||||||
|
} |
||||||
|
&.last{ |
||||||
|
margin-right: 10px; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.toolbar-btn { |
||||||
|
font-size: 16px; |
||||||
|
} |
||||||
|
|
||||||
|
.toolbar-left { |
||||||
|
flex: 1; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
} |
||||||
|
|
||||||
|
.toolbar-right { |
||||||
|
justify-self: flex-end; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
} |
||||||
|
|
||||||
|
.toolbar-el-btn{ |
||||||
|
margin-right: 0; |
||||||
|
margin-left: 10px; |
||||||
|
} |
||||||
|
|
||||||
|
.custom-ico{ |
||||||
|
display: block; |
||||||
|
width: 18px; |
||||||
|
height: 18px; |
||||||
|
background-size: 100% 100%; |
||||||
|
|
||||||
|
&.view-variables{ |
||||||
|
background-image: url('../images/view-variables.png'); |
||||||
|
&:hover{ |
||||||
|
background-image: url('../images/view-variables_hover.png'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
&.startup-parameters{ |
||||||
|
background-image: url('../images/startup-params.png'); |
||||||
|
&:hover{ |
||||||
|
background-image: url('../images/startup-params_hover.png'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
&.full-screen-open{ |
||||||
|
background-image: url('../images/full-screen-open.png'); |
||||||
|
&:hover{ |
||||||
|
background-image: url('../images/full-screen-open_hover.png'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
&.full-screen-close{ |
||||||
|
background-image: url('../images/full-screen-close.png'); |
||||||
|
&:hover{ |
||||||
|
background-image: url('../images/full-screen-close_hover.png'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
&.graph-format{ |
||||||
|
background-image: url('../images/graph-format.png'); |
||||||
|
&:hover{ |
||||||
|
background-image: url('../images/graph-format_hover.png'); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,199 @@ |
|||||||
|
/* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
<template> |
||||||
|
<div class="dag-toolbar"> |
||||||
|
<h3>{{ dagChart.name || $t("Create process") }}</h3> |
||||||
|
<el-tooltip |
||||||
|
v-if="dagChart.name" |
||||||
|
class="toolbar-operation" |
||||||
|
:content="$t('Copy name')" |
||||||
|
placement="bottom" |
||||||
|
> |
||||||
|
<i class="el-icon-copy-document" @click="copyName"></i> |
||||||
|
</el-tooltip> |
||||||
|
<textarea ref="textarea" cols="30" rows="10" class="transparent"></textarea> |
||||||
|
<div class="toolbar-left"> |
||||||
|
<el-tooltip |
||||||
|
:content="$t('View variables')" |
||||||
|
placement="bottom" |
||||||
|
class="toolbar-operation" |
||||||
|
> |
||||||
|
<i |
||||||
|
class="custom-ico view-variables" |
||||||
|
v-if="$route.name === 'projects-instance-details'" |
||||||
|
@click="toggleVariableView" |
||||||
|
></i> |
||||||
|
</el-tooltip> |
||||||
|
<el-tooltip |
||||||
|
:content="$t('Startup parameter')" |
||||||
|
placement="bottom" |
||||||
|
class="toolbar-operation" |
||||||
|
> |
||||||
|
<i |
||||||
|
class="custom-ico startup-parameters" |
||||||
|
v-if="$route.name === 'projects-instance-details'" |
||||||
|
@click="toggleParamView" |
||||||
|
></i> |
||||||
|
</el-tooltip> |
||||||
|
</div> |
||||||
|
<div class="toolbar-right"> |
||||||
|
<el-tooltip |
||||||
|
class="toolbar-operation" |
||||||
|
:content="$t('Delete selected lines or nodes')" |
||||||
|
placement="bottom" |
||||||
|
> |
||||||
|
<i class="el-icon-delete" @click="removeCells"></i> |
||||||
|
</el-tooltip> |
||||||
|
<el-tooltip |
||||||
|
class="toolbar-operation" |
||||||
|
:content="$t('Download')" |
||||||
|
placement="bottom" |
||||||
|
> |
||||||
|
<i class="el-icon-download" @click="downloadPNG"></i> |
||||||
|
</el-tooltip> |
||||||
|
<el-tooltip |
||||||
|
class="toolbar-operation" |
||||||
|
:content="$t('Full Screen')" |
||||||
|
placement="bottom" |
||||||
|
> |
||||||
|
<i |
||||||
|
:class="[ |
||||||
|
'custom-ico', |
||||||
|
dagChart.fullScreen |
||||||
|
? 'full-screen-close' |
||||||
|
: 'full-screen-open', |
||||||
|
]" |
||||||
|
@click="toggleFullScreen" |
||||||
|
></i> |
||||||
|
</el-tooltip> |
||||||
|
<el-tooltip |
||||||
|
class="toolbar-operation last" |
||||||
|
:content="$t('Format DAG')" |
||||||
|
placement="bottom" |
||||||
|
> |
||||||
|
<i class="custom-ico graph-format" @click="chartFormat"></i> |
||||||
|
</el-tooltip> |
||||||
|
<el-button |
||||||
|
class="toolbar-el-btn" |
||||||
|
type="primary" |
||||||
|
size="mini" |
||||||
|
@click="saveProcess" |
||||||
|
>{{ $t("Save") }}</el-button |
||||||
|
> |
||||||
|
<el-button |
||||||
|
class="toolbar-el-btn" |
||||||
|
v-if="$route.query.subProcessCodes" |
||||||
|
type="primary" |
||||||
|
size="mini" |
||||||
|
icon="el-icon-back" |
||||||
|
@click="dagChart.returnToPrevProcess" |
||||||
|
> |
||||||
|
{{ $t("Return_1") }} |
||||||
|
</el-button> |
||||||
|
<el-button |
||||||
|
class="toolbar-el-btn" |
||||||
|
type="primary" |
||||||
|
icon="el-icon-switch-button" |
||||||
|
size="mini" |
||||||
|
v-if="type === 'instance' || 'definition'" |
||||||
|
@click="returnToListPage" |
||||||
|
> |
||||||
|
{{ $t("Close") }} |
||||||
|
</el-button> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import { findComponentDownward } from '@/module/util/' |
||||||
|
|
||||||
|
export default { |
||||||
|
name: 'dag-toolbar', |
||||||
|
inject: ['dagChart'], |
||||||
|
data () { |
||||||
|
return { |
||||||
|
canvasRef: null |
||||||
|
} |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
getDagCanvasRef () { |
||||||
|
if (this.canvasRef) { |
||||||
|
return this.canvasRef |
||||||
|
} else { |
||||||
|
const canvas = findComponentDownward(this.dagChart, 'dag-canvas') |
||||||
|
this.canvasRef = canvas |
||||||
|
return canvas |
||||||
|
} |
||||||
|
}, |
||||||
|
toggleVariableView () { |
||||||
|
findComponentDownward(this.$root, 'assist-dag-index')._toggleView() |
||||||
|
}, |
||||||
|
toggleParamView () { |
||||||
|
findComponentDownward( |
||||||
|
this.$root, |
||||||
|
'starting-params-dag-index' |
||||||
|
)._toggleParam() |
||||||
|
}, |
||||||
|
toggleFullScreen () { |
||||||
|
this.dagChart.toggleFullScreen() |
||||||
|
}, |
||||||
|
saveProcess () { |
||||||
|
const canvas = this.getDagCanvasRef() |
||||||
|
const nodes = canvas.getNodes() |
||||||
|
if (!nodes.length) { |
||||||
|
this.$message.error(this.$t('Failed to create node to save')) |
||||||
|
return |
||||||
|
} |
||||||
|
this.dagChart.toggleSaveDialog(true) |
||||||
|
}, |
||||||
|
downloadPNG () { |
||||||
|
const canvas = this.getDagCanvasRef() |
||||||
|
canvas.downloadPNG(this.processName) |
||||||
|
}, |
||||||
|
removeCells () { |
||||||
|
const canvas = this.getDagCanvasRef() |
||||||
|
const selections = canvas.getSelections() |
||||||
|
canvas.removeCells(selections) |
||||||
|
}, |
||||||
|
copyName () { |
||||||
|
const textarea = this.$refs.textarea |
||||||
|
textarea.value = this.dagChart.name |
||||||
|
textarea.select() |
||||||
|
document.execCommand('copy') |
||||||
|
this.$message(this.$t('Copy success')) |
||||||
|
}, |
||||||
|
chartFormat () { |
||||||
|
const canvas = this.getDagCanvasRef() |
||||||
|
canvas.format() |
||||||
|
}, |
||||||
|
// TODO |
||||||
|
refreshTaskState () {}, |
||||||
|
returnToListPage () { |
||||||
|
let $name = this.$route.name |
||||||
|
if ($name && $name.indexOf('definition') !== -1) { |
||||||
|
this.$router.push({ name: 'projects-definition-list' }) |
||||||
|
} else { |
||||||
|
this.$router.push({ name: 'projects-instance-list' }) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style lang="scss" scoped> |
||||||
|
@import "./toolbar"; |
||||||
|
</style> |
@ -0,0 +1,323 @@ |
|||||||
|
/* |
||||||
|
* 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' |
||||||
|
export const X6_PORT_IN_NAME = 'dag-port-in' |
||||||
|
|
||||||
|
const EDGE = '#999999' |
||||||
|
const BG_BLUE = 'rgba(40, 143, 255, 0.1)' |
||||||
|
const BG_WHITE = '#FFFFFF' |
||||||
|
const NODE_BORDER = '#e4e4e4' |
||||||
|
const TITLE = '#333' |
||||||
|
const STROKE_BLUE = '#288FFF' |
||||||
|
|
||||||
|
export const PORT_PROPS = { |
||||||
|
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: EDGE, |
||||||
|
text: '+', |
||||||
|
textAnchor: 'middle', |
||||||
|
x: 0, |
||||||
|
y: 3 |
||||||
|
}, |
||||||
|
'circle-outer': { |
||||||
|
stroke: EDGE, |
||||||
|
strokeWidth: 1, |
||||||
|
r: 6, |
||||||
|
fill: BG_WHITE |
||||||
|
}, |
||||||
|
'circle-inner': { |
||||||
|
r: 4, |
||||||
|
fill: 'transparent' |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
[X6_PORT_IN_NAME]: { |
||||||
|
position: { |
||||||
|
name: 'absolute', |
||||||
|
args: { |
||||||
|
x: 0, |
||||||
|
y: 24 |
||||||
|
} |
||||||
|
}, |
||||||
|
markup: [ |
||||||
|
{ |
||||||
|
tagName: 'g', |
||||||
|
selector: 'body', |
||||||
|
className: 'in-port-body', |
||||||
|
children: [{ |
||||||
|
tagName: 'circle', |
||||||
|
selector: 'circle', |
||||||
|
className: 'circle' |
||||||
|
}] |
||||||
|
} |
||||||
|
], |
||||||
|
attrs: { |
||||||
|
body: { |
||||||
|
magnet: true |
||||||
|
}, |
||||||
|
circle: { |
||||||
|
r: 4, |
||||||
|
strokeWidth: 0, |
||||||
|
fill: 'transparent' |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const PORT_HIGHLIGHT_PROPS = { |
||||||
|
[X6_PORT_OUT_NAME]: { |
||||||
|
attrs: { |
||||||
|
'circle-outer': { |
||||||
|
stroke: STROKE_BLUE, |
||||||
|
fill: BG_BLUE |
||||||
|
}, |
||||||
|
'plus-text': { |
||||||
|
fill: STROKE_BLUE |
||||||
|
}, |
||||||
|
'circle-inner': { |
||||||
|
fill: STROKE_BLUE |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
[X6_PORT_IN_NAME]: {} |
||||||
|
} |
||||||
|
|
||||||
|
export const NODE_PROPS = { |
||||||
|
width: 220, |
||||||
|
height: 48, |
||||||
|
markup: [ |
||||||
|
{ |
||||||
|
tagName: 'rect', |
||||||
|
selector: 'body' |
||||||
|
}, |
||||||
|
{ |
||||||
|
tagName: 'image', |
||||||
|
selector: 'image' |
||||||
|
}, |
||||||
|
{ |
||||||
|
tagName: 'text', |
||||||
|
selector: 'title' |
||||||
|
} |
||||||
|
// {
|
||||||
|
// tagName: 'foreignObject',
|
||||||
|
// selector: 'fo',
|
||||||
|
// children: [
|
||||||
|
// {
|
||||||
|
// tagName: 'body',
|
||||||
|
// selector: 'fo-body',
|
||||||
|
// ns: 'http://www.w3.org/1999/xhtml',
|
||||||
|
// children: [{
|
||||||
|
// tagName: 'i',
|
||||||
|
// selector: 'state',
|
||||||
|
// className: 'state-icon el-icon-circle-check'
|
||||||
|
// }]
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
], |
||||||
|
attrs: { |
||||||
|
body: { |
||||||
|
refWidth: '100%', |
||||||
|
refHeight: '100%', |
||||||
|
rx: 6, |
||||||
|
ry: 6, |
||||||
|
pointerEvents: 'visiblePainted', |
||||||
|
fill: BG_WHITE, |
||||||
|
stroke: NODE_BORDER, |
||||||
|
strokeWidth: 1 |
||||||
|
}, |
||||||
|
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 |
||||||
|
}, |
||||||
|
state: { |
||||||
|
style: { |
||||||
|
display: 'block', |
||||||
|
width: '100%', |
||||||
|
height: '100%', |
||||||
|
fontSize: '18px' |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
ports: { |
||||||
|
...PORT_PROPS, |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
id: X6_PORT_OUT_NAME, |
||||||
|
group: X6_PORT_OUT_NAME |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: X6_PORT_IN_NAME, |
||||||
|
group: X6_PORT_IN_NAME |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const NODE_HIGHLIGHT_PROPS = { |
||||||
|
attrs: { |
||||||
|
body: { |
||||||
|
fill: BG_BLUE, |
||||||
|
stroke: STROKE_BLUE, |
||||||
|
strokeDasharray: '5,2' |
||||||
|
}, |
||||||
|
title: { |
||||||
|
fill: STROKE_BLUE |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const EDGE_PROPS = { |
||||||
|
attrs: { |
||||||
|
line: { |
||||||
|
stroke: EDGE, |
||||||
|
strokeWidth: 0.8, |
||||||
|
targetMarker: { |
||||||
|
tagName: 'path', |
||||||
|
fill: EDGE, |
||||||
|
strokeWidth: 0, |
||||||
|
d: 'M 6 -3 0 0 6 3 Z' |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
connector: { |
||||||
|
name: 'rounded' |
||||||
|
}, |
||||||
|
router: { |
||||||
|
name: 'er', |
||||||
|
args: { |
||||||
|
offset: 20, |
||||||
|
min: 20, |
||||||
|
direction: 'L' |
||||||
|
} |
||||||
|
}, |
||||||
|
defaultLabel: { |
||||||
|
markup: [ |
||||||
|
{ |
||||||
|
tagName: 'rect', |
||||||
|
selector: 'body' |
||||||
|
}, |
||||||
|
{ |
||||||
|
tagName: 'text', |
||||||
|
selector: 'label' |
||||||
|
} |
||||||
|
], |
||||||
|
attrs: { |
||||||
|
label: { |
||||||
|
fill: EDGE, |
||||||
|
fontSize: 14, |
||||||
|
textAnchor: 'middle', |
||||||
|
textVerticalAnchor: 'middle', |
||||||
|
pointerEvents: 'none' |
||||||
|
}, |
||||||
|
body: { |
||||||
|
ref: 'label', |
||||||
|
fill: BG_WHITE, |
||||||
|
stroke: EDGE, |
||||||
|
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_HIGHLIGHT_PROPS = { |
||||||
|
attrs: { |
||||||
|
line: { |
||||||
|
stroke: STROKE_BLUE, |
||||||
|
targetMarker: { |
||||||
|
fill: STROKE_BLUE |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
defaultLabel: { |
||||||
|
attrs: { |
||||||
|
label: { |
||||||
|
fill: STROKE_BLUE |
||||||
|
}, |
||||||
|
body: { |
||||||
|
fill: BG_WHITE, |
||||||
|
stroke: STROKE_BLUE |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
/* |
||||||
|
* 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"]{ |
||||||
|
.in-port-body{ |
||||||
|
&.adsorbed,&.available{ |
||||||
|
.circle { |
||||||
|
stroke: $STROKE_BLUE; |
||||||
|
stroke-width: 4; |
||||||
|
fill: $BG_WHITE; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,195 +0,0 @@ |
|||||||
/* |
|
||||||
* 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 Vue from 'vue' |
|
||||||
import _ from 'lodash' |
|
||||||
import i18n from '@/module/i18n' |
|
||||||
import { jsPlumb } from 'jsplumb' |
|
||||||
import JSP from './plugIn/jsPlumbHandle' |
|
||||||
import DownChart from './plugIn/downChart' |
|
||||||
import store from '@/conf/home/store' |
|
||||||
import dagre from 'dagre' |
|
||||||
|
|
||||||
/** |
|
||||||
* Prototype method |
|
||||||
*/ |
|
||||||
const Dag = function () { |
|
||||||
this.dag = {} |
|
||||||
this.instance = {} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* init |
|
||||||
* @dag dag vue instance |
|
||||||
*/ |
|
||||||
Dag.prototype.init = function ({ dag, instance }) { |
|
||||||
this.dag = dag |
|
||||||
this.instance = instance |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* set init config |
|
||||||
*/ |
|
||||||
Dag.prototype.setConfig = function (o) { |
|
||||||
JSP.setConfig(o) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* create dag |
|
||||||
*/ |
|
||||||
Dag.prototype.create = function () { |
|
||||||
const self = this |
|
||||||
const plumbIns = jsPlumb.getInstance() |
|
||||||
plumbIns.reset() |
|
||||||
plumbIns.ready(() => { |
|
||||||
JSP.init({ |
|
||||||
dag: this.dag, |
|
||||||
instance: this.instance, |
|
||||||
options: { |
|
||||||
onRemoveNodes ($id) { |
|
||||||
self.dag.removeEventModelById($id) |
|
||||||
} |
|
||||||
} |
|
||||||
}) |
|
||||||
|
|
||||||
// init event
|
|
||||||
JSP.handleEvent() |
|
||||||
|
|
||||||
// init draggable
|
|
||||||
JSP.draggable() |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Action event on the right side of the toolbar |
|
||||||
*/ |
|
||||||
Dag.prototype.toolbarEvent = function ({ item, code, is }) { |
|
||||||
const self = this |
|
||||||
switch (code) { |
|
||||||
case 'pointer': |
|
||||||
JSP.handleEventPointer(is) |
|
||||||
break |
|
||||||
case 'line': |
|
||||||
JSP.handleEventLine(is) |
|
||||||
break |
|
||||||
case 'remove': |
|
||||||
JSP.handleEventRemove() |
|
||||||
break |
|
||||||
case 'screen': |
|
||||||
JSP.handleEventScreen({ item, is }) |
|
||||||
break |
|
||||||
case 'download': |
|
||||||
Vue.prototype.$confirm(`${i18n.$t('Please confirm whether the workflow has been saved before downloading')}`, `${i18n.$t('Download')}`, { |
|
||||||
confirmButtonText: `${i18n.$t('Confirm')}`, |
|
||||||
cancelButtonText: `${i18n.$t('Cancel')}`, |
|
||||||
type: 'warning' |
|
||||||
}).then(() => { |
|
||||||
DownChart.download({ |
|
||||||
dagThis: self.dag |
|
||||||
}) |
|
||||||
}).catch(() => { |
|
||||||
}) |
|
||||||
break |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Echo data display |
|
||||||
*/ |
|
||||||
Dag.prototype.backfill = function (arg) { |
|
||||||
const that = this |
|
||||||
if (arg) { |
|
||||||
const marginX = 100 |
|
||||||
const g = new dagre.graphlib.Graph() |
|
||||||
g.setGraph({}) |
|
||||||
g.setDefaultEdgeLabel(function () { return {} }) |
|
||||||
|
|
||||||
for (const i in store.state.dag.locations) { |
|
||||||
const location = store.state.dag.locations[i] |
|
||||||
g.setNode(i, { label: i, width: Math.min(location.name.length * 7, 170), height: 150 }) |
|
||||||
} |
|
||||||
|
|
||||||
for (const i in store.state.dag.connects) { |
|
||||||
const connect = store.state.dag.connects[i] |
|
||||||
g.setEdge(connect.endPointSourceId, connect.endPointTargetId) |
|
||||||
} |
|
||||||
dagre.layout(g) |
|
||||||
|
|
||||||
const dataObject = {} |
|
||||||
g.nodes().forEach(function (v) { |
|
||||||
const node = g.node(v) |
|
||||||
const location = store.state.dag.locations[node.label] |
|
||||||
const obj = {} |
|
||||||
obj.name = location.name |
|
||||||
obj.x = node.x + marginX |
|
||||||
obj.y = node.y |
|
||||||
obj.targetarr = location.targetarr |
|
||||||
dataObject[node.label] = obj |
|
||||||
}) |
|
||||||
jsPlumb.ready(() => { |
|
||||||
JSP.init({ |
|
||||||
dag: this.dag, |
|
||||||
instance: this.instance, |
|
||||||
options: { |
|
||||||
onRemoveNodes ($id) { |
|
||||||
that.dag.removeEventModelById($id) |
|
||||||
} |
|
||||||
} |
|
||||||
}) |
|
||||||
// Backfill
|
|
||||||
JSP.jspBackfill({ |
|
||||||
// connects
|
|
||||||
connects: _.cloneDeep(store.state.dag.connects), |
|
||||||
// Node location information
|
|
||||||
locations: _.cloneDeep(dataObject), |
|
||||||
// Node data
|
|
||||||
largeJson: _.cloneDeep(store.state.dag.tasks) |
|
||||||
}) |
|
||||||
}) |
|
||||||
} else { |
|
||||||
const plumbIns = jsPlumb.getInstance() |
|
||||||
plumbIns.reset() |
|
||||||
plumbIns.ready(() => { |
|
||||||
JSP.init({ |
|
||||||
dag: this.dag, |
|
||||||
instance: this.instance, |
|
||||||
options: { |
|
||||||
onRemoveNodes ($id) { |
|
||||||
that.dag.removeEventModelById($id) |
|
||||||
} |
|
||||||
} |
|
||||||
}) |
|
||||||
// Backfill
|
|
||||||
JSP.jspBackfill({ |
|
||||||
// connects
|
|
||||||
connects: _.cloneDeep(store.state.dag.connects), |
|
||||||
// Node location information
|
|
||||||
locations: _.cloneDeep(store.state.dag.locations), |
|
||||||
// Node data
|
|
||||||
largeJson: _.cloneDeep(store.state.dag.tasks) |
|
||||||
}) |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get dag storage format data |
|
||||||
*/ |
|
||||||
Dag.prototype.saveStore = function () { |
|
||||||
return JSP.saveStore() |
|
||||||
} |
|
||||||
|
|
||||||
export default new Dag() |
|
After Width: | Height: | Size: 463 B |
After Width: | Height: | Size: 652 B |
After Width: | Height: | Size: 575 B |
After Width: | Height: | Size: 575 B |
After Width: | Height: | Size: 458 B |
After Width: | Height: | Size: 461 B |
After Width: | Height: | Size: 903 B |
After Width: | Height: | Size: 903 B |
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.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: 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: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 858 B |
After Width: | Height: | Size: 858 B |
@ -1,122 +0,0 @@ |
|||||||
/* |
|
||||||
* 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 _ from 'lodash' |
|
||||||
import canvg from 'canvg' |
|
||||||
import { tasksAll } from './util' |
|
||||||
import html2canvas from 'html2canvas' |
|
||||||
import { findComponentDownward } from '@/module/util/' |
|
||||||
|
|
||||||
const DownChart = function () { |
|
||||||
this.dag = {} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get interception location information |
|
||||||
*/ |
|
||||||
DownChart.prototype.maxVal = function () { |
|
||||||
return new Promise((resolve, reject) => { |
|
||||||
// All nodes
|
|
||||||
const tasksAllList = tasksAll() |
|
||||||
const dom = $('.dag-container') |
|
||||||
const y = parseInt(_.maxBy(tasksAllList, 'y').y + 60) |
|
||||||
const x = parseInt(_.maxBy(tasksAllList, 'x').x + 100) |
|
||||||
|
|
||||||
resolve({ |
|
||||||
width: (x > 600 ? x : dom.width()) + 100, |
|
||||||
height: (y > 500 ? y : dom.height()) + 100 |
|
||||||
}) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Download to image |
|
||||||
*/ |
|
||||||
DownChart.prototype.download = function ({ dagThis }) { |
|
||||||
this.dag = dagThis |
|
||||||
|
|
||||||
this.maxVal().then(({ width, height }) => { |
|
||||||
// Dom to save
|
|
||||||
const copyDom = $('#canvas') |
|
||||||
// gain
|
|
||||||
const scale = 1 |
|
||||||
// divReport is the id of the dom that needs to be intercepted into a picture
|
|
||||||
const svgElem = copyDom.find('svg') |
|
||||||
svgElem.each((index, node) => { |
|
||||||
// svg handle
|
|
||||||
const nodesToRecover = [] |
|
||||||
const nodesToRemove = [] |
|
||||||
const parentNode = node.parentNode |
|
||||||
const svg = node.outerHTML.trim() |
|
||||||
const canvas = document.createElement('canvas') |
|
||||||
canvg(canvas, svg) |
|
||||||
if (node.style.position) { |
|
||||||
canvas.style.position += node.style.position |
|
||||||
canvas.style.left += node.style.left |
|
||||||
canvas.style.top += node.style.top |
|
||||||
} |
|
||||||
nodesToRecover.push({ |
|
||||||
parent: parentNode, |
|
||||||
child: node |
|
||||||
}) |
|
||||||
parentNode.removeChild(node) |
|
||||||
nodesToRemove.push({ |
|
||||||
parent: parentNode, |
|
||||||
child: canvas |
|
||||||
}) |
|
||||||
parentNode.appendChild(canvas) |
|
||||||
}) |
|
||||||
|
|
||||||
const canvas = document.createElement('canvas') |
|
||||||
// canvas width
|
|
||||||
canvas.width = width * scale |
|
||||||
// canvas height
|
|
||||||
canvas.height = height * scale |
|
||||||
|
|
||||||
const content = canvas.getContext('2d') |
|
||||||
content.scale(scale, scale) |
|
||||||
// Get the offset of the element relative to the inspection
|
|
||||||
const rect = copyDom.get(0).getBoundingClientRect() |
|
||||||
// Set the context position, the value is a negative value relative to the window offset, let the picture reset
|
|
||||||
content.translate(-rect.left, -rect.top) |
|
||||||
|
|
||||||
html2canvas(copyDom[0], { |
|
||||||
dpi: window.devicePixelRatio * 2, |
|
||||||
scale: scale, |
|
||||||
width: width, |
|
||||||
canvas: canvas, |
|
||||||
heigth: height, |
|
||||||
useCORS: true // Enable cross-domain configuration
|
|
||||||
}).then((canvas) => { |
|
||||||
const name = `${this.dag.name}.png` |
|
||||||
const url = canvas.toDataURL('image/png', 1) |
|
||||||
setTimeout(() => { |
|
||||||
const triggerDownload = $('<a>').attr('href', url).attr('download', name).appendTo('body') |
|
||||||
triggerDownload[0].click() |
|
||||||
triggerDownload.remove() |
|
||||||
}, 100) |
|
||||||
|
|
||||||
// To refresh the dag instance, otherwise you can't re-plot
|
|
||||||
setTimeout(() => { |
|
||||||
// Refresh current dag
|
|
||||||
findComponentDownward(this.dag.$root, `${this.dag.type}-details`).init() |
|
||||||
}, 500) |
|
||||||
}) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
export default new DownChart() |
|
@ -1,814 +0,0 @@ |
|||||||
/* |
|
||||||
* 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 'jquery-ui/ui/widgets/draggable' |
|
||||||
import 'jquery-ui/ui/widgets/droppable' |
|
||||||
import 'jquery-ui/ui/widgets/resizable' |
|
||||||
import _ from 'lodash' |
|
||||||
import i18n from '@/module/i18n' |
|
||||||
import { jsPlumb } from 'jsplumb' |
|
||||||
import DragZoom from './dragZoom' |
|
||||||
import store from '@/conf/home/store' |
|
||||||
import router from '@/conf/home/router' |
|
||||||
import { uuid, findComponentDownward } from '@/module/util/' |
|
||||||
|
|
||||||
import { |
|
||||||
tasksAll, |
|
||||||
rtTasksTpl, |
|
||||||
setSvgColor, |
|
||||||
saveTargetarr, |
|
||||||
rtTargetarrArr, |
|
||||||
computeScale |
|
||||||
} from './util' |
|
||||||
import multiDrag from './multiDrag' |
|
||||||
|
|
||||||
const JSP = function () { |
|
||||||
this.dag = {} |
|
||||||
this.selectedElement = {} |
|
||||||
|
|
||||||
this.config = { |
|
||||||
// Whether to drag
|
|
||||||
isDrag: true, |
|
||||||
// Whether to allow connection
|
|
||||||
isAttachment: false, |
|
||||||
// Whether to drag a new node
|
|
||||||
isNewNodes: true, |
|
||||||
// Whether to support double-click node events
|
|
||||||
isDblclick: true, |
|
||||||
// Whether to support right-click menu events
|
|
||||||
isContextmenu: true, |
|
||||||
// Whether to allow click events
|
|
||||||
isClick: false |
|
||||||
} |
|
||||||
} |
|
||||||
/** |
|
||||||
* dag init |
|
||||||
*/ |
|
||||||
JSP.prototype.init = function ({ dag, instance, options }) { |
|
||||||
// Get the dag component instance
|
|
||||||
this.dag = dag |
|
||||||
// Get jsplumb instance
|
|
||||||
this.JspInstance = instance |
|
||||||
// Get JSP options
|
|
||||||
this.options = options || {} |
|
||||||
// Register jsplumb connection type and configuration
|
|
||||||
this.JspInstance.registerConnectionType('basic', { |
|
||||||
anchor: 'Continuous', |
|
||||||
connector: 'Bezier' // Line type
|
|
||||||
}) |
|
||||||
|
|
||||||
// Initial configuration
|
|
||||||
this.setConfig({ |
|
||||||
isDrag: !store.state.dag.isDetails, |
|
||||||
isAttachment: false, |
|
||||||
isNewNodes: !store.state.dag.isDetails, // Permissions.getAuth() === false ? false : !store.state.dag.isDetails,
|
|
||||||
isDblclick: true, |
|
||||||
isContextmenu: true, |
|
||||||
isClick: false |
|
||||||
}) |
|
||||||
|
|
||||||
// Monitor line click
|
|
||||||
this.JspInstance.bind('click', e => { |
|
||||||
// Untie event
|
|
||||||
if (this.config.isClick) { |
|
||||||
this.connectClick(e) |
|
||||||
} else { |
|
||||||
findComponentDownward(this.dag.$root, 'dag-chart')._createLineLabel({ id: e._jsPlumb.overlays.label.canvas.id, sourceId: e.sourceId, targetId: e.targetId }) |
|
||||||
} |
|
||||||
}) |
|
||||||
|
|
||||||
// Drag and drop
|
|
||||||
if (this.config.isNewNodes) { |
|
||||||
DragZoom.init() |
|
||||||
} |
|
||||||
|
|
||||||
// support multi drag
|
|
||||||
multiDrag() |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* set config attribute |
|
||||||
*/ |
|
||||||
JSP.prototype.setConfig = function (o) { |
|
||||||
this.config = Object.assign(this.config, {}, o) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Node binding event |
|
||||||
*/ |
|
||||||
JSP.prototype.tasksEvent = function (selfId) { |
|
||||||
const tasks = $(`#${selfId}`) |
|
||||||
// Bind right event
|
|
||||||
tasks.on('contextmenu', e => { |
|
||||||
this.tasksContextmenu(e) |
|
||||||
return false |
|
||||||
}) |
|
||||||
|
|
||||||
// Binding double click event
|
|
||||||
tasks.find('.icos').bind('dblclick', e => { |
|
||||||
this.tasksDblclick(e) |
|
||||||
}) |
|
||||||
|
|
||||||
// Binding click event
|
|
||||||
tasks.on('click', e => { |
|
||||||
this.tasksClick(e) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Dag node drag and drop processing |
|
||||||
*/ |
|
||||||
JSP.prototype.draggable = function () { |
|
||||||
if (this.config.isNewNodes) { |
|
||||||
let selfId |
|
||||||
const self = this |
|
||||||
$('.toolbar-btn .roundedRect').draggable({ |
|
||||||
scope: 'plant', |
|
||||||
helper: 'clone', |
|
||||||
containment: $('.dag-model'), |
|
||||||
stop: function (e, ui) { |
|
||||||
}, |
|
||||||
drag: function () { |
|
||||||
$('body').find('.tooltip.fade.top.in').remove() |
|
||||||
} |
|
||||||
}) |
|
||||||
|
|
||||||
$('#canvas').droppable({ |
|
||||||
scope: 'plant', |
|
||||||
drop: function (ev, ui) { |
|
||||||
let id = 'tasks-' + Math.ceil(Math.random() * 100000) // eslint-disable-line
|
|
||||||
|
|
||||||
let scale = computeScale($(this)) |
|
||||||
scale = scale || 1 |
|
||||||
|
|
||||||
// Get mouse coordinates and after scale coordinate
|
|
||||||
const left = parseInt(ui.offset.left - $(this).offset().left) / scale |
|
||||||
const top = parseInt(ui.offset.top - $(this).offset().top) / scale |
|
||||||
// Generate template node
|
|
||||||
$('#canvas').append(rtTasksTpl({ |
|
||||||
id: id, |
|
||||||
name: id, |
|
||||||
x: left, |
|
||||||
y: top, |
|
||||||
isAttachment: self.config.isAttachment, |
|
||||||
taskType: findComponentDownward(self.dag.$root, 'dag-chart').dagBarId |
|
||||||
})) |
|
||||||
|
|
||||||
// Get the generated node
|
|
||||||
const thisDom = jsPlumb.getSelector('.statemachine-demo .w') |
|
||||||
|
|
||||||
// Generating a connection node
|
|
||||||
self.JspInstance.batch(() => { |
|
||||||
self.initNode(thisDom[thisDom.length - 1]) |
|
||||||
}) |
|
||||||
selfId = id |
|
||||||
|
|
||||||
self.tasksEvent(selfId) |
|
||||||
|
|
||||||
// Dom structure is not generated without pop-up form form
|
|
||||||
if ($(`#${selfId}`).html()) { |
|
||||||
// dag event
|
|
||||||
findComponentDownward(self.dag.$root, 'dag-chart')._createNodes({ |
|
||||||
id: selfId |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Echo json processing and old data structure processing |
|
||||||
*/ |
|
||||||
JSP.prototype.jsonHandle = function ({ largeJson, locations }) { |
|
||||||
_.map(largeJson, v => { |
|
||||||
// Generate template
|
|
||||||
$('#canvas').append(rtTasksTpl({ |
|
||||||
id: v.id, |
|
||||||
name: v.name, |
|
||||||
x: locations[v.id].x, |
|
||||||
y: locations[v.id].y, |
|
||||||
targetarr: locations[v.id].targetarr, |
|
||||||
isAttachment: this.config.isAttachment, |
|
||||||
taskType: v.type, |
|
||||||
runFlag: v.runFlag, |
|
||||||
nodenumber: locations[v.id].nodenumber, |
|
||||||
successNode: v.conditionResult === undefined ? '' : v.conditionResult.successNode[0], |
|
||||||
failedNode: v.conditionResult === undefined ? '' : v.conditionResult.failedNode[0] |
|
||||||
})) |
|
||||||
|
|
||||||
// contextmenu event
|
|
||||||
$(`#${v.id}`).on('contextmenu', e => { |
|
||||||
this.tasksContextmenu(e) |
|
||||||
return false |
|
||||||
}) |
|
||||||
|
|
||||||
// dblclick event
|
|
||||||
$(`#${v.id}`).find('.icos').bind('dblclick', e => { |
|
||||||
this.tasksDblclick(e) |
|
||||||
}) |
|
||||||
|
|
||||||
// click event
|
|
||||||
$(`#${v.id}`).bind('click', e => { |
|
||||||
this.tasksClick(e) |
|
||||||
}) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Initialize a single node |
|
||||||
*/ |
|
||||||
JSP.prototype.initNode = function (el) { |
|
||||||
// Whether to drag
|
|
||||||
if (this.config.isDrag) { |
|
||||||
this.JspInstance.draggable(el, { |
|
||||||
containment: 'dag-container' |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
// Node attribute configuration
|
|
||||||
this.JspInstance.makeSource(el, { |
|
||||||
filter: '.ep', |
|
||||||
anchor: 'Continuous', |
|
||||||
connectorStyle: { |
|
||||||
stroke: '#2d8cf0', |
|
||||||
strokeWidth: 2, |
|
||||||
outlineStroke: 'transparent', |
|
||||||
outlineWidth: 4 |
|
||||||
}, |
|
||||||
// This place is leaking
|
|
||||||
// connectionType: "basic",
|
|
||||||
extract: { |
|
||||||
action: 'the-action' |
|
||||||
}, |
|
||||||
maxConnections: -1 |
|
||||||
}) |
|
||||||
|
|
||||||
// Node connection property configuration
|
|
||||||
this.JspInstance.makeTarget(el, { |
|
||||||
dropOptions: { hoverClass: 'dragHover' }, |
|
||||||
anchor: 'Continuous', |
|
||||||
allowLoopback: false // Forbid yourself to connect yourself
|
|
||||||
}) |
|
||||||
this.JspInstance.fire('jsPlumbDemoNodeAdded', el) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Node right click menu |
|
||||||
*/ |
|
||||||
JSP.prototype.tasksContextmenu = function (event) { |
|
||||||
if (this.config.isContextmenu) { |
|
||||||
const routerName = router.history.current.name |
|
||||||
// state
|
|
||||||
const isOne = routerName === 'projects-definition-details' && this.dag.releaseState !== 'NOT_RELEASE' |
|
||||||
// hide
|
|
||||||
const isTwo = store.state.dag.isDetails |
|
||||||
|
|
||||||
const html = [ |
|
||||||
`<a href="javascript:" id="startRunning" class="${isOne ? '' : 'disbled'}"><em class="el-icon-video-play"></em><span>${i18n.$t('Start')}</span></a>`, |
|
||||||
`<a href="javascript:" id="editNodes" class="${isTwo ? 'disbled' : ''}"><em class="el-icon-edit-outline"></em><span>${i18n.$t('Edit')}</span></a>`, |
|
||||||
`<a href="javascript:" id="copyNodes" class="${isTwo ? 'disbled' : ''}"><em class="el-icon-copy-document"></em><span>${i18n.$t('Copy')}</span></a>`, |
|
||||||
`<a href="javascript:" id="removeNodes" class="${isTwo ? 'disbled' : ''}"><em class="el-icon-delete"></em><span>${i18n.$t('Delete')}</span></a>` |
|
||||||
] |
|
||||||
|
|
||||||
const operationHtml = () => { |
|
||||||
return html.splice(',') |
|
||||||
} |
|
||||||
|
|
||||||
const e = event |
|
||||||
const $id = e.currentTarget.id |
|
||||||
const $contextmenu = $('#contextmenu') |
|
||||||
const $name = $(`#${$id}`).find('.name-p').text() |
|
||||||
const $left = e.pageX + document.body.scrollLeft - 5 |
|
||||||
const $top = e.pageY + document.body.scrollTop - 5 |
|
||||||
$contextmenu.css({ |
|
||||||
left: $left, |
|
||||||
top: $top, |
|
||||||
visibility: 'visible' |
|
||||||
}) |
|
||||||
// Action bar
|
|
||||||
$contextmenu.html('').append(operationHtml) |
|
||||||
|
|
||||||
if (isOne) { |
|
||||||
// start run
|
|
||||||
$('#startRunning').on('click', () => { |
|
||||||
const name = store.state.dag.name |
|
||||||
const id = router.history.current.params.id |
|
||||||
store.dispatch('dag/getStartCheck', { processDefinitionId: id }).then(res => { |
|
||||||
this.dag.startRunning({ id: id, name: name }, $name, 'contextmenu') |
|
||||||
}) |
|
||||||
}) |
|
||||||
} |
|
||||||
if (!isTwo) { |
|
||||||
// edit node
|
|
||||||
$('#editNodes').click(ev => { |
|
||||||
findComponentDownward(this.dag.$root, 'dag-chart')._createNodes({ |
|
||||||
id: $id, |
|
||||||
type: $(`#${$id}`).attr('data-tasks-type') |
|
||||||
}) |
|
||||||
}) |
|
||||||
// delete node
|
|
||||||
$('#removeNodes').click(ev => { |
|
||||||
this.removeNodes($id) |
|
||||||
}) |
|
||||||
|
|
||||||
// copy node
|
|
||||||
$('#copyNodes').click(res => { |
|
||||||
this.copyNodes($id) |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Node double click event |
|
||||||
*/ |
|
||||||
JSP.prototype.tasksDblclick = function (e) { |
|
||||||
// Untie event
|
|
||||||
if (this.config.isDblclick) { |
|
||||||
const id = $(e.currentTarget.offsetParent).attr('id') |
|
||||||
findComponentDownward(this.dag.$root, 'dag-chart')._createNodes({ |
|
||||||
id: id, |
|
||||||
type: $(`#${id}`).attr('data-tasks-type') |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Node click event |
|
||||||
*/ |
|
||||||
JSP.prototype.tasksClick = function (e) { |
|
||||||
let $id |
|
||||||
const self = this |
|
||||||
const $body = $('body') |
|
||||||
if (this.config.isClick) { |
|
||||||
const $connect = this.selectedElement.connect |
|
||||||
$('.w').removeClass('jtk-tasks-active') |
|
||||||
$(e.currentTarget).addClass('jtk-tasks-active') |
|
||||||
if ($connect) { |
|
||||||
setSvgColor($connect, '#2d8cf0') |
|
||||||
this.selectedElement.connect = null |
|
||||||
} |
|
||||||
this.selectedElement.id = $(e.currentTarget).attr('id') |
|
||||||
|
|
||||||
// Unbind copy and paste events
|
|
||||||
$body.unbind('copy').unbind('paste') |
|
||||||
// Copy binding id
|
|
||||||
$id = self.selectedElement.id |
|
||||||
|
|
||||||
$body.bind({ |
|
||||||
copy: function () { |
|
||||||
$id = self.selectedElement.id |
|
||||||
}, |
|
||||||
paste: function () { |
|
||||||
$id && self.copyNodes($id) |
|
||||||
} |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Remove binding events |
|
||||||
* paste |
|
||||||
*/ |
|
||||||
JSP.prototype.removePaste = function () { |
|
||||||
const $body = $('body') |
|
||||||
// Unbind copy and paste events
|
|
||||||
$body.unbind('copy').unbind('paste') |
|
||||||
// Remove selected node parameters
|
|
||||||
this.selectedElement.id = null |
|
||||||
// Remove node selection effect
|
|
||||||
$('.w').removeClass('jtk-tasks-active') |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Line click event |
|
||||||
*/ |
|
||||||
JSP.prototype.connectClick = function (e) { |
|
||||||
// Set svg color
|
|
||||||
setSvgColor(e, '#0097e0') |
|
||||||
const $id = this.selectedElement.id |
|
||||||
if ($id) { |
|
||||||
$(`#${$id}`).removeClass('jtk-tasks-active') |
|
||||||
this.selectedElement.id = null |
|
||||||
} |
|
||||||
this.selectedElement.connect = e |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* toolbarEvent |
|
||||||
* @param {Pointer} |
|
||||||
*/ |
|
||||||
JSP.prototype.handleEventPointer = function (is) { |
|
||||||
this.setConfig({ |
|
||||||
isClick: is, |
|
||||||
isAttachment: false |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* toolbarEvent |
|
||||||
* @param {Line} |
|
||||||
*/ |
|
||||||
JSP.prototype.handleEventLine = function (is) { |
|
||||||
const wDom = $('.w') |
|
||||||
this.setConfig({ |
|
||||||
isAttachment: is |
|
||||||
}) |
|
||||||
is ? wDom.addClass('jtk-ep') : wDom.removeClass('jtk-ep') |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* toolbarEvent |
|
||||||
* @param {Remove} |
|
||||||
*/ |
|
||||||
JSP.prototype.handleEventRemove = function () { |
|
||||||
const $id = this.selectedElement.id || null |
|
||||||
const $connect = this.selectedElement.connect || null |
|
||||||
if ($id) { |
|
||||||
this.removeNodes(this.selectedElement.id) |
|
||||||
} else { |
|
||||||
this.removeConnect($connect) |
|
||||||
} |
|
||||||
|
|
||||||
// Monitor whether to edit DAG
|
|
||||||
store.commit('dag/setIsEditDag', true) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Delete node |
|
||||||
*/ |
|
||||||
JSP.prototype.removeNodes = function ($id) { |
|
||||||
// Delete node processing(data-targetarr)
|
|
||||||
_.map(tasksAll(), v => { |
|
||||||
const targetarr = v.targetarr.split(',') |
|
||||||
if (targetarr.length) { |
|
||||||
const newArr = _.filter(targetarr, v1 => v1 !== $id) |
|
||||||
$(`#${v.id}`).attr('data-targetarr', newArr.toString()) |
|
||||||
} |
|
||||||
}) |
|
||||||
// delete node
|
|
||||||
this.JspInstance.remove($id) |
|
||||||
|
|
||||||
// delete dom
|
|
||||||
$(`#${$id}`).remove() |
|
||||||
|
|
||||||
// callback onRemoveNodes event
|
|
||||||
this.options && this.options.onRemoveNodes && this.options.onRemoveNodes($id) |
|
||||||
const connects = [] |
|
||||||
_.map(this.JspInstance.getConnections(), v => { |
|
||||||
connects.push({ |
|
||||||
endPointSourceId: v.sourceId, |
|
||||||
endPointTargetId: v.targetId, |
|
||||||
label: v._jsPlumb.overlays.label.canvas.innerText |
|
||||||
}) |
|
||||||
}) |
|
||||||
// Storage line dependence
|
|
||||||
store.commit('dag/setConnects', connects) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Delete connection |
|
||||||
*/ |
|
||||||
JSP.prototype.removeConnect = function ($connect) { |
|
||||||
if (!$connect) { |
|
||||||
return |
|
||||||
} |
|
||||||
// Remove connections and remove node and node dependencies
|
|
||||||
const targetId = $connect.targetId |
|
||||||
const sourceId = $connect.sourceId |
|
||||||
let targetarr = rtTargetarrArr(targetId) |
|
||||||
if (targetarr.length) { |
|
||||||
targetarr = _.filter(targetarr, v => v !== sourceId) |
|
||||||
$(`#${targetId}`).attr('data-targetarr', targetarr.toString()) |
|
||||||
} |
|
||||||
if ($(`#${sourceId}`).attr('data-tasks-type') === 'CONDITIONS') { |
|
||||||
$(`#${sourceId}`).attr('data-nodenumber', Number($(`#${sourceId}`).attr('data-nodenumber')) - 1) |
|
||||||
} |
|
||||||
this.JspInstance.deleteConnection($connect) |
|
||||||
|
|
||||||
this.selectedElement = {} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Copy node |
|
||||||
*/ |
|
||||||
JSP.prototype.copyNodes = function ($id) { |
|
||||||
let newNodeInfo = _.cloneDeep(_.find(store.state.dag.tasks, v => v.id === $id)) |
|
||||||
const newNodePors = store.state.dag.locations[$id] |
|
||||||
// Unstored nodes do not allow replication
|
|
||||||
if (!newNodePors) { |
|
||||||
return |
|
||||||
} |
|
||||||
// Generate random id
|
|
||||||
const newUuId = `${uuid() + uuid()}` |
|
||||||
const id = newNodeInfo.id.length > 8 ? newNodeInfo.id.substr(0, 7) : newNodeInfo.id |
|
||||||
const name = newNodeInfo.name.length > 8 ? newNodeInfo.name.substr(0, 7) : newNodeInfo.name |
|
||||||
|
|
||||||
// new id
|
|
||||||
const newId = `${id || ''}-${newUuId}` |
|
||||||
// new name
|
|
||||||
const newName = `${name || ''}-${newUuId}` |
|
||||||
// coordinate x
|
|
||||||
const newX = newNodePors.x + 100 |
|
||||||
// coordinate y
|
|
||||||
const newY = newNodePors.y + 40 |
|
||||||
|
|
||||||
// Generate template node
|
|
||||||
$('#canvas').append(rtTasksTpl({ |
|
||||||
id: newId, |
|
||||||
name: newName, |
|
||||||
x: newX, |
|
||||||
y: newY, |
|
||||||
isAttachment: this.config.isAttachment, |
|
||||||
taskType: newNodeInfo.type |
|
||||||
})) |
|
||||||
|
|
||||||
// Get the generated node
|
|
||||||
const thisDom = jsPlumb.getSelector('.statemachine-demo .w') |
|
||||||
|
|
||||||
// Copy node information
|
|
||||||
newNodeInfo = Object.assign(newNodeInfo, { |
|
||||||
id: newId, |
|
||||||
name: newName |
|
||||||
}) |
|
||||||
|
|
||||||
// Add new node
|
|
||||||
store.commit('dag/addTasks', newNodeInfo) |
|
||||||
// Add node location information
|
|
||||||
store.commit('dag/addLocations', { |
|
||||||
[newId]: { |
|
||||||
name: newName, |
|
||||||
targetarr: '', |
|
||||||
nodenumber: 0, |
|
||||||
x: newX, |
|
||||||
y: newY |
|
||||||
} |
|
||||||
}) |
|
||||||
|
|
||||||
// Generating a connection node
|
|
||||||
this.JspInstance.batch(() => { |
|
||||||
this.initNode(thisDom[thisDom.length - 1]) |
|
||||||
// Add events to nodes
|
|
||||||
this.tasksEvent(newId) |
|
||||||
}) |
|
||||||
} |
|
||||||
/** |
|
||||||
* toolbarEvent |
|
||||||
* @param {Screen} |
|
||||||
*/ |
|
||||||
JSP.prototype.handleEventScreen = function ({ item, is }) { |
|
||||||
let screenOpen = true |
|
||||||
if (is) { |
|
||||||
item.icon = 'el-icon-aim' |
|
||||||
screenOpen = true |
|
||||||
} else { |
|
||||||
item.icon = 'el-icon-full-screen' |
|
||||||
screenOpen = false |
|
||||||
} |
|
||||||
const $mainLayoutModel = $('.main-layout-model') |
|
||||||
if (screenOpen) { |
|
||||||
$mainLayoutModel.addClass('dag-screen') |
|
||||||
} else { |
|
||||||
$mainLayoutModel.removeClass('dag-screen') |
|
||||||
} |
|
||||||
} |
|
||||||
/** |
|
||||||
* save task |
|
||||||
* @param tasks |
|
||||||
* @param locations |
|
||||||
* @param connects |
|
||||||
*/ |
|
||||||
JSP.prototype.saveStore = function () { |
|
||||||
return new Promise((resolve, reject) => { |
|
||||||
const connects = [] |
|
||||||
const locations = {} |
|
||||||
const tasks = [] |
|
||||||
|
|
||||||
const is = (id) => { |
|
||||||
return !!_.filter(tasksAll(), v => v.id === id).length |
|
||||||
} |
|
||||||
|
|
||||||
// task
|
|
||||||
_.map(_.cloneDeep(store.state.dag.tasks), v => { |
|
||||||
if (is(v.id)) { |
|
||||||
const preTasks = [] |
|
||||||
const id = $(`#${v.id}`) |
|
||||||
const tar = id.attr('data-targetarr') |
|
||||||
const idDep = tar ? id.attr('data-targetarr').split(',') : [] |
|
||||||
if (idDep.length) { |
|
||||||
_.map(idDep, v1 => { |
|
||||||
preTasks.push($(`#${v1}`).find('.name-p').text()) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
let tasksParam = _.assign(v, { |
|
||||||
preTasks: preTasks, |
|
||||||
depList: null |
|
||||||
}) |
|
||||||
|
|
||||||
// Sub-workflow has no retries and interval
|
|
||||||
if (v.type === 'SUB_PROCESS') { |
|
||||||
tasksParam = _.omit(tasksParam, ['maxRetryTimes', 'retryInterval']) |
|
||||||
} |
|
||||||
|
|
||||||
tasks.push(tasksParam) |
|
||||||
} |
|
||||||
}) |
|
||||||
|
|
||||||
if (store.state.dag.connects.length === this.JspInstance.getConnections().length) { |
|
||||||
_.map(store.state.dag.connects, u => { |
|
||||||
connects.push({ |
|
||||||
endPointSourceId: u.endPointSourceId, |
|
||||||
endPointTargetId: u.endPointTargetId, |
|
||||||
label: u.label |
|
||||||
}) |
|
||||||
}) |
|
||||||
} else if (store.state.dag.connects.length > 0 && store.state.dag.connects.length < this.JspInstance.getConnections().length) { |
|
||||||
_.map(this.JspInstance.getConnections(), v => { |
|
||||||
connects.push({ |
|
||||||
endPointSourceId: v.sourceId, |
|
||||||
endPointTargetId: v.targetId, |
|
||||||
label: v._jsPlumb.overlays.label.canvas.innerText |
|
||||||
}) |
|
||||||
}) |
|
||||||
_.map(store.state.dag.connects, u => { |
|
||||||
_.map(connects, v => { |
|
||||||
if (u.label && u.endPointSourceId === v.endPointSourceId && u.endPointTargetId === v.endPointTargetId) { |
|
||||||
v.label = u.label |
|
||||||
} |
|
||||||
}) |
|
||||||
}) |
|
||||||
} else if (store.state.dag.connects.length === 0) { |
|
||||||
_.map(this.JspInstance.getConnections(), v => { |
|
||||||
connects.push({ |
|
||||||
endPointSourceId: v.sourceId, |
|
||||||
endPointTargetId: v.targetId, |
|
||||||
label: v._jsPlumb.overlays.label.canvas.innerText |
|
||||||
}) |
|
||||||
}) |
|
||||||
} else if (store.state.dag.connects.length > this.JspInstance.getConnections().length) { |
|
||||||
_.map(this.JspInstance.getConnections(), v => { |
|
||||||
connects.push({ |
|
||||||
endPointSourceId: v.sourceId, |
|
||||||
endPointTargetId: v.targetId, |
|
||||||
label: v._jsPlumb.overlays.label.canvas.innerText |
|
||||||
}) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
_.map(tasksAll(), v => { |
|
||||||
locations[v.id] = { |
|
||||||
name: v.name, |
|
||||||
targetarr: v.targetarr, |
|
||||||
nodenumber: v.nodenumber, |
|
||||||
x: v.x, |
|
||||||
y: v.y |
|
||||||
} |
|
||||||
}) |
|
||||||
|
|
||||||
// Storage node
|
|
||||||
store.commit('dag/setTasks', tasks) |
|
||||||
// Store coordinate information
|
|
||||||
store.commit('dag/setLocations', locations) |
|
||||||
// Storage line dependence
|
|
||||||
store.commit('dag/setConnects', connects) |
|
||||||
|
|
||||||
resolve({ |
|
||||||
connects: connects, |
|
||||||
tasks: tasks, |
|
||||||
locations: locations |
|
||||||
}) |
|
||||||
}) |
|
||||||
} |
|
||||||
/** |
|
||||||
* Event processing |
|
||||||
*/ |
|
||||||
|
|
||||||
JSP.prototype.handleEvent = function () { |
|
||||||
this.JspInstance.bind('beforeDrop', function (info) { |
|
||||||
const sourceId = info.sourceId// 出
|
|
||||||
const targetId = info.targetId// 入
|
|
||||||
/** |
|
||||||
* Recursive search for nodes |
|
||||||
*/ |
|
||||||
let recursiveVal |
|
||||||
const recursiveTargetarr = (arr, targetId) => { |
|
||||||
for (const i in arr) { |
|
||||||
if (arr[i] === targetId) { |
|
||||||
recursiveVal = targetId |
|
||||||
} else { |
|
||||||
recursiveTargetarr(rtTargetarrArr(arr[i]), targetId) |
|
||||||
} |
|
||||||
} |
|
||||||
return recursiveVal |
|
||||||
} |
|
||||||
|
|
||||||
// Connection to connected nodes is not allowed
|
|
||||||
if (_.findIndex(rtTargetarrArr(targetId), v => v === sourceId) !== -1) { |
|
||||||
return false |
|
||||||
} |
|
||||||
|
|
||||||
// Recursive form to find if the target Targetarr has a sourceId
|
|
||||||
if (recursiveTargetarr(rtTargetarrArr(sourceId), targetId)) { |
|
||||||
return false |
|
||||||
} |
|
||||||
|
|
||||||
if ($(`#${sourceId}`).attr('data-tasks-type') === 'CONDITIONS' && $(`#${sourceId}`).attr('data-nodenumber') === 2) { |
|
||||||
return false |
|
||||||
} else { |
|
||||||
$(`#${sourceId}`).attr('data-nodenumber', Number($(`#${sourceId}`).attr('data-nodenumber')) + 1) |
|
||||||
} |
|
||||||
|
|
||||||
// Storage node dependency information
|
|
||||||
saveTargetarr(sourceId, targetId) |
|
||||||
|
|
||||||
// Monitor whether to edit DAG
|
|
||||||
store.commit('dag/setIsEditDag', true) |
|
||||||
|
|
||||||
return true |
|
||||||
}) |
|
||||||
} |
|
||||||
/** |
|
||||||
* Backfill data processing |
|
||||||
*/ |
|
||||||
JSP.prototype.jspBackfill = function ({ connects, locations, largeJson }) { |
|
||||||
// Backfill nodes
|
|
||||||
this.jsonHandle({ |
|
||||||
largeJson: largeJson, |
|
||||||
locations: locations |
|
||||||
}) |
|
||||||
|
|
||||||
const wNodes = jsPlumb.getSelector('.statemachine-demo .w') |
|
||||||
|
|
||||||
// Backfill line
|
|
||||||
this.JspInstance.batch(() => { |
|
||||||
for (let i = 0; i < wNodes.length; i++) { |
|
||||||
this.initNode(wNodes[i]) |
|
||||||
} |
|
||||||
_.map(connects, v => { |
|
||||||
let sourceId = v.endPointSourceId.split('-') |
|
||||||
let targetId = v.endPointTargetId.split('-') |
|
||||||
const labels = v.label |
|
||||||
if (sourceId.length === 4 && targetId.length === 4) { |
|
||||||
sourceId = `${sourceId[0]}-${sourceId[1]}-${sourceId[2]}` |
|
||||||
targetId = `${targetId[0]}-${targetId[1]}-${targetId[2]}` |
|
||||||
} else { |
|
||||||
sourceId = v.endPointSourceId |
|
||||||
targetId = v.endPointTargetId |
|
||||||
} |
|
||||||
|
|
||||||
if ($(`#${sourceId}`).attr('data-tasks-type') === 'CONDITIONS' && $(`#${sourceId}`).attr('data-successnode') === $(`#${targetId}`).find('.name-p').text()) { |
|
||||||
this.JspInstance.connect({ |
|
||||||
source: sourceId, |
|
||||||
target: targetId, |
|
||||||
type: 'basic', |
|
||||||
paintStyle: { strokeWidth: 2, stroke: '#4caf50' }, |
|
||||||
HoverPaintStyle: { stroke: '#ccc', strokeWidth: 3 }, |
|
||||||
overlays: [['Label', { label: labels }]] |
|
||||||
}) |
|
||||||
} else if ($(`#${sourceId}`).attr('data-tasks-type') === 'CONDITIONS' && $(`#${sourceId}`).attr('data-failednode') === $(`#${targetId}`).find('.name-p').text()) { |
|
||||||
this.JspInstance.connect({ |
|
||||||
source: sourceId, |
|
||||||
target: targetId, |
|
||||||
type: 'basic', |
|
||||||
paintStyle: { strokeWidth: 2, stroke: '#252d39' }, |
|
||||||
HoverPaintStyle: { stroke: '#ccc', strokeWidth: 3 }, |
|
||||||
overlays: [['Label', { label: labels }]] |
|
||||||
}) |
|
||||||
} else { |
|
||||||
this.JspInstance.connect({ |
|
||||||
source: sourceId, |
|
||||||
target: targetId, |
|
||||||
type: 'basic', |
|
||||||
paintStyle: { strokeWidth: 2, stroke: '#2d8cf0' }, |
|
||||||
HoverPaintStyle: { stroke: '#ccc', strokeWidth: 3 }, |
|
||||||
overlays: [['Label', { label: labels }]] |
|
||||||
}) |
|
||||||
} |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
jsPlumb.fire('jsPlumbDemoLoaded', this.JspInstance) |
|
||||||
|
|
||||||
// Connection monitoring
|
|
||||||
this.handleEvent() |
|
||||||
|
|
||||||
// Drag and drop new nodes
|
|
||||||
this.draggable() |
|
||||||
} |
|
||||||
|
|
||||||
export default new JSP() |
|
@ -1,67 +0,0 @@ |
|||||||
/* |
|
||||||
* 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 JSP from './jsPlumbHandle' |
|
||||||
/** |
|
||||||
* when and only ctrl or meta key pressing, we can select one or more dags to drag |
|
||||||
*/ |
|
||||||
export default function () { |
|
||||||
// init
|
|
||||||
let selectableObjects = [] |
|
||||||
JSP.JspInstance.clearDragSelection() |
|
||||||
let ctrlPress = false |
|
||||||
|
|
||||||
let nodes = null |
|
||||||
const $window = $(window) |
|
||||||
|
|
||||||
$window.bind('keydown', function (event) { |
|
||||||
if (event.ctrlKey || event.metaKey) { |
|
||||||
if (nodes) { |
|
||||||
nodes.unbind('mousedown', select) |
|
||||||
} |
|
||||||
nodes = $('.jtk-draggable') |
|
||||||
nodes.bind('mousedown', select) |
|
||||||
ctrlPress = true |
|
||||||
} |
|
||||||
}) |
|
||||||
$window.bind('keyup', function (event) { |
|
||||||
clear() |
|
||||||
}) |
|
||||||
|
|
||||||
function select (event) { |
|
||||||
if (ctrlPress && event.button === 0) { |
|
||||||
let index = null |
|
||||||
if ((index = selectableObjects.indexOf(this)) !== -1) { |
|
||||||
selectableObjects.splice(index, 1) |
|
||||||
JSP.JspInstance.removeFromDragSelection(this) |
|
||||||
$(this).css('border-color', '') |
|
||||||
} else { |
|
||||||
selectableObjects.push(this) |
|
||||||
JSP.JspInstance.addToDragSelection(this) |
|
||||||
$(this).css('border-color', '#4af') |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function clear () { |
|
||||||
ctrlPress = false |
|
||||||
selectableObjects.map(item => { |
|
||||||
$(item).css('border-color', '') |
|
||||||
}) |
|
||||||
selectableObjects = [] |
|
||||||
JSP.JspInstance.clearDragSelection() |
|
||||||
} |
|
||||||
} |
|
@ -1,171 +0,0 @@ |
|||||||
/* |
|
||||||
* 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 _ from 'lodash' |
|
||||||
import i18n from '@/module/i18n' |
|
||||||
import store from '@/conf/home/store' |
|
||||||
|
|
||||||
/** |
|
||||||
* Node, to array |
|
||||||
*/ |
|
||||||
const rtTargetarrArr = (id) => { |
|
||||||
const ids = $(`#${id}`).attr('data-targetarr') |
|
||||||
return ids ? ids.split(',') : [] |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Store node id to targetarr |
|
||||||
*/ |
|
||||||
const saveTargetarr = (valId, domId) => { |
|
||||||
const $target = $(`#${domId}`) |
|
||||||
const targetStr = $target.attr('data-targetarr') ? $target.attr('data-targetarr') + `,${valId}` : `${valId}` |
|
||||||
$target.attr('data-targetarr', targetStr) |
|
||||||
} |
|
||||||
|
|
||||||
const rtBantpl = () => { |
|
||||||
return `<em class="ri-indeterminate-circle-line" data-toggle="tooltip" data-html="true" data-container="body" data-placement="left" title="${i18n.$t('Prohibition execution')}"></em>` |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* return node html |
|
||||||
*/ |
|
||||||
const rtTasksTpl = ({ id, name, x, y, targetarr, isAttachment, taskType, runFlag, nodenumber, successNode, failedNode }) => { |
|
||||||
let tpl = '' |
|
||||||
tpl += `<div class="w jtk-draggable jtk-droppable jtk-endpoint-anchor jtk-connected ${isAttachment ? 'jtk-ep' : ''}" data-targetarr="${targetarr || ''}" data-successNode="${successNode || ''}" data-failedNode="${failedNode || ''}" data-nodenumber="${nodenumber || 0}" data-tasks-type="${taskType}" id="${id}" style="left: ${x}px; top: ${y}px;">` |
|
||||||
tpl += '<div>' |
|
||||||
tpl += '<div class="state-p"></div>' |
|
||||||
tpl += `<div class="icos icos-${taskType}"></div>` |
|
||||||
tpl += `<span class="name-p">${name}</span>` |
|
||||||
tpl += '</div>' |
|
||||||
tpl += '<div class="ep"></div>' |
|
||||||
tpl += '<div class="ban-p">' |
|
||||||
if (runFlag === 'FORBIDDEN') { |
|
||||||
tpl += rtBantpl() |
|
||||||
} |
|
||||||
tpl += '</div>' |
|
||||||
tpl += '</div>' |
|
||||||
|
|
||||||
return tpl |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get all tasks nodes |
|
||||||
*/ |
|
||||||
const tasksAll = () => { |
|
||||||
const a = [] |
|
||||||
$('#canvas .w').each(function (idx, elem) { |
|
||||||
const e = $(elem) |
|
||||||
a.push({ |
|
||||||
id: e.attr('id'), |
|
||||||
name: e.find('.name-p').text(), |
|
||||||
targetarr: e.attr('data-targetarr') || '', |
|
||||||
nodenumber: e.attr('data-nodenumber'), |
|
||||||
x: parseInt(e.css('left'), 10), |
|
||||||
y: parseInt(e.css('top'), 10) |
|
||||||
}) |
|
||||||
}) |
|
||||||
return a |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Determine if name is in the current dag map |
|
||||||
* rely dom / backfill |
|
||||||
*/ |
|
||||||
const isNameExDag = (name, rely) => { |
|
||||||
if (rely === 'dom') { |
|
||||||
return _.findIndex(tasksAll(), v => v.name === name) !== -1 |
|
||||||
} else { |
|
||||||
return _.findIndex(store.state.dag.tasks, v => v.name === name) !== -1 |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Change svg line color |
|
||||||
*/ |
|
||||||
const setSvgColor = (e, color) => { |
|
||||||
// Traverse clear all colors
|
|
||||||
$('.jtk-connector').each((i, o) => { |
|
||||||
_.map($(o)[0].childNodes, v => { |
|
||||||
if ($(v).attr('fill') === '#ccc') { |
|
||||||
$(v).attr('fill', '#2d8cf0') |
|
||||||
} |
|
||||||
if ($(v).attr('fill') === '#4caf50') { |
|
||||||
$(v).attr('fill', '#4caf50').attr('stroke', '#4caf50').attr('stroke-width', 2) |
|
||||||
$(v).prev().attr('stroke', '#4caf50').attr('stroke-width', 2) |
|
||||||
} else if ($(v).attr('fill') === '#252d39') { |
|
||||||
$(v).attr('stroke', '#252d39').attr('stroke-width', 2) |
|
||||||
$(v).prev().attr('stroke', '#252d39').attr('stroke-width', 2) |
|
||||||
} else { |
|
||||||
$(v).attr('stroke', '#2d8cf0').attr('stroke-width', 2) |
|
||||||
} |
|
||||||
}) |
|
||||||
}) |
|
||||||
|
|
||||||
// Add color to the selection
|
|
||||||
_.map($(e.canvas)[0].childNodes, (v, i) => { |
|
||||||
if ($(v).attr('fill') === '#2d8cf0') { |
|
||||||
$(v).attr('fill', '#ccc') |
|
||||||
} |
|
||||||
$(v).attr('stroke', '#ccc') |
|
||||||
if ($(v).attr('class')) { |
|
||||||
$(v).attr('stroke-width', 2) |
|
||||||
} |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get all node ids |
|
||||||
*/ |
|
||||||
const allNodesId = () => { |
|
||||||
const idArr = [] |
|
||||||
$('.w').each((i, o) => { |
|
||||||
const $obj = $(o) |
|
||||||
const $span = $obj.find('.name-p').text() |
|
||||||
if ($span) { |
|
||||||
idArr.push({ |
|
||||||
id: $obj.attr('id'), |
|
||||||
name: $span |
|
||||||
}) |
|
||||||
} |
|
||||||
}) |
|
||||||
return idArr |
|
||||||
} |
|
||||||
/** |
|
||||||
* compute scale,because it cant get from jquery directly |
|
||||||
* @param el element |
|
||||||
* @returns {boolean|number} |
|
||||||
*/ |
|
||||||
const computeScale = function (el) { |
|
||||||
const matrix = el.css('transform') |
|
||||||
if (!matrix || matrix === 'none') { |
|
||||||
return false |
|
||||||
} |
|
||||||
const values = matrix.split('(')[1].split(')')[0].split(',') |
|
||||||
return Math.sqrt(values[0] * values[0] + values[1] * values[1]) |
|
||||||
} |
|
||||||
|
|
||||||
export { |
|
||||||
rtTargetarrArr, |
|
||||||
saveTargetarr, |
|
||||||
rtTasksTpl, |
|
||||||
tasksAll, |
|
||||||
isNameExDag, |
|
||||||
setSvgColor, |
|
||||||
allNodesId, |
|
||||||
rtBantpl, |
|
||||||
computeScale |
|
||||||
} |
|