近期项目中使用到d3插件绘制的流程图。

首先认识下dagre。dagre是专注于有向图布局的javascript库,由于dagre 仅仅专注于图形布局,需要使用其他方案根据 dagre 的布局信息来实际渲染图形,而 dagre-d3 就是 dagre 基于 D3 的渲染方案。

使用步骤

  • 1、下载d3及dagreD3
import dagreD3 from "dagre-d3";
import * as d3 from "d3";
  • 2、数据准备
    流程图作为一种有向图,与树图、网络图一样,数据由节点以及两点之间的边组成。
dataSet: {
        nodes: [
          {
            id: 0,
            label: "申请人",
            shape: "rect",
            color: "fill:#d81b13;stroke:transparent",
            toolText: ""
          },
          {
            id: 1,
            label: "支撑需求填写\n销售",
            shape: "rect",
            color: "",
            toolText: ""
          },
          {
            id: 2,
            label: "支撑方案填写\n一级支撑经理",
            shape: "rect",
            color: "",
            toolText: ""
          },
          {
            id: 3,
            label: "评审意见填写\n客响",
            shape: "rect",
            color: "",
            toolText: ""
          },
          {
            id: 4,
            label: "支撑方案和评审填写\n一级支撑经理",
            shape: "rect",
            color: "",
            toolText: ""
          },
          {
            id: 5,
            label: " 已结束 ",
            shape: "rect",
            color: "",
            toolText: ""
          }
        ],
        edges: [
          {id: 1, source: 0, target: 1, label: "", color: ""},
          {id: 2, source: 1, target: 2, label: "", color: ""},
          {id: 3, source: 2, target: 3, label: "", color: ""},
          {id: 4, source: 3, target: 4, label: "", color: ""},
          {id: 5, source: 4, target: 5, label: "", color: ""},
          {id: 6, source: 2, target: 5, label: "", color: ""}
        ]
      },
  • 3、绘制流程图
// 设置节点和连线
    renderGagre () {
      this.tooltip = this.createTooltip();
      // 创建graph对象
      const g = new dagreD3.graphlib.Graph();
      // 设置图
      g.setGraph({
        rankdir: "LR", // T:top B:bottom
        marginx: 60,
        marginy: 80,
        edgesep: 100,
        ranksep: 60
      });
      this.dataSet.nodes.forEach((item) => {
        g.setNode(item.id, {
          // 节点标签
          label: item.label,
          // 节点形状
          shape: item.shape,
          toolText: item.toolText,
          // 节点样式
          style: item.color ? item.color : "fill:#c0c1c3;stroke:transparent",// 根据后台数据来改变节点颜色
          labelStyle: "fill:#fff;",
          width: 83,
          height: 40
        });
      });
      this.dataSet.edges.forEach((item) => {
        g.setEdge(item.source, item.target, {
          // 边标签
          label: item.label,
          arrowheadStyle: item.color ? item.color : "fill:#c0c1c3;", // 根据后台数据来改变连线箭头的颜色
          // 边样式
          style: item.color
            ? item.color
            : "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px" // 根据后台数据来改变连线的颜色
        });
      });
      // 创建渲染器
      const render = new dagreD3.render();
      // 选择svg并添加一个g元素作为绘图容器
      const svgGroup = d3.select('svg').append("g");
      // 建立拖拽缩放
      const svg = d3.select("svg");
      const zoom = d3.zoom().on("zoom", function () {
      svgGroup.attr("transform", d3.event.transform);
      });
      svg.call(zoom);

      // 在绘图容器上运行渲染器生成流程图
      render(svgGroup, g);

      // 鼠标悬停显示隐藏tooptip
      svgGroup
        .selectAll("g.node")
        .on("mouseover", (v) => {
        // 假如当前toolText为"",则不展示
          if (g.node(v).toolText === "") {
            return;
          }
          this.tipVisible(g.node(v).toolText);
        })
        .on("mouseout", () => {
          this.tipHidden();
        });
    },

哦,对了要想实现上面的鼠标悬停的tooltip,还需要下面的js和css

  • js代码
// 创建提示框
    createTooltip () {
      return d3
        .select("body")
        .append("div")
        .classed("tooltip", true)
        .style("opacity", 0)
        .style("display", "none");
    },
    // tooltip显示
    tipVisible (textContent) {
      this.tooltip
        .transition()
        .duration(400)
        .style("opacity", 0.9)
        .style("display", "block");
      this.tooltip
        .html(textContent)
        .style("left", `${d3.event.pageX + 15}px`)
        .style("top", `${d3.event.pageY + 15}px`);
    },

    // tooltip隐藏
    tipHidden () {
      this.tooltip
        .transition()
        .duration(400)
        .style("opacity", 0)
        .style("display", "none");
    }
  • css 代码
.tooltip {
     position: absolute;
     font-size: 12px;
     text-align: center;
     background-color: white;
     border-radius: 3px;
     box-shadow: rgb(174, 174, 174) 0px 0px 10px;
     cursor: pointer;
     display: inline-block;
     padding:10px;
 }

.tooltip>div {
     padding: 10px;
 }

以上就能实现一个结合后台数据动态渲染的流程图,在绘制流程图的时候,也遇到一些问题:

  • 1、要根据不同的状态,展示不同颜色的节点、连线。
    解决:本来绘制流程图在mouted函数中绘制就可出现,但是现在要实时根据数据来改变,所以要先获取到后台数据,然后再渲染流程图,而且获取后台数据要在mouted中就要获取到(之前在mouted绘制流程要去除,不然后续页面会出现两个流程图),不然一开始加载就来不显示。

  • 2、tooltip 显示不出来
    这个问题真的找了很久,一开始以为这个tooltip没有没有在页面上加载出来,但是当触发这个事件的时候,页面上的滚动条是有变化的,所以tooltip是已经绘制出来的,但是不知为何显示不出来。后来才找到原因,是因为我在流程图这个组件的样式标签上加上了scoped,从而影响了样式。

  • 3、在iview 模态框组件中使用组件,节点连线渲染出错
    这个问题解决可以看我这条博客使用iview的Modal组件中包含子组件,子组件加载出现问题

总结

以上就是我在项目中的例子,有关一些配置及相关的解释,大家可以参考这篇文章d3绘制流程图

Logo

前往低代码交流专区

更多推荐