如何绘制多堆栈,多对多关系图,如附图所示,使用JavaScript?

How to draw a multi stack, many to many relationship diagram, something like in the attached picture, using JavaScript?

我想用JavaScript画一个关系图(类似上图)。我尝试了很多 js 库,如 D3、GoJs、JsPlumb 等,但没有一个能满足我的要求。请在下方找到附加要求。

  1. 框(例如业务架构 > 流程 > 解决投诉)最多可以有四个内部级别。
  2. 任何框都可以与任何框连接(内部 - 在组内和外部 - 与其他组中的节点)
  3. 关系可以是单向和双向(单向箭头和双向箭头)并且应该有关系类型(例如过程)。
  4. 每个框(所有级别)都应该有四个可点击的热点
  5. 可以保存为模板以供日后修改
  6. 数据源是 JSON

此解决方案创建于 GoJS

这是结果。抱歉,我不想输入您屏幕截图中的所有数据,所以我只输入了前两组。而且我也没有很好地设计样式。

这是完整的页面,包括 JSON 格式的图表模型数据:

<!DOCTYPE html>
<html>
<head>
<title>Simple Nested Relationships in GoJS</title>
<!-- Copyright 1998-2017 by Northwoods Software Corporation. -->
<meta charset="UTF-8">
<script src="go.js"></script>
<script id="code">
  function init() {
    var $ = go.GraphObject.make;

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

    function makePort(id, spot) {
      return $(go.Shape,
        {
          fill: "transparent", strokeWidth: 0,
          width: 10, height: 10,
          alignment: spot, alignmentFocus: spot,
          portId: id,
          fromLinkable: true, toLinkable: true, cursor: "pointer"
        });
    }

    myDiagram.nodeTemplate =
      $(go.Node, "Spot",
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
        $(go.Panel, "Auto",
          $(go.Shape, "RoundedRectangle",
            { fill: "white" },
            new go.Binding("fill", "color"),
            new go.Binding("figure"),
            new go.Binding("angle", "figure", function(f) { return f === "Hexagon" ? 90 : 0; })),
          $(go.TextBlock,
            { margin: 4, editable: true, maxSize: new go.Size(150, NaN), textAlign: "center" },
            new go.Binding("text").makeTwoWay())
        ),
        makePort("T", go.Spot.Top),
        makePort("R", go.Spot.Right),
        makePort("B", go.Spot.Bottom),
        makePort("L", go.Spot.Left)
      );

    myDiagram.groupTemplate =
      $(go.Group, "Spot",
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
        $(go.Panel, "Auto",
          $(go.Shape,
            { fill: "whitesmoke" },
            new go.Binding("fill", "color"),
            new go.Binding("stroke", "color", go.Brush.darken)),
          $(go.Panel, "Vertical",
            $(go.TextBlock,
              { margin: 4, editable: true },
              new go.Binding("text").makeTwoWay()),
            $(go.Placeholder, { padding: 10 })
          )
        ),
        makePort("T", go.Spot.Top),
        makePort("R", go.Spot.Right),
        makePort("B", go.Spot.Bottom),
        makePort("L", go.Spot.Left)
      );

    myDiagram.linkTemplate =
      $(go.Link,
        $(go.Shape, { strokeWidth: 2 }),
        $(go.Shape, { fromArrow: "Standard", visible: false },
          new go.Binding("visible", "from")),
        $(go.Shape, { toArrow: "Standard" }),
        $(go.TextBlock,
          { font: "11px sans-serif", editable: true },
          new go.Binding("text").makeTwoWay())
      );

    myDiagram.model = go.Model.fromJson(
      '{ "class": "go.GraphLinksModel",\
        "linkFromPortIdProperty": "fp",\
        "linkToPortIdProperty": "tp",\
        "nodeDataArray": [\
          { "key": 1, "text": "Strategic Planning", "color": "lightgreen", "isGroup": true, "loc": "10.5 22" },\
          { "key": 11, "text": "Business Policy", "color": "rgba(182, 255, 180, 1)", "isGroup": true, "group": 1, "loc": "21 116" },\
          { "key": 111, "text": "Adhere to FSA Guidelines", "color": "lightgreen", "figure": "Hexagon", "group": 11, "loc": "31 126" },\
          { "key": 12, "text": "Strategy", "color": "rgba(182, 255, 180, 1)", "isGroup": true, "group": 1, "loc": "162 55" },\
          { "key": 121, "text": "Adopt Customer\nComplaints Process", "color": "lightskyblue", "group": 12, "loc": "172 65" },\
          { "key": 122, "text": "Mission and\nVision", "color": "plum", "group": 12, "loc": "274 136" },\
          { "key": 123, "text": "Improve Customer Satisfaction", "color": "lightskyblue", "group": 12, "loc": "177 206" },\
          { "key": 124, "text": "Decisions made wlil Maximize Benefit to the Enterprise", "color": "lightskyblue", "group": 12, "loc": "304 203" },\
          { "key": 2, "text": "Business Architecture", "color": "silver", "isGroup": true, "loc": "503 21" },\
          { "key": 21, "text": "Process", "color": "lightgray", "isGroup": true, "group": 2, "loc": "514 54" },\
          { "key": 211, "text": "Complaints\n Handling", "color": "lightgreen", "figure":"Hexagon", "group": 21, "loc": "546 35" },\
          { "key": 212, "text": "Resolve \nComplaint", "color": "lightskyblue", "group": 21, "loc": "679 64" },\
          { "key": 213, "text": "Complaints\n Handling", "color": "lightskyblue", "group": 21, "loc": "550 145" },\
          { "key": 22, "text": "Organization", "color": "lightgray", "isGroup": true, "group": 2, "loc": "842 55" },\
          { "key": 221, "text": "Complaints \nCall Handler", "color": "lightskyblue", "group": 22, "loc": "852 65.6" },\
          { "key": -18, "text": "Complaints \nClient", "color": "lightskyblue", "group": 22, "loc": "854 142" },\
          { "key": -19, "text": "Complaints \nManager", "color": "lightskyblue", "group": 22, "loc": "852 225" },\
          { "key": 23, "text": "Business Service", "color": "lightgray", "isGroup": true, "group": 2, "loc": "514 217" },\
          { "key": 231, "text": "Manage \nComplaints", "color": "lightskyblue", "group": 23, "loc": "550 247" }\
        ],\
          "linkDataArray": [\
            { "from": 111, "to": 123, "fp": "R", "tp": "L" },\
            { "from": 121, "to": 123, "fp": "B", "tp": "T", "text": "Process" },\
            { "from": 121, "to": 122, "fp": "B", "tp": "T", "text": "Process" },\
            { "from": 121, "to": 211, "fp": "R", "tp": "L", "text": "Process" },\
            { "from": 121, "to": 231, "fp": "R", "tp": "L", "text": "Process" },\
            { "from": 211, "to": 213, "fp": "B", "tp": "T", "text": "Process type:\nProcess" },\
            { "from": 231, "to": 213, "fp": "T", "tp": "B", "text": "Process" },\
            { "from": 231, "to": 212, "fp": "T", "tp": "B" },\
            { "from": 212, "to": -19, "fp": "B", "tp": "L", "text": "Process" },\
            { "from": 212, "to": -18, "fp": "B", "tp": "L", "text": "Process" },\
            { "from": 212, "to": 221, "fp": "R", "tp": "L", "text": "Process" }\
          ]\
      }'
    );
  }
</script>
</head>
<body onload="init()">
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
</body>
</html>