1.新增高德自定义图层解决方案:遗留问题:鼠标点击地图后会吸附到地图上,操作滚轮可取消吸附
2.新增gojs+leaflet解决方案:研究进度:案例简单结合,如采用该方案需要深度学习gojs和leaflet,将两者进行深度融合
This commit is contained in:
parent
9fc0b40cd6
commit
e4024064d1
|
@ -8,14 +8,16 @@
|
||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||||
"@antv/x6": "^1.32.3-beta.1",
|
"@antv/x6": "^1.32.3-beta.1",
|
||||||
"@antv/x6-vue-shape": "^1.4.0",
|
"@antv/x6-vue-shape": "^1.4.0",
|
||||||
"@vue/composition-api": "^1.6.3",
|
"@vue/composition-api": "^1.6.3",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"element-ui": "^2.15.9",
|
"element-ui": "^2.15.9",
|
||||||
|
"gojs": "^2.2.14",
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue-json-viewer": "^2.2.22",
|
"vue-baidu-map": "^0.21.22",
|
||||||
"vue-baidu-map": "^0.21.22"
|
"vue-json-viewer": "^2.2.22"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "~4.5.13",
|
"@vue/cli-plugin-babel": "~4.5.13",
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.8.0/dist/leaflet.css">
|
||||||
|
<script src="https://unpkg.com/leaflet@1.8.0/dist/leaflet.js"></script>
|
||||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
10
src/App.vue
10
src/App.vue
|
@ -1,15 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<index-stencil></index-stencil>
|
<!-- <index-stencil></index-stencil> -->
|
||||||
|
<!-- <index-gaode></index-gaode> -->
|
||||||
|
<index-gojs></index-gojs>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import indexStencil from "./demo/index-stencil.vue";
|
import indexStencil from "./demo/index-stencil.vue";
|
||||||
|
import indexGaode from "./demo/index-gaode.vue";
|
||||||
|
import indexGojs from "./gojsDemo/index-gojs.vue";
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
components: {
|
components: {
|
||||||
indexStencil
|
indexStencil,
|
||||||
|
indexGaode,
|
||||||
|
indexGojs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,572 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="tools-bar">
|
||||||
|
<el-popover
|
||||||
|
placement="bottom"
|
||||||
|
width="200"
|
||||||
|
class="el-po"
|
||||||
|
trigger="click">
|
||||||
|
<div ref="stencilContainer" class="x6-stencil"></div>
|
||||||
|
<i class="el-icon-s-operation opt-btn" slot="reference" ></i>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
|
<div class="content" :style="{ height: contentHeight }">
|
||||||
|
<!-- <div ref="stencilContainer" class="x6-stencil"></div> -->
|
||||||
|
<div class="middle-box">
|
||||||
|
<div
|
||||||
|
ref="container"
|
||||||
|
class="x6-content"
|
||||||
|
:style="{ height: contentHeight }"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
id="map-container"
|
||||||
|
class="mp-view"
|
||||||
|
:style="{ height: contentHeight }"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Graph, Shape, Addon,Path } from '@antv/x6';
|
||||||
|
const { Stencil } = Addon
|
||||||
|
const { Rect, Circle } = Shape
|
||||||
|
import AMapLoader from '@amap/amap-jsapi-loader';
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
contentHeight:'0px', //容器高度,x6高度
|
||||||
|
mapHeight: "200px", //地图高度
|
||||||
|
center: {lng: 0, lat: 0},//地图中心点变量
|
||||||
|
zoom: 3,//地图缩放变量
|
||||||
|
|
||||||
|
graph:undefined,//x6画布
|
||||||
|
stencil:undefined,//x6拖拽
|
||||||
|
|
||||||
|
projection:undefined,//地图projection参数
|
||||||
|
|
||||||
|
map:null,//高德地图
|
||||||
|
data : {
|
||||||
|
// 节点
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'node1', // String,可选,节点的唯一标识
|
||||||
|
x: 0, // Number,必选,节点位置的 x 值
|
||||||
|
y: 0, // Number,必选,节点位置的 y 值
|
||||||
|
width: 80, // Number,可选,节点大小的 width 值
|
||||||
|
height: 40, // Number,可选,节点大小的 height 值
|
||||||
|
label: 'hello', // String,节点标签
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'node2', // String,节点的唯一标识
|
||||||
|
x: 160, // Number,必选,节点位置的 x 值
|
||||||
|
y: 180, // Number,必选,节点位置的 y 值
|
||||||
|
width: 80, // Number,可选,节点大小的 width 值
|
||||||
|
height: 40, // Number,可选,节点大小的 height 值
|
||||||
|
label: 'world', // String,节点标签
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 边
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
source: 'node1', // String,必须,起始节点 id
|
||||||
|
target: 'node2', // String,必须,目标节点 id
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.init()
|
||||||
|
this.initCell()
|
||||||
|
this.initGraph();
|
||||||
|
this.initStencil()
|
||||||
|
this.initGaodeMap()
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
onRender() {
|
||||||
|
//根据地理坐标计算屏幕坐标
|
||||||
|
|
||||||
|
//获取所有的点
|
||||||
|
const container = this.$refs.container
|
||||||
|
const ports = container.querySelectorAll(
|
||||||
|
'.x6-port-body',
|
||||||
|
)
|
||||||
|
//每个点的屏幕坐标重新计算
|
||||||
|
this.graph.getNodes().forEach((node)=>{
|
||||||
|
let mapPoint = node.store.data.mapPoint
|
||||||
|
//由地理位置转换为屏幕位置
|
||||||
|
var lnglat = new AMap.LngLat(mapPoint.lng,mapPoint.lat);
|
||||||
|
var pixel = this.map.lngLatToContainer(lnglat);
|
||||||
|
node.position(pixel.round().x,pixel.round().y)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
init() {
|
||||||
|
this.contentHeight = document.documentElement.clientHeight -45 + "px";
|
||||||
|
this.mapHeight = document.documentElement.clientHeight -45 + "px";
|
||||||
|
},
|
||||||
|
//初始化图形
|
||||||
|
initCell(){
|
||||||
|
//桩
|
||||||
|
const ports = {
|
||||||
|
groups: {
|
||||||
|
top: {
|
||||||
|
position: 'top',
|
||||||
|
attrs: {
|
||||||
|
circle: {
|
||||||
|
r: 4,
|
||||||
|
magnet: true,
|
||||||
|
stroke: '#5F95FF',
|
||||||
|
strokeWidth: 1,
|
||||||
|
fill: '#fff',
|
||||||
|
style: {
|
||||||
|
visibility: 'hidden',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
right: {
|
||||||
|
position: 'right',
|
||||||
|
attrs: {
|
||||||
|
circle: {
|
||||||
|
r: 4,
|
||||||
|
magnet: true,
|
||||||
|
stroke: '#5F95FF',
|
||||||
|
strokeWidth: 1,
|
||||||
|
fill: '#fff',
|
||||||
|
style: {
|
||||||
|
visibility: 'hidden',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
bottom: {
|
||||||
|
position: 'bottom',
|
||||||
|
attrs: {
|
||||||
|
circle: {
|
||||||
|
r: 4,
|
||||||
|
magnet: true,
|
||||||
|
stroke: '#5F95FF',
|
||||||
|
strokeWidth: 1,
|
||||||
|
fill: '#fff',
|
||||||
|
style: {
|
||||||
|
visibility: 'hidden',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
left: {
|
||||||
|
position: 'left',
|
||||||
|
attrs: {
|
||||||
|
circle: {
|
||||||
|
r: 4,
|
||||||
|
magnet: true,
|
||||||
|
stroke: '#5F95FF',
|
||||||
|
strokeWidth: 1,
|
||||||
|
fill: '#fff',
|
||||||
|
style: {
|
||||||
|
visibility: 'hidden',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
group: 'top',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'right',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'bottom',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'left',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
//自定义节点
|
||||||
|
Graph.registerNode(
|
||||||
|
'custom-rect',
|
||||||
|
{
|
||||||
|
inherit: 'rect',
|
||||||
|
width: 66,
|
||||||
|
height: 36,
|
||||||
|
attrs: {
|
||||||
|
body: {
|
||||||
|
strokeWidth: 1,
|
||||||
|
stroke: '#5F95FF',
|
||||||
|
fill: '#EFF4FF',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
fontSize: 12,
|
||||||
|
fill: '#262626',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ports: { ...ports },
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
//初始化高德地图
|
||||||
|
initGaodeMap(){
|
||||||
|
AMapLoader.load({
|
||||||
|
key:'5cebc9186d74c44afbfee1e178a89782',
|
||||||
|
version:'2.0',
|
||||||
|
plugins:['']
|
||||||
|
}).then((AMap)=>{
|
||||||
|
this.map = new AMap.Map("map-container",{
|
||||||
|
zoom:5,//初始化地图级别
|
||||||
|
center:[105.602725,37.076636],//初始化地图中心位置
|
||||||
|
})
|
||||||
|
|
||||||
|
//var x6Dom = document.createElement('container');
|
||||||
|
var x6Dom = this.$refs.container
|
||||||
|
var x6 = document.getElementsByClassName("x6-graph-svg")[1];
|
||||||
|
console.log("x6Dom",this.$refs.container)
|
||||||
|
//创建自定义图层,添加到地图
|
||||||
|
var customLayer = new AMap.CustomLayer(x6Dom, {
|
||||||
|
zIndex: 12,
|
||||||
|
zooms: [3, 18], // 设置可见级别,[最小级别,最大级别]
|
||||||
|
dragEnable: true
|
||||||
|
});
|
||||||
|
customLayer.render = this.onRender;
|
||||||
|
this.map.add(customLayer)
|
||||||
|
|
||||||
|
}).catch(e=>{
|
||||||
|
console.log(e)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
//初始化画布
|
||||||
|
initGraph(){
|
||||||
|
console.log("x6图层",this.$refs.container)
|
||||||
|
let mapZoom = this.zoom
|
||||||
|
this.graph = new Graph({
|
||||||
|
container: this.$refs.container,
|
||||||
|
background: {//背景
|
||||||
|
color: 'transport', // 设置画布背景颜色
|
||||||
|
},
|
||||||
|
grid: {//网格
|
||||||
|
size: 10, // 网格大小 10px
|
||||||
|
visible: true, // 渲染网格背景
|
||||||
|
},
|
||||||
|
// panning: {//画布是否可以拖动
|
||||||
|
// enabled: true, //开启支持拖拽平移
|
||||||
|
// modifiers: 'shift', //按下修饰键并点击鼠标才能触发画布拖拽
|
||||||
|
// },
|
||||||
|
// mousewheel: {//滚轮缩放画布
|
||||||
|
// enabled: true,
|
||||||
|
// modifiers: ['ctrl'],
|
||||||
|
// guard(e){//判断一个滚轮事件是否应该被处理
|
||||||
|
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
snapline: true,//对齐线
|
||||||
|
selecting: {//点选/框选
|
||||||
|
enabled: true,//开启点选/框选
|
||||||
|
rubberband: true,//启用框选
|
||||||
|
showNodeSelectionBox: true,//是否显示节点的选择框
|
||||||
|
},
|
||||||
|
resizing:true,//缩放节点
|
||||||
|
highlighting: {//高亮选项,指定触发某种交互时的高亮样式
|
||||||
|
// 当链接桩可以被链接时,在链接桩外围渲染一个蓝色
|
||||||
|
magnetAdsorbed: {
|
||||||
|
name: 'stroke',
|
||||||
|
args: {
|
||||||
|
attrs: {
|
||||||
|
//fill: '#5F95FF',
|
||||||
|
stroke: '#5F95FF',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
//当可以当父节点时,高亮
|
||||||
|
embedding: {
|
||||||
|
name: 'stroke',
|
||||||
|
args: {
|
||||||
|
padding: -1,
|
||||||
|
attrs: {
|
||||||
|
stroke: '#73d13d',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
connecting:{//连线配置
|
||||||
|
//设置连接桩自动吸附,吸附距离为20
|
||||||
|
snap: {
|
||||||
|
radius: 20,
|
||||||
|
},
|
||||||
|
allowBlank:false,//是否允许连接到画布空白位置的点,设置为否
|
||||||
|
allowNode:false,//是否允许边链接到节点(非节点上的链接桩)
|
||||||
|
allowEdge:false,//是否允许边链接到另一个边
|
||||||
|
anchor: 'center',//当连接到节点时,通过 anchor 来指定被连接的节点的锚点
|
||||||
|
connectionPoint: 'anchor',//指定连接点
|
||||||
|
router: {//路由将边的路径点 vertices 做进一步转换处理
|
||||||
|
name: 'manhattan',
|
||||||
|
args: {
|
||||||
|
padding: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
connector: {//连接器
|
||||||
|
name: 'rounded',
|
||||||
|
args: {
|
||||||
|
radius: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
createEdge() {//创建新的边
|
||||||
|
return new Shape.Edge({
|
||||||
|
attrs: {
|
||||||
|
line: {
|
||||||
|
//stroke: '#A2B1C3',
|
||||||
|
stroke: 'green',
|
||||||
|
strokeWidth: 3,
|
||||||
|
targetMarker: {
|
||||||
|
name: 'block',
|
||||||
|
width: 12,
|
||||||
|
height: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
zIndex: 20,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
validateConnection({ targetMagnet }) {//在移动边的时候判断连接是否有效
|
||||||
|
return !!targetMagnet
|
||||||
|
},
|
||||||
|
},
|
||||||
|
//节点嵌套
|
||||||
|
embedding:{
|
||||||
|
enabled:true,//开启节点嵌套
|
||||||
|
findParent(node){//在节点被移动时通过 findParent 指定的方法返回父节点
|
||||||
|
const bbox = node.node.getBBox()
|
||||||
|
return this.getNodes().filter((node) => {
|
||||||
|
// 只有 data.parent 为 true 的节点才是父节点
|
||||||
|
const data = node.getData()
|
||||||
|
if (data && data.canBeParent) {
|
||||||
|
const targetBBox = node.getBBox()
|
||||||
|
return bbox.isIntersectWithRect(targetBBox)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//限制子节点的移动范围
|
||||||
|
translating: {
|
||||||
|
restrict(view) {
|
||||||
|
if(view){
|
||||||
|
const cell = view.cell
|
||||||
|
if (cell.isNode()) {
|
||||||
|
const parent = cell.getParent()
|
||||||
|
if (parent) {
|
||||||
|
return parent.getBBox()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
//this.graph.fromJSON(this.data)
|
||||||
|
|
||||||
|
//事件注册
|
||||||
|
//鼠标移动到节点上,显示桩、给当前节点添加删除按钮
|
||||||
|
this.graph.on('node:mouseenter', ( e ) => {
|
||||||
|
const container = this.$refs.container
|
||||||
|
const ports = container.querySelectorAll(
|
||||||
|
'.x6-port-body',
|
||||||
|
)
|
||||||
|
this.showPorts(ports, true)
|
||||||
|
this.showNodeTool(e.node,true)
|
||||||
|
//当鼠标移动到节点上,关闭地图的拖拽
|
||||||
|
this.changeMapDrag(false)
|
||||||
|
})
|
||||||
|
//鼠标移出节点,隐藏桩、删除当前节点删除按钮
|
||||||
|
this.graph.on('node:mouseleave', ( e) => {
|
||||||
|
const container = this.$refs.container
|
||||||
|
const ports = container.querySelectorAll(
|
||||||
|
'.x6-port-body',
|
||||||
|
)
|
||||||
|
this.showPorts(ports, false)
|
||||||
|
this.showNodeTool(e.node,false)
|
||||||
|
//当鼠标移动到节点上,开启地图的拖拽
|
||||||
|
this.changeMapDrag(true)
|
||||||
|
})
|
||||||
|
//鼠标移动到线上,给当前线添加删除按钮
|
||||||
|
this.graph.on('edge:mouseenter',(e)=>{
|
||||||
|
this.showEdgeTool(e.edge,true)
|
||||||
|
})
|
||||||
|
//鼠标移出线,删除当前线的删除按钮
|
||||||
|
this.graph.on('edge:mouseleave',(e)=>{
|
||||||
|
this.showEdgeTool(e.edge,false)
|
||||||
|
})
|
||||||
|
//移入父节点,父节点改变后
|
||||||
|
this.graph.on('node:change:parent', ({ node }) => {
|
||||||
|
node.attr({
|
||||||
|
body:{
|
||||||
|
stroke: 'none',
|
||||||
|
fill: '#47C769',
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
text: '节点-过程',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
//节点添加到画布中
|
||||||
|
this.graph.on('node:added',(args)=>{
|
||||||
|
//将在容器的位置坐标转换为地图的地理坐标
|
||||||
|
this.pixelToPoint(args.node,args.node.store.data.position)
|
||||||
|
})
|
||||||
|
//节点在画布上移动
|
||||||
|
this.graph.on('node:change:position',(args)=>{
|
||||||
|
//将在容器的位置坐标转换为地图的地理坐标
|
||||||
|
this.pixelToPoint(args.node,args.current)
|
||||||
|
})
|
||||||
|
//画布平移
|
||||||
|
// this.graph.on('translate', ({ tx, ty }) => {
|
||||||
|
// console.log("平移tx",tx)
|
||||||
|
// //console.log("平移ty",ty)
|
||||||
|
// this.mapScrollBy(tx,ty)
|
||||||
|
// })
|
||||||
|
},
|
||||||
|
//初始化拖拽Stencil
|
||||||
|
initStencil(){
|
||||||
|
this.stencil = new Stencil({
|
||||||
|
target: this.graph,//设置画布
|
||||||
|
stencilGraphWidth: 200,//模板画布宽度
|
||||||
|
stencilGraphHeight: 80,//模板画布高度
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
name: 'group1',//分组名称
|
||||||
|
title: '元素',//分组标题
|
||||||
|
collapsable: false,//分组是否可折叠,默认为 true
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
//将Stencil挂载到页面上
|
||||||
|
this.$refs.stencilContainer.appendChild(this.stencil.container)
|
||||||
|
//定义模板节点
|
||||||
|
const process = this.graph.createNode({
|
||||||
|
shape: 'custom-rect',
|
||||||
|
label: '过程',
|
||||||
|
zIndex: 10,
|
||||||
|
attrs: {
|
||||||
|
body: {
|
||||||
|
stroke: 'none',
|
||||||
|
fill: '#3199FF',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const node = this.graph.createNode({
|
||||||
|
shape: 'custom-rect',
|
||||||
|
label: '节点',
|
||||||
|
zIndex: 1,
|
||||||
|
data: {
|
||||||
|
canBeParent: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
//装载模板节点
|
||||||
|
this.stencil.load([process,node], 'group1')
|
||||||
|
},
|
||||||
|
//链接桩显示/隐藏事件
|
||||||
|
showPorts(ports,show){
|
||||||
|
ports.forEach(port => {
|
||||||
|
port.style.visibility = show?'visible' : 'hidden'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
//节点上的删除按钮添加/删除事件
|
||||||
|
showNodeTool(node,show){
|
||||||
|
if(show){
|
||||||
|
node.addTools({
|
||||||
|
name: 'button-remove',
|
||||||
|
args: {
|
||||||
|
x: '100%',
|
||||||
|
y: 0,
|
||||||
|
offset: { x: -3, y: 3 },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
node.removeTools()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//线上的删除按钮添加/删除事件
|
||||||
|
showEdgeTool(edge,show){
|
||||||
|
if(show){
|
||||||
|
edge.addTools({
|
||||||
|
name: 'button-remove',
|
||||||
|
args: { distance: -40 },
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
edge.removeTools()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//将像素坐标转换为地理坐标
|
||||||
|
pixelToPoint(node,position){
|
||||||
|
let ePoint = this.map.containerToLngLat(new AMap.Pixel(position.x, position.y))
|
||||||
|
//将数据记录到node中
|
||||||
|
node.store.data.mapPoint = ePoint
|
||||||
|
console.log("新位置数据",node)
|
||||||
|
},
|
||||||
|
//地图平移
|
||||||
|
mapScrollBy(tx,ty){
|
||||||
|
this.projection.panBy(tx,ty)
|
||||||
|
},
|
||||||
|
//关闭/开启地图拖拽
|
||||||
|
changeMapDrag(canDrag){
|
||||||
|
this.map.setStatus({
|
||||||
|
dragEnable:canDrag
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.tools-bar{
|
||||||
|
height: 45px;
|
||||||
|
line-height: 45px;
|
||||||
|
background: #fff;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.opt-btn{
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 35px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
.x6-stencil{
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
.middle-box{
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
.x6-content {
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.mp-view{
|
||||||
|
width: 100%;
|
||||||
|
position:absolute;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-popover{
|
||||||
|
height: 500px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style>
|
||||||
|
.el-popover{
|
||||||
|
height: 120px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,580 @@
|
||||||
|
<!--
|
||||||
|
* @FilePath src/views/dashboard/components/FlowChart/index.vue
|
||||||
|
* @Created Bay丶<baizhanying@autobio.com.cn> 2021-11-10 14:23:50
|
||||||
|
* @Modified Bay丶<baizhanying@autobio.com.cn> 2021-11-10 18:11:22
|
||||||
|
* @Description https://github.com/NorthwoodsSoftware/GoJS/blob/master/samples/flowchart.html
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="diagram-contanier">
|
||||||
|
<div id="diagram-tool">
|
||||||
|
<slot>
|
||||||
|
<el-button type="primary" @click="save">保存</el-button>
|
||||||
|
<el-button @click="load">加载</el-button>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
<div id="diagram-wrap">
|
||||||
|
<div id="diagram-palette" ref="palette" />
|
||||||
|
<!-- <div id="diagram" ref="diagram" /> -->
|
||||||
|
<!-- <div>
|
||||||
|
<div id="diagram" ref="diagram" />
|
||||||
|
</div> -->
|
||||||
|
<div id="map"
|
||||||
|
class="mapDiagram leaflet-container leaflet-touch leaflet-retina leaflet-fade-anim leaflet-touch-zoom leaflet-grab leaflet-touch-drag"
|
||||||
|
tabindex="0"
|
||||||
|
style="position: relative;width:100%">
|
||||||
|
<div style="height:100%" id="diagram" ref="diagram" />
|
||||||
|
</div>
|
||||||
|
<!-- <div id="diagram" ref="diagram" /> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import go from "gojs";
|
||||||
|
const $ = go.GraphObject.make;
|
||||||
|
export default {
|
||||||
|
name: "Index",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
diagram: null,
|
||||||
|
palette: null,
|
||||||
|
|
||||||
|
textStyle: {
|
||||||
|
font: "bold 9pt Lato, Helvetica, Arial, sans-serif",
|
||||||
|
stroke: "#F8F8F8",
|
||||||
|
},
|
||||||
|
chart: {
|
||||||
|
class: "GraphLinksModel",
|
||||||
|
linkFromPortIdProperty: "fromPort",
|
||||||
|
linkToPortIdProperty: "toPort",
|
||||||
|
nodeDataArray: [
|
||||||
|
{
|
||||||
|
category: "Start",
|
||||||
|
text: "开始",
|
||||||
|
key: -1,
|
||||||
|
loc: "-107.94070280440474 -309.08209495195183",
|
||||||
|
},
|
||||||
|
{ text: "流程", key: -2, loc: "-107.68839591351332 -189.421875" },
|
||||||
|
{
|
||||||
|
category: "Conditional",
|
||||||
|
text: "逻辑判断",
|
||||||
|
key: -3,
|
||||||
|
loc: "-107.84415803147851 -72.68596298078074",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "End",
|
||||||
|
text: "结束",
|
||||||
|
key: -4,
|
||||||
|
loc: "-109.21840230579596 76.632035837822",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
linkDataArray: [
|
||||||
|
{
|
||||||
|
from: -2,
|
||||||
|
to: -3,
|
||||||
|
fromPort: "B",
|
||||||
|
toPort: "T",
|
||||||
|
points: [
|
||||||
|
-107.68839591351332,
|
||||||
|
-169.421875,
|
||||||
|
-107.68839591351332,
|
||||||
|
-159.421875,
|
||||||
|
-107.68839591351332,
|
||||||
|
-131.05391899039037,
|
||||||
|
-107.84415803147851,
|
||||||
|
-131.05391899039037,
|
||||||
|
-107.84415803147851,
|
||||||
|
-102.68596298078074,
|
||||||
|
-107.84415803147851,
|
||||||
|
-92.68596298078074,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: -1,
|
||||||
|
to: -2,
|
||||||
|
fromPort: "B",
|
||||||
|
toPort: "T",
|
||||||
|
points: [
|
||||||
|
-107.94070280440474,
|
||||||
|
-289.08209495195183,
|
||||||
|
-107.94070280440474,
|
||||||
|
-279.08209495195183,
|
||||||
|
-107.94070280440474,
|
||||||
|
-249.25198497597592,
|
||||||
|
-107.68839591351332,
|
||||||
|
-249.25198497597592,
|
||||||
|
-107.68839591351332,
|
||||||
|
-219.421875,
|
||||||
|
-107.68839591351332,
|
||||||
|
-209.421875,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: -3,
|
||||||
|
to: -4,
|
||||||
|
fromPort: "B",
|
||||||
|
toPort: "T",
|
||||||
|
visible: true,
|
||||||
|
points: [
|
||||||
|
-107.84415803147851,
|
||||||
|
-52.685962980780744,
|
||||||
|
-107.84415803147851,
|
||||||
|
-42.685962980780744,
|
||||||
|
-107.84415803147851,
|
||||||
|
1.9730364285206292,
|
||||||
|
-109.21840230579596,
|
||||||
|
1.9730364285206292,
|
||||||
|
-109.21840230579596,
|
||||||
|
46.632035837822,
|
||||||
|
-109.21840230579596,
|
||||||
|
56.632035837822,
|
||||||
|
],
|
||||||
|
text: "是",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: -3,
|
||||||
|
to: -2,
|
||||||
|
fromPort: "R",
|
||||||
|
toPort: "T",
|
||||||
|
visible: true,
|
||||||
|
points: [
|
||||||
|
-47.844158031478514,
|
||||||
|
-72.68596298078074,
|
||||||
|
-37.844158031478514,
|
||||||
|
-72.68596298078074,
|
||||||
|
-37.844158031478514,
|
||||||
|
-240.921875,
|
||||||
|
-107.68839591351332,
|
||||||
|
-240.921875,
|
||||||
|
-107.68839591351332,
|
||||||
|
-219.421875,
|
||||||
|
-107.68839591351332,
|
||||||
|
-209.421875,
|
||||||
|
],
|
||||||
|
text: "否",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
//地图数据
|
||||||
|
defaultZoom:6,
|
||||||
|
defaultOrigin:[50.02185841773444, 0.15380859375],
|
||||||
|
myLeafletMap:undefined,
|
||||||
|
myUpdatingGoJS:false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const showLinkLabel = (e) => {
|
||||||
|
var label = e.subject.findObject("LABEL");
|
||||||
|
if (label !== null)
|
||||||
|
label.visible = e.subject.fromNode.data.category === "Conditional";
|
||||||
|
};
|
||||||
|
|
||||||
|
this.diagram = $(go.Diagram, this.$refs["diagram"], {
|
||||||
|
LinkDrawn: showLinkLabel, // this DiagramEvent listener is defined below
|
||||||
|
LinkRelinked: showLinkLabel,
|
||||||
|
"undoManager.isEnabled": true, // enable undo & redo
|
||||||
|
});
|
||||||
|
this.setLinkTemplate();
|
||||||
|
this.createPattle();
|
||||||
|
|
||||||
|
// 设置 linkFromPortIdProperty 属性, 避免 link 自己重绘
|
||||||
|
this.diagram.model.linkFromPortIdProperty = "fromPort"; // 必须记住portIds
|
||||||
|
this.diagram.model.linkToPortIdProperty = "toPort";
|
||||||
|
|
||||||
|
this.diagram.toolManager.linkingTool.temporaryLink.routing =
|
||||||
|
go.Link.Orthogonal;
|
||||||
|
this.diagram.toolManager.relinkingTool.temporaryLink.routing =
|
||||||
|
go.Link.Orthogonal;
|
||||||
|
|
||||||
|
//this.initLeaflet()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
//初始化地图
|
||||||
|
initLeaflet(){
|
||||||
|
let myLeafletMap = this.myLeafletMap
|
||||||
|
myLeafletMap = L.map("map", {}).setView(this.defaultOrigin, this.defaultZoom);
|
||||||
|
L.tileLayer("https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}", {
|
||||||
|
attribution: '© <a href="https://www.mapbox.com/about/maps/">Mapbox</a> © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
||||||
|
maxZoom: 18,
|
||||||
|
minZoom: 2,
|
||||||
|
tileSize: 512,
|
||||||
|
zoomOffset: -1,
|
||||||
|
id: "mapbox/streets-v11",
|
||||||
|
accessToken: "pk.eyJ1IjoiZ29qcyIsImEiOiJjaXppcnNkbDgwMzQ3MnFsNDFnY2phb2QwIn0.7AuVKrWdxQnJxa_W7qC3-w"
|
||||||
|
}).addTo(myLeafletMap);
|
||||||
|
|
||||||
|
// myLeafletMap.on("zoomend", updateNodes);
|
||||||
|
// myLeafletMap.on("move", updatePosition);
|
||||||
|
// myLeafletMap.on("moveend", updatePosition);
|
||||||
|
},
|
||||||
|
// updateNodes() { // called when zoom level has changed
|
||||||
|
// this.myUpdatingGoJS = true;
|
||||||
|
// myDiagram.commit(diag => {
|
||||||
|
// diag.nodes.each(n => n.updateTargetBindings("latlong")); // without virtualization this can be slow if there are many nodes
|
||||||
|
// }, null);
|
||||||
|
// myUpdatingGoJS = false;
|
||||||
|
// },
|
||||||
|
|
||||||
|
save() {
|
||||||
|
console.log(this.diagram.model.toJson());
|
||||||
|
},
|
||||||
|
load() {
|
||||||
|
this.diagram.model = go.Model.fromJson(this.chart);
|
||||||
|
},
|
||||||
|
createPattle() {
|
||||||
|
this.diagram.nodeTemplateMap.add(
|
||||||
|
"", // the default category
|
||||||
|
$(
|
||||||
|
go.Node,
|
||||||
|
"Table",
|
||||||
|
this.nodeStyle(),
|
||||||
|
// the main object is a Panel that surrounds a TextBlock with a rectangular Shape
|
||||||
|
$(
|
||||||
|
go.Panel,
|
||||||
|
"Auto",
|
||||||
|
$(
|
||||||
|
go.Shape,
|
||||||
|
"RoundedRectangle",
|
||||||
|
{
|
||||||
|
desiredSize: new go.Size(120, 40),
|
||||||
|
fill: "#006266",
|
||||||
|
strokeWidth: 0,
|
||||||
|
},
|
||||||
|
new go.Binding("figure", "figure"),
|
||||||
|
new go.Binding("fill", "color")
|
||||||
|
),
|
||||||
|
$(
|
||||||
|
go.TextBlock,
|
||||||
|
this.textStyle,
|
||||||
|
{
|
||||||
|
margin: 8,
|
||||||
|
maxSize: new go.Size(160, NaN),
|
||||||
|
wrap: go.TextBlock.WrapFit,
|
||||||
|
editable: true,
|
||||||
|
},
|
||||||
|
new go.Binding("text").makeTwoWay()
|
||||||
|
),
|
||||||
|
{
|
||||||
|
// define a context menu for each node
|
||||||
|
contextMenu: $(
|
||||||
|
"ContextMenu", // that has one button
|
||||||
|
$(
|
||||||
|
"ContextMenuButton",
|
||||||
|
{
|
||||||
|
"ButtonBorder.fill": "white",
|
||||||
|
_buttonFillOver: "skyblue",
|
||||||
|
},
|
||||||
|
$(go.TextBlock, "change color"),
|
||||||
|
{ click: this.changeColor }
|
||||||
|
)
|
||||||
|
// more ContextMenuButtons would go here
|
||||||
|
), // end Adornment
|
||||||
|
}
|
||||||
|
),
|
||||||
|
// four named ports, one on each side:
|
||||||
|
this.makePort("T", go.Spot.Top, go.Spot.Top, false, true),
|
||||||
|
this.makePort("L", go.Spot.Left, go.Spot.Left, true, true),
|
||||||
|
this.makePort("R", go.Spot.Right, go.Spot.Right, true, true),
|
||||||
|
this.makePort("B", go.Spot.Bottom, go.Spot.Bottom, true, false)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
this.diagram.nodeTemplateMap.add(
|
||||||
|
"Conditional",
|
||||||
|
$(
|
||||||
|
go.Node,
|
||||||
|
"Table",
|
||||||
|
this.nodeStyle(),
|
||||||
|
// the main object is a Panel that surrounds a TextBlock with a rectangular Shape
|
||||||
|
$(
|
||||||
|
go.Panel,
|
||||||
|
"Auto",
|
||||||
|
$(
|
||||||
|
go.Shape,
|
||||||
|
"Diamond",
|
||||||
|
{
|
||||||
|
desiredSize: new go.Size(120, 40),
|
||||||
|
fill: "#F79F1F",
|
||||||
|
strokeWidth: 0,
|
||||||
|
},
|
||||||
|
new go.Binding("figure", "figure")
|
||||||
|
),
|
||||||
|
$(
|
||||||
|
go.TextBlock,
|
||||||
|
this.textStyle,
|
||||||
|
{
|
||||||
|
margin: 8,
|
||||||
|
maxSize: new go.Size(160, NaN),
|
||||||
|
wrap: go.TextBlock.WrapFit,
|
||||||
|
editable: true,
|
||||||
|
},
|
||||||
|
new go.Binding("text").makeTwoWay()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
// four named ports, one on each side:
|
||||||
|
this.makePort("T", go.Spot.Top, go.Spot.Top, false, true),
|
||||||
|
this.makePort("L", go.Spot.Left, go.Spot.Left, true, true),
|
||||||
|
this.makePort("R", go.Spot.Right, go.Spot.Right, true, true),
|
||||||
|
this.makePort("B", go.Spot.Bottom, go.Spot.Bottom, true, false)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
this.diagram.nodeTemplateMap.add(
|
||||||
|
"Start",
|
||||||
|
$(
|
||||||
|
go.Node,
|
||||||
|
"Table",
|
||||||
|
this.nodeStyle(),
|
||||||
|
$(
|
||||||
|
go.Panel,
|
||||||
|
"Spot",
|
||||||
|
$(go.Shape, "Ellipse", {
|
||||||
|
desiredSize: new go.Size(120, 40),
|
||||||
|
fill: "#009432",
|
||||||
|
strokeWidth: 0,
|
||||||
|
}),
|
||||||
|
$(go.TextBlock, "Start", this.textStyle, new go.Binding("text"))
|
||||||
|
),
|
||||||
|
// 创建端口, 以供连线(开始节点仅创建底部一个端口)
|
||||||
|
// this.makePort('L', go.Spot.Left, go.Spot.Left, true, false),
|
||||||
|
// this.makePort('R', go.Spot.Right, go.Spot.Right, true, false),
|
||||||
|
this.makePort("B", go.Spot.Bottom, go.Spot.Bottom, true, false)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
this.diagram.nodeTemplateMap.add(
|
||||||
|
"End",
|
||||||
|
$(
|
||||||
|
go.Node,
|
||||||
|
"Table",
|
||||||
|
this.nodeStyle(),
|
||||||
|
$(
|
||||||
|
go.Panel,
|
||||||
|
"Spot",
|
||||||
|
$(go.Shape, "Ellipse", {
|
||||||
|
desiredSize: new go.Size(120, 40),
|
||||||
|
fill: "#EA2027",
|
||||||
|
strokeWidth: 0,
|
||||||
|
}),
|
||||||
|
$(go.TextBlock, "End", this.textStyle, new go.Binding("text"))
|
||||||
|
),
|
||||||
|
// 创建端口, 以供连线(结束节点仅创建顶部一个端口)
|
||||||
|
this.makePort("T", go.Spot.Top, go.Spot.Top, false, true)
|
||||||
|
// this.makePort('L', go.Spot.Left, go.Spot.Left, false, true),
|
||||||
|
// this.makePort('R', go.Spot.Right, go.Spot.Right, false, true)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.palette = $(
|
||||||
|
go.Palette,
|
||||||
|
this.$refs["palette"], // must name or refer to the DIV HTML element
|
||||||
|
{
|
||||||
|
// Instead of the default animation, use a custom fade-down
|
||||||
|
allowZoom: false,
|
||||||
|
"animationManager.initialAnimationStyle": go.AnimationManager.None,
|
||||||
|
InitialAnimationStarting: this.animateFadeDown, // Instead, animate with this function
|
||||||
|
nodeTemplateMap: this.diagram.nodeTemplateMap, // share the templates used by myDiagram
|
||||||
|
model: new go.GraphLinksModel([
|
||||||
|
// specify the contents of the Palette
|
||||||
|
{ category: "Start", text: "开始" },
|
||||||
|
{ text: "流程" },
|
||||||
|
{ category: "Conditional", text: "逻辑判断" },
|
||||||
|
{ category: "End", text: "结束" },
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
changeColor(e, obj) {
|
||||||
|
this.diagram.commit((d) => {
|
||||||
|
// get the context menu that holds the button that was clicked
|
||||||
|
var contextmenu = obj.part;
|
||||||
|
// get the node data to which the Node is data bound
|
||||||
|
var nodedata = contextmenu.data;
|
||||||
|
// compute the next color for the node
|
||||||
|
var newcolor = "lightblue";
|
||||||
|
switch (nodedata.color) {
|
||||||
|
case "lightblue":
|
||||||
|
newcolor = "lightgreen";
|
||||||
|
break;
|
||||||
|
case "lightgreen":
|
||||||
|
newcolor = "lightyellow";
|
||||||
|
break;
|
||||||
|
case "lightyellow":
|
||||||
|
newcolor = "orange";
|
||||||
|
break;
|
||||||
|
case "orange":
|
||||||
|
newcolor = "lightblue";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// modify the node data
|
||||||
|
// this evaluates data Bindings and records changes in the UndoManager
|
||||||
|
d.model.set(nodedata, "color", newcolor);
|
||||||
|
}, "changed color");
|
||||||
|
},
|
||||||
|
// 设置 全局 link 样式
|
||||||
|
setLinkTemplate() {
|
||||||
|
this.diagram.linkTemplate = $(
|
||||||
|
go.Link, // the whole link panel
|
||||||
|
{
|
||||||
|
routing: go.Link.AvoidsNodes, // 避免 link 穿过节点
|
||||||
|
curve: go.Link.JumpOver, // 交叉线 设置
|
||||||
|
corner: 5, // 连接线转角弧度
|
||||||
|
toShortLength: 4,
|
||||||
|
relinkableFrom: true,
|
||||||
|
relinkableTo: true,
|
||||||
|
reshapable: true,
|
||||||
|
// resegmentable: true,
|
||||||
|
// mouse-overs subtly highlight links:
|
||||||
|
mouseEnter: function (e, link) {
|
||||||
|
link.findObject("HIGHLIGHT").stroke = "rgba(30,144,255,0.2)";
|
||||||
|
},
|
||||||
|
mouseLeave: function (e, link) {
|
||||||
|
link.findObject("HIGHLIGHT").stroke = "transparent";
|
||||||
|
},
|
||||||
|
selectionAdorned: false,
|
||||||
|
},
|
||||||
|
new go.Binding("points").makeTwoWay(),
|
||||||
|
$(
|
||||||
|
go.Shape, // the highlight shape, normally transparent
|
||||||
|
{
|
||||||
|
isPanelMain: true,
|
||||||
|
strokeWidth: 8,
|
||||||
|
stroke: "transparent",
|
||||||
|
name: "HIGHLIGHT",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
$(
|
||||||
|
go.Shape, // the link path shape
|
||||||
|
{ isPanelMain: true, stroke: "gray", strokeWidth: 2 },
|
||||||
|
new go.Binding("stroke", "isSelected", function (sel) {
|
||||||
|
return sel ? "dodgerblue" : "gray";
|
||||||
|
}).ofObject()
|
||||||
|
),
|
||||||
|
$(
|
||||||
|
go.Shape, // the arrowhead
|
||||||
|
{ toArrow: "standard", strokeWidth: 0, fill: "gray" }
|
||||||
|
),
|
||||||
|
$(
|
||||||
|
go.Panel,
|
||||||
|
"Auto", // the link label, normally not visible
|
||||||
|
{
|
||||||
|
visible: false,
|
||||||
|
name: "LABEL",
|
||||||
|
segmentIndex: 2,
|
||||||
|
segmentFraction: 0.5,
|
||||||
|
},
|
||||||
|
new go.Binding("visible", "visible").makeTwoWay(),
|
||||||
|
$(
|
||||||
|
go.Shape,
|
||||||
|
"RoundedRectangle", // the label shape
|
||||||
|
{ fill: "#F8F8F8", strokeWidth: 0 }
|
||||||
|
),
|
||||||
|
$(
|
||||||
|
go.TextBlock,
|
||||||
|
"是", // the label
|
||||||
|
{
|
||||||
|
textAlign: "center",
|
||||||
|
font: "8pt helvetica, arial, sans-serif",
|
||||||
|
stroke: "#333333",
|
||||||
|
editable: true,
|
||||||
|
},
|
||||||
|
new go.Binding("text").makeTwoWay()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
nodeStyle() {
|
||||||
|
return [
|
||||||
|
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(
|
||||||
|
go.Point.stringify
|
||||||
|
),
|
||||||
|
{ locationSpot: go.Spot.Center },
|
||||||
|
];
|
||||||
|
},
|
||||||
|
// 创建端口, 以供连线
|
||||||
|
makePort(name, align, spot, output, input) {
|
||||||
|
var horizontal =
|
||||||
|
align.equals(go.Spot.Top) || align.equals(go.Spot.Bottom);
|
||||||
|
// the port is basically just a transparent rectangle that stretches along the side of the node,
|
||||||
|
// and becomes colored when the mouse passes over it
|
||||||
|
return $(go.Shape, {
|
||||||
|
fill: "transparent", // changed to a color in the mouseEnter event handler
|
||||||
|
strokeWidth: 0, // no stroke
|
||||||
|
width: horizontal ? NaN : 8, // if not stretching horizontally, just 8 wide
|
||||||
|
height: !horizontal ? NaN : 8, // if not stretching vertically, just 8 tall
|
||||||
|
alignment: align, // align the port on the main Shape
|
||||||
|
stretch: horizontal
|
||||||
|
? go.GraphObject.Horizontal
|
||||||
|
: go.GraphObject.Vertical,
|
||||||
|
portId: name, // declare this object to be a "port"
|
||||||
|
fromSpot: spot, // declare where links may connect at this port
|
||||||
|
fromLinkable: output, // declare whether the user may draw links from here
|
||||||
|
toSpot: spot, // declare where links may connect at this port
|
||||||
|
toLinkable: input, // declare whether the user may draw links to here
|
||||||
|
cursor: "pointer", // show a different cursor to indicate potential link point
|
||||||
|
mouseEnter: function (e, port) {
|
||||||
|
// the PORT argument will be this Shape
|
||||||
|
if (!e.diagram.isReadOnly) port.fill = "rgba(255,0,255,0.5)";
|
||||||
|
},
|
||||||
|
mouseLeave: function (e, port) {
|
||||||
|
port.fill = "transparent";
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
animateFadeDown(e) {
|
||||||
|
var diagram = e.diagram;
|
||||||
|
var animation = new go.Animation();
|
||||||
|
animation.isViewportUnconstrained = true; // So Diagram positioning rules let the animation start off-screen
|
||||||
|
animation.easing = go.Animation.EaseOutExpo;
|
||||||
|
animation.duration = 900;
|
||||||
|
// Fade "down", in other words, fade in from above
|
||||||
|
animation.add(
|
||||||
|
diagram,
|
||||||
|
"position",
|
||||||
|
diagram.position.copy().offset(0, 200),
|
||||||
|
diagram.position
|
||||||
|
);
|
||||||
|
animation.add(diagram, "opacity", 0, 1);
|
||||||
|
animation.start();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
#diagram-contanier {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
#diagram-tool {
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
background-color: #dfdfdf;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#diagram-wrap {
|
||||||
|
flex-grow: 1;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
#diagram-palette {
|
||||||
|
flex: 0 0 140px;
|
||||||
|
height: 100%;
|
||||||
|
// border-right: 1px solid #dfdfdf;
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
#diagram {
|
||||||
|
width: 100%;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep canvas {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,711 @@
|
||||||
|
<!--
|
||||||
|
* @FilePath src/views/dashboard/components/FlowChart/index.vue
|
||||||
|
* @Created Bay丶<baizhanying@autobio.com.cn> 2021-11-10 14:23:50
|
||||||
|
* @Modified Bay丶<baizhanying@autobio.com.cn> 2021-11-10 18:11:22
|
||||||
|
* @Description https://github.com/NorthwoodsSoftware/GoJS/blob/master/samples/flowchart.html
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="diagram-contanier">
|
||||||
|
<div id="diagram-tool">
|
||||||
|
<slot>
|
||||||
|
<!-- <el-button type="primary" @click="save">保存</el-button> -->
|
||||||
|
<!-- <el-button @click="load">加载</el-button> -->
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
<div id="diagram-wrap">
|
||||||
|
<div id="diagram-palette" ref="palette" />
|
||||||
|
<div id="map"
|
||||||
|
class="mapDiagram leaflet-container leaflet-touch leaflet-retina leaflet-fade-anim leaflet-touch-zoom leaflet-grab leaflet-touch-drag"
|
||||||
|
tabindex="0"
|
||||||
|
style="position: relative;">
|
||||||
|
<div id="diagram"
|
||||||
|
class="mapDiagram"
|
||||||
|
style="position: relative; -webkit-tap-highlight-color: rgba(255, 255, 255, 0); cursor: default;z-index: 701;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import go from "gojs";
|
||||||
|
const $ = go.GraphObject.make;
|
||||||
|
export default {
|
||||||
|
name: "Index",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
diagram: null,
|
||||||
|
palette: null,
|
||||||
|
|
||||||
|
textStyle: {
|
||||||
|
font: "bold 9pt Lato, Helvetica, Arial, sans-serif",
|
||||||
|
stroke: "#F8F8F8",
|
||||||
|
},
|
||||||
|
chart: {
|
||||||
|
class: "GraphLinksModel",
|
||||||
|
linkFromPortIdProperty: "fromPort",
|
||||||
|
linkToPortIdProperty: "toPort",
|
||||||
|
nodeDataArray: [
|
||||||
|
{
|
||||||
|
category: "Start",
|
||||||
|
text: "开始",
|
||||||
|
key: -1,
|
||||||
|
loc: "-107.94070280440474 -309.08209495195183",
|
||||||
|
},
|
||||||
|
{ text: "流程", key: -2, loc: "-107.68839591351332 -189.421875" },
|
||||||
|
{
|
||||||
|
category: "Conditional",
|
||||||
|
text: "逻辑判断",
|
||||||
|
key: -3,
|
||||||
|
loc: "-107.84415803147851 -72.68596298078074",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "End",
|
||||||
|
text: "结束",
|
||||||
|
key: -4,
|
||||||
|
loc: "-109.21840230579596 76.632035837822",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
linkDataArray: [
|
||||||
|
{
|
||||||
|
from: -2,
|
||||||
|
to: -3,
|
||||||
|
fromPort: "B",
|
||||||
|
toPort: "T",
|
||||||
|
points: [
|
||||||
|
-107.68839591351332,
|
||||||
|
-169.421875,
|
||||||
|
-107.68839591351332,
|
||||||
|
-159.421875,
|
||||||
|
-107.68839591351332,
|
||||||
|
-131.05391899039037,
|
||||||
|
-107.84415803147851,
|
||||||
|
-131.05391899039037,
|
||||||
|
-107.84415803147851,
|
||||||
|
-102.68596298078074,
|
||||||
|
-107.84415803147851,
|
||||||
|
-92.68596298078074,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: -1,
|
||||||
|
to: -2,
|
||||||
|
fromPort: "B",
|
||||||
|
toPort: "T",
|
||||||
|
points: [
|
||||||
|
-107.94070280440474,
|
||||||
|
-289.08209495195183,
|
||||||
|
-107.94070280440474,
|
||||||
|
-279.08209495195183,
|
||||||
|
-107.94070280440474,
|
||||||
|
-249.25198497597592,
|
||||||
|
-107.68839591351332,
|
||||||
|
-249.25198497597592,
|
||||||
|
-107.68839591351332,
|
||||||
|
-219.421875,
|
||||||
|
-107.68839591351332,
|
||||||
|
-209.421875,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: -3,
|
||||||
|
to: -4,
|
||||||
|
fromPort: "B",
|
||||||
|
toPort: "T",
|
||||||
|
visible: true,
|
||||||
|
points: [
|
||||||
|
-107.84415803147851,
|
||||||
|
-52.685962980780744,
|
||||||
|
-107.84415803147851,
|
||||||
|
-42.685962980780744,
|
||||||
|
-107.84415803147851,
|
||||||
|
1.9730364285206292,
|
||||||
|
-109.21840230579596,
|
||||||
|
1.9730364285206292,
|
||||||
|
-109.21840230579596,
|
||||||
|
46.632035837822,
|
||||||
|
-109.21840230579596,
|
||||||
|
56.632035837822,
|
||||||
|
],
|
||||||
|
text: "是",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: -3,
|
||||||
|
to: -2,
|
||||||
|
fromPort: "R",
|
||||||
|
toPort: "T",
|
||||||
|
visible: true,
|
||||||
|
points: [
|
||||||
|
-47.844158031478514,
|
||||||
|
-72.68596298078074,
|
||||||
|
-37.844158031478514,
|
||||||
|
-72.68596298078074,
|
||||||
|
-37.844158031478514,
|
||||||
|
-240.921875,
|
||||||
|
-107.68839591351332,
|
||||||
|
-240.921875,
|
||||||
|
-107.68839591351332,
|
||||||
|
-219.421875,
|
||||||
|
-107.68839591351332,
|
||||||
|
-209.421875,
|
||||||
|
],
|
||||||
|
text: "否",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
//地图数据
|
||||||
|
defaultZoom:6,
|
||||||
|
defaultOrigin:[50.02185841773444, 0.15380859375],
|
||||||
|
myLeafletMap:undefined,
|
||||||
|
myUpdatingGoJS:false,
|
||||||
|
|
||||||
|
//goJs数据
|
||||||
|
diagram:undefined,
|
||||||
|
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created(){
|
||||||
|
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.init()
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init(){
|
||||||
|
//初始化地图
|
||||||
|
this.initLeaflet()
|
||||||
|
//初始化GoJS
|
||||||
|
this.initGoJs()
|
||||||
|
this.initTemplate()
|
||||||
|
this.initManager()
|
||||||
|
this.initData()
|
||||||
|
|
||||||
|
//初始化左侧元素
|
||||||
|
//this.setLinkTemplate();
|
||||||
|
this.createPattle()
|
||||||
|
},
|
||||||
|
//初始化地图
|
||||||
|
initLeaflet(){
|
||||||
|
this.myLeafletMap = L.map("map", {}).setView(this.defaultOrigin, this.defaultZoom);
|
||||||
|
L.tileLayer("https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}", {
|
||||||
|
attribution: '© <a href="https://www.mapbox.com/about/maps/">Mapbox</a> © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
||||||
|
maxZoom: 18,
|
||||||
|
minZoom: 2,
|
||||||
|
tileSize: 512,
|
||||||
|
zoomOffset: -1,
|
||||||
|
id: "mapbox/streets-v11",
|
||||||
|
accessToken: "pk.eyJ1IjoiZ29qcyIsImEiOiJjaXppcnNkbDgwMzQ3MnFsNDFnY2phb2QwIn0.7AuVKrWdxQnJxa_W7qC3-w"
|
||||||
|
}).addTo(this.myLeafletMap);
|
||||||
|
|
||||||
|
this.myLeafletMap.on("zoomend", this.updateNodes);
|
||||||
|
this.myLeafletMap.on("move", this.updatePosition);
|
||||||
|
this.myLeafletMap.on("moveend", this.updatePosition);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateNodes() { // called when zoom level has changed
|
||||||
|
this.myUpdatingGoJS = true;
|
||||||
|
this.diagram.commit(diag => {
|
||||||
|
diag.nodes.each(n => n.updateTargetBindings("latlong")); // without virtualization this can be slow if there are many nodes
|
||||||
|
}, null);
|
||||||
|
this.myUpdatingGoJS = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
updatePosition() { // called when map has been panned (i.e. top-left corner is at a different latlong)
|
||||||
|
console.log("地图",this.myLeafletMap)
|
||||||
|
const mapb = this.myLeafletMap.getBounds();
|
||||||
|
const pos = this.myLeafletMap.project([mapb.getNorth(), mapb.getWest()], this.myLeafletMap.getZoom());
|
||||||
|
this.diagram.position = new go.Point(pos.x, pos.y);
|
||||||
|
},
|
||||||
|
|
||||||
|
initGoJs(){
|
||||||
|
this.diagram = $(go.Diagram, "diagram",{
|
||||||
|
"InitialLayoutCompleted": e => this.updatePosition(),
|
||||||
|
"dragSelectingTool.isEnabled": false,
|
||||||
|
"animationManager.isEnabled": false,
|
||||||
|
scrollMode: go.Diagram.InfiniteScroll,
|
||||||
|
allowZoom: false,
|
||||||
|
allowHorizontalScroll: false,
|
||||||
|
allowVerticalScroll: false,
|
||||||
|
hasHorizontalScrollbar: false,
|
||||||
|
hasVerticalScrollbar: false,
|
||||||
|
padding: 0,
|
||||||
|
defaultCursor: "default",
|
||||||
|
"toolManager.hoverDelay": 100, // how quickly tooltips are shown
|
||||||
|
"undoManager.isEnabled": true,
|
||||||
|
"ModelChanged": e => {
|
||||||
|
if (e.change === go.ChangedEvent.Transaction &&(e.propertyName === "FinishedUndo" || e.propertyName === "FinishedRedo")) {
|
||||||
|
setTimeout(() => this.updateNodes());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
initTemplate(){
|
||||||
|
const toolTipTemplate =
|
||||||
|
$("ToolTip", $(go.TextBlock, { margin: 4 },
|
||||||
|
new go.Binding("text", "", d => d.key + "\nlocation: [" + d.latlong.join(", ") + "]"))
|
||||||
|
);
|
||||||
|
this.diagram.nodeTemplate =
|
||||||
|
$(go.Node, "Auto",
|
||||||
|
{ toolTip: toolTipTemplate, locationSpot: go.Spot.Center, cursor: "pointer" },
|
||||||
|
$(go.Shape, "Circle",
|
||||||
|
{
|
||||||
|
fill: "rgba(0, 255, 0, .4)", stroke: "#082D47", strokeWidth: 1,
|
||||||
|
width: 7, height: 7
|
||||||
|
}),
|
||||||
|
// A two-way data binding with an Array of latitude,longitude numbers.
|
||||||
|
// We have to explicitly avoid updating the source data Array
|
||||||
|
// when myUpdatingGoJS is true; otherwise there would be accumulating errors.
|
||||||
|
new go.Binding("location", "latlong", data => {
|
||||||
|
const pos = this.myLeafletMap.project(data, this.myLeafletMap.getZoom());
|
||||||
|
return new go.Point(pos.x, pos.y);
|
||||||
|
}).makeTwoWay((pt, data) => {
|
||||||
|
if (myUpdatingGoJS) {
|
||||||
|
return data.latlong; // no-op
|
||||||
|
} else {
|
||||||
|
const ll = this.myLeafletMap.unproject(L.point(pt.x, pt.y), this.myLeafletMap.getZoom());
|
||||||
|
return [ll.lat, ll.lng];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.diagram.linkTemplate =
|
||||||
|
$(go.Link,
|
||||||
|
{ layerName: "Background", curve: go.Link.Bezier, curviness: 5 },
|
||||||
|
$(go.Shape, { strokeWidth: 3, stroke: "rgba(100,100,255,.7)" })
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
initManager(){
|
||||||
|
let leaflet = this.myLeafletMap
|
||||||
|
this.diagram.toolManager.draggingTool.doActivate = function() {
|
||||||
|
leaflet.dragging.disable();
|
||||||
|
go.DraggingTool.prototype.doActivate.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.diagram.toolManager.draggingTool.doDeactivate = function() {
|
||||||
|
leaflet.dragging.enable();
|
||||||
|
go.DraggingTool.prototype.doDeactivate.call(this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
initData(){
|
||||||
|
this.diagram.model = new go.GraphLinksModel(
|
||||||
|
[
|
||||||
|
// France
|
||||||
|
{ key: "Paris", latlong: [48.876569, 2.359017] },
|
||||||
|
{ key: "Brest", latlong: [48.387778, -4.479921] },
|
||||||
|
{ key: "Rennes", latlong: [48.103375, -1.672809] },
|
||||||
|
{ key: "Le Mans", latlong: [47.995562, 0.192413] },
|
||||||
|
{ key: "Nantes", latlong: [47.217579, -1.541839] },
|
||||||
|
{ key: "Tours", latlong: [47.388502, 0.694500] },
|
||||||
|
{ key: "Le Havre", latlong: [49.492755, 0.125278] },
|
||||||
|
{ key: "Rouen", latlong: [49.449031, 1.094128] },
|
||||||
|
{ key: "Lille", latlong: [50.636379, 3.070620] },
|
||||||
|
|
||||||
|
// Belgium
|
||||||
|
{ key: "Brussels", latlong: [50.836271, 4.333963] },
|
||||||
|
{ key: "Antwerp", latlong: [51.217495, 4.421204] },
|
||||||
|
{ key: "Liege", latlong: [50.624168, 5.566008] },
|
||||||
|
|
||||||
|
// UK
|
||||||
|
{ key: "London", latlong: [51.531132, -0.125132] },
|
||||||
|
{ key: "Bristol", latlong: [51.449541, -2.581118] },
|
||||||
|
{ key: "Birmingham", latlong: [52.477405, -1.898494] },
|
||||||
|
{ key: "Liverpool", latlong: [53.408396, -2.978809] },
|
||||||
|
{ key: "Manchester", latlong: [53.476346, -2.229651] },
|
||||||
|
{ key: "Leeds", latlong: [53.795480, -1.548345] },
|
||||||
|
{ key: "Glasgow", latlong: [55.863287, -4.250989] },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ from: "Brest", to: "Rennes" },
|
||||||
|
{ from: "Rennes", to: "Le Mans" },
|
||||||
|
{ from: "Nantes", to: "Le Mans" },
|
||||||
|
{ from: "Le Mans", to: "Paris" },
|
||||||
|
{ from: "Tours", to: "Paris" },
|
||||||
|
{ from: "Le Havre", to: "Rouen" },
|
||||||
|
{ from: "Rouen", to: "Paris" },
|
||||||
|
{ from: "Lille", to: "Paris" },
|
||||||
|
{ from: "London", to: "Lille" },
|
||||||
|
|
||||||
|
{ from: "Lille", to: "Brussels" },
|
||||||
|
{ from: "Brussels", to: "Antwerp" },
|
||||||
|
{ from: "Brussels", to: "Liege" },
|
||||||
|
|
||||||
|
{ from: "Bristol", to: "London" },
|
||||||
|
{ from: "Birmingham", to: "London" },
|
||||||
|
{ from: "Leeds", to: "London" },
|
||||||
|
{ from: "Liverpool", to: "Birmingham" },
|
||||||
|
{ from: "Manchester", to: "Liverpool" },
|
||||||
|
{ from: "Manchester", to: "Leeds" },
|
||||||
|
{ from: "Glasgow", to: "Manchester" },
|
||||||
|
{ from: "Glasgow", to: "Leeds" }
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
createPattle() {
|
||||||
|
this.diagram.nodeTemplateMap.add(
|
||||||
|
"", // the default category
|
||||||
|
$(
|
||||||
|
go.Node,
|
||||||
|
"Table",
|
||||||
|
this.nodeStyle(),
|
||||||
|
// the main object is a Panel that surrounds a TextBlock with a rectangular Shape
|
||||||
|
$(
|
||||||
|
go.Panel,
|
||||||
|
"Auto",
|
||||||
|
$(
|
||||||
|
go.Shape,
|
||||||
|
"RoundedRectangle",
|
||||||
|
{
|
||||||
|
desiredSize: new go.Size(120, 40),
|
||||||
|
fill: "#006266",
|
||||||
|
strokeWidth: 0,
|
||||||
|
},
|
||||||
|
new go.Binding("figure", "figure"),
|
||||||
|
new go.Binding("fill", "color")
|
||||||
|
),
|
||||||
|
$(
|
||||||
|
go.TextBlock,
|
||||||
|
this.textStyle,
|
||||||
|
{
|
||||||
|
margin: 8,
|
||||||
|
maxSize: new go.Size(160, NaN),
|
||||||
|
wrap: go.TextBlock.WrapFit,
|
||||||
|
editable: true,
|
||||||
|
},
|
||||||
|
new go.Binding("text").makeTwoWay()
|
||||||
|
),
|
||||||
|
{
|
||||||
|
// define a context menu for each node
|
||||||
|
contextMenu: $(
|
||||||
|
"ContextMenu", // that has one button
|
||||||
|
$(
|
||||||
|
"ContextMenuButton",
|
||||||
|
{
|
||||||
|
"ButtonBorder.fill": "white",
|
||||||
|
_buttonFillOver: "skyblue",
|
||||||
|
},
|
||||||
|
$(go.TextBlock, "change color"),
|
||||||
|
{ click: this.changeColor }
|
||||||
|
)
|
||||||
|
// more ContextMenuButtons would go here
|
||||||
|
), // end Adornment
|
||||||
|
}
|
||||||
|
),
|
||||||
|
// four named ports, one on each side:
|
||||||
|
this.makePort("T", go.Spot.Top, go.Spot.Top, false, true),
|
||||||
|
this.makePort("L", go.Spot.Left, go.Spot.Left, true, true),
|
||||||
|
this.makePort("R", go.Spot.Right, go.Spot.Right, true, true),
|
||||||
|
this.makePort("B", go.Spot.Bottom, go.Spot.Bottom, true, false)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
this.diagram.nodeTemplateMap.add(
|
||||||
|
"Conditional",
|
||||||
|
$(
|
||||||
|
go.Node,
|
||||||
|
"Table",
|
||||||
|
this.nodeStyle(),
|
||||||
|
// the main object is a Panel that surrounds a TextBlock with a rectangular Shape
|
||||||
|
$(
|
||||||
|
go.Panel,
|
||||||
|
"Auto",
|
||||||
|
$(
|
||||||
|
go.Shape,
|
||||||
|
"Diamond",
|
||||||
|
{
|
||||||
|
desiredSize: new go.Size(120, 40),
|
||||||
|
fill: "#F79F1F",
|
||||||
|
strokeWidth: 0,
|
||||||
|
},
|
||||||
|
new go.Binding("figure", "figure")
|
||||||
|
),
|
||||||
|
$(
|
||||||
|
go.TextBlock,
|
||||||
|
this.textStyle,
|
||||||
|
{
|
||||||
|
margin: 8,
|
||||||
|
maxSize: new go.Size(160, NaN),
|
||||||
|
wrap: go.TextBlock.WrapFit,
|
||||||
|
editable: true,
|
||||||
|
},
|
||||||
|
new go.Binding("text").makeTwoWay()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
// four named ports, one on each side:
|
||||||
|
this.makePort("T", go.Spot.Top, go.Spot.Top, false, true),
|
||||||
|
this.makePort("L", go.Spot.Left, go.Spot.Left, true, true),
|
||||||
|
this.makePort("R", go.Spot.Right, go.Spot.Right, true, true),
|
||||||
|
this.makePort("B", go.Spot.Bottom, go.Spot.Bottom, true, false)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
this.diagram.nodeTemplateMap.add(
|
||||||
|
"Start",
|
||||||
|
$(
|
||||||
|
go.Node,
|
||||||
|
"Table",
|
||||||
|
this.nodeStyle(),
|
||||||
|
$(
|
||||||
|
go.Panel,
|
||||||
|
"Spot",
|
||||||
|
$(go.Shape, "Ellipse", {
|
||||||
|
desiredSize: new go.Size(120, 40),
|
||||||
|
fill: "#009432",
|
||||||
|
strokeWidth: 0,
|
||||||
|
}),
|
||||||
|
$(go.TextBlock, "Start", this.textStyle, new go.Binding("text"))
|
||||||
|
),
|
||||||
|
// 创建端口, 以供连线(开始节点仅创建底部一个端口)
|
||||||
|
// this.makePort('L', go.Spot.Left, go.Spot.Left, true, false),
|
||||||
|
// this.makePort('R', go.Spot.Right, go.Spot.Right, true, false),
|
||||||
|
this.makePort("B", go.Spot.Bottom, go.Spot.Bottom, true, false)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
this.diagram.nodeTemplateMap.add(
|
||||||
|
"End",
|
||||||
|
$(
|
||||||
|
go.Node,
|
||||||
|
"Table",
|
||||||
|
this.nodeStyle(),
|
||||||
|
$(
|
||||||
|
go.Panel,
|
||||||
|
"Spot",
|
||||||
|
$(go.Shape, "Ellipse", {
|
||||||
|
desiredSize: new go.Size(120, 40),
|
||||||
|
fill: "#EA2027",
|
||||||
|
strokeWidth: 0,
|
||||||
|
}),
|
||||||
|
$(go.TextBlock, "End", this.textStyle, new go.Binding("text"))
|
||||||
|
),
|
||||||
|
// 创建端口, 以供连线(结束节点仅创建顶部一个端口)
|
||||||
|
this.makePort("T", go.Spot.Top, go.Spot.Top, false, true)
|
||||||
|
// this.makePort('L', go.Spot.Left, go.Spot.Left, false, true),
|
||||||
|
// this.makePort('R', go.Spot.Right, go.Spot.Right, false, true)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.palette = $(
|
||||||
|
go.Palette,
|
||||||
|
this.$refs["palette"], // must name or refer to the DIV HTML element
|
||||||
|
{
|
||||||
|
// Instead of the default animation, use a custom fade-down
|
||||||
|
allowZoom: false,
|
||||||
|
"animationManager.initialAnimationStyle": go.AnimationManager.None,
|
||||||
|
InitialAnimationStarting: this.animateFadeDown, // Instead, animate with this function
|
||||||
|
nodeTemplateMap: this.diagram.nodeTemplateMap, // share the templates used by myDiagram
|
||||||
|
model: new go.GraphLinksModel([
|
||||||
|
// specify the contents of the Palette
|
||||||
|
{ category: "Start", text: "开始" },
|
||||||
|
{ text: "流程" },
|
||||||
|
{ category: "Conditional", text: "逻辑判断" },
|
||||||
|
{ category: "End", text: "结束" },
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
changeColor(e, obj) {
|
||||||
|
this.diagram.commit((d) => {
|
||||||
|
// get the context menu that holds the button that was clicked
|
||||||
|
var contextmenu = obj.part;
|
||||||
|
// get the node data to which the Node is data bound
|
||||||
|
var nodedata = contextmenu.data;
|
||||||
|
// compute the next color for the node
|
||||||
|
var newcolor = "lightblue";
|
||||||
|
switch (nodedata.color) {
|
||||||
|
case "lightblue":
|
||||||
|
newcolor = "lightgreen";
|
||||||
|
break;
|
||||||
|
case "lightgreen":
|
||||||
|
newcolor = "lightyellow";
|
||||||
|
break;
|
||||||
|
case "lightyellow":
|
||||||
|
newcolor = "orange";
|
||||||
|
break;
|
||||||
|
case "orange":
|
||||||
|
newcolor = "lightblue";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// modify the node data
|
||||||
|
// this evaluates data Bindings and records changes in the UndoManager
|
||||||
|
d.model.set(nodedata, "color", newcolor);
|
||||||
|
}, "changed color");
|
||||||
|
},
|
||||||
|
// 设置 全局 link 样式
|
||||||
|
setLinkTemplate() {
|
||||||
|
this.diagram.linkTemplate = $(
|
||||||
|
go.Link, // the whole link panel
|
||||||
|
{
|
||||||
|
routing: go.Link.AvoidsNodes, // 避免 link 穿过节点
|
||||||
|
curve: go.Link.JumpOver, // 交叉线 设置
|
||||||
|
corner: 5, // 连接线转角弧度
|
||||||
|
toShortLength: 4,
|
||||||
|
relinkableFrom: true,
|
||||||
|
relinkableTo: true,
|
||||||
|
reshapable: true,
|
||||||
|
// resegmentable: true,
|
||||||
|
// mouse-overs subtly highlight links:
|
||||||
|
mouseEnter: function (e, link) {
|
||||||
|
link.findObject("HIGHLIGHT").stroke = "rgba(30,144,255,0.2)";
|
||||||
|
},
|
||||||
|
mouseLeave: function (e, link) {
|
||||||
|
link.findObject("HIGHLIGHT").stroke = "transparent";
|
||||||
|
},
|
||||||
|
selectionAdorned: false,
|
||||||
|
},
|
||||||
|
new go.Binding("points").makeTwoWay(),
|
||||||
|
$(
|
||||||
|
go.Shape, // the highlight shape, normally transparent
|
||||||
|
{
|
||||||
|
isPanelMain: true,
|
||||||
|
strokeWidth: 8,
|
||||||
|
stroke: "transparent",
|
||||||
|
name: "HIGHLIGHT",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
$(
|
||||||
|
go.Shape, // the link path shape
|
||||||
|
{ isPanelMain: true, stroke: "gray", strokeWidth: 2 },
|
||||||
|
new go.Binding("stroke", "isSelected", function (sel) {
|
||||||
|
return sel ? "dodgerblue" : "gray";
|
||||||
|
}).ofObject()
|
||||||
|
),
|
||||||
|
$(
|
||||||
|
go.Shape, // the arrowhead
|
||||||
|
{ toArrow: "standard", strokeWidth: 0, fill: "gray" }
|
||||||
|
),
|
||||||
|
$(
|
||||||
|
go.Panel,
|
||||||
|
"Auto", // the link label, normally not visible
|
||||||
|
{
|
||||||
|
visible: false,
|
||||||
|
name: "LABEL",
|
||||||
|
segmentIndex: 2,
|
||||||
|
segmentFraction: 0.5,
|
||||||
|
},
|
||||||
|
new go.Binding("visible", "visible").makeTwoWay(),
|
||||||
|
$(
|
||||||
|
go.Shape,
|
||||||
|
"RoundedRectangle", // the label shape
|
||||||
|
{ fill: "#F8F8F8", strokeWidth: 0 }
|
||||||
|
),
|
||||||
|
$(
|
||||||
|
go.TextBlock,
|
||||||
|
"是", // the label
|
||||||
|
{
|
||||||
|
textAlign: "center",
|
||||||
|
font: "8pt helvetica, arial, sans-serif",
|
||||||
|
stroke: "#333333",
|
||||||
|
editable: true,
|
||||||
|
},
|
||||||
|
new go.Binding("text").makeTwoWay()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
nodeStyle() {
|
||||||
|
return [
|
||||||
|
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(
|
||||||
|
go.Point.stringify
|
||||||
|
),
|
||||||
|
{ locationSpot: go.Spot.Center },
|
||||||
|
];
|
||||||
|
},
|
||||||
|
// 创建端口, 以供连线
|
||||||
|
makePort(name, align, spot, output, input) {
|
||||||
|
var horizontal =
|
||||||
|
align.equals(go.Spot.Top) || align.equals(go.Spot.Bottom);
|
||||||
|
// the port is basically just a transparent rectangle that stretches along the side of the node,
|
||||||
|
// and becomes colored when the mouse passes over it
|
||||||
|
return $(go.Shape, {
|
||||||
|
fill: "transparent", // changed to a color in the mouseEnter event handler
|
||||||
|
strokeWidth: 0, // no stroke
|
||||||
|
width: horizontal ? NaN : 8, // if not stretching horizontally, just 8 wide
|
||||||
|
height: !horizontal ? NaN : 8, // if not stretching vertically, just 8 tall
|
||||||
|
alignment: align, // align the port on the main Shape
|
||||||
|
stretch: horizontal
|
||||||
|
? go.GraphObject.Horizontal
|
||||||
|
: go.GraphObject.Vertical,
|
||||||
|
portId: name, // declare this object to be a "port"
|
||||||
|
fromSpot: spot, // declare where links may connect at this port
|
||||||
|
fromLinkable: output, // declare whether the user may draw links from here
|
||||||
|
toSpot: spot, // declare where links may connect at this port
|
||||||
|
toLinkable: input, // declare whether the user may draw links to here
|
||||||
|
cursor: "pointer", // show a different cursor to indicate potential link point
|
||||||
|
mouseEnter: function (e, port) {
|
||||||
|
// the PORT argument will be this Shape
|
||||||
|
if (!e.diagram.isReadOnly) port.fill = "rgba(255,0,255,0.5)";
|
||||||
|
},
|
||||||
|
mouseLeave: function (e, port) {
|
||||||
|
port.fill = "transparent";
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
animateFadeDown(e) {
|
||||||
|
var diagram = e.diagram;
|
||||||
|
var animation = new go.Animation();
|
||||||
|
animation.isViewportUnconstrained = true; // So Diagram positioning rules let the animation start off-screen
|
||||||
|
animation.easing = go.Animation.EaseOutExpo;
|
||||||
|
animation.duration = 900;
|
||||||
|
// Fade "down", in other words, fade in from above
|
||||||
|
animation.add(
|
||||||
|
diagram,
|
||||||
|
"position",
|
||||||
|
diagram.position.copy().offset(0, 200),
|
||||||
|
diagram.position
|
||||||
|
);
|
||||||
|
animation.add(diagram, "opacity", 0, 1);
|
||||||
|
animation.start();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.myDiagramDiv {
|
||||||
|
z-index: 701;
|
||||||
|
}
|
||||||
|
.mapDiagram {
|
||||||
|
border: solid 1px black;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
#diagram-contanier {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
#diagram-tool {
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
background-color: #dfdfdf;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#diagram-wrap {
|
||||||
|
flex-grow: 1;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
#diagram-palette {
|
||||||
|
flex: 0 0 140px;
|
||||||
|
height: 100%;
|
||||||
|
// border-right: 1px solid #dfdfdf;
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
#diagram {
|
||||||
|
width: 100%;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep canvas {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,199 @@
|
||||||
|
<!--
|
||||||
|
* @FilePath src/views/gantt/components/GanttChart/index.vue
|
||||||
|
* @Created Bay丶<baizhanying@autobio.com.cn> 2021-11-11 11:12:27
|
||||||
|
* @Modified Bay丶<baizhanying@autobio.com.cn> 2021-11-11 14:12:06
|
||||||
|
* @Description
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="map"
|
||||||
|
class="mapDiagram leaflet-container leaflet-touch leaflet-retina leaflet-fade-anim leaflet-touch-zoom leaflet-grab leaflet-touch-drag"
|
||||||
|
tabindex="0"
|
||||||
|
style="position: relative;">
|
||||||
|
<!-- <div id="myDiagramDiv"
|
||||||
|
class="mapDiagram"
|
||||||
|
style="position: relative; -webkit-tap-highlight-color: rgba(255, 255, 255, 0); cursor: default;">
|
||||||
|
</div> -->
|
||||||
|
<div id="diagram" ref="diagram" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import go from "gojs";
|
||||||
|
const $ = go.GraphObject.make;
|
||||||
|
export default {
|
||||||
|
name: "GanttChart",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
diagram: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.diagram = $(
|
||||||
|
go.Diagram,
|
||||||
|
this.$refs["diagram"], // Diagram refers to its DIV HTML element by id
|
||||||
|
{
|
||||||
|
_widthFactor: 1, // a scale for the nodes' positions and widths
|
||||||
|
isReadOnly: true, // deny the user permission to alter the diagram or zoom in or out
|
||||||
|
allowZoom: false, // 禁止缩放
|
||||||
|
allowSelect: false, // 禁止选中
|
||||||
|
allowHorizontalScroll: false, // 禁止画布横向滚动
|
||||||
|
allowVerticalScroll: false, // 禁止画布纵向滚动
|
||||||
|
"grid.visible": true, // 在背景显示一个网格
|
||||||
|
"grid.gridCellSize": new go.Size(30, 150), // 在背景网格 格子大小
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.createNodeTemplates();
|
||||||
|
this.crateLinkTemplate();
|
||||||
|
|
||||||
|
this.diagram.model = new go.GraphLinksModel(
|
||||||
|
[
|
||||||
|
// node data
|
||||||
|
{ key: "a", color: "coral", width: 120, loc: new go.Point(0, 40) },
|
||||||
|
{ key: "b", color: "turquoise", width: 160, loc: new go.Point(0, 60) },
|
||||||
|
{ key: "c", color: "coral", width: 150, loc: new go.Point(120, 80) },
|
||||||
|
{
|
||||||
|
key: "d",
|
||||||
|
color: "turquoise",
|
||||||
|
width: 190,
|
||||||
|
loc: new go.Point(120, 100),
|
||||||
|
},
|
||||||
|
{ key: "e", color: "coral", width: 150, loc: new go.Point(270, 120) },
|
||||||
|
{
|
||||||
|
key: "f",
|
||||||
|
color: "turquoise",
|
||||||
|
width: 130,
|
||||||
|
loc: new go.Point(310, 140),
|
||||||
|
},
|
||||||
|
{ key: "g", color: "coral", width: 155, loc: new go.Point(420, 160) },
|
||||||
|
{ key: "begin", category: "start", loc: new go.Point(-15, 20) },
|
||||||
|
{ key: "end", category: "end", loc: new go.Point(575, 180) },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// link data
|
||||||
|
{ from: "begin", to: "a" },
|
||||||
|
{ from: "begin", to: "b" },
|
||||||
|
{ from: "a", to: "c" },
|
||||||
|
{ from: "a", to: "d" },
|
||||||
|
{ from: "b", to: "e" },
|
||||||
|
{ from: "c", to: "e" },
|
||||||
|
{ from: "d", to: "f" },
|
||||||
|
{ from: "e", to: "g" },
|
||||||
|
{ from: "f", to: "end" },
|
||||||
|
{ from: "g", to: "end" },
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
const dateScale = $(
|
||||||
|
go.Part,
|
||||||
|
"Graduated",
|
||||||
|
{
|
||||||
|
graduatedTickUnit: 1,
|
||||||
|
graduatedMin: 0,
|
||||||
|
graduatedMax: 3,
|
||||||
|
pickable: false,
|
||||||
|
location: new go.Point(0, 0),
|
||||||
|
},
|
||||||
|
$(go.Shape, {
|
||||||
|
name: "line",
|
||||||
|
strokeWidth: 0,
|
||||||
|
geometryString: "M0 0 H" + 450,
|
||||||
|
}),
|
||||||
|
$(go.TextBlock, {
|
||||||
|
name: "labels",
|
||||||
|
font: "10pt sans-serif",
|
||||||
|
alignmentFocus: new go.Spot(0, 0, -3, -3),
|
||||||
|
graduatedFunction: function (v) {
|
||||||
|
var d = new Date(2017, 6, 23);
|
||||||
|
d.setDate(d.getDate() + v * 7);
|
||||||
|
// format date output to string
|
||||||
|
var options = { month: "short", day: "2-digit" };
|
||||||
|
return d.toLocaleDateString("en-US", options);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.diagram.add(dateScale);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
createNodeTemplates() {
|
||||||
|
// create the template for the standard nodes
|
||||||
|
this.diagram.nodeTemplateMap.add(
|
||||||
|
"",
|
||||||
|
$(
|
||||||
|
go.Node,
|
||||||
|
"Auto",
|
||||||
|
// links come from the right and go to the left side of the top of the node
|
||||||
|
{ fromSpot: go.Spot.Right, toSpot: new go.Spot(0.001, 0, 11, 0) },
|
||||||
|
$(
|
||||||
|
go.Shape,
|
||||||
|
"Rectangle",
|
||||||
|
{ height: 15 },
|
||||||
|
new go.Binding("fill", "color"),
|
||||||
|
new go.Binding("width", "width")
|
||||||
|
),
|
||||||
|
$(
|
||||||
|
go.TextBlock,
|
||||||
|
{ margin: 2, alignment: go.Spot.Left },
|
||||||
|
new go.Binding("text", "key")
|
||||||
|
),
|
||||||
|
// using a function in the Binding allows the value to
|
||||||
|
// change when Diagram.updateAllTargetBindings is called
|
||||||
|
new go.Binding("location", "loc", function (l) {
|
||||||
|
return new go.Point(l.x, l.y);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// create the template for the start node
|
||||||
|
this.diagram.nodeTemplateMap.add(
|
||||||
|
"start",
|
||||||
|
$(
|
||||||
|
go.Node,
|
||||||
|
{ fromSpot: go.Spot.Right, toSpot: go.Spot.Top, selectable: false },
|
||||||
|
$(go.Shape, "Diamond", { height: 15, width: 15 }),
|
||||||
|
// make the location of the start node is not scalable
|
||||||
|
new go.Binding("location", "loc")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// create the template for the end node
|
||||||
|
this.diagram.nodeTemplateMap.add(
|
||||||
|
"end",
|
||||||
|
$(
|
||||||
|
go.Node,
|
||||||
|
{ fromSpot: go.Spot.Right, toSpot: go.Spot.Top, selectable: false },
|
||||||
|
$(go.Shape, "Diamond", { height: 15, width: 15 }),
|
||||||
|
// make the location of the end node (with location.x < 0) scalable
|
||||||
|
new go.Binding("location", "loc", function (l) {
|
||||||
|
if (l.x >= 0) return new go.Point(l.x, l.y);
|
||||||
|
else return l;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
crateLinkTemplate() {
|
||||||
|
this.diagram.linkTemplate = $(
|
||||||
|
go.Link,
|
||||||
|
{
|
||||||
|
routing: go.Link.Orthogonal,
|
||||||
|
corner: 3,
|
||||||
|
toShortLength: 2,
|
||||||
|
selectable: false,
|
||||||
|
},
|
||||||
|
$(go.Shape, { strokeWidth: 2 }),
|
||||||
|
$(go.Shape, { toArrow: "OpenTriangle" })
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
#diagram {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
::v-deep canvas {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,19 @@
|
||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<FlowChart />
|
||||||
|
<!-- <GanttChart /> -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import FlowChart from "./components/FlowChart";
|
||||||
|
import GanttChart from "./components/GanttChart";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "App",
|
||||||
|
components: {
|
||||||
|
FlowChart,
|
||||||
|
GanttChart,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
Loading…
Reference in New Issue