* 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 |
||||
} |