Added flowchart stuff
This commit is contained in:
parent
59aa7f6b93
commit
ff1ed77907
1013
package-lock.json
generated
1013
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
|
"node-sass": "^5.0.0",
|
||||||
|
"sass-loader": "^10.1.0",
|
||||||
"vue": "^2.6.12",
|
"vue": "^2.6.12",
|
||||||
"vue-material": "^1.0.0-beta-15",
|
"vue-material": "^1.0.0-beta-15",
|
||||||
"vue-router": "^3.2.0",
|
"vue-router": "^3.2.0",
|
||||||
|
36
src/simple-flowchart/assets/utilty/position.js
Normal file
36
src/simple-flowchart/assets/utilty/position.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param element {HTMLElement}
|
||||||
|
* @return {{top: number, left: number}}
|
||||||
|
*/
|
||||||
|
function getOffsetRect (element) {
|
||||||
|
let box = element.getBoundingClientRect()
|
||||||
|
|
||||||
|
let scrollTop = window.pageYOffset
|
||||||
|
let scrollLeft = window.pageXOffset
|
||||||
|
|
||||||
|
let top = box.top + scrollTop
|
||||||
|
let left = box.left + scrollLeft
|
||||||
|
|
||||||
|
return {top: Math.round(top), left: Math.round(left)}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param event {MouseEvent}
|
||||||
|
* @param element {HTMLElement}
|
||||||
|
* @return {{x: number, y: number}}
|
||||||
|
*/
|
||||||
|
function getMousePosition (element, event) {
|
||||||
|
let mouseX = event.pageX || event.clientX + document.documentElement.scrollLeft
|
||||||
|
let mouseY = event.pageY || event.clientY + document.documentElement.scrollTop
|
||||||
|
|
||||||
|
let offset = getOffsetRect(element)
|
||||||
|
let x = mouseX - offset.left
|
||||||
|
let y = mouseY - offset.top
|
||||||
|
|
||||||
|
return [x, y];
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
getMousePosition, getOffsetRect
|
||||||
|
}
|
102
src/simple-flowchart/components/FlowchartLink.vue
Normal file
102
src/simple-flowchart/components/FlowchartLink.vue
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<template>
|
||||||
|
<g @mouseover="handleMouseOver"
|
||||||
|
@mouseleave="handleMouseLeave">
|
||||||
|
<path :d="dAttr" :style="pathStyle"></path>
|
||||||
|
<a v-if="show.delete" @click="deleteLink">
|
||||||
|
<text
|
||||||
|
text-anchor="middle"
|
||||||
|
:transform="arrowTransform"
|
||||||
|
font-size="22">×</text>
|
||||||
|
</a>
|
||||||
|
<path v-else d="M -1 -1 L 0 1 L 1 -1 z"
|
||||||
|
:style="arrowStyle"
|
||||||
|
:transform="arrowTransform"></path>
|
||||||
|
</g>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'FlowchartLink',
|
||||||
|
props: {
|
||||||
|
// start point position [x, y]
|
||||||
|
start: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return [0, 0]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// end point position [x, y]
|
||||||
|
end: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return [0, 0]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
id: Number,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
show: {
|
||||||
|
delete: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleMouseOver() {
|
||||||
|
if (this.id) {
|
||||||
|
this.show.delete = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleMouseLeave() {
|
||||||
|
this.show.delete = false;
|
||||||
|
},
|
||||||
|
caculateCenterPoint() {
|
||||||
|
// caculate arrow position: the center point between start and end
|
||||||
|
const dx = (this.end[0] - this.start[0]) / 2;
|
||||||
|
const dy = (this.end[1] - this.start[1]) / 2;
|
||||||
|
return [this.start[0] + dx, this.start[1] + dy];
|
||||||
|
},
|
||||||
|
caculateRotation() {
|
||||||
|
// caculate arrow rotation
|
||||||
|
const angle = -Math.atan2(this.end[0] - this.start[0], this.end[1] - this.start[1]);
|
||||||
|
const degree = angle * 180 / Math.PI;
|
||||||
|
return degree < 0 ? degree + 360 : degree;
|
||||||
|
},
|
||||||
|
deleteLink() {
|
||||||
|
this.$emit('deleteLink')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
pathStyle() {
|
||||||
|
return {
|
||||||
|
stroke: 'rgb(255, 136, 85)',
|
||||||
|
strokeWidth: 2.73205,
|
||||||
|
fill: 'none',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
arrowStyle() {
|
||||||
|
return {
|
||||||
|
stroke: 'rgb(255, 136, 85)',
|
||||||
|
strokeWidth: 5.73205,
|
||||||
|
fill: 'none',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
arrowTransform() {
|
||||||
|
const [arrowX, arrowY] = this.caculateCenterPoint();
|
||||||
|
const degree = this.caculateRotation()
|
||||||
|
return `translate(${arrowX}, ${arrowY}) rotate(${degree})`;
|
||||||
|
},
|
||||||
|
dAttr() {
|
||||||
|
let cx = this.start[0], cy = this.start[1], ex = this.end[0], ey = this.end[1];
|
||||||
|
let x1 = cx, y1 = cy + 50, x2 = ex, y2 = ey - 50;
|
||||||
|
return `M ${cx}, ${cy} C ${x1}, ${y1}, ${x2}, ${y2}, ${ex}, ${ey}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
g {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
214
src/simple-flowchart/components/FlowchartNode.vue
Normal file
214
src/simple-flowchart/components/FlowchartNode.vue
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flowchart-node" :style="nodeStyle"
|
||||||
|
@mousedown="handleMousedown"
|
||||||
|
@mouseover="handleMouseOver"
|
||||||
|
@mouseleave="handleMouseLeave"
|
||||||
|
v-bind:class="{selected: options.selected === id}">
|
||||||
|
|
||||||
|
<!--- Input port --->
|
||||||
|
<div class="node-port node-input"
|
||||||
|
v-if="haveInput"
|
||||||
|
@mousedown="inputMouseDown"
|
||||||
|
@mouseup="inputMouseUp">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--- The box itself --->
|
||||||
|
<div class="node-main">
|
||||||
|
<div v-text="type" class="node-type"></div>
|
||||||
|
<div class="node-label">Content</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--- Output port --->
|
||||||
|
<div class="node-port node-output"
|
||||||
|
v-if="haveOutput"
|
||||||
|
@mousedown="outputMouseDown">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="show.delete" class="node-delete">×</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'FlowchartNode',
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
default: 1000,
|
||||||
|
validator(val) {
|
||||||
|
return typeof val === 'number'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
validator(val) {
|
||||||
|
return typeof val === 'number'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
validator(val) {
|
||||||
|
return typeof val === 'number'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'Default'
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {
|
||||||
|
centerX: 1024,
|
||||||
|
scale: 1,
|
||||||
|
centerY: 140,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
haveInput: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
haveOutput: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
show: {
|
||||||
|
delete: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
nodeStyle() {
|
||||||
|
return {
|
||||||
|
top: this.options.centerY + this.y * this.options.scale + 'px', // remove: this.options.offsetTop +
|
||||||
|
left: this.options.centerX + this.x * this.options.scale + 'px', // remove: this.options.offsetLeft +
|
||||||
|
transform: `scale(${this.options.scale})`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleMousedown(e) {
|
||||||
|
const target = e.target || e.srcElement;
|
||||||
|
// console.log(target);
|
||||||
|
if (target.className.indexOf('node-input') < 0 && target.className.indexOf('node-output') < 0) {
|
||||||
|
this.$emit('nodeSelected', e);
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
},
|
||||||
|
handleMouseOver() {
|
||||||
|
this.show.delete = true;
|
||||||
|
},
|
||||||
|
handleMouseLeave() {
|
||||||
|
this.show.delete = false;
|
||||||
|
},
|
||||||
|
outputMouseDown(e) {
|
||||||
|
this.$emit('linkingStart')
|
||||||
|
e.preventDefault();
|
||||||
|
},
|
||||||
|
inputMouseDown(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
},
|
||||||
|
inputMouseUp(e) {
|
||||||
|
this.$emit('linkingStop')
|
||||||
|
e.preventDefault();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
<style scoped lang="scss">
|
||||||
|
$themeColor: rgb(85, 108, 255);
|
||||||
|
$portSize: 12;
|
||||||
|
|
||||||
|
.flowchart-node {
|
||||||
|
margin: 0;
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
position: absolute;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: none;
|
||||||
|
background: white;
|
||||||
|
z-index: 1;
|
||||||
|
opacity: .9;
|
||||||
|
cursor: move;
|
||||||
|
transform-origin: top left;
|
||||||
|
|
||||||
|
.node-main {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.node-type {
|
||||||
|
background: $themeColor;
|
||||||
|
color: white;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-label {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-port {
|
||||||
|
position: absolute;
|
||||||
|
width: #{$portSize}px;
|
||||||
|
height: #{$portSize}px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%);
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 100px;
|
||||||
|
background: white;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $themeColor;
|
||||||
|
border: 1px solid $themeColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-input {
|
||||||
|
top: #{-2+$portSize/-2}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-output {
|
||||||
|
bottom: #{-2+$portSize/-2}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-delete {
|
||||||
|
position: absolute;
|
||||||
|
right: -6px;
|
||||||
|
top: -6px;
|
||||||
|
font-size: 12px;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
color: $themeColor;
|
||||||
|
cursor: pointer;
|
||||||
|
background: white;
|
||||||
|
border: 1px solid $themeColor;
|
||||||
|
border-radius: 100px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $themeColor;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
box-shadow: 0 0 0 2px $themeColor;
|
||||||
|
}
|
||||||
|
</style>
|
273
src/simple-flowchart/components/SimpleFlowchart.vue
Normal file
273
src/simple-flowchart/components/SimpleFlowchart.vue
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flowchart-container"
|
||||||
|
@mousemove="handleMove"
|
||||||
|
@mouseup="handleUp"
|
||||||
|
@mousedown="handleDown">
|
||||||
|
|
||||||
|
<svg width="100%" height="100%">
|
||||||
|
<flowchart-link v-bind.sync="link"
|
||||||
|
v-for="(link, index) in lines"
|
||||||
|
:key="`link${index}`"
|
||||||
|
@deleteLink="linkDelete(link.id)">
|
||||||
|
</flowchart-link>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<flowchart-node v-bind.sync="node"
|
||||||
|
v-for="(node, index) in scene.nodes"
|
||||||
|
:key="`node${index}`"
|
||||||
|
:options="nodeOptions"
|
||||||
|
@linkingStart="linkingStart(node.id)"
|
||||||
|
@linkingStop="linkingStop(node.id)"
|
||||||
|
@nodeSelected="nodeSelected(node.id, $event)">
|
||||||
|
</flowchart-node>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import FlowchartLink from './FlowchartLink.vue';
|
||||||
|
import FlowchartNode from './FlowchartNode.vue';
|
||||||
|
import { getMousePosition } from '../assets/utilty/position';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'VueFlowchart',
|
||||||
|
props: {
|
||||||
|
scene: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {
|
||||||
|
centerX: 1024,
|
||||||
|
scale: 1,
|
||||||
|
centerY: 140,
|
||||||
|
nodes: [],
|
||||||
|
links: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
action: {
|
||||||
|
linking: false,
|
||||||
|
dragging: false,
|
||||||
|
scrolling: false,
|
||||||
|
selected: 0,
|
||||||
|
},
|
||||||
|
mouse: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
lastX: 0,
|
||||||
|
lastY: 0,
|
||||||
|
},
|
||||||
|
draggingLink: null,
|
||||||
|
rootDivOffset: {
|
||||||
|
top: 0,
|
||||||
|
left: 0
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
FlowchartLink,
|
||||||
|
FlowchartNode,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
nodeOptions() {
|
||||||
|
return {
|
||||||
|
centerY: this.scene.centerY,
|
||||||
|
centerX: this.scene.centerX,
|
||||||
|
scale: this.scene.scale,
|
||||||
|
offsetTop: this.rootDivOffset.top,
|
||||||
|
offsetLeft: this.rootDivOffset.left,
|
||||||
|
selected: this.action.selected,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lines() {
|
||||||
|
const lines = this.scene.links.map((link) => {
|
||||||
|
const fromNode = this.findNodeWithID(link.from)
|
||||||
|
const toNode = this.findNodeWithID(link.to)
|
||||||
|
let x, y, cy, cx, ex, ey;
|
||||||
|
x = this.scene.centerX + fromNode.x;
|
||||||
|
y = this.scene.centerY + fromNode.y;
|
||||||
|
[cx, cy] = this.getPortPosition('bottom', x, y);
|
||||||
|
x = this.scene.centerX + toNode.x;
|
||||||
|
y = this.scene.centerY + toNode.y;
|
||||||
|
[ex, ey] = this.getPortPosition('top', x, y);
|
||||||
|
return {
|
||||||
|
start: [cx, cy],
|
||||||
|
end: [ex, ey],
|
||||||
|
id: link.id,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
if (this.draggingLink) {
|
||||||
|
let x, y, cy, cx;
|
||||||
|
const fromNode = this.findNodeWithID(this.draggingLink.from)
|
||||||
|
x = this.scene.centerX + fromNode.x;
|
||||||
|
y = this.scene.centerY + fromNode.y;
|
||||||
|
[cx, cy] = this.getPortPosition('bottom', x, y);
|
||||||
|
// push temp dragging link, mouse cursor postion = link end postion
|
||||||
|
lines.push({
|
||||||
|
start: [cx, cy],
|
||||||
|
end: [this.draggingLink.mx, this.draggingLink.my],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.rootDivOffset.top = this.$el ? this.$el.offsetTop : 0;
|
||||||
|
this.rootDivOffset.left = this.$el ? this.$el.offsetLeft : 0;
|
||||||
|
// console.log(22222, this.rootDivOffset);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
findNodeWithID(id) {
|
||||||
|
return this.scene.nodes.find((item) => {
|
||||||
|
return id === item.id
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getPortPosition(type, x, y) {
|
||||||
|
if (type === 'top') {
|
||||||
|
return [x + 40, y];
|
||||||
|
}
|
||||||
|
else if (type === 'bottom') {
|
||||||
|
return [x + 40, y + 80];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
linkingStart(index) {
|
||||||
|
this.action.linking = true;
|
||||||
|
this.draggingLink = {
|
||||||
|
from: index,
|
||||||
|
mx: 0,
|
||||||
|
my: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
linkingStop(index) {
|
||||||
|
// add new Link
|
||||||
|
if (this.draggingLink && this.draggingLink.from !== index) {
|
||||||
|
// check link existence
|
||||||
|
const existed = this.scene.links.find((link) => {
|
||||||
|
return link.from === this.draggingLink.from && link.to === index;
|
||||||
|
})
|
||||||
|
if (!existed) {
|
||||||
|
let maxID = Math.max(0, ...this.scene.links.map((link) => {
|
||||||
|
return link.id
|
||||||
|
}))
|
||||||
|
const newLink = {
|
||||||
|
id: maxID + 1,
|
||||||
|
from: this.draggingLink.from,
|
||||||
|
to: index,
|
||||||
|
};
|
||||||
|
this.scene.links.push(newLink)
|
||||||
|
this.$emit('linkAdded', newLink)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.draggingLink = null
|
||||||
|
},
|
||||||
|
linkDelete(id) {
|
||||||
|
const deletedLink = this.scene.links.find((item) => {
|
||||||
|
return item.id === id;
|
||||||
|
});
|
||||||
|
if (deletedLink) {
|
||||||
|
this.scene.links = this.scene.links.filter((item) => {
|
||||||
|
return item.id !== id;
|
||||||
|
});
|
||||||
|
this.$emit('linkBreak', deletedLink);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodeSelected(id, e) {
|
||||||
|
this.action.dragging = id;
|
||||||
|
this.action.selected = id;
|
||||||
|
this.$emit('nodeClick', id);
|
||||||
|
this.mouse.lastX = e.pageX || e.clientX + document.documentElement.scrollLeft
|
||||||
|
this.mouse.lastY = e.pageY || e.clientY + document.documentElement.scrollTop
|
||||||
|
},
|
||||||
|
handleMove(e) {
|
||||||
|
if (this.action.linking) {
|
||||||
|
[this.mouse.x, this.mouse.y] = getMousePosition(this.$el, e);
|
||||||
|
[this.draggingLink.mx, this.draggingLink.my] = [this.mouse.x, this.mouse.y];
|
||||||
|
}
|
||||||
|
if (this.action.dragging) {
|
||||||
|
this.mouse.x = e.pageX || e.clientX + document.documentElement.scrollLeft
|
||||||
|
this.mouse.y = e.pageY || e.clientY + document.documentElement.scrollTop
|
||||||
|
let diffX = this.mouse.x - this.mouse.lastX;
|
||||||
|
let diffY = this.mouse.y - this.mouse.lastY;
|
||||||
|
|
||||||
|
this.mouse.lastX = this.mouse.x;
|
||||||
|
this.mouse.lastY = this.mouse.y;
|
||||||
|
this.moveSelectedNode(diffX, diffY);
|
||||||
|
}
|
||||||
|
if (this.action.scrolling) {
|
||||||
|
[this.mouse.x, this.mouse.y] = getMousePosition(this.$el, e);
|
||||||
|
let diffX = this.mouse.x - this.mouse.lastX;
|
||||||
|
let diffY = this.mouse.y - this.mouse.lastY;
|
||||||
|
|
||||||
|
this.mouse.lastX = this.mouse.x;
|
||||||
|
this.mouse.lastY = this.mouse.y;
|
||||||
|
|
||||||
|
this.scene.centerX += diffX;
|
||||||
|
this.scene.centerY += diffY;
|
||||||
|
|
||||||
|
// this.hasDragged = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleUp(e) {
|
||||||
|
const target = e.target || e.srcElement;
|
||||||
|
if (this.$el.contains(target)) {
|
||||||
|
if (typeof target.className !== 'string' || target.className.indexOf('node-input') < 0) {
|
||||||
|
this.draggingLink = null;
|
||||||
|
}
|
||||||
|
if (typeof target.className === 'string' && target.className.indexOf('node-delete') > -1) {
|
||||||
|
// console.log('delete2', this.action.dragging);
|
||||||
|
this.nodeDelete(this.action.dragging);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.action.linking = false;
|
||||||
|
this.action.dragging = null;
|
||||||
|
this.action.scrolling = false;
|
||||||
|
},
|
||||||
|
handleDown(e) {
|
||||||
|
const target = e.target || e.srcElement;
|
||||||
|
// console.log('for scroll', target, e.keyCode, e.which)
|
||||||
|
if ((target === this.$el || target.matches('svg, svg *')) && e.which === 1) {
|
||||||
|
this.action.scrolling = true;
|
||||||
|
[this.mouse.lastX, this.mouse.lastY] = getMousePosition(this.$el, e);
|
||||||
|
this.action.selected = null; // deselectAll
|
||||||
|
}
|
||||||
|
this.$emit('canvasClick', e);
|
||||||
|
},
|
||||||
|
moveSelectedNode(dx, dy) {
|
||||||
|
let index = this.scene.nodes.findIndex((item) => {
|
||||||
|
return item.id === this.action.dragging
|
||||||
|
})
|
||||||
|
let left = this.scene.nodes[index].x + dx / this.scene.scale;
|
||||||
|
let top = this.scene.nodes[index].y + dy / this.scene.scale;
|
||||||
|
this.$set(this.scene.nodes, index, Object.assign(this.scene.nodes[index], {
|
||||||
|
x: left,
|
||||||
|
y: top,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
nodeDelete(id) {
|
||||||
|
this.scene.nodes = this.scene.nodes.filter((node) => {
|
||||||
|
return node.id !== id;
|
||||||
|
})
|
||||||
|
this.scene.links = this.scene.links.filter((link) => {
|
||||||
|
return link.from !== id && link.to !== id
|
||||||
|
})
|
||||||
|
this.$emit('nodeDelete', id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.flowchart-container {
|
||||||
|
margin: 0;
|
||||||
|
background: #f3f3f3;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 100%;
|
||||||
|
svg {
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
3
src/simple-flowchart/index.js
Normal file
3
src/simple-flowchart/index.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
import SimpleFlowchart from './components/SimpleFlowchart.vue'
|
||||||
|
export default SimpleFlowchart;
|
@ -44,8 +44,8 @@
|
|||||||
</md-drawer>
|
</md-drawer>
|
||||||
|
|
||||||
<md-content id="diagram-container">
|
<md-content id="diagram-container">
|
||||||
<md-progress-bar v-if="true" class="md-accent" md-mode="indeterminate"></md-progress-bar>
|
|
||||||
|
|
||||||
|
<simple-flowchart :scene.sync="diagram"></simple-flowchart>
|
||||||
<div id="fab-holder">
|
<div id="fab-holder">
|
||||||
<md-button class="md-fab" @click="performAddElement">
|
<md-button class="md-fab" @click="performAddElement">
|
||||||
<md-icon>add</md-icon>
|
<md-icon>add</md-icon>
|
||||||
@ -58,12 +58,53 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
import SimpleFlowchart from '@/simple-flowchart';
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Dashboard',
|
name: 'Dashboard',
|
||||||
components: {},
|
components: {SimpleFlowchart},
|
||||||
data: () => ({
|
data: () => ({
|
||||||
showNavigation: false,
|
showNavigation: false,
|
||||||
showNewElementChooser: false
|
showNewElementChooser: false,
|
||||||
|
diagram: {
|
||||||
|
centerX: 1024,
|
||||||
|
centerY: 140,
|
||||||
|
scale: 1,
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
x: -700,
|
||||||
|
y: -69,
|
||||||
|
type: 'Action',
|
||||||
|
data: {"text" : "lofasz"},
|
||||||
|
haveInput: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
x: -357,
|
||||||
|
y: 80,
|
||||||
|
type: 'Script',
|
||||||
|
label: 'test2',
|
||||||
|
haveOutput: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
x: -557,
|
||||||
|
y: 80,
|
||||||
|
type: 'Rule',
|
||||||
|
label: 'test3',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
from: 2, // node id the link start
|
||||||
|
to: 4, // node id the link end
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
methods: {
|
methods: {
|
||||||
performAddElement() {
|
performAddElement() {
|
||||||
|
Loading…
Reference in New Issue
Block a user