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",
......
<?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
...@@ -88,3 +101,20 @@ export function animateFlow(myDiagram:go.Diagram){ ...@@ -88,3 +101,20 @@ export function animateFlow(myDiagram:go.Diagram){
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'},
] ]
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 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