Commit 3c4cf820 by 吴斌

update:优化连线路径

parent e5012b0e
......@@ -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">
<el-button text icon="Setting"></el-button>
<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">
<template #default>
<el-icon>
......@@ -56,7 +56,7 @@
<div id="inspector-property"></div>
<el-divider direction="horizontal"></el-divider>
</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 v-for="port in nodePortArray">
<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";
import {SnapLinkReshapingTool} from "./kit/extensions/SnapLinkReshapingTool.ts";
import {Edit, View} from "@element-plus/icons-vue";
import {ElMessage} from "element-plus";
import {PolylineLinkingTool} from "./kit/extensions/PolylineLinkingTool.ts";
// 变量定义
// region
// 图表配置
const diagramConfig = ref({
mode: "readonly",
const diagramConfig = ref<Record<string, any>>({
mode: "edit",
})
// 是否模拟实时数据
......@@ -244,6 +246,7 @@ const analyzePathsName = ref<Record<string, any>[][]>([])
// let inputSearchNode = ref('')
// 用于计算源区域的滚动条区域高度
let nodeAreaHeight = ref()
// endregion
onMounted(()=>{
getNodeSourceScrollAreaHeight()
......@@ -253,13 +256,13 @@ onMounted(()=>{
changeDataRealTime()
})
// 为了在页面大小变化时,让节点区域的滚动条区域高度自适应
function getNodeSourceScrollAreaHeight(){
// 滚动区域高度为浏览器窗口高度 - 144px(固定的上方ToolBar + 本页面的输入框和最下方的按钮)
nodeAreaHeight.value = window.innerHeight - 144 + 'px'
function changeDiagramConfig(key:string, value:string){
diagramConfig.value[key] = value
freshDiagramConfig()
}
function diagramModeChanged(){
function freshDiagramConfig(){
let mode = diagramConfig.value.mode
if (mode === 'readonly'){
myDiagram.isReadOnly = true
......@@ -268,11 +271,13 @@ function diagramModeChanged(){
}
}
function exportData(){
console.log(myDiagram.model.toJson())
}
// tool-bar 相关函数
// 素材区相关函数
// region
// endregion
// Toolbar 相关函数
// region
function importData(){
let json = { "class": "GraphLinksModel",
"copiesArrays": true,
......@@ -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]}
]}
myDiagram.model = go.Model.fromJson(json)
changeDiagramConfig('mode', 'readonly')
}
function exportData(){
console.log(myDiagram.model.toJson())
}
function saveData(){
console.log(selectedNode.value)
}
// endregion
// diagram 部分
// region
function initDiagram(){
myDiagram = GO(go.Diagram, "myDiagram", {
initialContentAlignment: go.Spot.Center,
......@@ -360,6 +373,7 @@ function initDiagram(){
GO(go.Shape, "Diamond", {segmentIndex: 0, cursor: "pointer", desiredSize: new go.Size(8, 8), fill: "tomato", stroke: "darkred"}),
"relinkingTool.toHandleArchetype":
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;
......@@ -376,10 +390,17 @@ function initDiagram(){
})
// install the PortShiftingTool as a "mouse move" tool
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.linkToPortIdProperty = "toPort";
myDiagram.model.copiesArrays = true;
myDiagram.model.copiesArrayObjects = true;
//多种类型的连接线
myDiagram.linkTemplateMap.add('Arrow', ArrowLink)
myDiagram.linkTemplateMap.add('Flow', FlowLink)
......@@ -388,6 +409,7 @@ function initDiagram(){
myDiagram.nodeTemplateMap.add('svg', makeDiagramNodeTemplate('svg'))
myDiagram.nodeTemplateMap.add('geometry', makeDiagramNodeTemplate('geometry'))
myDiagram.nodeTemplateMap.add('shape', makeDiagramNodeTemplate('shape'))
}
function listenDiagram(){
......@@ -398,6 +420,8 @@ function listenDiagram(){
})
//监听选中时间,用于右侧info-panel,展示选中节点的信息
myDiagram.addDiagramListener('ChangedSelection', ()=>{
selectedNode.value = null
selectedLink.value = null
let currentSelected = myDiagram.selection.first()
if (currentSelected === null) {
ifSelectNode.value = false
......@@ -462,8 +486,10 @@ function cancelHighlightPath(pathIndex:number){
cancelHighlightLink(path[i], path[i+1], myDiagram)
}
}
// endregion
// 右侧info-panel相关函数
// region
function initInspector(){
// inspector =
new Inspector('inspector-property', myDiagram,
......@@ -631,12 +657,21 @@ function analyzeData(){
analyzePathsName.value.push(pathName)
}
}
//endregion
// 其他
// region
// 浏览器大小变化时
window.onresize = function (){
getNodeSourceScrollAreaHeight()
}
// 为了在页面大小变化时,让节点区域的滚动条区域高度自适应
function getNodeSourceScrollAreaHeight(){
// 滚动区域高度为浏览器窗口高度 - 144px(固定的上方ToolBar + 本页面的输入框和最下方的按钮)
nodeAreaHeight.value = window.innerHeight - 144 + 'px'
}
// endregion
</script>
<style>
......
......@@ -57,15 +57,21 @@ MultiArrowLink.prototype.makeGeometry = function() {
export var FlowLink =
GO(go.Link,{
routing: go.Link.AvoidsNodes,
routing: go.Link.Orthogonal,
curve: go.Link.JumpGap,
corner: 5,
toShortLength: 4,
relinkableFrom: true,
relinkableTo: true,
reshapable: true,
adjusting: go.Link.Stretch,
resegmentable: true,
},
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: 'black',name:"FLOW", strokeDashArray: [10, 10]}),
);
......@@ -90,10 +96,15 @@ export function animateFlow(myDiagram:go.Diagram){
export var ArrowLink =
GO(go.Link,
{selectable: true, selectionAdornmentTemplate: linkSelectionAdornmentTemplate},
{selectable: 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("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
{isPanelMain: true, strokeWidth: 2},
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