Commit 3c4cf820 by 吴斌

update:优化连线路径

parent e5012b0e
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<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 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">
<el-button text icon="Setting"></el-button> <el-button text icon="Setting"></el-button>
<div class="flex-1"></div> <div class="flex-1"></div>
<el-radio-group v-model="diagramConfig.mode" @change="diagramModeChanged"> <el-radio-group v-model="diagramConfig.mode" @change="freshDiagramConfig">
<el-radio-button label="readonly"> <el-radio-button label="readonly">
<template #default> <template #default>
<el-icon> <el-icon>
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
<div id="inspector-property"></div> <div id="inspector-property"></div>
<el-divider direction="horizontal"></el-divider> <el-divider direction="horizontal"></el-divider>
</div> </div>
<div v-show="ifSelectNode" class="flex flex-col w-full"> <div v-show="ifSelectNode" class="flex flex-col w-full" v-if="selectedNode">
<div class="text-[16px] font-bold text-black">端口</div> <div class="text-[16px] font-bold text-black">端口</div>
<div v-for="port in nodePortArray"> <div v-for="port in nodePortArray">
<div class="flex flex-row space-x-0" @mouseenter="highlightPort(port['portId'], true)" @mouseleave="highlightPort(port['portId'], false)"> <div class="flex flex-row space-x-0" @mouseenter="highlightPort(port['portId'], true)" @mouseleave="highlightPort(port['portId'], false)">
...@@ -183,10 +183,12 @@ import {uuid} from "vue3-uuid"; ...@@ -183,10 +183,12 @@ import {uuid} from "vue3-uuid";
import {SnapLinkReshapingTool} from "./kit/extensions/SnapLinkReshapingTool.ts"; import {SnapLinkReshapingTool} from "./kit/extensions/SnapLinkReshapingTool.ts";
import {Edit, View} from "@element-plus/icons-vue"; import {Edit, View} from "@element-plus/icons-vue";
import {ElMessage} from "element-plus"; import {ElMessage} from "element-plus";
import {PolylineLinkingTool} from "./kit/extensions/PolylineLinkingTool.ts";
// 变量定义
// region
// 图表配置 // 图表配置
const diagramConfig = ref({ const diagramConfig = ref<Record<string, any>>({
mode: "readonly", mode: "edit",
}) })
// 是否模拟实时数据 // 是否模拟实时数据
...@@ -244,6 +246,7 @@ const analyzePathsName = ref<Record<string, any>[][]>([]) ...@@ -244,6 +246,7 @@ const analyzePathsName = ref<Record<string, any>[][]>([])
// let inputSearchNode = ref('') // let inputSearchNode = ref('')
// 用于计算源区域的滚动条区域高度 // 用于计算源区域的滚动条区域高度
let nodeAreaHeight = ref() let nodeAreaHeight = ref()
// endregion
onMounted(()=>{ onMounted(()=>{
getNodeSourceScrollAreaHeight() getNodeSourceScrollAreaHeight()
...@@ -253,13 +256,13 @@ onMounted(()=>{ ...@@ -253,13 +256,13 @@ onMounted(()=>{
changeDataRealTime() changeDataRealTime()
}) })
// 为了在页面大小变化时,让节点区域的滚动条区域高度自适应
function getNodeSourceScrollAreaHeight(){ function changeDiagramConfig(key:string, value:string){
// 滚动区域高度为浏览器窗口高度 - 144px(固定的上方ToolBar + 本页面的输入框和最下方的按钮) diagramConfig.value[key] = value
nodeAreaHeight.value = window.innerHeight - 144 + 'px' freshDiagramConfig()
} }
function diagramModeChanged(){ function freshDiagramConfig(){
let mode = diagramConfig.value.mode let mode = diagramConfig.value.mode
if (mode === 'readonly'){ if (mode === 'readonly'){
myDiagram.isReadOnly = true myDiagram.isReadOnly = true
...@@ -268,11 +271,13 @@ function diagramModeChanged(){ ...@@ -268,11 +271,13 @@ function diagramModeChanged(){
} }
} }
function exportData(){
console.log(myDiagram.model.toJson())
}
// tool-bar 相关函数 // 素材区相关函数
// region
// endregion
// Toolbar 相关函数
// region
function importData(){ function importData(){
let json = { "class": "GraphLinksModel", let json = { "class": "GraphLinksModel",
"copiesArrays": true, "copiesArrays": true,
...@@ -319,13 +324,21 @@ function importData(){ ...@@ -319,13 +324,21 @@ function importData(){
{"from":-11,"to":-10,"fromPort":"ec58dcb4-39fb-400e-bb0e-018ecee51d56","toPort":"ba564564-2ac8-48da-a430-3af06ea4997d","category":"Arrow", 'highlightColor':'red', 'color':'black',"points":[220,-733.25,220,-743.25,92.125,-743.25,92.125,-401.25,58.25,-401.25,48.25,-401.25]} {"from":-11,"to":-10,"fromPort":"ec58dcb4-39fb-400e-bb0e-018ecee51d56","toPort":"ba564564-2ac8-48da-a430-3af06ea4997d","category":"Arrow", 'highlightColor':'red', 'color':'black',"points":[220,-733.25,220,-743.25,92.125,-743.25,92.125,-401.25,58.25,-401.25,48.25,-401.25]}
]} ]}
myDiagram.model = go.Model.fromJson(json) myDiagram.model = go.Model.fromJson(json)
changeDiagramConfig('mode', 'readonly')
}
function exportData(){
console.log(myDiagram.model.toJson())
} }
function saveData(){ function saveData(){
console.log(selectedNode.value) console.log(selectedNode.value)
} }
// endregion
// diagram 部分 // diagram 部分
// region
function initDiagram(){ function initDiagram(){
myDiagram = GO(go.Diagram, "myDiagram", { myDiagram = GO(go.Diagram, "myDiagram", {
initialContentAlignment: go.Spot.Center, initialContentAlignment: go.Spot.Center,
...@@ -360,6 +373,7 @@ function initDiagram(){ ...@@ -360,6 +373,7 @@ 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), "linkReshapingTool":GO(SnapLinkReshapingTool),
"LinkReshaped": e => { "LinkReshaped": e => {
e.subject.adjusting = go.Link.End; e.subject.adjusting = go.Link.End;
...@@ -376,10 +390,17 @@ function initDiagram(){ ...@@ -376,10 +390,17 @@ function initDiagram(){
}) })
// install the PortShiftingTool as a "mouse move" tool // install the PortShiftingTool as a "mouse move" tool
myDiagram.toolManager.mouseMoveTools.insertAt(0, new PortShiftingTool()); myDiagram.toolManager.mouseMoveTools.insertAt(0, new PortShiftingTool());
// 暂时不用 有可能需要连接线可以自己规划路径而不是 连接端口自动产生
// let tool = new PolylineLinkingTool();
// tool.temporaryLink.routing = go.Link.Orthogonal; // optional, but need to keep link template in sync, below
// myDiagram.toolManager.linkingTool = tool;
myDiagram.model.linkFromPortIdProperty = "fromPort"; // necessary to remember portIds myDiagram.model.linkFromPortIdProperty = "fromPort"; // necessary to remember portIds
myDiagram.model.linkToPortIdProperty = "toPort"; myDiagram.model.linkToPortIdProperty = "toPort";
myDiagram.model.copiesArrays = true; myDiagram.model.copiesArrays = true;
myDiagram.model.copiesArrayObjects = true; myDiagram.model.copiesArrayObjects = true;
//多种类型的连接线 //多种类型的连接线
myDiagram.linkTemplateMap.add('Arrow', ArrowLink) myDiagram.linkTemplateMap.add('Arrow', ArrowLink)
myDiagram.linkTemplateMap.add('Flow', FlowLink) myDiagram.linkTemplateMap.add('Flow', FlowLink)
...@@ -388,6 +409,7 @@ function initDiagram(){ ...@@ -388,6 +409,7 @@ function initDiagram(){
myDiagram.nodeTemplateMap.add('svg', makeDiagramNodeTemplate('svg')) myDiagram.nodeTemplateMap.add('svg', makeDiagramNodeTemplate('svg'))
myDiagram.nodeTemplateMap.add('geometry', makeDiagramNodeTemplate('geometry')) myDiagram.nodeTemplateMap.add('geometry', makeDiagramNodeTemplate('geometry'))
myDiagram.nodeTemplateMap.add('shape', makeDiagramNodeTemplate('shape')) myDiagram.nodeTemplateMap.add('shape', makeDiagramNodeTemplate('shape'))
} }
function listenDiagram(){ function listenDiagram(){
...@@ -398,6 +420,8 @@ function listenDiagram(){ ...@@ -398,6 +420,8 @@ function listenDiagram(){
}) })
//监听选中时间,用于右侧info-panel,展示选中节点的信息 //监听选中时间,用于右侧info-panel,展示选中节点的信息
myDiagram.addDiagramListener('ChangedSelection', ()=>{ myDiagram.addDiagramListener('ChangedSelection', ()=>{
selectedNode.value = null
selectedLink.value = null
let currentSelected = myDiagram.selection.first() let currentSelected = myDiagram.selection.first()
if (currentSelected === null) { if (currentSelected === null) {
ifSelectNode.value = false ifSelectNode.value = false
...@@ -462,8 +486,10 @@ function cancelHighlightPath(pathIndex:number){ ...@@ -462,8 +486,10 @@ function cancelHighlightPath(pathIndex:number){
cancelHighlightLink(path[i], path[i+1], myDiagram) cancelHighlightLink(path[i], path[i+1], myDiagram)
} }
} }
// endregion
// 右侧info-panel相关函数 // 右侧info-panel相关函数
// region
function initInspector(){ function initInspector(){
// inspector = // inspector =
new Inspector('inspector-property', myDiagram, new Inspector('inspector-property', myDiagram,
...@@ -631,12 +657,21 @@ function analyzeData(){ ...@@ -631,12 +657,21 @@ function analyzeData(){
analyzePathsName.value.push(pathName) analyzePathsName.value.push(pathName)
} }
} }
//endregion
// 其他
// region
// 浏览器大小变化时 // 浏览器大小变化时
window.onresize = function (){ window.onresize = function (){
getNodeSourceScrollAreaHeight() getNodeSourceScrollAreaHeight()
} }
// 为了在页面大小变化时,让节点区域的滚动条区域高度自适应
function getNodeSourceScrollAreaHeight(){
// 滚动区域高度为浏览器窗口高度 - 144px(固定的上方ToolBar + 本页面的输入框和最下方的按钮)
nodeAreaHeight.value = window.innerHeight - 144 + 'px'
}
// endregion
</script> </script>
<style> <style>
......
...@@ -57,15 +57,21 @@ MultiArrowLink.prototype.makeGeometry = function() { ...@@ -57,15 +57,21 @@ MultiArrowLink.prototype.makeGeometry = function() {
export var FlowLink = export var FlowLink =
GO(go.Link,{ GO(go.Link,{
routing: go.Link.AvoidsNodes, routing: go.Link.Orthogonal,
curve: go.Link.JumpGap, curve: go.Link.JumpGap,
corner: 5, corner: 5,
toShortLength: 4, toShortLength: 4,
relinkableFrom: true, relinkableFrom: true,
relinkableTo: true, relinkableTo: true,
reshapable: true, reshapable: true,
adjusting: go.Link.Stretch,
resegmentable: true,
}, },
new go.Binding("points", "points").makeTwoWay(), new go.Binding("points", "points").makeTwoWay(),
new go.Binding("routing", "routing", go.Binding.parseEnum(go.Link, go.Link.AvoidsNodes))
.makeTwoWay(go.Binding.toString),
new go.Binding("adjusting", "adjusting", go.Binding.parseEnum(go.Link, go.Link.None))
.makeTwoWay(go.Binding.toString),
GO(go.Shape, { isPanelMain:true, strokeWidth: 5, stroke: 'gray'}), GO(go.Shape, { isPanelMain:true, strokeWidth: 5, stroke: 'gray'}),
GO(go.Shape, { isPanelMain:true, strokeWidth: 5, stroke: 'black',name:"FLOW", strokeDashArray: [10, 10]}), GO(go.Shape, { isPanelMain:true, strokeWidth: 5, stroke: 'black',name:"FLOW", strokeDashArray: [10, 10]}),
); );
...@@ -90,10 +96,15 @@ export function animateFlow(myDiagram:go.Diagram){ ...@@ -90,10 +96,15 @@ export function animateFlow(myDiagram:go.Diagram){
export var ArrowLink = export var ArrowLink =
GO(go.Link, GO(go.Link,
{selectable: true, selectionAdornmentTemplate: linkSelectionAdornmentTemplate}, {selectable: true},
{relinkableFrom: true, relinkableTo: true, reshapable: true}, {relinkableFrom: true, relinkableTo: true, reshapable: true},
{routing: go.Link.AvoidsNodes, curve: go.Link.JumpGap, toShortLength: 4}, {routing: go.Link.Orthogonal , curve: go.Link.JumpGap, toShortLength: 4, adjusting: go.Link.Stretch},
{resegmentable: true},
new go.Binding("points", "points").makeTwoWay(), new go.Binding("points", "points").makeTwoWay(),
new go.Binding("routing", "routing", go.Binding.parseEnum(go.Link, go.Link.AvoidsNodes))
.makeTwoWay(go.Binding.toString),
new go.Binding("adjusting", "adjusting", go.Binding.parseEnum(go.Link, go.Link.None))
.makeTwoWay(go.Binding.toString),
GO(go.Shape, // the link path shape GO(go.Shape, // the link path shape
{isPanelMain: true, strokeWidth: 2}, {isPanelMain: true, strokeWidth: 2},
new go.Binding("stroke", "", data => linkColorBind(data)), new go.Binding("stroke", "", data => linkColorBind(data)),
......
/*
* 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 PolylineLinkingTool class the user to draw a new {@link Link} by clicking where the route should go,
* until clicking on a valid target port.
*
* This tool supports routing both orthogonal and straight links.
* You can customize the {@link LinkingBaseTool#temporaryLink} as needed to affect the
* appearance and behavior of the temporary link that is shown during the linking operation.
* You can customize the {@link LinkingTool#archetypeLinkData} to specify property values
* that can be data-bound by your link template for the Links that are actually created.
*
* If you want to experiment with this extension, try the <a href="../../extensionsTS/PolylineLinking.html">Polyline Linking</a> sample.
* @category Tool Extension
*/
export class PolylineLinkingTool extends go.LinkingTool {
private _firstMouseDown: boolean = false;
private _horizontal: boolean = false;
/**
* Constructs an PolylineLinkingTool, sets {@link #portGravity} to 0, and sets the name for the tool.
*/
constructor() {
super();
this.portGravity = 0; // must click on a target port in order to complete the link
this.name = 'PolylineLinking';
}
/**
* @hidden @internal
* This internal method adds a point to the route.
* During the operation of this tool, the very last point changes to follow the mouse point.
* This method is called by {@link #doMouseDown} in order to add a new "last" point.
* @param {Point} p
*/
private addPoint(p: go.Point): void {
if (this._firstMouseDown) return;
const pts = this.temporaryLink.points.copy();
this._horizontal = !this._horizontal;
pts.add(p.copy());
this.temporaryLink.points = pts;
}
/**
* @hidden @internal
* This internal method moves the last point of the temporary Link's route.
* This is called by {@link #doMouseMove} and other methods that want to adjust the end of the route.
* @param {Point} p
*/
private moveLastPoint(p: go.Point): void {
if (this._firstMouseDown) return;
const pts = this.temporaryLink.points.copy();
if (this.temporaryLink.isOrthogonal) {
const q = pts.elt(pts.length - 3).copy();
if (this._horizontal) {
q.y = p.y;
} else {
q.x = p.x;
}
pts.setElt(pts.length - 2, q);
}
pts.setElt(pts.length - 1, p.copy());
this.temporaryLink.points = pts;
}
/**
* @hidden @internal
* This internal method removes the last point of the temporary Link's route.
* This is called by the "Z" command in {@link #doKeyDown}
* and by {@link #doMouseUp} when a valid target port is found and we want to
* discard the current mouse point from the route.
*/
private removeLastPoint(): void {
if (this._firstMouseDown) return;
const pts = this.temporaryLink.points.copy();
if (pts.length === 0) return;
pts.removeAt(pts.length - 1);
this.temporaryLink.points = pts;
this._horizontal = !this._horizontal;
}
/**
* Use a "crosshair" cursor.
*/
public doActivate(): void {
super.doActivate();
this.diagram.currentCursor = 'crosshair';
// until a mouse down occurs, allow the temporary link to be routed to the temporary node/port
this._firstMouseDown = true;
}
/**
* Add a point to the route that the temporary Link is accumulating.
*/
public doMouseDown(): void {
if (!this.isActive) {
this.doActivate();
}
if (this.diagram.lastInput.left) {
if (this._firstMouseDown) {
this._firstMouseDown = false;
// disconnect the temporary node/port from the temporary link
// so that it doesn't lose the points that are accumulating
if (this.isForwards) {
this.temporaryLink.toNode = null;
} else {
this.temporaryLink.fromNode = null;
}
const pts = this.temporaryLink.points;
const ult = pts.elt(pts.length - 1);
const penult = pts.elt(pts.length - 2);
this._horizontal = (ult.x === penult.x);
}
// a new temporary end point, the previous one is now "accepted"
this.addPoint(this.diagram.lastInput.documentPoint);
} else { // e.g. right mouse down
this.doCancel();
}
}
/**
* Have the temporary link reach to the last mouse point.
*/
public doMouseMove(): void {
if (this.isActive) {
this.moveLastPoint(this.diagram.lastInput.documentPoint);
super.doMouseMove();
}
}
/**
* If this event happens on a valid target port (as determined by {@link LinkingBaseTool#findTargetPort}),
* we complete the link drawing operation. {@link #insertLink} is overridden to transfer the accumulated
* route drawn by user clicks to the new {@link Link} that was created.
*
* If this event happens elsewhere in the diagram, this tool is not stopped: the drawing of the route continues.
*/
public doMouseUp(): void {
if (!this.isActive) return;
const target = this.findTargetPort(this.isForwards);
if (target !== null) {
if (this._firstMouseDown) {
super.doMouseUp();
} else {
let pts;
this.removeLastPoint(); // remove temporary point
const spot = this.isForwards ? target.toSpot : target.fromSpot;
if (spot.equals(go.Spot.None)) {
const pt = this.temporaryLink.getLinkPointFromPoint(target.part as go.Node, target,
target.getDocumentPoint(go.Spot.Center),
this.temporaryLink.points.elt(this.temporaryLink.points.length - 2),
!this.isForwards);
this.moveLastPoint(pt);
pts = this.temporaryLink.points.copy();
if (this.temporaryLink.isOrthogonal) {
pts.insertAt(pts.length - 2, pts.elt(pts.length - 2));
}
} else {
// copy the route of saved points, because we're about to recompute it
pts = this.temporaryLink.points.copy();
// terminate the link in the expected manner by letting the
// temporary link connect with the temporary node/port and letting the
// normal route computation take place
if (this.isForwards) {
this.copyPortProperties(target.part as go.Node, target, this.temporaryToNode, this.temporaryToPort, true);
this.temporaryLink.toNode = target.part as go.Node;
} else {
this.copyPortProperties(target.part as go.Node, target, this.temporaryFromNode, this.temporaryFromPort, false);
this.temporaryLink.fromNode = target.part as go.Node;
}
this.temporaryLink.updateRoute();
// now copy the final one or two points of the temporary link's route
// into the route built up in the PTS List.
const natpts = this.temporaryLink.points;
const numnatpts = natpts.length;
if (numnatpts >= 2) {
if (numnatpts >= 3) {
const penult = natpts.elt(numnatpts - 2);
pts.insertAt(pts.length - 1, penult);
if (this.temporaryLink.isOrthogonal) {
pts.insertAt(pts.length - 1, penult);
}
}
const ult = natpts.elt(numnatpts - 1);
pts.setElt(pts.length - 1, ult);
}
}
// save desired route in temporary link;
// insertLink will copy the route into the new real Link
this.temporaryLink.points = pts;
super.doMouseUp();
}
}
}
/**
* This method overrides the standard link creation method by additionally
* replacing the default link route with the custom one laid out by the user.
*/
public insertLink(fromnode: go.Node, fromport: go.GraphObject, tonode: go.Node, toport: go.GraphObject): go.Link | null {
const link = super.insertLink(fromnode, fromport, tonode, toport);
if (link !== null && !this._firstMouseDown) {
// ignore natural route by replacing with route accumulated by this tool
link.points = this.temporaryLink.points;
}
return link;
}
/**
* This supports the "Z" command during this tool's operation to remove the last added point of the route.
* Type ESCAPE to completely cancel the operation of the tool.
*/
public doKeyDown(): void {
if (!this.isActive) return;
const e = this.diagram.lastInput;
if (e.key === 'Z' && this.temporaryLink.points.length > (this.temporaryLink.isOrthogonal ? 4 : 3)) { // undo
// remove a point, and then treat the last one as a temporary one
this.removeLastPoint();
this.moveLastPoint(e.documentPoint);
} else {
super.doKeyDown();
}
}
}
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