Commit a4e4f57f by 吴斌

update:大更新。UI几乎重做,美化。调整文件结构和页面关系。

parent 8e7797fb
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en" class="no-scrollbar h-full w-full">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue + TS</title> <title>Vite + Vue + TS</title>
</head> </head>
<body> <body class="h-full w-full m-0">
<div id="app"></div> <div id="app" class="h-full w-full max-w-none"></div>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>
<style>
.no-scrollbar {
overflow: hidden; /* 显示滚动条但不占用空间 */
}
/* 隐藏 Edge 和 Chrome 浏览器的滚动条 */
.no-scrollbar::-webkit-scrollbar {
width: 0;
height: 0;
}
/* 隐藏 Edge 和 Chrome 浏览器的滚动条 thumb */
.no-scrollbar::-webkit-scrollbar-thumb {
background: transparent;
}
</style>
</html> </html>
...@@ -357,6 +357,11 @@ ...@@ -357,6 +357,11 @@
"resolved": "https://registry.npmmirror.com/@types/node/-/node-20.6.0.tgz", "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.6.0.tgz",
"integrity": "sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg==" "integrity": "sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg=="
}, },
"@types/uuid": {
"version": "8.3.4",
"resolved": "https://registry.npmmirror.com/@types/uuid/-/uuid-8.3.4.tgz",
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw=="
},
"@types/web-bluetooth": { "@types/web-bluetooth": {
"version": "0.0.16", "version": "0.0.16",
"resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz", "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
...@@ -2013,6 +2018,11 @@ ...@@ -2013,6 +2018,11 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true "dev": true
}, },
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
},
"vite": { "vite": {
"version": "4.4.9", "version": "4.4.9",
"resolved": "https://registry.npmmirror.com/vite/-/vite-4.4.9.tgz", "resolved": "https://registry.npmmirror.com/vite/-/vite-4.4.9.tgz",
...@@ -2063,6 +2073,15 @@ ...@@ -2063,6 +2073,15 @@
"semver": "^7.3.8" "semver": "^7.3.8"
} }
}, },
"vue3-uuid": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/vue3-uuid/-/vue3-uuid-1.0.0.tgz",
"integrity": "sha512-TyRCP6KgHHRjwWVI4Eyhd5+eZi9bspkohuXJGEQArtc5VMYKX938ridCff3NpqxPjdawqK+6AZ7rKh9AhT9Jtw==",
"requires": {
"@types/uuid": "^8.0.0",
"uuid": "^8.1.0"
}
},
"which": { "which": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
......
...@@ -9,10 +9,12 @@ ...@@ -9,10 +9,12 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^2.1.0",
"@types/node": "^20.6.0", "@types/node": "^20.6.0",
"element-plus": "^2.3.12", "element-plus": "^2.3.12",
"gojs": "^2.1.48", "gojs": "^2.1.48",
"vue": "^3.3.4" "vue": "^3.3.4",
"vue3-uuid": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^4.2.3", "@vitejs/plugin-vue": "^4.2.3",
......
<template> <template>
<div class="space-y-4 h-90vh"> <div class="w-full h-full flex flex-col">
<div class="flex h-full space-x-4"> <div id="tool-bar" class="pl-1 pr-1 bg-white h-10 flex items-center space-x-1 border-0.5 border-gray-500 border-solid rounded-t-[8px] p-1">
<div class="w-[400px] h-full border-1 border-black rounded border-solid"> <el-button text size="small" :disabled="!diagramConfig.canUndo">
<el-collapse> <template #icon>
<el-collapse-item class="m-2" v-for="category in categoryList" :title=category.name> <i class="icon-undo"></i>
<SourceCollapse class="w-full h-[240px] m-4" :picture_category="category.key"></SourceCollapse> </template>
</el-collapse-item> </el-button>
</el-collapse> <el-button text :disabled="!diagramConfig.canRedo">
</div> <template #icon>
<div id="myDiagram" class="w-full h-full border-1 border-black rounded border-solid"></div> <i class="icon-redo"></i>
<div class="w-[400px] h-full border-1 border-black rounded border-solid flex-col text-center"> </template>
<div id="myInfo" class="p-4 inspector"></div> </el-button>
<div class="text-center m-4"> <el-button text icon="Setting"></el-button>
<el-button @click="showAddPropertyDialog">添加监听属性</el-button> <div class="flex-1"></div>
</div> <el-button icon="Refresh" @click="changeData">数据变化</el-button>
<div class="text-center m-4"> <el-button icon="Download" @click="importData">导入</el-button>
<el-button @click="analyseData">数据分析</el-button> <el-button type="primary" icon="Upload" @click="exportData">导出</el-button>
<el-button class="mr-2" type="success" icon="CircleCheck" @click="saveData"></el-button>
</div>
<div class="flex-1 flex border-1 border-t-0 border-solid border-gray-500 rounded-b-[8px]">
<div id="sourceCollapse" class="w-[200px] rounded-bl-[8px] bg-white flex flex-col items-center p-2 space-y-1">
<!-- 看情况要不要加-->
<!-- <el-input-->
<!-- v-model="inputSearchNode"-->
<!-- class="w-9/10"-->
<!-- placeholder="搜索图形"-->
<!-- :prefix-icon="Search"-->
<!-- />-->
<div class="w-full" :style="{height: nodeAreaHeight}">
<el-scrollbar :max-height="nodeAreaHeight">
<SourceNodeCollection></SourceNodeCollection>
</el-scrollbar>
</div> </div>
<AnalyzeData class="w-full max-h-full" :nodeMap="nodeMap" :paths="paths" :myDiagram="myDiagram"></AnalyzeData> <el-divider></el-divider>
<el-button type="primary" icon="Plus">添加图形</el-button>
</div> </div>
</div> <div id="myDiagram" class="flex-1 border-0.5 border-solid border-gray-500 border-t-0 "></div>
<div class="flex"> <div id="info-panel" class="w-[200px] rounded-br-[8px]">
<div class="w-full"></div> <el-tabs type="border-card" class="info-tabs w-full h-full">
<el-button @click="changeData">数据变化</el-button> <el-tab-pane label="节点">
<el-button @click="importData">导入</el-button> <el-empty description="未选择节点" v-if="!ifSelectNode"></el-empty>
<el-button type="primary" @click="exportData">导出</el-button> <div class="space-y-1 p-2 flex flex-col" v-show="ifSelectNode">
</div> <div class="text-[16px] font-bold text-black">属性</div>
<div id="inspector-property"></div>
<el-divider direction="horizontal"></el-divider>
<div class="text-[16px] font-bold text-black">端口</div>
<div v-for="port in nodePortArray">
<div class="flex flex-row space-x-0" @mouseenter="highlightPort(port['portId'], true)" @mouseleave="highlightPort(port['portId'], false)">
<el-text>{{port['name']}}</el-text>
<div class="flex-1"></div>
<el-button text icon="edit" class="w-5" @click="editPort(port['portId'])"></el-button>
<el-button text icon="delete" class="w-5" type="danger" @click="deletePort(port['portId'])"></el-button>
</div>
</div>
<el-button icon="CirclePlusFilled" type="primary" @click="()=>showAddPortDialog=true">添加端口</el-button>
<el-divider direction="horizontal"></el-divider>
</div>
</el-tab-pane>
<el-tab-pane label="参数">
<el-empty description="未选择节点" v-if="!ifSelectNode"></el-empty>
<div class="space-y-1 p-2 flex flex-col" v-show="ifSelectNode">
<el-dialog v-model="addProperty" title="添加监听属性"> </div>
<el-form :model="propertyForm"> </el-tab-pane>
<el-form-item label="监听属性:"> </el-tabs>
<el-select v-model="propertyForm.key"> </div>
<el-option v-for="item in propertyForm.propertyList" :label="item" :value="item"></el-option> </div>
<!-- 添加图形模板弹窗-->
<!-- 添加端口弹窗-->
<el-dialog v-model="showAddPortDialog" title="添加端口" class="w-[500px] h-[500px]">
<el-form v-model="addPortForm">
<el-form-item label="端口名称">
<el-input v-model="addPortForm.name"></el-input>
</el-form-item>
<el-form-item label="端口类型">
<el-select v-model="addPortForm.type">
<el-option v-for="portType in portTypeList" :label="portType.label" :value="portType.value"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="显示名称:"> <el-form-item label="允许流出">
<el-input v-model="propertyForm.name"></el-input> <el-checkbox v-model="addPortForm.fromLinkable"></el-checkbox>
</el-form-item> </el-form-item>
<el-form-item label="显示方式:"> <el-form-item label="允许进入">
<el-select v-model="propertyForm.showIfPresent"> <el-checkbox v-model="addPortForm.toLinkable"></el-checkbox>
<el-option label="总是显示" value="false"></el-option>
<el-option label="存在时显示" value="true"></el-option>
</el-select>
</el-form-item> </el-form-item>
<el-form-item label="是否只读:"> <el-form-item label="最大连接">
<el-checkbox v-model="propertyForm.readOnly"></el-checkbox> <el-input v-model="addPortForm.maxLinks"></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="addProperty = false">Cancel</el-button> <el-button type="primary" @click="confirmAddPort">添加</el-button>
<el-button type="primary" @click="addInspector"> </template>
Confirm </el-dialog>
</el-button> <!-- 修改端口-->
<el-dialog v-model="showEditPortDialog" title="修改端口" class="w-[500px] h-[500px]">
<el-form v-model="editPortForm">
<el-form-item label="端口名称">
<el-input v-model="editPortForm.name"></el-input>
</el-form-item>
<el-form-item label="允许流出">
<el-checkbox v-model="editPortForm.fromLinkable"></el-checkbox>
</el-form-item>
<el-form-item label="允许进入">
<el-checkbox v-model="editPortForm.toLinkable"></el-checkbox>
</el-form-item>
<el-form-item label="最大连接">
<el-input v-model="editPortForm.maxLinks"></el-input>
</el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="confirmEditPort">确定</el-button>
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {onMounted, ref, triggerRef} from "vue";
import SourceNodeCollection from "./components/source/source-node-collection.vue";
import go from "gojs";
import {GO} from "./kit/GOJSKit.ts";
import {createRotatingTool, makeDiagramNodeTemplate} from "./kit/NodeTemplateKit.ts";
import {GuidedDraggingTool} from "./kit/extensions/GuidedDraggingTool.ts";
import {PortShiftingTool} from "./kit/extensions/PortShiftingTool.ts";
import {ArrowLink, FlowLink} from "./kit/LinkTemplateKit.ts";
import {Inspector} from "./kit/extensions/DataInspector.ts";
import {uuid} from "vue3-uuid";
import {SnapLinkReshapingTool} from "./kit/extensions/SnapLinkReshapingTool.ts";
import * as go from "gojs"; const diagramConfig = ref({
import SourceCollapse from "./components/source-panel/source-collapse.vue"; // 图表是否可以进行撤销操作
import {onMounted, ref} from "vue"; canUndo: false,
import {picCategory} from "./kit/ModelData.ts"; // 图表是否可以进行重做操作
import {findNodesAllWayBetween, getMapData, getSelectedNodes, GO, makePort, showSmallPorts} from "./kit/GOJSKit.ts"; canRedo: false
import { })
makeKitTopRotatingTool,
nodeResizeAdornmentTemplate, nodeRotateAdornmentTemplate, let myDiagram:any = null
nodeSelectionAdornmentTemplate let inspector:any = null
} from "./kit/NodeTemplateKit.ts"; const ifSelectNode = ref(false)
import {animateFlow, FlowLink, linkSelectionAdornmentTemplate} from "./kit/LinkTemplateKit.ts"; const selectedNode = ref()
import {Inspector} from "./kit/DataInspector.ts"; const nodePortArray = ref([])
import {ElMessage} from "element-plus";
import AnalyzeData from "./components/info/analyze-data.vue"; const showAddPortDialog = ref(false)
const addPortForm = ref({
const categoryList: any = ref(null) name: '端口1',
let myDiagram: any = null; type: "0 0.5",
toLinkable:true,
let inspector:any=null; fromLinkable:true,
maxLinks: 1,
const addProperty = ref(false) })
let propertyForm:any =ref(null); const showEditPortDialog = ref(false)
const editPortForm = ref({
let nodeMap = ref({}); portId:'',
let paths = ref<any[][]>([]); name: '端口1',
toLinkable:true,
function showAddPropertyDialog(){ fromLinkable:true,
let node = myDiagram.selection.first(); maxLinks: 1,
if (node === null){ })
ElMessage.error("请先选择一个节点") const portTypeList =[
} {label:'左侧', value:"0 0.5"},
propertyForm.value = { {label:'上方', value:"0.5 0"},
key:'', {label:'右侧', value:"1 0.5"},
propertyList: Object.keys(node.data), {label:'下方', value:"0.5 1"},
name: '', ]
showIfPresent: '',
readOnly: false, // 源节点搜索
// let inputSearchNode = ref('')
// 用于计算源区域的滚动条区域高度
let nodeAreaHeight = ref()
onMounted(()=>{
getNodeSourceScrollAreaHeight()
initDiagram()
listenDiagram()
initInspector()
})
// 为了在页面大小变化时,让节点区域的滚动条区域高度自适应
function getNodeSourceScrollAreaHeight(){
// 滚动区域高度为浏览器窗口高度 - 144px(固定的上方ToolBar + 本页面的输入框和最下方的按钮)
nodeAreaHeight.value = window.innerHeight - 144 + 'px'
}
function exportData(){
console.log(myDiagram.model.toJson())
}
// tool-bar 相关函数
function importData(){
let json = { "class": "GraphLinksModel",
"copiesArrays": true,
"copiesArrayObjects": true,
"linkFromPortIdProperty": "fromPort",
"linkToPortIdProperty": "toPort",
"nodeDataArray": [
{"name":"电脑","nodeCollection":"computer","img":"src/assets/models/computer/电脑.svg","category":"svg","key":-1,"loc":"-220 0","portArray":[{"portId":"port1","name":"输入1","alignment":"0 0.5","fromLinkable":true,"toLinkable":true,"fromSpot":"0 0.5","toSpot":"0 0.5","background":"black","isHighlighted":false,"maxLinks":"1"},{"portId":"port2","name":"输入2","alignment":"1 0.5","fromLinkable":true,"toLinkable":true,"fromSpot":"1 0.5","toSpot":"1 0.5","background":"black","isHighlighted":false,"maxLinks":"1"},{"name":"端口1","portId":"0b897516-0022-4797-82ce-e8899dc189ea","alignment":"0.5 0","fromLinkable":true,"toLinkable":true,"maxLinks":1,"fromSpot":"0.5 0","toSpot":"0.5 0","background":"black","isHighlighted":false}]},
{"name":"电脑","node_category":"computer","img":"src/assets/models/computer/电脑.svg","category":"svg","key":-2,"loc":"120 -180","portArray":[{"portId":"port1","name":"输入1","alignment":"0 0.5","fromLinkable":true,"toLinkable":true,"fromSpot":"0 0.5","toSpot":"0 0.5","background":"black","isHighlighted":false,"maxLinks":"1"}]}
],
"linkDataArray": [{"from":-2,"to":-1,"fromPort":"port1","toPort":"0b897516-0022-4797-82ce-e8899dc189ea","points":[67.5,-180,57.5,-180,-220,-180,-220,-121.25,-220,-62.5,-220,-52.5]}]
} }
addProperty.value = true myDiagram.model = go.Model.fromJson(json)
} }
function initPalette() { function saveData(){
categoryList.value = picCategory // console.log('nodeCount', nodeCount.count)
console.log(selectedNode.value?.data)
}
function changeData(){
let nodeDataArray = myDiagram.model.nodeDataArray
nodeDataArray.forEach((item:any)=>{
if(item.portArray){
item.portArray.forEach((port:any)=>{
myDiagram.model.setDataProperty(port, 'background', port['background'] === 'black'?'red':'black')
})
}
})
} }
function initDiagram() { // diagram 部分
function initDiagram(){
myDiagram = GO(go.Diagram, "myDiagram", { myDiagram = GO(go.Diagram, "myDiagram", {
initialContentAlignment: go.Spot.Center, initialContentAlignment: go.Spot.Center,
//允许撤销 //允许撤销
...@@ -114,12 +229,17 @@ function initDiagram() { ...@@ -114,12 +229,17 @@ function initDiagram() {
//滚轮缩放 //滚轮缩放
"allowZoom": true, "allowZoom": true,
//旋转点相关 //旋转点相关
"rotatingTool": GO(makeKitTopRotatingTool()), "rotatingTool": GO(createRotatingTool()),
"rotatingTool.handleAngle": 270, "rotatingTool.handleAngle": 270,
"rotatingTool.handleDistance": 50, "rotatingTool.handleDistance": 50,
"rotatingTool.snapAngleMultiple": 15, "rotatingTool.snapAngleMultiple": 15,
"rotatingTool.snapAngleEpsilon": 15, "rotatingTool.snapAngleEpsilon": 15,
//连接线相关 //拖动相关
'draggingTool': new GuidedDraggingTool(),
'draggingTool.horizontalGuidelineColor': 'blue',
'draggingTool.verticalGuidelineColor': 'blue',
'draggingTool.centerGuidelineColor': 'green',
'draggingTool.guidelineWidth': 1,
"draggingTool.dragsLink": true, "draggingTool.dragsLink": true,
"draggingTool.isGridSnapEnabled": true, "draggingTool.isGridSnapEnabled": true,
"draggingTool.isComplexRoutingRealtime": false, "draggingTool.isComplexRoutingRealtime": false,
...@@ -133,6 +253,11 @@ function initDiagram() { ...@@ -133,6 +253,11 @@ function initDiagram() {
GO(go.Shape, "Diamond", {segmentIndex: 0, cursor: "pointer", desiredSize: new go.Size(8, 8), fill: "tomato", stroke: "darkred"}), GO(go.Shape, "Diamond", {segmentIndex: 0, cursor: "pointer", desiredSize: new go.Size(8, 8), fill: "tomato", stroke: "darkred"}),
"relinkingTool.toHandleArchetype": "relinkingTool.toHandleArchetype":
GO(go.Shape, "Diamond", {segmentIndex: -1, cursor: "pointer", desiredSize: new go.Size(8, 8), fill: "darkred", stroke: "tomato"}), GO(go.Shape, "Diamond", {segmentIndex: -1, cursor: "pointer", desiredSize: new go.Size(8, 8), fill: "darkred", stroke: "tomato"}),
"linkReshapingTool":GO(SnapLinkReshapingTool),
"LinkReshaped": e => {
e.subject.adjusting = go.Link.End;
e.subject.routing = go.Link.Orthogonal;
},
"linkReshapingTool.handleArchetype": "linkReshapingTool.handleArchetype":
GO(go.Shape, "Diamond", {desiredSize: new go.Size(7, 7), fill: "lightblue", stroke: "deepskyblue"}), GO(go.Shape, "Diamond", {desiredSize: new go.Size(7, 7), fill: "lightblue", stroke: "deepskyblue"}),
//调整大小 //调整大小
...@@ -140,102 +265,54 @@ function initDiagram() { ...@@ -140,102 +265,54 @@ function initDiagram() {
GO(go.Shape, "Rectangle", {desiredSize: new go.Size(7, 7), fill: "lightblue", stroke: "deepskyblue"}), GO(go.Shape, "Rectangle", {desiredSize: new go.Size(7, 7), fill: "lightblue", stroke: "deepskyblue"}),
"resizingTool.isGridSnapEnabled": true, "resizingTool.isGridSnapEnabled": true,
//tooltip //tooltip
"toolManager.hoverDelay": 200 , "toolManager.hoverDelay": 200,
}) })
myDiagram.nodeTemplate = GO(go.Node, "Auto", // install the PortShiftingTool as a "mouse move" tool
new go.Binding('location', "loc", go.Point.parse).makeTwoWay(go.Point.stringify), myDiagram.toolManager.mouseMoveTools.insertAt(0, new PortShiftingTool());
{selectable: true, selectionAdornmentTemplate: nodeSelectionAdornmentTemplate}, myDiagram.model.linkFromPortIdProperty = "fromPort"; // necessary to remember portIds
{resizable: true, resizeObjectName: "view", resizeAdornmentTemplate: nodeResizeAdornmentTemplate}, myDiagram.model.linkToPortIdProperty = "toPort";
{rotatable: true, rotateAdornmentTemplate: nodeRotateAdornmentTemplate, locationSpot: go.Spot.Center}, //多种类型的连接线
new go.Binding('angle').makeTwoWay(), myDiagram.linkTemplateMap.add('', ArrowLink)
GO(go.Picture, {name: "view", cursor: "pointer", width: 100, height: 100, imageStretch: go.GraphObject.Fill, isPanelMain:true}, myDiagram.linkTemplateMap.add('Flow', FlowLink)
new go.Binding("source", "img"),
new go.Binding('desiredSize', "img_size", go.Size.parse).makeTwoWay(go.Size.stringify) //多种类型的节点
), myDiagram.nodeTemplateMap.add('svg', makeDiagramNodeTemplate('svg', 100))
GO(go.TextBlock, {margin: 4, text: "", editable: true, alignment: go.Spot.Center, stroke:"blue", background:"white"}, myDiagram.nodeTemplateMap.add('geometry', makeDiagramNodeTemplate('geometry', 100))
new go.Binding("text", "name").makeTwoWay(), myDiagram.nodeTemplateMap.add('shape', makeDiagramNodeTemplate('shape', 100))
new go.Binding('angle', "angle", v=>-v),
),
GO(go.TextBlock, {margin: 4, text: "", editable: true, alignment: go.Spot.Top},
new go.Binding("text", "press"),
new go.Binding("stroke", "",
function(data,_node) { return data.press>50?"red":"green"}
),
new go.Binding('angle', "angle", v=>-v),
),
// four small named ports, one on each side:
makePort("T", go.Spot.Top, true, true),
makePort("L", go.Spot.Left, true, true),
makePort("R", go.Spot.Right, true, true),
makePort("B", go.Spot.Bottom, true, true),
{ // handle mouse enter/leave events to show/hide the ports
mouseEnter: function (_e, node) {
showSmallPorts(node, true);
},
mouseLeave: function (_e, node) {
showSmallPorts(node, false);
},
},
{
toolTip:GO("ToolTip",{background:"lightgray"},
GO(go.Panel, "Vertical",
GO(go.TextBlock, {margin: 4},
new go.Binding("text", "thick", v=>"厚度:"+v+'mm'),
new go.Binding("stroke", "",
function(data,_node) { return data.thick>10?"red":"green"})
),
GO(go.TextBlock, {margin: 4},
new go.Binding("text", "temperature", v=>"温度:"+v+'℃'),
new go.Binding("stroke", "",
function(data,_node) { return data.temperature>10?"red":"green"}),
),
GO(go.TextBlock, {margin: 4},
new go.Binding("text", "press", v=>"压力:"+v+'MPa'),
new go.Binding("stroke", "",
function(data,_node) { return data.press>10?"red":"green"}),
),
),
),
}
);
//多箭头连接线
myDiagram.linkTemplateMap.add("Arrow",
GO(go.Link,
{selectable: true, selectionAdornmentTemplate: linkSelectionAdornmentTemplate},
{relinkableFrom: true, relinkableTo: true, reshapable: true},
{routing: go.Link.AvoidsNodes, curve: go.Link.JumpGap, toShortLength: 4},
new go.Binding("points", "points").makeTwoWay(),
GO(go.Shape, // the link path shape
{isPanelMain: true, strokeWidth: 2},
new go.Binding("stroke", "color").makeTwoWay(),
new go.Binding("fill", "color"),
),
GO(go.Shape, { toArrow: "Standard", stroke: null }, new go.Binding("fill", "color")),
GO(go.TextBlock,
{ segmentIndex: 0, segmentOffset: new go.Point(NaN, NaN), segmentOrientation: go.Link.OrientUpright, editable:true, text:""},
new go.Binding("text", "from_text").makeTwoWay()
),
GO(go.TextBlock,
{segmentOffset: new go.Point(NaN, NaN), editable:true, text:""},
new go.Binding("text", "middle_text").makeTwoWay()
),
GO(go.TextBlock,
{ segmentIndex: -1, segmentOffset: new go.Point(NaN, NaN), segmentOrientation: go.Link.OrientUpright, editable:true, text:"" },
new go.Binding("text", "to_text").makeTwoWay()
),
)
);
myDiagram.linkTemplateMap.add("Flow", FlowLink);
} }
function initInfo() { function listenDiagram(){
inspector = new Inspector('myInfo', myDiagram, myDiagram.addDiagramListener('BackgroundSingleClicked', ()=>{
console.log(myDiagram.model.toJson())
})
myDiagram.addDiagramListener('Modified', ()=>{
})
myDiagram.addDiagramListener('ChangedSelection', ()=>{
freshSelectionNode()
})
}
function freshSelectionNode(){
let selectedNodeKey = myDiagram.selection.first()?.data?.key
selectedNode.value = myDiagram.findNodeForKey(selectedNodeKey)
if(selectedNode.value !== null){
ifSelectNode.value = true
nodePortArray.value = selectedNode.value.data.portArray
}else{
ifSelectNode.value = false
nodePortArray.value = []
}
triggerRef(nodePortArray)
}
// 右侧info-panel相关函数
function initInspector(){
inspector = new Inspector('inspector-property', myDiagram,
{ {
includesOwnProperties: false, includesOwnProperties: false,
properties: { properties: {
//节点基本信息 //节点基本信息
"key": {show: Inspector.showIfPresent}, "key": {show: Inspector.showIfPresent, readOnly:true},
"name": {show: Inspector.showIfNode, name:'名称'}, "name": {show: Inspector.showIfNode, name:'名称'},
//连接线基本信息 //连接线基本信息
"from":{show: Inspector.showIfLink, name:"源", readOnly:true}, "from":{show: Inspector.showIfLink, name:"源", readOnly:true},
...@@ -246,163 +323,167 @@ function initInfo() { ...@@ -246,163 +323,167 @@ function initInfo() {
"category": {show:Inspector.showIfLink, name:"类别", type: "select", choices: ["Arrow", "Flow"]}, "category": {show:Inspector.showIfLink, name:"类别", type: "select", choices: ["Arrow", "Flow"]},
"thick":{show: Inspector.showIfNode, name:"厚度", unit:"mm"}, "thick":{show: Inspector.showIfNode, name:"厚度", unit:"mm"},
"temperature":{show: Inspector.showIfNode, name:"温度", unit:"℃"}, "temperature":{show: Inspector.showIfNode, name:"温度高低", unit:"℃"},
"press": {show: Inspector.showIfNode, name:"压力", unit:'MPa'}, "press": {show: Inspector.showIfNode, name:"压力", unit:'MPa'},
"thing":{show: Inspector.showIfNode, name:"物料"}, "thing":{show: Inspector.showIfNode, name:"物料"},
} }
}); }
)
} }
function addInspector(){ function confirmAddPort(){
let value = propertyForm.value myDiagram.startTransaction("addPort");
inspector.properties[value.key] = {} let node = myDiagram.findNodeForData(selectedNode.value.data) as go.Node
if (value.showIfPresent === "true"){ if(!node.data.portArray) {
inspector.properties[value.key].show = Inspector.showIfPresent node.data.portArray = []
} // node.updateTargetBindings('portArray')
if (value.readOnly){
inspector.properties[value.key].readOnly = true
} }
if (value.name){ let newPort = {
inspector.properties[value.key].name = value.name name: addPortForm.value.name,
portId: uuid.v4(),
alignment:addPortForm.value.type,
fromLinkable: addPortForm.value.fromLinkable,
toLinkable: addPortForm.value.toLinkable,
maxLinks: addPortForm.value.maxLinks,
fromSpot: addPortForm.value.type,
toSpot: addPortForm.value.type,
} }
addProperty.value = false let portArray = node.data.portArray
portArray.push(newPort)
// myDiagram.model.insertArrayItem(portArray, -1, newPort);
myDiagram.model.setDataProperty(node.data, 'portArray', portArray)
node.updateTargetBindings('portArray')
freshSelectionNode()
myDiagram.commitTransaction("addPort");
showAddPortDialog.value = false
} }
function addListener(){ function editPort(portId:string){
// 监听连线事件,为所有新添加的线默认加上 "Arrow" 类别 let node = myDiagram.findNodeForKey(selectedNode.value.data.key) as go.Node
myDiagram.addDiagramListener("LinkDrawn", function(e:go.DiagramEvent) { let portArray = node.data.portArray
let link = e.subject; portArray.forEach((port:any)=>{
let linkDataArray = myDiagram.model.linkDataArray if(port.portId === portId){
linkDataArray.forEach((item:any)=>{ editPortForm.value = {
if (item === link.data && !item.category){ portId:portId,
myDiagram.model.set(item, "category", "Arrow") name:port.name,
toLinkable: port.toLinkable,
fromLinkable: port.fromLinkable,
maxLinks: port.maxLinks
} }
}) }
}); })
} showEditPortDialog.value = true
function importData() {
myDiagram.model = go.Model.fromJson(
{ "class": "GraphLinksModel",
"nodeDataArray": [
{"name":"压缩机","picture_category":"ammonia","img":"src/assets/models/ammonia/压缩机.svg","key":-7,"loc":"-460 120","angle":90,"img_size":"50 50","press":25,"thick":"0.91","temperature":4},
{"name":"冷却器-2","picture_category":"ammonia","img":"src/assets/models/ammonia/冷却器-2.svg","key":-6,"loc":"-310 120","img_size":"70 70","angle":180,"press":83,"thick":"0.73","temperature":6},
{"name":"换热器","picture_category":"ammonia","img":"src/assets/models/ammonia/换热器.svg","key":-3,"loc":"-170 130","img_size":"60 60","press":0,"thick":"0.82","temperature":6},
{"name":"换热器","picture_category":"ammonia","img":"src/assets/models/ammonia/换热器.svg","key":-4,"loc":"-170 250","img_size":"60 60","thick":"0.80","press":57,"temperature":1,"thing":""},
{"name":"氨分离罐","picture_category":"ammonia","img":"src/assets/models/ammonia/氨分离罐.svg","key":-1,"loc":"-170 370","angle":180,"img_size":"70 70","thick":"0.99","press":97,"temperature":1,"thing":""},
{"name":"冷却器-2","picture_category":"ammonia","img":"src/assets/models/ammonia/冷却器-2.svg","key":-8,"loc":"-170 0","img_size":"50 50","angle":270,"press":61,"thick":"0.69","temperature":2},
{"name":"冷却器","picture_category":"ammonia","img":"src/assets/models/ammonia/冷却器.svg","key":-5,"loc":"-300 -50","img_size":"60 72","press":33,"thick":"1.00","temperature":9},
{"name":"氨分离罐","picture_category":"ammonia","img":"src/assets/models/ammonia/氨分离罐.svg","key":-9,"loc":"-460 -50","angle":270,"img_size":"90 90","press":4,"thick":"0.06","temperature":7},
{"name":"合成塔","picture_category":"ammonia","img":"src/assets/models/ammonia/合成塔.svg","key":-2,"loc":"40 0","img_size":"240 240","press":87,"thick":"0.96","temperature":6},
{"name":"开工炉","picture_category":"ammonia","img":"src/assets/models/ammonia/开工炉.svg","key":-10,"loc":"300 110","thick":"0.08","press":18,"temperature":4,"thing":""}
],
"linkDataArray": [
{"from":-7,"to":-6,"points":[-435,120,-425,120,-390,120,-390,120,-355,120,-345,120],"from_text":"","to_text":"","middle_text":"","category":"Arrow"},
{"to":-7,"points":[-581,120,-571,120,-533,120,-533,120,-495,120,-485,120],"from_text":"原料气","middle_text":"","to_text":"","category":"Arrow"},
{"from":-1,"to":-4,"points":[-170,335,-170,325,-170,307.5,-170,307.5,-170,290,-170,280],"category":"Arrow"},
{"from":-6,"to":-1,"points":[-310,155,-310,165,-310,370,-262.5,370,-215,370,-205,370],"category":"Arrow"},
{"from":-8,"to":-3,"points":[-170,25,-170,35,-170,62.5,-170,62.5,-170,90,-170,100],"category":"Arrow"},
{"from":-3,"to":-5,"points":[-200,130,-210,130,-235,130,-235,-50,-260,-50,-270,-50],"category":"Arrow"},
{"from":-5,"to":-9,"points":[-330,-50,-340,-50,-372.5,-50,-372.5,-50,-405,-50,-415,-50],"category":"Arrow"},
{"from":-9,"points":[-460,-95,-460,-105,-460,-117,-508,-117,-556,-117,-566,-117],"from_text":"","middle_text":"","to_text":"燃料气","category":"Arrow"},
{"from":-9,"points":[-460,-5,-460,5,-460,19,-513,19,-566,19,-576,19],"from_text":"","middle_text":"","to_text":"氨产品","category":"Arrow"},
{"from":-4,"to":-10,"points":[-140,250,-130,250,300,250,300,210,300,170,300,160],"category":"Arrow"},
{"from":-4,"to":-2,"points":[-170,220,-170,210,-170,186.5126932185735,40,186.5126932185735,40,130,40,120],"category":"Arrow"},
{"from":-10,"to":-2,"points":[250,110,240,110,205,110,205,0,170,0,160,0],"category":"Arrow"},
{"from":-1,"points":[-135,370,-125,370,-97.3671875,370,-97.3671875,370,-69.734375,370,-59.734375,370],"category":"Arrow"},
{"from":-2,"to":-8, "category":"Arrow"}
]}
);
nodeMap.value = getMapData(myDiagram)
} }
function exportData() { function confirmEditPort(){
console.log(myDiagram.model.toJson()) myDiagram.startTransaction('editPortInfo')
// console.log(myDiagram.position) let node = myDiagram.findNodeForKey(selectedNode.value.data.key) as go.Node
let portArray = node.data.portArray
portArray.forEach((port:any)=>{
if(port.portId === editPortForm.value.portId){
myDiagram.model.setDataProperty(port, 'name', editPortForm.value.name)
myDiagram.model.setDataProperty(port, 'toLinkable', editPortForm.value.toLinkable)
myDiagram.model.setDataProperty(port, 'fromLinkable', editPortForm.value.fromLinkable)
myDiagram.model.setDataProperty(port, 'maxLinks', editPortForm.value.maxLinks)
}
})
myDiagram.commitTransaction('commitPortInfo')
freshSelectionNode()
showEditPortDialog.value = false
} }
function analyseData(){ function deletePort(portId:string){
let selectedNodes = getSelectedNodes(myDiagram) myDiagram.startTransaction("deletePort");
nodeMap.value = getMapData(myDiagram) let model = myDiagram.model
let nodeDataArray = model.nodeDataArray
if (selectedNodes.length != 2){ nodeDataArray.forEach((item:any)=>{
ElMessage.error("只能选择两个节点") if(item.key === selectedNode.value.data.key){
return if(!item.portArray) item.portArray = []
} let portArray = item.portArray
portArray.forEach((port:any, index:number)=>{
let start_node = myDiagram.findNodeForKey(selectedNodes[0].key) if(port.portId === portId){
let end_node = myDiagram.findNodeForKey(selectedNodes[1].key) myDiagram.model.removeArrayItem(portArray, index);
}
paths.value = findNodesAllWayBetween(start_node.data.key, end_node.data.key, myDiagram) })
}
})
freshSelectionNode()
myDiagram.commitTransaction("deletePort");
} }
function highlightPort(portId:string, highlight:boolean){
function changeData(){ myDiagram.startTransaction("highlight");
let model = myDiagram.model let model = myDiagram.model
let nodeDataArray = myDiagram.model.nodeDataArray let nodeDataArray = model.nodeDataArray
nodeDataArray.forEach((item:any)=>{ nodeDataArray.forEach((item:any)=>{
if (item.picture_category === "ammonia"){ if(item.key === selectedNode.value.data.key){
model.set(item, "press", Math.floor(Math.random()*100)) if(!item.portArray) item.portArray = []
model.set(item, "thick", Math.random().toFixed(2)), let portArray = item.portArray
model.set(item, "temperature", Math.floor(Math.random()*10)) portArray.forEach((port:any)=>{
if(port.portId === portId){
myDiagram.model.setDataProperty(port, 'isHighlighted', highlight)
}
})
} }
}) })
// setInterval(()=>{ myDiagram.commitTransaction("highlight");
// let nodeDataArray = myDiagram.model.nodeDataArray
// nodeDataArray.forEach((item:any)=>{
// if (item.picture_category === "ammonia"){
// model.set(item, "press", Math.floor(Math.random()*100))
// model.set(item, "thick", Math.random().toFixed(2)),
// model.set(item, "temperature", Math.floor(Math.random()*10))
// }
// })
// },5000)
} }
onMounted(() => {
initPalette();
initDiagram();
initInfo();
importData();
changeData();
animateFlow(myDiagram);
addListener();
})
</script>
// 浏览器大小变化时
window.onresize = function (){
getNodeSourceScrollAreaHeight()
}
</script>
<style> <style>
/* info-panel */
.inspector { .inspector {
display: inline-block; display: flex;
font: bold 14px helvetica, sans-serif; font: 14px helvetica, sans-serif;
background-color: #212121; /* Grey 900 */ color: black; /* Grey 100 */
color: #F5F5F5; /* Grey 100 */
cursor: default; cursor: default;
} }
.inspector table { .inspector table {
border-collapse: separate; border-collapse: separate;
border-spacing: 2px; border-spacing: 2px;
width: 100%;
} }
.inspector td, th { .inspector td, th {
padding:2px; padding:2px;
white-space:nowrap;
} }
.inspector input { .inspector input {
background-color: #424242; /* Grey 800 */ background-color: white;
color: #F5F5F5; /* Grey 100 */ color: black;
font: bold 12px helvetica, sans-serif; border:1px solid black;
border:0px;
border-radius: 2px; border-radius: 2px;
padding: 2px; padding: 2px;
height: 20px;
width: 80px;
} }
.inspector input:disabled { .inspector input:disabled {
background-color: #BDBDBD; /* Grey 400 */ background-color: #BDBDBD; /* Grey 400 */
color: #616161; /* Grey 700 */ color: #616161; /* Grey 700 */
border: 1px solid #9E9E9E; /* Grey 500 */
} }
.inspector select { .inspector select {
background-color: #424242; background-color: #424242;
} }
.el-tabs--border-card > .el-tabs__content {
padding: 0px !important;
color: #6b778c;
font-size: 32px;
font-weight: 600;
}
</style> </style>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1697162103120" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4029" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M928 704h-192c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h136.533333c-76.8 100.266667-196.266667 160-326.4 160-228.266667 0-413.866667-185.6-413.866666-416 0-17.066667-14.933333-32-32-32s-32 14.933333-32 32c0 264.533333 213.333333 480 477.866666 480 134.4 0 260.266667-57.6 349.866667-153.6v89.6c0 17.066667 14.933333 32 32 32s32-14.933333 32-32v-192c0-17.066667-14.933333-32-32-32zM546.133333 32C407.466667 32 281.6 91.733333 192 189.866667V96C192 78.933333 177.066667 64 160 64S128 78.933333 128 96v192c0 17.066667 14.933333 32 32 32h192c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32h-132.266667c76.8-100.266667 196.266667-160 326.4-160C774.4 96 960 281.6 960 512c0 17.066667 14.933333 32 32 32S1024 529.066667 1024 512C1024 247.466667 810.666667 32 546.133333 32z" fill="#212121" p-id="4030"></path></svg>
\ No newline at end of file
@font-face {
font-family: "iconfont"; /* Project id 4282018 */
src: url('iconfont.woff2?t=1697008868886') format('woff2'),
url('iconfont.woff?t=1697008868886') format('woff'),
url('iconfont.ttf?t=1697008868886') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 24px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
[class^="icon-"], [class*=" icon-"] {
font-family: "iconfont" !important;
font-size: 24px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-undo:before {
content: "\e639";
}
.icon-redo:before {
content: "\e63e";
}
.icon-save:before {
content: "\e63e";
}
<template>
<div :id="picture_category"></div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import go from 'gojs'
import {GO} from '../../kit/GOJSKit'
import { SourcePictureNodeTemplate} from '../../kit/NodeTemplateKit';
import {picModel} from '../../kit/ModelData.ts'
const props = defineProps({
picture_category: {
type: String,
default: 'valve'
}
})
let sourcePalette: any = ref(null)
let sourceModel:any = ref(null)
function init(){
sourcePalette = GO(go.Palette, props.picture_category, {
layout: GO(go.GridLayout, { alignment: go.GridLayout.Location }),
nodeTemplate: SourcePictureNodeTemplate,
allowZoom: false
})
sourceModel.value = picModel.filter((item:any) => {
return item.picture_category === props.picture_category
})
sourcePalette.model = new go.GraphLinksModel(sourceModel.value)
}
onMounted(() => {
init()
})
</script>
\ No newline at end of file
<template>
<div>
<div :id="category.key" class="w-full"></div>
</div>
</template>
<script setup lang="ts">
import {onMounted, ref} from "vue";
import {GO} from "../../kit/GOJSKit";
import go from "gojs";
import {SourceNodeModel} from "../../kit/ModelData";
import { makeSourcePanelNodeTemplate} from "../../kit/NodeTemplateKit";
const props = defineProps({
category: {
type: Object,
default: {
key: 'valve',
name: '阀门',
type: 'svg'
}
}
})
let sourceCollapsePalette = ref<any>(null)
let sourceCollapseItemNodeModel = ref<any[]>([])
function init(){
sourceCollapsePalette = GO(go.Palette, props.category.key, {
allowZoom: false,
"toolManager.hoverDelay": 200 ,
layout: GO(go.GridLayout,
{ alignment: go.GridLayout.Location },
{ cellSize: new go.Size(45, 45), spacing: new go.Size(5, 5)},
{wrappingColumn: 4},
),
nodeTemplate: makeSourcePanelNodeTemplate(props.category.type, 30)
})
sourceCollapseItemNodeModel.value = SourceNodeModel.filter((item:any) => {
return item.nodeCollection === props.category.key
})
sourceCollapsePalette.model = new go.GraphLinksModel(sourceCollapseItemNodeModel.value)
// 45 是节点的高度,5是节点默认的上下间距
let paletteHeight = Math.floor(sourceCollapseItemNodeModel.value.length / 4) * 45 + 5
if (sourceCollapseItemNodeModel.value.length % 4 !== 0){
paletteHeight += 45+5
}
let paletteDiv = document.getElementById(props.category.key)
if (paletteDiv !== null){
// 为了确保tooltip的完整显示,所以需要一个最小高度
if (paletteHeight < 130) paletteHeight = 130
paletteDiv.style.height = paletteHeight + 'px'
}
}
onMounted(()=>{
init()
})
</script>
\ No newline at end of file
<template>
<el-collapse>
<el-collapse-item v-for="category in SourceNodeCategory" :title="category.name">
<SourceCategoryCollapse :category="category"></SourceCategoryCollapse>
</el-collapse-item>
</el-collapse>
</template>
<script setup lang="ts">
import {onMounted, ref} from "vue";
import {SourceNodeCategory} from "../../kit/ModelData";
import SourceCategoryCollapse from "./source-category-collapse.vue";
let nodeCategoryList = ref()
onMounted(()=>{
nodeCategoryList.value = SourceNodeCategory
})
</script>
\ No newline at end of file
...@@ -2,41 +2,6 @@ import go from 'gojs'; ...@@ -2,41 +2,6 @@ import go from 'gojs';
export var GO = go.GraphObject.make; export var GO = go.GraphObject.make;
/**
* 用于连接的锚点效果
* @param {String} name 锚点名称
* @param {Spot} spot 锚点位置
* @param {Boolean} output 是否可以从此锚点连接出去
* @param {Boolean} input 是否可以连接到此锚点
*/
export function makePort(name:any, spot:any, output:any, input:any) {
return GO(go.Shape, "Circle",
{
fill: null,
stroke: null,
desiredSize: new go.Size(10, 10),
alignment: spot,
alignmentFocus: spot,
portId: name,
fromSpot: spot, toSpot: spot,
fromLinkable: output, toLinkable: input,
cursor: "pointer"
});
}
/**
* 展示连接锚点
* @param {Node} node 节点
* @param {Boolean} show 是否展示
*/
export function showSmallPorts(node:any, show:any) {
node.ports.each(function(port:any) {
if (port.portId !== "") { // don't change the default port, which is the big shape
port.fill = show ? "rgba(0,0,0,.3)" : null;
}
});
}
/** /**
* 获取节点的所有连接,存储为一个Map。{key:{next:[], data:{}}} * 获取节点的所有连接,存储为一个Map。{key:{next:[], data:{}}}
*/ */
......
...@@ -64,14 +64,27 @@ export var FlowLink = ...@@ -64,14 +64,27 @@ export var FlowLink =
relinkableFrom: true, relinkableFrom: true,
relinkableTo: true, relinkableTo: true,
reshapable: true, reshapable: true,
// resegmentable: true,
}, },
new go.Binding("points", "points").makeTwoWay(), new go.Binding("points", "points").makeTwoWay(),
GO(go.Shape, { isPanelMain:true, strokeWidth: 5, stroke: 'red'}), GO(go.Shape, { isPanelMain:true, strokeWidth: 5, stroke: 'gray'}),
GO(go.Shape, { isPanelMain:true, strokeWidth: 5, stroke: 'green',name:"FLOW", strokeDashArray: [10, 10]}), GO(go.Shape, { isPanelMain:true, strokeWidth: 5, stroke: 'black',name:"FLOW", strokeDashArray: [10, 10]}),
GO(go.Shape, { toArrow: "Standard", stroke: null })
); );
export var ArrowLink =
GO(go.Link,
{selectable: true, selectionAdornmentTemplate: linkSelectionAdornmentTemplate},
{relinkableFrom: true, relinkableTo: true, reshapable: true},
{routing: go.Link.AvoidsNodes, curve: go.Link.JumpGap, toShortLength: 4},
new go.Binding("points", "points").makeTwoWay(),
GO(go.Shape, // the link path shape
{isPanelMain: true, strokeWidth: 2},
new go.Binding("stroke", "color").makeTwoWay(),
new go.Binding("fill", "color"),
),
GO(go.Shape, { toArrow: "Standard", stroke: null }, new go.Binding("fill", "color")),
makeLinkLabel()
)
export function animateFlow(myDiagram:go.Diagram){ export function animateFlow(myDiagram:go.Diagram){
// if (myAnimation) myAnimation.stop(); // if (myAnimation) myAnimation.stop();
// Animate the flow in the pipes // Animate the flow in the pipes
...@@ -87,4 +100,21 @@ export function animateFlow(myDiagram:go.Diagram){ ...@@ -87,4 +100,21 @@ export function animateFlow(myDiagram:go.Diagram){
// Run indefinitely // Run indefinitely
myAnimation.runCount = Infinity; myAnimation.runCount = Infinity;
myAnimation.start(); myAnimation.start();
}
export function makeLinkLabel(){
return [
GO(go.TextBlock,
{ segmentIndex: 0, segmentOffset: new go.Point(NaN, NaN), segmentOrientation: go.Link.OrientUpright, editable:true, text:""},
new go.Binding("text", "from_text").makeTwoWay()
),
GO(go.TextBlock,
{segmentOffset: new go.Point(NaN, NaN), editable:true, text:""},
new go.Binding("text", "middle_text").makeTwoWay()
),
GO(go.TextBlock,
{ segmentIndex: -1, segmentOffset: new go.Point(NaN, NaN), segmentOrientation: go.Link.OrientUpright, editable:true, text:"" },
new go.Binding("text", "to_text").makeTwoWay()
),
]
} }
\ No newline at end of file
/**
export const picCategory = [ * 源节点模型分类
{key:"valve",name:"阀门"}, * @type {{key: string; name: string; type: string}[]}
{key:"pump",name:"泵"}, * key: 节点类别
{key:"computer",name:"计算机"}, * name: 节点类别名称
{key:"ammonia", name:"氨气合成装置"} * type: 节点类别类型, svg: 图片, geometry: 路径绘制, shape: gojs内置形状
*/
export const SourceNodeCategory: { key: string; name: string; type: string }[] = [
{key:"valve",name:"阀门", type:"svg"},
{key:"pump",name:"泵", type:"svg"},
{key:"computer",name:"计算机", type:"svg"},
{key:"ammonia", name:"氨气合成装置", type:"svg"},
{key:"pipe", name:"管道", type:"geometry"},
{key:"shape", name:"形状", type:"shape"},
] ]
export const picModel = [ export const SourceNodeModel = [
{ name: "computer1", picture_category: "computer", img: "src/assets/models/computer/电脑.svg"}, //{name: 节点名称, nodeCollection: 节点类别, img: 节点图片路径, geometry: 节点形状, ports: 端口}
{ name: "valve2", picture_category: "valve", img: "src/assets/models/valve/阀-2.svg"}, //svg
{ name: "valve3", picture_category: "valve", img: "src/assets/models/valve/阀.svg"}, //computer
{ name: "valve4", picture_category: "valve", img: "src/assets/models/valve/阀门-01.svg"}, { name: "电脑", nodeCollection: "computer", img: "src/assets/models/computer/电脑.svg", category:'svg'},
{ name: "valve5", picture_category: "valve", img: "src/assets/models/valve/阀门2-01.svg"}, //valve
{name: "pump1", picture_category: "pump", img: "src/assets/models/pump/立式泵.svg"}, { name: "阀-2", nodeCollection: "valve", img: "src/assets/models/valve/阀-2.svg", category:'svg'},
{name: "pump2", picture_category: "pump", img: "src/assets/models/pump/卧式泵.svg"}, { name: "阀", nodeCollection: "valve", img: "src/assets/models/valve/阀.svg", category:'svg'},
{name: "pump3", picture_category: "pump", img: "src/assets/models/pump/旋转泵.svg"}, { name: "阀门-01", nodeCollection: "valve", img: "src/assets/models/valve/阀门-01.svg", category:'svg'},
{name: "pump4", picture_category: "pump", img: "src/assets/models/pump/油池泵.svg"}, { name: "阀门2-01", nodeCollection: "valve", img: "src/assets/models/valve/阀门2-01.svg", category:'svg'},
{name:"氨分离罐", picture_category:"ammonia", img:"src/assets/models/ammonia/氨分离罐.svg"}, //pump
{name:"合成塔", picture_category:"ammonia", img:"src/assets/models/ammonia/合成塔.svg"}, {name: "立式泵", nodeCollection: "pump", img: "src/assets/models/pump/立式泵.svg", category:'svg'},
{name:"换热器", picture_category:"ammonia", img:"src/assets/models/ammonia/换热器.svg"}, {name: "卧式泵", nodeCollection: "pump", img: "src/assets/models/pump/卧式泵.svg", category:'svg'},
{name:"开工炉", picture_category:"ammonia", img:"src/assets/models/ammonia/开工炉.svg"}, {name: "旋转泵", nodeCollection: "pump", img: "src/assets/models/pump/旋转泵.svg", category:'svg'},
{name:"冷却器", picture_category:"ammonia", img:"src/assets/models/ammonia/冷却器.svg"}, {name: "油池泵", nodeCollection: "pump", img: "src/assets/models/pump/油池泵.svg", category:'svg'},
{name:"冷却器-2", picture_category:"ammonia", img:"src/assets/models/ammonia/冷却器-2.svg"}, //ammonia
{name:"压缩机", picture_category:"ammonia", img:"src/assets/models/ammonia/压缩机.svg"}, {name:"氨分离罐", nodeCollection:"ammonia", img:"src/assets/models/ammonia/氨分离罐.svg", category:'svg'},
{name:"合成塔", nodeCollection:"ammonia", img:"src/assets/models/ammonia/合成塔.svg", category:'svg'},
{name:"换热器", nodeCollection:"ammonia", img:"src/assets/models/ammonia/换热器.svg", category:'svg'},
{name:"开工炉", nodeCollection:"ammonia", img:"src/assets/models/ammonia/开工炉.svg", category:'svg'},
{name:"冷却器", nodeCollection:"ammonia", img:"src/assets/models/ammonia/冷却器.svg", category:'svg'},
{name:"冷却器-2", nodeCollection:"ammonia", img:"src/assets/models/ammonia/冷却器-2.svg", category:'svg'},
{name:"压缩机", nodeCollection:"ammonia", img:"src/assets/models/ammonia/压缩机.svg", category:'svg'},
//shape
{name:"矩形", nodeCollection: "shape", shape: "Rectangle", fill: "white", stroke:"black", category: 'shape'},
//geometry
{name:"弯头", nodeCollection: "pipe", geometry: "F1 M0 40 L0 30 Q0 0 30 0 L40 0 40 20 30 20 Q20 20 20 30 L20 40z", fill: "rgba(128, 128, 128, 0.5)", category: 'geometry'},
] ]
import {GO, makePort, showSmallPorts} from './GOJSKit.js'; import {GO} from "./GOJSKit";
import go from 'gojs'; import go from "gojs";
// 节点选中时的边框效果 // 源节点图片或形状大小
export var nodeSelectionAdornmentTemplate =
GO(go.Adornment, "Auto",
GO(go.Shape, { fill: null, stroke: "deepskyblue", strokeWidth: 1.5, strokeDashArray: [4, 2] }),
GO(go.Placeholder)
);
// 表示节点旋转的锚点效果 // 源节点的模板--type: svg
export const makeKitTopRotatingTool = () => ( export function makeSVGNode (size:number) {
return GO(go.Picture,
{ width:size, height:size, imageStretch: go.GraphObject.Fill },
new go.Binding("source", "img"),
new go.Binding('desiredSize', "size", go.Size.parse),
)
}
// 源节点的模板--type: geometry
export function makeGeometryNode(size:number) {
return GO(go.Shape,
{width:size,height:size, geometryStretch: go.GraphObject.Fill},
new go.Binding("geometryString", "geometry"),
new go.Binding("fill", "fill"),
new go.Binding("stroke", "stroke"),
new go.Binding('desiredSize', "size", go.Size.parse),
)
}
// 源节点的模板--type: shape
export function makeShapeNode (size:number){
return GO(go.Shape,
{width:size,height:size, geometryStretch: go.GraphObject.Fill},
new go.Binding("figure", "shape"),
new go.Binding("fill", "fill"),
new go.Binding("stroke", "stroke"),
// todo 边框会失效
new go.Binding('desiredSize', "size", go.Size.parse),
)
}
/*
* 不同类型返回不同的节点模板
*/
export function makeNodeTemplate(type:string, size:number){
switch (type) {
case 'svg':
return makeSVGNode(size)
case 'geometry':
return makeGeometryNode(size)
case 'shape':
return makeShapeNode(size)
}
}
/*
* 创建节点模板,相比于 makeNodeTemplate 外面套了一层公共部分,方便整体修改
*/
export function makeDiagramNodeTemplate(type:string, size:number){
return GO(go.Node, "Spot",
{selectable: true, selectionAdornmentTemplate: nodeSelectionAdornment},
{resizable: true, resizeAdornmentTemplate: nodeResizeAdornment},
{rotatable: true, rotateAdornmentTemplate: nodeRotateAdornment, locationSpot: go.Spot.Center},
{itemTemplate: nodePortTemplate},
new go.Binding('location', "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
new go.Binding('angle').makeTwoWay(),
new go.Binding('desiredSize', "size", go.Size.parse).makeTwoWay(go.Size.stringify),
new go.Binding("itemArray", "portArray"),
makeNodeTemplate(type, size),
)
}
export function makeSourcePanelNodeTemplate(type:string, size:number){
return GO(go.Node, "Auto",
{selectionAdornmentTemplate:GO(go.Adornment)},
{width:40, height:40 },
{
mouseEnter: function (_e, node:any){
node.findObject("SHAPE").fill = "rgba(0, 0, 0, 0.1)"
},
mouseLeave: function (_e, node:any){
node.findObject("SHAPE").fill = null
}
},
GO(go.Shape, "RoundedRectangle",
{width:40, height:40,stroke:null, fill:null, name:"SHAPE"},
),
makeNodeTemplate(type, size),
{
toolTip: GO('ToolTip', {width:100},
GO(go.Panel, "Vertical",
makeNodeTemplate(type, size*3),
GO(go.Shape, "LineH", {width:100,height:10}),
GO(go.TextBlock, {margin:5 }, new go.Binding("text", "name"))
)
)
}
)
}
// 重写旋转工具
export const createRotatingTool = () => (
class KitTopRotatingTool extends go.RotatingTool { class KitTopRotatingTool extends go.RotatingTool {
updateAdornments(part: any) { updateAdornments(part: any) {
go.RotatingTool.prototype.updateAdornments.call(this, part) go.RotatingTool.prototype.updateAdornments.call(this, part)
const adornment = part.findAdornment('Rotating'); const adornment = part.findAdornment('Rotating');
if (adornment !== null) { if (adornment !== null) {
adornment.location = part.rotateObject.getDocumentPoint(new go.Spot(0.5, 0, 0, -30)) // above middle top adornment.location = part.rotateObject.getDocumentPoint(new go.Spot(0.5, 0, 0, -20)) // above middle top
} }
} }
} }
) )
export var nodeRotateAdornmentTemplate =
GO(go.Adornment, // 节点旋转装饰器
{ locationSpot: go.Spot.Center, locationObjectName: "CIRCLE" }, export var nodeRotateAdornment = GO(go.Adornment,
GO(go.Shape, "Circle", { name: "CIRCLE", cursor: "pointer", desiredSize: new go.Size(7, 7), fill: "lightblue", stroke: "deepskyblue" }), { locationSpot: go.Spot.Center, locationObjectName: "Rotate" },
GO(go.Shape, { geometryString: "M3.5 7 L3.5 30", isGeometryPositioned: true, stroke: "deepskyblue", strokeWidth: 1.5, strokeDashArray: [4, 2] }) GO(go.Shape, {name: "Rotate", cursor: "grab", desiredSize: new go.Size(12, 12), fill: "deepskyblue", stroke: "deepskyblue", geometryString:"M928 704h-192c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h136.533333c-76.8 100.266667-196.266667 160-326.4 160-228.266667 0-413.866667-185.6-413.866666-416 0-17.066667-14.933333-32-32-32s-32 14.933333-32 32c0 264.533333 213.333333 480 477.866666 480 134.4 0 260.266667-57.6 349.866667-153.6v89.6c0 17.066667 14.933333 32 32 32s32-14.933333 32-32v-192c0-17.066667-14.933333-32-32-32zM546.133333 32C407.466667 32 281.6 91.733333 192 189.866667V96C192 78.933333 177.066667 64 160 64S128 78.933333 128 96v192c0 17.066667 14.933333 32 32 32h192c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32h-132.266667c76.8-100.266667 196.266667-160 326.4-160C774.4 96 960 281.6 960 512c0 17.066667 14.933333 32 32 32S1024 529.066667 1024 512C1024 247.466667 810.666667 32 546.133333 32z" }),
); );
// 可以调整节点大小的锚点效果 // 节点选中效果 装饰器
export var nodeResizeAdornmentTemplate = export var nodeSelectionAdornment = GO(go.Adornment, "Auto",
GO(go.Adornment, "Spot", GO(go.Shape, { fill: null, stroke: "deepskyblue", strokeWidth: 1.5, strokeDashArray: [4, 2] }),
{ locationSpot: go.Spot.Right }, GO(go.Placeholder)
GO(go.Placeholder),
GO(go.Shape, { alignment: go.Spot.TopLeft, cursor: "nw-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
GO(go.Shape, { alignment: go.Spot.Top, cursor: "n-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
GO(go.Shape, { alignment: go.Spot.TopRight, cursor: "ne-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
GO(go.Shape, { alignment: go.Spot.Left, cursor: "w-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
GO(go.Shape, { alignment: go.Spot.Right, cursor: "e-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
GO(go.Shape, { alignment: go.Spot.BottomLeft, cursor: "se-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
GO(go.Shape, { alignment: go.Spot.Bottom, cursor: "s-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
GO(go.Shape, { alignment: go.Spot.BottomRight, cursor: "sw-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" })
); );
export var SourcePictureNodeTemplate =
GO(go.Node, "Vertical",
GO(go.Picture,
{ width:80,height:80},
new go.Binding("source", "img")
),
GO(go.TextBlock,
{ margin: 8 ,text: "123"},
new go.Binding("text", "name"))
)
export var DiagramPictureNodeTemplate = GO(go.Node, "Spot", // 节点调整大小装饰器
new go.Binding('location', "loc", go.Point.parse).makeTwoWay(go.Point.stringify), export var nodeResizeAdornment = GO(go.Adornment, "Spot",
{selectable: true, selectionAdornmentTemplate: nodeSelectionAdornmentTemplate}, { locationSpot: go.Spot.Right },
{ resizable: true, resizeObjectName: "PANEL", resizeAdornmentTemplate: nodeResizeAdornmentTemplate }, GO(go.Placeholder),
{ rotatable: true, rotateAdornmentTemplate: nodeRotateAdornmentTemplate, locationSpot:go.Spot.Center }, GO(go.Shape, { alignment: go.Spot.TopLeft, cursor: "nw-resize", desiredSize: new go.Size(6, 6), fill: "white", stroke: "deepskyblue" }),
{resizable: true, resizeObjectName: "view"}, GO(go.Shape, { alignment: go.Spot.Top, cursor: "n-resize", desiredSize: new go.Size(6, 6), fill: "white", stroke: "deepskyblue" }),
new go.Binding('angle').makeTwoWay(), GO(go.Shape, { alignment: go.Spot.TopRight, cursor: "ne-resize", desiredSize: new go.Size(6, 6), fill: "white", stroke: "deepskyblue" }),
GO(go.Panel, "Vertical", GO(go.Shape, { alignment: go.Spot.Left, cursor: "w-resize", desiredSize: new go.Size(6, 6), fill: "white", stroke: "deepskyblue" }),
{name:"PANEL", cursor: "pointer", background: "lightblue"}, GO(go.Shape, { alignment: go.Spot.Right, cursor: "e-resize", desiredSize: new go.Size(6, 6), fill: "white", stroke: "deepskyblue" }),
GO(go.Panel, "Viewbox",{name:"view", viewboxStretch: go.GraphObject.Uniform}, GO(go.Shape, { alignment: go.Spot.BottomLeft, cursor: "se-resize", desiredSize: new go.Size(6, 6), fill: "white", stroke: "deepskyblue" }),
new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify), GO(go.Shape, { alignment: go.Spot.Bottom, cursor: "s-resize", desiredSize: new go.Size(6, 6), fill: "white", stroke: "deepskyblue" }),
GO(go.Picture, new go.Binding("source", "img")), GO(go.Shape, { alignment: go.Spot.BottomRight, cursor: "sw-resize", desiredSize: new go.Size(6, 6), fill: "white", stroke: "deepskyblue" })
),
GO(go.TextBlock, {margin: 4, text:"", editable:true, background:"green", alignment:go.Spot.Bottom, alignmentFocus:go.Spot.Top}, new go.Binding("text", "name").makeTwoWay()),
),
// four small named ports, one on each side:
makePort("T", go.Spot.Top, true, true),
makePort("L", go.Spot.Left, true, true),
makePort("R", go.Spot.Right, true, true),
makePort("B", go.Spot.Bottom, true, true),
{ // handle mouse enter/leave events to show/hide the ports
mouseEnter: function(_e, node) { showSmallPorts(node, true);},
mouseLeave: function(_e, node) { showSmallPorts(node, false); }
}
); );
export var nodePortTemplate = GO(go.Panel, "Spot",
{background: "black", desiredSize: new go.Size(10, 10), cursor:"pointer"},
new go.Binding("portId", "portId"),
new go.Binding("alignment", "alignment", go.Spot.parse).makeTwoWay(go.Spot.stringify),
new go.Binding("fromLinkable", "fromLinkable"),
new go.Binding("toLinkable", "toLinkable"),
new go.Binding("fromSpot", "fromSpot", go.Spot.parse).makeTwoWay(go.Spot.stringify),
new go.Binding("toSpot", "toSpot", go.Spot.parse).makeTwoWay(go.Spot.stringify),
new go.Binding('maxLinks', 'maxLinks'),
new go.Binding('background', '', (data)=>{
return data['isHighlighted']? 'yellow': 'black'
}),
{
toolTip: GO('ToolTip',
GO(go.TextBlock, { margin: 4 }, new go.Binding('text', 'name'))
)
}
)
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* Copyright (C) 1998-2021 by Northwoods Software Corporation. All Rights Reserved.
*/
/*
* This is an extension and not part of the main GoJS library.
* Note that the API for this class may change with any version, even point releases.
* If you intend to use an extension in production, you should copy the code to your own source directory.
* Extensions can be found in the GoJS kit under the extensions or extensionsTS folders.
* See the Extensions intro page (https://gojs.net/latest/intro/extensions.html) for more information.
*/
import go from 'gojs';
/**
* The GuidedDraggingTool class makes guidelines visible as the parts are dragged around a diagram
* when the selected part is nearly aligned with another part.
*
* If you want to experiment with this extension, try the <a href="../../extensionsTS/GuidedDragging.html">Guided Dragging</a> sample.
* @category Tool Extension
*/
export class GuidedDraggingTool extends go.DraggingTool {
// horizontal guidelines
private guidelineHtop: go.Part;
private guidelineHbottom: go.Part;
private guidelineHcenter: go.Part;
// vertical guidelines
private guidelineVleft: go.Part;
private guidelineVright: go.Part;
private guidelineVcenter: go.Part;
// properties that the programmer can modify
private _guidelineSnapDistance: number = 6;
private _isGuidelineEnabled: boolean = true;
private _horizontalGuidelineColor: string = 'gray';
private _verticalGuidelineColor: string = 'gray';
private _centerGuidelineColor: string = 'gray';
private _guidelineWidth: number = 1;
private _searchDistance: number = 1000;
private _isGuidelineSnapEnabled: boolean = true;
/**
* Constructs a GuidedDraggingTool and sets up the temporary guideline parts.
*/
constructor() {
super();
const partProperties = { layerName: 'Tool', isInDocumentBounds: false };
const shapeProperties = { stroke: 'gray', isGeometryPositioned: true };
const $ = go.GraphObject.make;
// temporary parts for horizonal guidelines
this.guidelineHtop =
$(go.Part, partProperties,
$(go.Shape, shapeProperties, { geometryString: 'M0 0 100 0' }));
this.guidelineHbottom =
$(go.Part, partProperties,
$(go.Shape, shapeProperties, { geometryString: 'M0 0 100 0' }));
this.guidelineHcenter =
$(go.Part, partProperties,
$(go.Shape, shapeProperties, { geometryString: 'M0 0 100 0' }));
// temporary parts for vertical guidelines
this.guidelineVleft =
$(go.Part, partProperties,
$(go.Shape, shapeProperties, { geometryString: 'M0 0 0 100' }));
this.guidelineVright =
$(go.Part, partProperties,
$(go.Shape, shapeProperties, { geometryString: 'M0 0 0 100' }));
this.guidelineVcenter =
$(go.Part, partProperties,
$(go.Shape, shapeProperties, { geometryString: 'M0 0 0 100' }));
}
/**
* Gets or sets the margin of error for which guidelines show up.
*
* The default value is 6.
* Guidelines will show up when the aligned nods are ± 6px away from perfect alignment.
*/
get guidelineSnapDistance(): number { return this._guidelineSnapDistance; }
set guidelineSnapDistance(val: number) {
if (typeof val !== 'number' || isNaN(val) || val < 0) throw new Error('new value for GuideddraggingTool.guidelineSnapDistance must be a non-negative number');
if (this._guidelineSnapDistance !== val) {
this._guidelineSnapDistance = val;
}
}
/**
* Gets or sets whether the guidelines are enabled or disable.
*
* The default value is true.
*/
get isGuidelineEnabled(): boolean { return this._isGuidelineEnabled; }
set isGuidelineEnabled(val: boolean) {
if (typeof val !== 'boolean') throw new Error('new value for GuidedDraggingTool.isGuidelineEnabled must be a boolean value.');
if (this._isGuidelineEnabled !== val) {
this._isGuidelineEnabled = val;
}
}
/**
* Gets or sets the color of horizontal guidelines.
*
* The default value is "gray".
*/
get horizontalGuidelineColor(): string { return this._horizontalGuidelineColor; }
set horizontalGuidelineColor(val: string) {
if (this._horizontalGuidelineColor !== val) {
this._horizontalGuidelineColor = val;
(this.guidelineHbottom.elements.first() as go.Shape).stroke = this._horizontalGuidelineColor;
(this.guidelineHtop.elements.first() as go.Shape).stroke = this._horizontalGuidelineColor;
}
}
/**
* Gets or sets the color of vertical guidelines.
*
* The default value is "gray".
*/
get verticalGuidelineColor(): string { return this._verticalGuidelineColor; }
set verticalGuidelineColor(val: string) {
if (this._verticalGuidelineColor !== val) {
this._verticalGuidelineColor = val;
(this.guidelineVleft.elements.first() as go.Shape).stroke = this._verticalGuidelineColor;
(this.guidelineVright.elements.first() as go.Shape).stroke = this._verticalGuidelineColor;
}
}
/**
* Gets or sets the color of center guidelines.
*
* The default value is "gray".
*/
get centerGuidelineColor(): string { return this._centerGuidelineColor; }
set centerGuidelineColor(val: string) {
if (this._centerGuidelineColor !== val) {
this._centerGuidelineColor = val;
(this.guidelineVcenter.elements.first() as go.Shape).stroke = this._centerGuidelineColor;
(this.guidelineHcenter.elements.first() as go.Shape).stroke = this._centerGuidelineColor;
}
}
/**
* Gets or sets the width guidelines.
*
* The default value is 1.
*/
get guidelineWidth(): number { return this._guidelineWidth; }
set guidelineWidth(val: number) {
if (typeof val !== 'number' || isNaN(val) || val < 0) throw new Error('New value for GuidedDraggingTool.guidelineWidth must be a non-negative number.');
if (this._guidelineWidth !== val) {
this._guidelineWidth = val;
(this.guidelineVcenter.elements.first() as go.Shape).strokeWidth = val;
(this.guidelineHcenter.elements.first() as go.Shape).strokeWidth = val;
(this.guidelineVleft.elements.first() as go.Shape).strokeWidth = val;
(this.guidelineVright.elements.first() as go.Shape).strokeWidth = val;
(this.guidelineHbottom.elements.first() as go.Shape).strokeWidth = val;
(this.guidelineHtop.elements.first() as go.Shape).strokeWidth = val;
}
}
/**
* Gets or sets the distance around the selected part to search for aligned parts.
*
* The default value is 1000.
* Set this to Infinity if you want to search the entire diagram no matter how far away.
*/
get searchDistance(): number { return this._searchDistance; }
set searchDistance(val: number) {
if (typeof val !== 'number' || isNaN(val) || val <= 0) throw new Error('new value for GuidedDraggingTool.searchDistance must be a positive number.');
if (this._searchDistance !== val) {
this._searchDistance = val;
}
}
/**
* Gets or sets whether snapping to guidelines is enabled.
*
* The default value is true.
*/
get isGuidelineSnapEnabled(): boolean { return this._isGuidelineSnapEnabled; }
set isGuidelineSnapEnabled(val: boolean) {
if (typeof val !== 'boolean') throw new Error('new value for GuidedDraggingTool.isGuidelineSnapEnabled must be a boolean.');
if (this._isGuidelineSnapEnabled !== val) {
this._isGuidelineSnapEnabled = val;
}
}
/**
* Removes all of the guidelines from the grid.
*/
public clearGuidelines(): void {
this.diagram.remove(this.guidelineHbottom);
this.diagram.remove(this.guidelineHcenter);
this.diagram.remove(this.guidelineHtop);
this.diagram.remove(this.guidelineVleft);
this.diagram.remove(this.guidelineVright);
this.diagram.remove(this.guidelineVcenter);
}
/**
* Calls the base method and removes the guidelines from the graph.
*/
public doDeactivate(): void {
super.doDeactivate();
// clear any guidelines when dragging is done
this.clearGuidelines();
}
/**
* Shows vertical and horizontal guidelines for the dragged part.
*/
public doDragOver(pt: go.Point, obj: go.GraphObject): void {
// clear all existing guidelines in case either show... method decides to show a guideline
this.clearGuidelines();
// gets the selected part
const draggingParts = this.copiedParts || this.draggedParts;
if (draggingParts === null) return;
const partItr = draggingParts.iterator;
if (partItr.next()) {
const part = partItr.key;
this.showHorizontalMatches(part, this.isGuidelineEnabled, false);
this.showVerticalMatches(part, this.isGuidelineEnabled, false);
}
}
/**
* On a mouse-up, snaps the selected part to the nearest guideline.
* If not snapping, the part remains at its position.
*/
public doDropOnto(pt: go.Point, obj: go.GraphObject): void {
this.clearGuidelines();
// gets the selected (perhaps copied) Part
const draggingParts = this.copiedParts || this.draggedParts;
if (draggingParts === null) return;
const partItr = draggingParts.iterator;
if (partItr.next()) {
const part = partItr.key;
// snaps only when the mouse is released without shift modifier
const e = this.diagram.lastInput;
const snap = this.isGuidelineSnapEnabled && !e.shift;
this.showHorizontalMatches(part, false, snap); // false means don't show guidelines
this.showVerticalMatches(part, false, snap);
}
}
/**
* When nodes are shifted due to being guided upon a drop, make sure all connected link routes are invalidated,
* since the node is likely to have moved a different amount than all its connected links in the regular
* operation of the DraggingTool.
*/
public invalidateLinks(node: go.Part): void {
if (node instanceof go.Node) node.invalidateConnectedLinks();
}
/**
* This predicate decides whether or not the given Part should guide the dragged part.
* @param {Part} part a stationary Part to which the dragged part might be aligned
* @param {Part} guidedpart the Part being dragged
*/
protected isGuiding(part: go.Part, guidedpart: go.Part): boolean {
return part instanceof go.Part &&
!part.isSelected &&
!(part instanceof go.Link) &&
guidedpart instanceof go.Part &&
part.containingGroup === guidedpart.containingGroup &&
part.layer !== null && !part.layer.isTemporary;
}
/**
* This finds parts that are aligned near the selected part along horizontal lines. It compares the selected
* part to all parts within a rectangle approximately twice the {@link #searchDistance} wide.
* The guidelines appear when a part is aligned within a margin-of-error equal to {@link #guidelineSnapDistance}.
* @param {Node} part
* @param {boolean} guideline if true, show guideline
* @param {boolean} snap if true, snap the part to where the guideline would be
*/
public showHorizontalMatches(part: go.Part, guideline: boolean, snap: boolean): void {
const objBounds = part.locationObject.getDocumentBounds();
const p0 = objBounds.y;
const p1 = objBounds.y + objBounds.height / 2;
const p2 = objBounds.y + objBounds.height;
const marginOfError = this.guidelineSnapDistance;
const distance = this.searchDistance;
// compares with parts within narrow vertical area
const area = objBounds.copy();
area.inflate(distance, marginOfError + 1);
const otherObjs = this.diagram.findObjectsIn(area,
(obj) => obj.part as go.Part,
(p) => this.isGuiding(p as go.Part, part),
true) as go.Set<go.Part>;
let bestDiff: number = marginOfError;
let bestObj: any = null; // TS 2.6 won't let this be go.Part | null
let bestSpot: go.Spot = go.Spot.Default;
let bestOtherSpot: go.Spot = go.Spot.Default;
// horizontal line -- comparing y-values
otherObjs.each((other) => {
if (other === part) return; // ignore itself
const otherBounds = other.locationObject.getDocumentBounds();
const q0 = otherBounds.y;
const q1 = otherBounds.y + otherBounds.height / 2;
const q2 = otherBounds.y + otherBounds.height;
// compare center with center of OTHER part
if (Math.abs(p1 - q1) < bestDiff) {
bestDiff = Math.abs(p1 - q1);
bestObj = other;
bestSpot = go.Spot.Center;
bestOtherSpot = go.Spot.Center;
}
// compare top side with top and bottom sides of OTHER part
if (Math.abs(p0 - q0) < bestDiff) {
bestDiff = Math.abs(p0 - q0);
bestObj = other;
bestSpot = go.Spot.Top;
bestOtherSpot = go.Spot.Top;
} else if (Math.abs(p0 - q2) < bestDiff) {
bestDiff = Math.abs(p0 - q2);
bestObj = other;
bestSpot = go.Spot.Top;
bestOtherSpot = go.Spot.Bottom;
}
// compare bottom side with top and bottom sides of OTHER part
if (Math.abs(p2 - q0) < bestDiff) {
bestDiff = Math.abs(p2 - q0);
bestObj = other;
bestSpot = go.Spot.Bottom;
bestOtherSpot = go.Spot.Top;
} else if (Math.abs(p2 - q2) < bestDiff) {
bestDiff = Math.abs(p2 - q2);
bestObj = other;
bestSpot = go.Spot.Bottom;
bestOtherSpot = go.Spot.Bottom;
}
});
if (bestObj !== null) {
const offsetX = objBounds.x - part.actualBounds.x;
const offsetY = objBounds.y - part.actualBounds.y;
const bestBounds = bestObj.locationObject.getDocumentBounds();
// line extends from x0 to x2
const x0 = Math.min(objBounds.x, bestBounds.x) - 10;
const x2 = Math.max(objBounds.x + objBounds.width, bestBounds.x + bestBounds.width) + 10;
// find bestObj's desired Y
const bestPoint = new go.Point().setRectSpot(bestBounds, bestOtherSpot);
if (bestSpot === go.Spot.Center) {
if (snap) {
// call Part.move in order to automatically move member Parts of Groups
part.move(new go.Point(objBounds.x - offsetX, bestPoint.y - objBounds.height / 2 - offsetY));
this.invalidateLinks(part);
}
if (guideline) {
this.guidelineHcenter.position = new go.Point(x0, bestPoint.y);
this.guidelineHcenter.elt(0).width = x2 - x0;
this.diagram.add(this.guidelineHcenter);
}
} else if (bestSpot === go.Spot.Top) {
if (snap) {
part.move(new go.Point(objBounds.x - offsetX, bestPoint.y - offsetY));
this.invalidateLinks(part);
}
if (guideline) {
this.guidelineHtop.position = new go.Point(x0, bestPoint.y);
this.guidelineHtop.elt(0).width = x2 - x0;
this.diagram.add(this.guidelineHtop);
}
} else if (bestSpot === go.Spot.Bottom) {
if (snap) {
part.move(new go.Point(objBounds.x - offsetX, bestPoint.y - objBounds.height - offsetY));
this.invalidateLinks(part);
}
if (guideline) {
this.guidelineHbottom.position = new go.Point(x0, bestPoint.y);
this.guidelineHbottom.elt(0).width = x2 - x0;
this.diagram.add(this.guidelineHbottom);
}
}
}
}
/**
* This finds parts that are aligned near the selected part along vertical lines. It compares the selected
* part to all parts within a rectangle approximately twice the {@link #searchDistance} tall.
* The guidelines appear when a part is aligned within a margin-of-error equal to {@link #guidelineSnapDistance}.
* @param {Part} part
* @param {boolean} guideline if true, show guideline
* @param {boolean} snap if true, don't show guidelines but just snap the part to where the guideline would be
*/
public showVerticalMatches(part: go.Part, guideline: boolean, snap: boolean): void {
const objBounds = part.locationObject.getDocumentBounds();
const p0 = objBounds.x;
const p1 = objBounds.x + objBounds.width / 2;
const p2 = objBounds.x + objBounds.width;
const marginOfError = this.guidelineSnapDistance;
const distance = this.searchDistance;
// compares with parts within narrow vertical area
const area = objBounds.copy();
area.inflate(marginOfError + 1, distance);
const otherObjs = this.diagram.findObjectsIn(area,
(obj) => obj.part as go.Part,
(p) => this.isGuiding(p as go.Part, part),
true) as go.Set<go.Part>;
let bestDiff: number = marginOfError;
let bestObj: any = null; // TS 2.6 won't let this be go.Part | null
let bestSpot: go.Spot = go.Spot.Default;
let bestOtherSpot: go.Spot = go.Spot.Default;
// vertical line -- comparing x-values
otherObjs.each((other) => {
if (other === part) return; // ignore itself
const otherBounds = other.locationObject.getDocumentBounds();
const q0 = otherBounds.x;
const q1 = otherBounds.x + otherBounds.width / 2;
const q2 = otherBounds.x + otherBounds.width;
// compare center with center of OTHER part
if (Math.abs(p1 - q1) < bestDiff) {
bestDiff = Math.abs(p1 - q1);
bestObj = other;
bestSpot = go.Spot.Center;
bestOtherSpot = go.Spot.Center;
}
// compare left side with left and right sides of OTHER part
if (Math.abs(p0 - q0) < bestDiff) {
bestDiff = Math.abs(p0 - q0);
bestObj = other;
bestSpot = go.Spot.Left;
bestOtherSpot = go.Spot.Left;
} else if (Math.abs(p0 - q2) < bestDiff) {
bestDiff = Math.abs(p0 - q2);
bestObj = other;
bestSpot = go.Spot.Left;
bestOtherSpot = go.Spot.Right;
}
// compare right side with left and right sides of OTHER part
if (Math.abs(p2 - q0) < bestDiff) {
bestDiff = Math.abs(p2 - q0);
bestObj = other;
bestSpot = go.Spot.Right;
bestOtherSpot = go.Spot.Left;
} else if (Math.abs(p2 - q2) < bestDiff) {
bestDiff = Math.abs(p2 - q2);
bestObj = other;
bestSpot = go.Spot.Right;
bestOtherSpot = go.Spot.Right;
}
});
if (bestObj !== null) {
const offsetX = objBounds.x - part.actualBounds.x;
const offsetY = objBounds.y - part.actualBounds.y;
const bestBounds = bestObj.locationObject.getDocumentBounds();
// line extends from y0 to y2
const y0 = Math.min(objBounds.y, bestBounds.y) - 10;
const y2 = Math.max(objBounds.y + objBounds.height, bestBounds.y + bestBounds.height) + 10;
// find bestObj's desired X
const bestPoint = new go.Point().setRectSpot(bestBounds, bestOtherSpot);
if (bestSpot === go.Spot.Center) {
if (snap) {
// call Part.move in order to automatically move member Parts of Groups
part.move(new go.Point(bestPoint.x - objBounds.width / 2 - offsetX, objBounds.y - offsetY));
this.invalidateLinks(part);
}
if (guideline) {
this.guidelineVcenter.position = new go.Point(bestPoint.x, y0);
this.guidelineVcenter.elt(0).height = y2 - y0;
this.diagram.add(this.guidelineVcenter);
}
} else if (bestSpot === go.Spot.Left) {
if (snap) {
part.move(new go.Point(bestPoint.x - offsetX, objBounds.y - offsetY));
this.invalidateLinks(part);
}
if (guideline) {
this.guidelineVleft.position = new go.Point(bestPoint.x, y0);
this.guidelineVleft.elt(0).height = y2 - y0;
this.diagram.add(this.guidelineVleft);
}
} else if (bestSpot === go.Spot.Right) {
if (snap) {
part.move(new go.Point(bestPoint.x - objBounds.width - offsetX, objBounds.y - offsetY));
this.invalidateLinks(part);
}
if (guideline) {
this.guidelineVright.position = new go.Point(bestPoint.x, y0);
this.guidelineVright.elt(0).height = y2 - y0;
this.diagram.add(this.guidelineVright);
}
}
}
}
}
/*
* Copyright (C) 1998-2021 by Northwoods Software Corporation. All Rights Reserved.
*/
/*
* This is an extension and not part of the main GoJS library.
* Note that the API for this class may change with any version, even point releases.
* If you intend to use an extension in production, you should copy the code to your own source directory.
* Extensions can be found in the GoJS kit under the extensions or extensionsTS folders.
* See the Extensions intro page (https://gojs.net/latest/intro/extensions.html) for more information.
*/
import go from 'gojs';
/**
* The PortShiftingTool class lets a user move a port on a {@link Node}.
*
* This tool only works when the Node has a port (any GraphObject) marked with
* a non-null and non-empty portId that is positioned in a Spot Panel,
* and the user holds down the Shift key.
* It works by modifying that port's {@link GraphObject#alignment} property.
*
* If you want to experiment with this extension, try the <a href="../../extensionsTS/PortShifting.html">Port Shifting</a> sample.
* @category Tool Extension
*/
export class PortShiftingTool extends go.Tool {
/**
* The port being shifted.
*/
public port: go.GraphObject | null = null;
private _originalAlignment: go.Spot = go.Spot.Default;
/**
* Constructs a PortShiftingTool and sets the name for the tool.
*/
constructor() {
super();
this.name = 'PortShifting';
}
/**
* This tool can only start if the mouse has moved enough so that it is not a click,
* and if the mouse down point is on a GraphObject "port" in a Spot Panel,
* as determined by {@link #findPort}.
*/
public canStart(): boolean {
const diagram = this.diagram;
if (!super.canStart()) return false;
// require left button & that it has moved far enough away from the mouse down point, so it isn't a click
const e = diagram.lastInput;
if (!e.left || !e.shift) return false;
if (!this.isBeyondDragSize()) return false;
return this.findPort() !== null;
}
/**
* From the {@link GraphObject} at the mouse point, search up the visual tree until we get to
* an object that has the {@link GraphObject#portId} property set to a non-empty string, that is in a Spot Panel,
* and that is not the main element of the panel (typically the first element).
* @return {GraphObject} This returns null if no such port is at the mouse down point.
*/
public findPort(): go.GraphObject | null {
const diagram = this.diagram;
const e = diagram.firstInput;
let elt = diagram.findObjectAt(e.documentPoint, null, null);
if (elt === null || !(elt.part instanceof go.Node)) return null;
while (elt !== null && elt.panel !== null) {
if (elt.panel.type === go.Panel.Spot && elt.panel.findMainElement() !== elt &&
elt.portId !== null && elt.portId !== '') return elt;
elt = elt.panel;
}
return null;
}
/**
* Start a transaction, call {@link #findPort} and remember it as the "port" property,
* and remember the original value for the port's {@link GraphObject#alignment} property.
*/
public doActivate(): void {
this.startTransaction('Shifted Label');
this.port = this.findPort();
if (this.port !== null) {
this._originalAlignment = this.port.alignment.copy();
}
super.doActivate();
}
/**
* Stop any ongoing transaction.
*/
public doDeactivate(): void {
super.doDeactivate();
this.stopTransaction();
}
/**
* Clear any reference to a port element.
*/
public doStop(): void {
this.port = null;
super.doStop();
}
/**
* Restore the port's original value for GraphObject.alignment.
*/
public doCancel(): void {
if (this.port !== null) {
this.port.alignment = this._originalAlignment;
}
super.doCancel();
}
/**
* During the drag, call {@link #updateAlignment} in order to set the {@link GraphObject#alignment} of the port.
*/
public doMouseMove(): void {
if (!this.isActive) return;
this.updateAlignment();
}
/**
* At the end of the drag, update the alignment of the port and finish the tool,
* completing a transaction.
*/
public doMouseUp(): void {
if (!this.isActive) return;
this.updateAlignment();
this.transactionResult = 'Shifted Label';
this.stopTool();
}
/**
* Save the port's {@link GraphObject#alignment} as a fractional Spot in the Spot Panel
* that the port is in. Thus if the main element changes size, the relative positions
* of the ports will be maintained. But that does assume that the port must remain
* inside the main element -- it cannot wander away from the node.
* This does not modify the port's {@link GraphObject#alignmentFocus} property.
*/
public updateAlignment(): void {
if (this.port === null || this.port.panel === null) return;
const last = this.diagram.lastInput.documentPoint;
const main = this.port.panel.findMainElement();
if (main === null) return;
const tl = main.getDocumentPoint(go.Spot.TopLeft);
const br = main.getDocumentPoint(go.Spot.BottomRight);
const x = Math.max(0, Math.min((last.x - tl.x) / (br.x - tl.x), 1));
const y = Math.max(0, Math.min((last.y - tl.y) / (br.y - tl.y), 1));
this.port.alignment = new go.Spot(x, y);
}
}
/*
* Copyright (C) 1998-2021 by Northwoods Software Corporation. All Rights Reserved.
*/
/*
* This is an extension and not part of the main GoJS library.
* Note that the API for this class may change with any version, even point releases.
* If you intend to use an extension in production, you should copy the code to your own source directory.
* Extensions can be found in the GoJS kit under the extensions or extensionsTS folders.
* See the Extensions intro page (https://gojs.net/latest/intro/extensions.html) for more information.
*/
import go from 'gojs';
/**
* The SnapLinkReshapingTool class lets the user snap link reshaping handles to the nearest grid point.
* If {@link #avoidsNodes} is true and the link is orthogonal,
* it also avoids reshaping the link so that any adjacent segments cross over any avoidable nodes.
*
* If you want to experiment with this extension, try the <a href="../../extensionsTS/SnapLinkReshaping.html">Snap Link Reshaping</a> sample.
* @category Tool Extension
*/
export class SnapLinkReshapingTool extends go.LinkReshapingTool {
private _gridCellSize = new go.Size(NaN, NaN);
private _gridOrigin = new go.Point(NaN, NaN);
private _isGridSnapEnabled = true;
private _avoidsNodes = true;
// internal state
private _safePoint = new go.Point(NaN, NaN);
private _prevSegHoriz = false;
private _nextSegHoriz = false;
/**
* Gets or sets the {@link Size} of each grid cell to which link points will be snapped.
*
* The default value is NaNxNaN, which means use the {@link Diagram#grid}'s {@link Panel#gridCellSize}.
*/
get gridCellSize(): go.Size { return this._gridCellSize; }
set gridCellSize(val: go.Size) {
if (!(val instanceof go.Size)) throw new Error('new value for SnapLinkReshapingTool.gridCellSize must be a Size, not: ' + val);
this._gridCellSize = val.copy();
}
/**
* Gets or sets the {@link Point} origin for the grid to which link points will be snapped.
*
* The default value is NaN,NaN, which means use the {@link Diagram#grid}'s {@link Panel#gridOrigin}.
*/
get gridOrigin(): go.Point { return this._gridOrigin; }
set gridOrigin(val: go.Point) {
if (!(val instanceof go.Point)) throw new Error('new value for SnapLinkReshapingTool.gridOrigin must be a Point, not: ' + val);
this._gridOrigin = val.copy();
}
/**
* Gets or sets whether a reshape handle's position should be snapped to a grid point.
* This affects the behavior of {@link #computeReshape}.
*
* The default value is true.
*/
get isGridSnapEnabled(): boolean { return this._isGridSnapEnabled; }
set isGridSnapEnabled(val: boolean) {
if (typeof val !== 'boolean') throw new Error('new value for SnapLinkReshapingTool.isGridSnapEnabled must be a boolean, not: ' + val);
this._isGridSnapEnabled = val;
}
/**
* Gets or sets whether a reshape handle's position should only be dragged where the
* adjacent segments do not cross over any nodes.
* This affects the behavior of {@link #computeReshape}.
*
* The default value is true.
*/
get avoidsNodes(): boolean { return this._avoidsNodes; }
set avoidsNodes(val: boolean) {
if (typeof val !== 'boolean') throw new Error('new value for SnapLinkReshapingTool.avoidsNodes must be a boolean, not: ' + val);
this._avoidsNodes = val;
}
/**
* This override records information about the original point of the handle being dragged,
* if the {@link #adornedLink} is Orthogonal and if {@link #avoidsNodes} is true.
*/
public doActivate(): void {
super.doActivate();
if (this.isActive && this.avoidsNodes && this.adornedLink !== null && this.adornedLink.isOrthogonal && this.handle !== null) {
// assume the Link's route starts off correctly avoiding all nodes
this._safePoint = this.diagram.lastInput.documentPoint.copy();
const link = this.adornedLink;
const idx = this.handle.segmentIndex;
this._prevSegHoriz = Math.abs(link.getPoint(idx-1).y - link.getPoint(idx).y) < 0.5;
this._nextSegHoriz = Math.abs(link.getPoint(idx+1).y - link.getPoint(idx).y) < 0.5;
}
};
/**
* Pretend while dragging a reshape handle the mouse point is at the nearest grid point, if {@link #isGridSnapEnabled} is true.
* This uses {@link #gridCellSize} and {@link #gridOrigin}, unless those are not real values,
* in which case this uses the {@link Diagram#grid}'s {@link Panel#gridCellSize} and {@link Panel#gridOrigin}.
*
* If {@link #avoidsNodes} is true and the adorned Link is {@link Link#isOrthogonal},
* this method also avoids returning a Point that causes the adjacent segments, both before and after
* the current handle's index, to cross over any Nodes that are {@link Node#avoidable}.
*/
public computeReshape(p: go.Point): go.Point {
let pt = p;
const diagram = this.diagram;
if (this.isGridSnapEnabled) {
// first, find the grid to which we should snap
let cell = this.gridCellSize;
let orig = this.gridOrigin;
if (!cell.isReal() || cell.width === 0 || cell.height === 0) cell = diagram.grid.gridCellSize;
if (!orig.isReal()) orig = diagram.grid.gridOrigin;
// second, compute the closest grid point
pt = p.copy().snapToGrid(orig.x, orig.y, cell.width, cell.height);
}
if (this.avoidsNodes && this.adornedLink !== null && this.adornedLink.isOrthogonal) {
if (this._checkSegmentsOverlap(pt)) {
this._safePoint = pt.copy();
} else {
pt = this._safePoint.copy();
}
}
// then do whatever LinkReshapingTool would normally do as if the mouse were at that point
return super.computeReshape(pt);
}
/**
* @hidden @internal
* Internal method for seeing whether a moved handle will cause any
* adjacent orthogonal segments to cross over any avoidable nodes.
* Returns true if everything would be OK.
*/
private _checkSegmentsOverlap(pt: go.Point): boolean {
if (this.handle === null) return true;
if (this.adornedLink === null) return true;
const index = this.handle.segmentIndex;
if (index >= 1) {
const p1 = this.adornedLink.getPoint(index-1);
const r = new go.Rect(pt.x, pt.y, 0, 0);
const q1 = p1.copy();
if (this._prevSegHoriz) {
q1.y = pt.y;
} else {
q1.x = pt.x;
}
r.unionPoint(q1);
const overlaps = this.diagram.findPartsIn(r, true, false);
if (overlaps.any(function(p) { return p instanceof go.Node && p.avoidable; })) return false;
if (index >= 2) {
const p0 = this.adornedLink.getPoint(index-2);
const r = new go.Rect(q1.x, q1.y, 0, 0);
if (this._prevSegHoriz) {
r.unionPoint(new go.Point(q1.x, p0.y));
} else {
r.unionPoint(new go.Point(p0.x, q1.y));
}
const overlaps = this.diagram.findPartsIn(r, true, false);
if (overlaps.any(function(p) { return p instanceof go.Node && p.avoidable; })) return false;
}
}
if (index < this.adornedLink.pointsCount-1) {
const p2 = this.adornedLink.getPoint(index+1);
const r = new go.Rect(pt.x, pt.y, 0, 0);
const q2 = p2.copy();
if (this._nextSegHoriz) {
q2.y = pt.y;
} else {
q2.x = pt.x;
}
r.unionPoint(q2);
const overlaps = this.diagram.findPartsIn(r, true, false);
if (overlaps.any(function(p) { return p instanceof go.Node && p.avoidable; })) return false;
if (index < this.adornedLink.pointsCount-2) {
const p3 = this.adornedLink.getPoint(index+2);
const r = new go.Rect(q2.x, q2.y, 0, 0);
if (this._nextSegHoriz) {
r.unionPoint(new go.Point(q2.x, p3.y));
} else {
r.unionPoint(new go.Point(p3.x, q2.y));
}
const overlaps = this.diagram.findPartsIn(r, true, false);
if (overlaps.any(function(p) { return p instanceof go.Node && p.avoidable; })) return false;
}
}
return true;
};
}
import { createApp } from 'vue' import { createApp } from 'vue'
// import './style.css' // import App from './App-old.vue'
// import './index.css'
import App from './App.vue' import App from './App.vue'
import gojs from 'gojs' import gojs from 'gojs'
import ElementUI from 'element-plus'; import ElementUI from 'element-plus';
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import "element-plus/theme-chalk/index.css" import "element-plus/theme-chalk/index.css"
import 'virtual:uno.css' import 'virtual:uno.css'
import './assets/iconfont/iconfont.css'
let app = createApp(App, {go: gojs}) let app = createApp(App, {go: gojs})
app.use(ElementUI) app.use(ElementUI)
// 注册element-plus的图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.mount('#app') app.mount('#app')
...@@ -18,7 +18,8 @@ ...@@ -18,7 +18,8 @@
"strict": true, "strict": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true,
"allowSyntheticDefaultImports": true
}, },
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }] "references": [{ "path": "./tsconfig.node.json" }]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment