The confusion of custom nodes

How does the selection box just include the nodes?I think there is no blank area for the select box.Just like this

Selection and resize handles surround the actual bounds of the Part.selectionObject and Part.resizeObject, respectively. So your objects must be larger than you think they are.

Thanks for your quick answer, unfortunately that’s custom nodes have a fixed size.The blue box shown in the figure is larger than the my objects
When I want to zoom in,just stretching the blue box,no change in the node size.

Here is what I want to achieve: Is this achievable? Thanks in advance.

Code:

myDiagram =
  $(go.Diagram, "myDiagramDiv",
      {
        initialContentAlignment: go.Spot.Center,
        "undoManager.isEnabled": true
      });

go.Shape.defineFigureGenerator("LoadSwitch", function(shape, w, h) {
  var geo = new go.Geometry();
  var fig = new go.PathFigure(0, (15/h)*h, true);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Arc, 90, 360, 0, 0, (15/w)*w, (15/h)*h));

  var fig = new go.PathFigure(-(10/w)* w, -(10/h) * h, false);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, (10/w)* w, -(10/h)* h));
  fig.add(new go.PathSegment(go.PathSegment.Line, (10/w)* w, (10/h)* h));
  fig.add(new go.PathSegment(go.PathSegment.Line, -(10/w)* w, (10/h)* h));
  fig.add(new go.PathSegment(go.PathSegment.Line, -(10/w)* w, -(10.5/h) * h));

  var fig = new go.PathFigure(-(15/w)* w, 0);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, -(7.5/w)* w, 0).close());
  var fig = new go.PathFigure(-(7.5/w)* w, 0);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, (8.75/w) * w, -(4/h)*h).close());
  var fig = new go.PathFigure((7.5/w)* w, -(5/h)*h);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, (7.5/w) * w, (5/h)*h).close());
  var fig = new go.PathFigure((7.5/w)* w, 0);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, (15/w) * w, 0).close());

  var fig = new go.PathFigure((2/w) * w, 0, false);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Arc, 180, 360, (5/w) * w, 0, (3/w)*w, (3/h)*h));

  var fig = new go.PathFigure(0, -(6/h)*h);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, (4/w)* w, -(6/h)* h));
  fig.add(new go.PathSegment(go.PathSegment.Line, (4/w)* w, -(3/h)* h));
  fig.add(new go.PathSegment(go.PathSegment.Line, 0, -(3/h)* h));
  fig.add(new go.PathSegment(go.PathSegment.Line, 0, -(6/h) * h));
 
  var fig = new go.PathFigure((18/w)* w, -(10/h)*h,false);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line,(18/w)* w, -(3/h)*h).close());
  fig.add(new go.PathSegment(go.PathSegment.Move, (16/w)* w, -(10/h)*h));
  fig.add(new go.PathSegment(go.PathSegment.Line,(20/w)* w, -(10/h)*h).close());
  fig.add(new go.PathSegment(go.PathSegment.Move, (16/w)* w, -(3/h)*h));
  fig.add(new go.PathSegment(go.PathSegment.Line,(20/w)* w, -(3/h)*h).close());
  return geo;
});

myDiagram.nodeTemplate =
  $(go.Node, "Auto" ,
    { resizable: true,background: "red"},
    
    $(go.Shape, "LoadSwitch", 
      new go.Binding("fill", "color"),
      new go.Binding("figure", "fig"),
      new go.Binding("position"))
  );
var model = $(go.Model);
model.nodeDataArray =
[
  {color:"lightblue",fig: "LoadSwitch",position: new go.Point(300, 200)}
];
myDiagram.model = model;

Result:

A Shape can only have a single Shape.fill brush and a Shape.stroke brush. So if you want different stroke colors and/or fill colors, you will need to use multiple Shapes.

I suggest that you use a “Position” Panel with several Shapes whose Shape.isGeometryPositioned is set to true. Each Shape gets the geometry, fill, and stroke that you want. I cannot tell what your other requirements are, but it is possible that this “Position” Panel could be your Node.

I’m not sure what you want to do regarding resizing. Perhaps it would be easiest to wrap this “Position” Panel with a “Viewbox” Panel. GoJS Panels -- Northwoods Software, and have that panel be the one that the user resizes interactively.

Or maybe you want to have the Shapes have stretch: go.GraphObject.Fill. But that probably depends on the shape geometries being the same size to begin with.

Hi Walter,

Thanks for your prompt suggestions,I use a “Position” Panel with several Shapes to achieve it.But there is another problem,as shown in figure:

Code:
myDiagram =
(go.Diagram, "myDiagramDiv", { initialContentAlignment: go.Spot.Center, "grid.visible":true, "undoManager.isEnabled": true }); go.Shape.defineFigureGenerator("LoadSwitch", function(shape, w, h) { var geo = new go.Geometry(); var fig = new go.PathFigure(-(15/w)* w, 0); geo.add(fig); fig.add(new go.PathSegment(go.PathSegment.Line, -(7.5/w)* w, 0).close()); var fig = new go.PathFigure(-(7.5/w)* w, 0); geo.add(fig); fig.add(new go.PathSegment(go.PathSegment.Line, (8.75/w) * w, -(4/h)*h).close()); var fig = new go.PathFigure((7.5/w)* w, -(5/h)*h); geo.add(fig); fig.add(new go.PathSegment(go.PathSegment.Line, (7.5/w) * w, (5/h)*h).close()); var fig = new go.PathFigure((7.5/w)* w, 0); geo.add(fig); fig.add(new go.PathSegment(go.PathSegment.Line, (15/w) * w, 0).close()); var fig = new go.PathFigure((2/w) * w, 0, false); geo.add(fig); fig.add(new go.PathSegment(go.PathSegment.Arc, 180, 360, (5/w) * w, 0, (3/w)*w, (3/h)*h)); var fig = new go.PathFigure((18/w)* w, -(10/h)*h,false); geo.add(fig); fig.add(new go.PathSegment(go.PathSegment.Line,(18/w)* w, -(3/h)*h).close()); fig.add(new go.PathSegment(go.PathSegment.Move, (16/w)* w, -(10/h)*h)); fig.add(new go.PathSegment(go.PathSegment.Line,(20/w)* w, -(10/h)*h).close()); fig.add(new go.PathSegment(go.PathSegment.Move, (16/w)* w, -(3/h)*h)); fig.add(new go.PathSegment(go.PathSegment.Line,(20/w)* w, -(3/h)*h).close()); return geo; }); myDiagram.add( (go.Part, go.Panel.Viewbox, // or “Viewbox”
{ position: new go.Point(100, 0), background: null},
(go.Panel, "Position", (go.Shape, “Circle”, { width: 30, height: 30,stroke:null,fill:“rgba(255,102,0,.5)”,position: new go.Point(-15, -15)}),
(go.Shape, "LoadSwitch", { stroke: "rgba(255,0,0,1)",strokeWidth: 2,fill:null, position: new go.Point(-15, -10)}), (go.Shape, “Rectangle”, { width: 24, height: 20, stroke: “rgba(0,0,0,1)”,strokeWidth: 2,fill:null, position: new go.Point(-10, -10)}),
$(go.Shape, “Rectangle”, { width: 4, height: 3,stroke: “rgba(0,255,0,1)”,strokeWidth: 1,fill:“rgba(0,255,0,1)”, position: new go.Point(0, -6),angle: -15})
)
));

I see that you are making progress.

In your “LoadSwitch” figure, it is odd that you keep dividing and multiplying constants by the width or height. It is also odd that you are using negative coordinates. I think that is part of the problem. You should endeavor to produce a Geometry with the given width and height.

Note that when you create the LoadSwitch Shape you don’t specify the width and height, so they will default to 100x100. (But you do specify them for the other Shapes, as you should.)

Walter,thanks for your help,the problem has been solved!

Code:
go.Shape.defineFigureGenerator(“Semi_Circle”, function(shape, w, h) {
var geo = new go.Geometry();
var fig = new go.PathFigure(0,(6)*h, true);
geo.add(fig);
fig.add(new go.PathSegment(go.PathSegment.Arc, 0, 180,(6)*w, (6)*h, (6)*w, (6)*h));
return geo;
});
go.Shape.defineFigureGenerator(“Ground_Ring”, function(shape, w, h) {
var geo = new go.Geometry();
var fig = new go.PathFigure(0, 0,false);
geo.add(fig);
fig.add(new go.PathSegment(go.PathSegment.Line, (5)*w, (5)*h));
fig.add(new go.PathSegment(go.PathSegment.Line, -(5)*w, (5)*h));
fig.add(new go.PathSegment(go.PathSegment.Line, 0, 0).close());

  var fig = new go.PathFigure(0,(5)*h);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line,0,(8)*h).close());

  var fig = new go.PathFigure(-(3)*w,(8)*h);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line,(3)*w,(8)* h).close());

  var fig = new go.PathFigure(-(2)*w,(10)*h);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line,(2)*w,(10)* h).close());

  var fig = new go.PathFigure(-(1)*w,(12)*h);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line,(1)*w,(12)* h).close());
  return geo;
});
myPalette.add(
$(go.Part, go.Panel.Viewbox,  // or "Viewbox"
  {fromLinkable:true,toLinkable:true,resizable: true,rotatable: true,locationSpot: go.Spot.Center,locationObjectName: "SHAPE"},
  {background: null},
   $(go.Panel, "Position",
    $(go.Shape, "Circle", {fill:"rgba(255,255,255,0)",strokeWidth:2,stroke: "rgba(0,0,0,1)",width:12, height:12,position: new go.Point(0, 0)})
  )
));

myPalette.add(
$(go.Part, go.Panel.Viewbox,  // or "Viewbox"
  {fromLinkable:true,toLinkable:true,resizable: true,rotatable: true,locationSpot: go.Spot.Center,locationObjectName: "SHAPE"},
  {background: null},
  $(go.Panel, "Position",
    $(go.Shape, "Circle", {fill:"rgba(255,255,255,0)",strokeWidth:2,stroke: "rgba(0,0,0,1)",width:12, height:12,position: new go.Point(0, 0)}),
    $(go.Shape, "Semi_Circle", {fill:"rgba(0,0,0,1)",strokeWidth:2,stroke: "rgba(0,0,0,1)",height:1,width:1,position: new go.Point(0, 0)})
  )
));

myPalette.add(
$(go.Part, go.Panel.Viewbox,  // or "Viewbox"
  {fromLinkable:true,toLinkable:true,resizable: true,rotatable: true,locationSpot: go.Spot.Center,locationObjectName: "SHAPE"},
  {background: null},
  $(go.Panel, "Position",
    $(go.Shape, "Rectangle", {fill:"rgba(255,255,255,0)",strokeWidth:1,stroke:"rgba(0,0,0,0)",height:12,width:10,position: new go.Point(-5, 0)}),
    $(go.Shape, "Ground_Ring", {fill:"rgba(255,255,255,0)",strokeWidth:2,stroke: "rgba(0,0,0,1)",width:1, height:1,position: new go.Point(0, 0)})
  )
));

Qustion1:As the code shows,each node model is different.I don’t know how to write the myDiagram.nodeTemplate , myDiagram.linkTemplate,go.GraphLinksModel(nodeDataArray, linkDataArray).Here is what I want to achieve:
Qustion2:I want to define different ports for different objects,as shown in the following figure.

Well, I suppose you could have a different template for each kind of node. Logic Circuit

Or you could have a different template for each combination of ports. Data Flow Diagram

Or you could have a single template for many different configurations of ports. Pipes

A post was split to a new topic: Defining new node types interactively

Code:
var myDiagram =
$(go.Diagram, “myDiagramDiv”,
{
initialContentAlignment: go.Spot.Center,
“grid.visible”:true,
“grid.gridCellSize”:new go.Size(10,10),
“draggingTool.isGridSnapEnabled”:true,
“toolManager.hoverDelay”:100,
“toolManager.mouseWheelBehavior”:go.ToolManager.WheelZoom,
// “commandHandler.copiesTree”:true,
// “commandHandler.deletesTree”:true,
“undoManager.isEnabled”: true ,
allowDrop: true
});

// create the Palette
var myPalette =
  $(go.Palette, "myPaletteDiv",
    { // 定制GridLayout以对齐locationObjects的中心
      layout: $(go.GridLayout, { alignment: go.GridLayout.Location })
    });

function portStyle(input) {
  return {
    desiredSize: new go.Size(4, 4),
    fill: "rgba(0,0,255,1)",
    stroke:"rgba(0,0,255,0.5)",
    fromSpot: go.Spot.Right,
    fromLinkable: !input,
    toSpot: go.Spot.Left,
    toLinkable: input,
    toMaxLinks: 1,
    cursor: "pointer"
  };
}
go.Shape.defineFigureGenerator("LoadSwitch_I", function(shape, w, h) {
  var geo = new go.Geometry();
  var fig = new go.PathFigure(-(15)* w, 0);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, -(7.5)* w, 0).close());
  var fig = new go.PathFigure(-(7.5)* w, 0);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, (8.75) * w, -(4)*h).close());
  var fig = new go.PathFigure((7.5)* w, -(5)*h);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, (7.5) * w, (5)*h).close());
  var fig = new go.PathFigure((7.5)* w, 0);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, (15) * w, 0).close());
  var fig = new go.PathFigure((2) * w, 0, false);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Arc, 180, 360, (5) * w, 0, (3)*w, (3)*h));
  var fig = new go.PathFigure((18)* w, -(10)*h,false);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line,(18)* w, -(3)*h).close());
  fig.add(new go.PathSegment(go.PathSegment.Move, (16)* w, -(10)*h));
  fig.add(new go.PathSegment(go.PathSegment.Line,(20)* w, -(10)*h).close());
  fig.add(new go.PathSegment(go.PathSegment.Move, (16)* w, -(3)*h));
  fig.add(new go.PathSegment(go.PathSegment.Line,(20)* w, -(3)*h).close());
  return geo;
});
go.Shape.defineFigureGenerator("LoadSwitch", function(shape, w, h) {
  var geo = new go.Geometry();
  var fig = new go.PathFigure(-(15)* w, 0);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, -(7.5)* w, 0).close());
  var fig = new go.PathFigure(-(7.5)* w, 0);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, (8.75) * w, -(4)*h).close());
  var fig = new go.PathFigure((7.5)* w, -(5)*h);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, (7.5) * w, (5)*h).close());
  var fig = new go.PathFigure((7.5)* w, 0);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, (15) * w, 0).close());
  var fig = new go.PathFigure((2) * w, 0, false);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Arc, 180, 360, (5) * w, 0, (3)*w, (3)*h));
  return geo;
});
go.Shape.defineFigureGenerator("OpenLoadSwitch", function(shape, w, h) {
  var geo = new go.Geometry();
  var fig = new go.PathFigure(-(15)* w, 0);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, -(7.5)* w, 0).close());
  var fig = new go.PathFigure(-(7.5)* w, 0);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, (3.75) * w, -(7)*h).close());
  var fig = new go.PathFigure((7.5)* w, -(5)*h);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, (7.5) * w, (5)*h).close());
  var fig = new go.PathFigure((7.5)* w, 0);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, (15) * w, 0).close());
  var fig = new go.PathFigure((2) * w, 0, false);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Arc, 180, 360, (5) * w, 0, (3)*w, (3)*h));
  return geo;
});
go.Shape.defineFigureGenerator("Semi_Circle", function(shape, w, h) {
  var geo = new go.Geometry();
  var fig = new go.PathFigure(0,(6)*h, true);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Arc, 0, 180,(6)*w, (6)*h, (6)*w, (6)*h));
  return geo;
});
go.Shape.defineFigureGenerator("Ground_Ring", function(shape, w, h) {
  var geo = new go.Geometry();
  var fig = new go.PathFigure(0, 0,false);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, (5)*w, (5)*h));
  fig.add(new go.PathSegment(go.PathSegment.Line, -(5)*w, (5)*h));
  fig.add(new go.PathSegment(go.PathSegment.Line, 0, 0).close());

  var fig = new go.PathFigure(0,(5)*h);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line,0,(8)*h).close());

  var fig = new go.PathFigure(-(3)*w,(8)*h);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line,(3)*w,(8)* h).close());

  var fig = new go.PathFigure(-(2)*w,(10)*h);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line,(2)*w,(10)* h).close());

  var fig = new go.PathFigure(-(1)*w,(12)*h);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line,(1)*w,(12)* h).close());
  return geo;
});
go.Shape.defineFigureGenerator("Line_tower", function(shape, w, h) {
  var geo = new go.Geometry();
  var fig = new go.PathFigure(-(6)*w, -(6)*h);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, (6)*w, (6)*h).close());

  var fig = new go.PathFigure((6)*w, -(6)*h);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, -(6)*w, (6)*h).close());
  return geo;
});
go.Shape.defineFigureGenerator("Cable_line", function(shape, w, h) {
  var geo = new go.Geometry();
  var fig = new go.PathFigure(-(3)*w, -(3)*h,false);
  geo.add(fig);
  fig.add(new go.PathSegment(go.PathSegment.Line, (3)*w, 0));
  fig.add(new go.PathSegment(go.PathSegment.Line, -(3)*w, (3)*h));
  fig.add(new go.PathSegment(go.PathSegment.Line, -(3)*w, -(3)*h).close());
  return geo;
});
// define templates for each type of node
//水泥杆
var shuiniganTemplate =
$(go.Part, go.Panel.Viewbox,  // or "Viewbox"
  {resizable: true,rotatable: true,locationSpot: go.Spot.Center,locationObjectName: "SHAPE"},
  {background: null},
 new go.Binding("isShadowed", "isSelected").ofObject(),
   $(go.Panel, "Position",
    $(go.Shape, "Circle", {fill:"rgba(255,255,255,0)",strokeWidth:2,stroke: "rgba(0,0,0,1)",width:12, height:12,position: new go.Point(0, 0)}),
    $(go.Shape, "Rectangle", portStyle(false),  // the only to port
        { portId: "", position: new go.Point(14, 5)})
     // $(go.TextBlock,
     //  { 
     //    text: "水泥杆",
     //    position: new go.Point(-10, 14),
     //    editable: true 
     //  }
     //  ) 
    )
);
//高强度水泥杆
var gaoqiangdushuiniganTemplate =
$(go.Part, go.Panel.Viewbox,  // or "Viewbox"
  {resizable: true,rotatable: true,locationSpot: go.Spot.Center,locationObjectName: "SHAPE"},
  {background: null},
  $(go.Panel, "Position",
    $(go.Shape, "Circle", {fill:"rgba(255,255,255,0)",strokeWidth:2,stroke: "rgba(0,0,0,1)",width:12, height:12,position: new go.Point(0, 0)}),
    $(go.Shape, "Semi_Circle", {fill:"rgba(0,0,0,1)",strokeWidth:2,stroke: "rgba(0,0,0,1)",height:1,width:1,position: new go.Point(0, 0)}),
    $(go.Shape, "Rectangle", portStyle(false),  // the only toport
        { portId: "", position: new go.Point(14, 5)})
  )
);
//绝缘导线接地环
var ground_ringTemplate =
$(go.Part, go.Panel.Viewbox,  // or "Viewbox"
  {resizable: true,rotatable: true,locationSpot: go.Spot.Center,locationObjectName: "SHAPE"},
  {background: null},
  $(go.Panel, "Position",
    $(go.Shape, "Rectangle", {fill:"rgba(255,255,255,0)",strokeWidth:1,stroke:"rgba(0,0,0,0)",height:12,width:10,position: new go.Point(-5, 0)}),
    $(go.Shape, "Ground_Ring", {fill:"rgba(255,255,255,0)",strokeWidth:2,stroke: "rgba(0,0,0,1)",width:1, height:1,position: new go.Point(0, 0)}),
    $(go.Shape, "Rectangle", portStyle(true),  // the only fromport
        { portId: "", position: new go.Point(-1.5, 14)})
  )
);
//铁塔Line_tower
var line_towerTemplate =
$(go.Part, go.Panel.Viewbox,  // or "Viewbox"
  {resizable: true,rotatable: true,locationSpot: go.Spot.Center,locationObjectName: "SHAPE"},
  {background: null},
  $(go.Panel, "Position",
    $(go.Shape, "Rectangle", {fill:"rgba(255,255,255,0)",strokeWidth:2,stroke:"rgba(0,0,0,1)",height:12,width:12,position: new go.Point(-6, -6)}),
    $(go.Shape, "Line_tower", {fill:"rgba(255,255,255,0)",strokeWidth:2,stroke: "rgba(0,0,0,1)",width:1, height:1,position: new go.Point(0, 0)}),
    $(go.Shape, "Rectangle", portStyle(true),  // the  fromport
        { portId: "", position: new go.Point(-11, -2)}),
    $(go.Shape, "Rectangle", portStyle(false),  // the  toport
        { portId: "", position: new go.Point(8, -2)})
  )
);
//电缆终端头Cable_line
var cable_lineTemplate =
$(go.Part, go.Panel.Viewbox,  // or "Viewbox"
  {resizable: true,rotatable: true,locationSpot: go.Spot.Center,locationObjectName: "SHAPE"},
  {background: null},
  $(go.Panel, "Position",
    $(go.Shape, "Rectangle", {fill:"rgba(255,255,255,0)",strokeWidth:2,stroke:"rgba(0,0,0,0)",height:12,width:12,position: new go.Point(-6, -6)}),
    $(go.Shape, "Cable_line", {fill:"rgba(255,255,255,0)",strokeWidth:2,stroke: "rgba(0,0,0,1)",width:1, height:1,position: new go.Point(0, 0)}),
    $(go.Shape, "Rectangle", portStyle(true),  // the  fromport
        { portId: "", position: new go.Point(-8, -1.8)}),
    $(go.Shape, "Rectangle", portStyle(false),  // the  toport
        { portId: "", position: new go.Point(6, -1.8)})
  )
);
//环网房内闭合的负荷开关
var inloadswitchTemplate =
$(go.Part, go.Panel.Viewbox,  // or "Viewbox"
  {resizable: true,rotatable: true,locationSpot: go.Spot.Center,locationObjectName: "SHAPE"},
  {background: null},
  $(go.Panel, "Position",
    $(go.Shape, "LoadSwitch", {fill:"rgba(255,255,255,0)",strokeWidth:2,stroke: "rgba(255,0,255,1)",height:1,width:1}),
    $(go.Shape, "Rectangle", {fill:"rgba(255,255,255,0)",strokeWidth:1,stroke:"rgba(255,0,255,0)",height:20,width:24,position: new go.Point(-10, -10)}),
    $(go.Shape, "Rectangle", portStyle(true),  // the  fromport
        { portId: "", position: new go.Point(-19, -1.5)}),
    $(go.Shape, "Rectangle", portStyle(false),  // the  toport
        { portId: "", position: new go.Point(16, -1.5)})
  )
);
//环网房内打开的负荷开关
var inopenloadswitchTemplate =
$(go.Part, go.Panel.Viewbox,  // or "Viewbox"
  {resizable: true,rotatable: true,locationSpot: go.Spot.Center,locationObjectName: "SHAPE"},
  {background: null},
  $(go.Panel, "Position",
    $(go.Shape, "OpenLoadSwitch", {fill:"rgba(255,255,255,0)",strokeWidth:2,stroke: "rgba(255,0,255,1)",height:1,width:1}),
    $(go.Shape, "Rectangle", {fill:"rgba(255,255,255,0)",strokeWidth:1,stroke:"rgba(255,0,255,0)",height:20,width:24,position: new go.Point(-10, -10)}),
    $(go.Shape, "Rectangle", portStyle(true),  // the  fromport
        { portId: "", position: new go.Point(-19, -1.5)}),
    $(go.Shape, "Rectangle", portStyle(false),  // the  toport
        { portId: "", position: new go.Point(16, -1.5)})
  )
);
//环网房外打开的负荷开关
var outopenloadswitchTemplate =
$(go.Part, go.Panel.Viewbox,  // or "Viewbox"
  {resizable: true,rotatable: true,locationSpot: go.Spot.Center,locationObjectName: "SHAPE"},
  {background: null},
  $(go.Panel, "Position",
    $(go.Shape, "OpenLoadSwitch", {fill:"rgba(255,255,255,0)",strokeWidth:2,stroke: "rgba(255,0,255,1)",height:1,width:1}),
    $(go.Shape, "Rectangle", {fill:"rgba(255,255,255,0)",strokeWidth:1,stroke:"rgba(255,0,255,1)",height:20,width:24,position: new go.Point(-10, -10),strokeDashArray: [5, 5]}),
    $(go.Shape, "Rectangle", portStyle(true),  // the  fromport
        { portId: "", position: new go.Point(-19, -1.5)}),
    $(go.Shape, "Rectangle", portStyle(false),  // the  toport
        { portId: "", position: new go.Point(16, -1.5)})
  )
);
//非环网房内闭合的负荷开关
var ninloadswitchTemplate =
$(go.Part, go.Panel.Viewbox,  // or "Viewbox"
  {resizable: true,rotatable: true,locationSpot: go.Spot.Center,locationObjectName: "SHAPE"},
  {background: null},
  $(go.Panel, "Position",
    $(go.Shape, "LoadSwitch", {fill:"rgba(255,255,255,0)",strokeWidth:2,stroke: "rgba(255,0,0,1)",height:1,width:1}),
    $(go.Shape, "Rectangle", {fill:"rgba(255,255,255,0)",strokeWidth:1,stroke:"rgba(255,0,0,0)",height:20,width:24,position: new go.Point(-10, -10)}),
    $(go.Shape, "Rectangle", portStyle(true),  // the  fromport
        { portId: "", position: new go.Point(-19, -1.5)}),
    $(go.Shape, "Rectangle", portStyle(false),  // the  toport
        { portId: "", position: new go.Point(16, -1.5)})
  )
);
//电流自动化非环网房内闭合的负荷开关
var iautoninloadswitchTemplate =
$(go.Part, go.Panel.Viewbox,  // or "Viewbox"
  {resizable: true,rotatable: true,locationSpot: go.Spot.Center,locationObjectName: "SHAPE"},
  {background: null},
  $(go.Panel, "Position",
    $(go.Shape, "Circle", { width: 30, height: 30,stroke:null,fill:"rgba(255,102,0,.5)",position: new go.Point(-15, -15)}),
    $(go.Shape, "LoadSwitch_I", {width:1, height:1,stroke: "rgba(255,0,0,1)",strokeWidth: 2,fill:null, position: new go.Point(-0.15, -0.10)}),
    $(go.Shape, "Rectangle", { width: 24, height: 20, stroke: "rgba(0,0,0,0)",strokeWidth: 1,fill:null, position: new go.Point(-10, -10)}),
    $(go.Shape, "Rectangle", { width: 4, height: 3,stroke: "rgba(0,255,0,1)",strokeWidth: 1,fill:"rgba(0,255,0,1)", position: new go.Point(0, -6),angle: -15}),
    $(go.Shape, "Rectangle", portStyle(true),  // the  fromport
        { portId: "",position: new go.Point(-19, -1.5)}),
    $(go.Shape, "Rectangle", portStyle(false),  // the  toport
        { portId: "",position: new go.Point(16, -1.5)})
  )
);
//电流自动化非环网房外闭合的负荷开关
var iautonoutloadswitchTemplate =
$(go.Part, go.Panel.Viewbox,  // or "Viewbox"
  {locationSpot: go.Spot.Center,locationObjectName: "SHAPE"},
  {background: null},
  $(go.Panel, "Position",
    $(go.Shape, "Circle", { width: 30, height: 30,stroke:null,fill:"rgba(255,102,0,.5)",position: new go.Point(-15, -15)}),
    $(go.Shape, "LoadSwitch_I", {width:1, height:1,stroke: "rgba(255,0,0,1)",strokeWidth: 2,fill:null, position: new go.Point(-0.15, -0.10)}),
    $(go.Shape, "Rectangle", { width: 24, height: 20, stroke: "rgba(0,0,0,1)",strokeWidth: 2,fill:null, position: new go.Point(-10, -10)}),
    $(go.Shape, "Rectangle", { width: 4, height: 3,stroke: "rgba(0,255,0,1)",strokeWidth: 1,fill:"rgba(0,255,0,1)", position: new go.Point(0, -6),angle: -15}),
    $(go.Shape, "Rectangle", portStyle(true),  // the  fromport
        { portId: "",position: new go.Point(-19, -1.5)}),
    $(go.Shape, "Rectangle", portStyle(false),  // the  toport
        { portId: "",position: new go.Point(16, -1.5)})
  )
);

// creates relinkable Links that will avoid crossing Nodes when possible and will jump over other Links in their paths
myDiagram.linkTemplate =
  $(go.Link,
    {
      routing: go.Link.AvoidsNodes,
      curve: go.Link.JumpOver,
      corner: 3,
      relinkableFrom: true, relinkableTo: true,
      selectionAdorned: false, // Links are not adorned when selected so that their color remains visible.
      shadowOffset: new go.Point(0, 0), shadowBlur: 5, shadowColor: "blue",
    },
    // new go.Binding("isShadowed", "isSelected").ofObject(),
    // $(go.Shape,
    //   { name: "SHAPE", strokeWidth: 2, stroke: "orangered" }));
    $(go.Shape),
       $(go.Shape, { toArrow: "Standard" }));  

// 从没有零件开始
myDiagram.undoManager.isEnabled = true;

// add the templates created above to myDiagram and palette
myDiagram.nodeTemplateMap.add("shuinigan", shuiniganTemplate);
myDiagram.nodeTemplateMap.add("gaoqiangdushuinigan", gaoqiangdushuiniganTemplate);
myDiagram.nodeTemplateMap.add("ground_ring", ground_ringTemplate);
myDiagram.nodeTemplateMap.add("line_tower", line_towerTemplate);
myDiagram.nodeTemplateMap.add("cable_line", cable_lineTemplate);
myDiagram.nodeTemplateMap.add("inloadswitch", inloadswitchTemplate);
myDiagram.nodeTemplateMap.add("inopenloadswitch", inopenloadswitchTemplate);
myDiagram.nodeTemplateMap.add("outopenloadswitch", outopenloadswitchTemplate);
myDiagram.nodeTemplateMap.add("ninloadswitch", ninloadswitchTemplate);
myDiagram.nodeTemplateMap.add("iautoninloadswitch", iautoninloadswitchTemplate);
myDiagram.nodeTemplateMap.add("iautonoutloadswitch", iautonoutloadswitchTemplate);
// share the template map with the Palette
myPalette.nodeTemplateMap = myDiagram.nodeTemplateMap;
myPalette.model.nodeDataArray = [
  { category: "shuinigan" },
  { category: "gaoqiangdushuinigan" },
  { category: "ground_ring" },
  { category: "line_tower" },
  { category: "cable_line" },
  { category: "inloadswitch" },
  { category: "inopenloadswitch" },
  { category: "outopenloadswitch" },
  { category: "ninloadswitch" },
  { category: "iautoninloadswitch" },
  { category: "iautonoutloadswitch" }
];
myDiagram.model =
$(go.GraphLinksModel,
  { linkFromPortIdProperty: "fromPort",  // required information:
    linkToPortIdProperty: "toPort"     // identifies data
    });

Hi,walter.
I have a different template for each combination of ports.But I don’t know why I can’t connect the ports.

Because your Parts are not Nodes. Use go.Node, not go.Part.

Oh,thanks for your help,the problem has been solved!