antd/x6-graph——实现流程图绘制功能——技能提升

效果图:

解决步骤1:安装"@antv/x6": "^1.35.0"

js 复制代码
npm install @antv/x6@1.35.0

安装指定版本的antv/x6插件

解决步骤2:配置tools文件

assets/js中新增一个graphTools.js文件

内容如下:

js 复制代码
/* 
antv x6图谱相关工具函数
*/
export default {
  /* 
  初始化初始节点(开始,结束节点)
  x:x轴坐标
  y:y轴坐标
  id:开始节点id
  name:节点内容,默认为空
  type:节点类型,默认为空
  */
  gongxuNode(x, y, id, name, type) {
    let node = {
      shape: 'rect',
      type: type,
      id: id, // String,可选,节点的唯一标识
      x: x, // Number,必选,节点位置的 x 值
      y: y, // Number,必选,节点位置的 y 值
      width: 130, // Number,可选,节点大小的 width 值
      height: 30, // Number,可选,节点大小的 height 值
      label: "功能模块",
      attrs: {
        // 这里给生成的节点的body加上透明的边框,一定要给边框宽度加上>0的值,否则节点将不能连线
        body: {
          strokeWidth: 3, // 边框的粗细
          magnet: true, // 节点是否可以连线
        }
      },
      extraProperties: {
        IsOutSend: false,
        IsSkip: false,
        TimeSpan: 0
      },
      // html: `
      //       <div class="custom_node_initial">
      //         <div>
      //           <p title=${name}>${name||''}</p>
      //         </div>
      //       </div>
      //       `,
      // attrs: {
      //   // 这里给生成的节点的body加上透明的边框,一定要给边框宽度加上>0的值,否则节点将不能连线
      //   body: {
      //     stroke: 'transparent',
      //     strokeWidth: 3, // 边框的粗细
      //     magnet: true, // 节点是否可以连线
      //   }
      // },
    }
    return node
  },

  /* 
  初始化逻辑节点
  x:x轴坐标
  y:y轴坐标
  id:开始节点id
  name:节点内容,默认为空
  type:节点类型,默认为空
  */
  tiaojianNode(x, y, id, name, type) {
    let node = {
      shape: 'rect',
      type: type, // 动作所属类型
      id: id, // String,可选,节点的唯一标识
      x: x, // Number,必选,节点位置的 x 值
      y: y, // Number,必选,节点位置的 y 值
      width: 190, // Number,可选,节点大小的 width 值
      height: 30, // Number,可选,节点大小的 height 值
      label: "条件模块",
      attrs: {
        // 这里给生成的节点的body加上透明的边框,一定要给边框宽度加上>0的值,否则节点将不能连线
        body: {
          stroke: '#f90',
          fill: 'rgba(255, 172, 50, 0.2)',
          strokeWidth: 3, // 边框的粗细
          magnet: true, // 节点是否可以连线
        }
      },
      extraProperties: {
        IsOutSend: false,
        IsSkip: false,
        TimeSpan: 0
      },
      // html: `
      //         <div class="custom_node_logic">
      //           <div>
      //             <p title=${name}>${name||''}</p>
      //           </div>
      //         </div>
      //       `,
      // attrs: {
      //   body: {
      //     stroke: 'transparent',
      //     strokeWidth: 3,
      //     magnet: true,
      //   }
      // },
    }
    return node
  }
}

解决步骤3:页面引入及dom节点

js 复制代码
import { Graph } from "@antv/x6";
import Tools from "@/assets/js/graphTools.js";

页面初始化:

js 复制代码
 <!-- 画布部分 -->
<div class="canvas-card">
  <div id="container" @dragover="dragoverDiv"></div>
</div>

解决步骤4:data配置字段

js 复制代码
model: {
        // 节点
        nodes: [
          {
            id: "start", // String,可选,节点的唯一标识
            x: 10, // Number,必选,节点位置的 x 值
            y: 10, // Number,必选,节点位置的 y 值
            width: 130, // Number,可选,节点大小的 width 值
            height: 30, // Number,可选,节点大小的 height 值
            label: "开始",
            type: "0", // 开始类型
            extraProperties: {
              IsOutSend: false,
              IsSkip: false,
              TimeSpan: 0
            },
            attrs: {
              // 这里给生成的节点的body加上透明的边框,一定要给边框宽度加上>0的值,否则节点将不能连线
              body: {
                strokeWidth: 3, // 边框的粗细
                magnet: true // 节点是否可以连线
              }
            }
          },

          {
            id: "end", // String,可选,节点的唯一标识
            x: 1220, // Number,必选,节点位置的 x 值
            y: 600, // Number,必选,节点位置的 y 值
            width: 130, // Number,可选,节点大小的 width 值
            height: 30, // Number,可选,节点大小的 height 值
            label: "结束",
            type: "100", // 动作所属类型
            extraProperties: {
              IsOutSend: false
            },
            attrs: {
              // 这里给生成的节点的body加上透明的边框,一定要给边框宽度加上>0的值,否则节点将不能连线
              body: {
                strokeWidth: 3, // 边框的粗细
                magnet: true // 节点是否可以连线
              }
            }
          }
        ],
        // 边
        edges: [
          // {
          //   source: "node1", // String,必须,起始节点 id
          //   target: "node2", // String,必须,目标节点 id
          //   extraProperties: {
          //     IsOutSend: 11
          //   }
          // },
          // {
          //   source: "node1", // String,必须,起始节点 id
          //   target: "node3", // String,必须,目标节点 id
          //   extraProperties: {
          //     IsOutSend: 2222222222222222222
          //   } // String,必须,目标节点 id
          // }
        ]
      },

解决步骤5:初始化画布

js 复制代码
// 初始化流程图画布
    initGraph() {
      let container = document.getElementById("container");
      this.graph = new Graph({
        container: container, // 画布容器
        width: container.offsetWidth, // 画布宽
        height: container.offsetHeight, // 画布高
        background: false, // 背景(透明)
        snapline: true, // 对齐线
        // 配置连线规则
        connecting: {
          snap: true, // 自动吸附
          allowBlank: false, //是否允许连接到画布空白位置的点
          allowMulti: false, //是否允许在相同的起始节点和终止之间创建多条边
          allowLoop: false, //是否允许创建循环连线,即边的起始节点和终止节点为同一节点
          highlight: true, //拖动边时,是否高亮显示所有可用的节点
          validateEdge({ edge, type, previous }) {
            // 连线时设置折线
            edge.setRouter({
              name: "er"
            });
            // 设置连线样式
            // edge.setAttrs({
            //   line: {
            //     stroke: "#275da3",
            //     strokeWidth: 4
            //   }
            // });
            return true;
          }
        },
        panning: {
          enabled: true
        },
        mousewheel: {
          enabled: true // 支持滚动放大缩小
        },
        grid: {
          type: "mesh",
          size: 10, // 网格大小 10px
          visible: true, // 渲染网格背景
          args: {
            color: "#eeeeee", // 网格线/点颜色
            thickness: 2 // 网格线宽度/网格点大小
          }
        }
      });
      //有ID说明是编辑
      if (this.$route.query.id) {
        plantflowSet_get(this.$route.query.id).then(res => {
          this.name = res.data.name;
          this.remark = res.data.remark;
          var nodesArr = []; //点
          var edgesArr = []; //线
          res.data.elements.map(item => {
            nodesArr.push({
              id: item.code, // String,节点的唯一标识
              x: item.xCoordinate, // Number,必选,节点位置的 x 值
              y: item.yCoordinate, // Number,必选,节点位置的 y 值
              width: item.width, // Number,可选,节点大小的 width 值
              height: item.height, // Number,可选,节点大小的 height 值
              label: item.name,
              extraProperties: {
                ...item.extraProperties
              },
              type: item.type, // 条件类型
              attrs: {
                // 这里给生成的节点的body加上透明的边框,一定要给边框宽度加上>0的值,否则节点将不能连线
                body: {
                  fill:
                    item.type == 20
                      ? "rgba(255, 172, 50, 0.2)"
                      : "rgba(255, 172, 50, 0)",
                  stroke: item.type == 20 ? "#f90" : "#000",
                  strokeWidth: 3, // 边框的粗细
                  magnet: true // 节点是否可以连线
                }
              }
            });
          });

          res.data.connections.map(item => {
            edgesArr.push({
              source: item.startElementCode, // String,必须,起始节点 id
              target: item.endElementCode, // String,必须,目标节点 id
              ...item
              // extraProperties: {
              //   IsOutSend: 11
              // }
            });
          });
          this.model.nodes = nodesArr;
          this.model.edges = edgesArr;
          this.graph.fromJSON(this.model);
          this.nodeAddEvent();
        });
      } else {
        this.graph.fromJSON(this.model);
        this.nodeAddEvent();
      }
    },

解决步骤6:生成节点

js 复制代码
// 生成节点函数  0--开始节点, 10--工序节点, 20--条件节点, 100--结束节点
addHandleNode(x, y, id, name, type) {
  type == "20"
    ? this.graph.addNode(Tools.tiaojianNode(x, y, id, name, type))
    : this.graph.addNode(Tools.gongxuNode(x, y, id, name, type));
},

解决步骤7:节点点击

js 复制代码
//节点事件
    nodeAddEvent() {
      // 节点绑定点击事件
      this.graph.on("node:click", ({ e, x, y, node, view }) => {
        if(node.label === '开始'||node.label === '结束'){
          return false
        }
        // 判断是否有选中过节点
        if (this.curSelectNode) {
          // 移除选中状态
          this.curSelectNode.removeTools();
          // 判断两次选中节点是否相同
          if (this.curSelectNode !== node) {
            node.addTools([
              {
                name: "boundary",
                args: {
                  attrs: {
                    fill: "#16B8AA",
                    stroke: "#2F80EB",
                    strokeWidth: 1,
                    fillOpacity: 0.1
                  }
                }
              },
              {
                name: "button-remove",
                args: {
                  x: "100%",
                  y: 0,
                  offset: {
                    x: 0,
                    y: 0
                  }
                }
              }
            ]);
            this.curSelectNode = node;
          } else {
            this.curSelectNode = null;
          }
        } else {
          this.curSelectNode = node;
          node.addTools([
            {
              name: "boundary",
              args: {
                attrs: {
                  fill: "#16B8AA",
                  stroke: "#2F80EB",
                  strokeWidth: 1,
                  fillOpacity: 0.1
                }
              }
            },
            {
              name: "button-remove",
              args: {
                x: "100%",
                y: 0,
                offset: {
                  x: 0,
                  y: 0
                }
              }
            }
          ]);
        }

        let isNewNode = true;
        if (this.curSelectNode && this.curSelectNode.id) {
          this.graph.toJSON().cells.map(item => {
            if (item.id == this.curSelectNode.id) {
              this.editNode = item.label;
              this.nodeDataForm = item.extraProperties;
              this.formData1 = {
                ...this.nodeDataForm
              };

              this.formData1.StepKey = item.label;
              isNewNode = false;
              //点击的节点类型
              this.clickType = item.type;
              //点击条件
              if (item.type == "20") {
                this.$refs.tiaojianModalRef.openModules(item);
              }
            }
          });
          this.editNodeId = this.curSelectNode.id;
          // 如果点击是新节点
          if (isNewNode) {
            this.editNode = node.label;
            this.formData1.StepKey = node.label;
            this.nodeDataForm = node.extraProperties;
            //点击的节点类型
            this.clickType = node.type;
          }
        }
      });
      // 连线绑定悬浮事件
      this.graph.on("cell:mouseenter", ({ cell }) => {
        if (cell.shape == "edge") {
          cell.addTools([
            {
              name: "button-remove",
              label: "999999",
              args: {
                x: "100%",
                y: 0,
                offset: {
                  x: 0,
                  y: 0
                }
              }
            }
          ]);
          cell.setAttrs({
            line: {
              stroke: "#409EFF"
            }
          });
          cell.zIndex = 99;
        }
      });
      //点击连接线
      this.graph.on("cell:click", ({ cell }) => {
        var newArredge = [];

        this.graph.toJSON().cells.map(item => {
          if (item.shape == "edge") {
            newArredge.push(item);
          }
        });
        if (cell.shape == "edge") {
          this.xianVisible = true;
          //遍历获取点击的线
          newArredge.map(item => {
            if (
              (item.source == cell.source.cell &&
                item.target == cell.target.cell) ||
              (item.source.cell == this.itemSource &&
                item.target.cell == this.itemTarget) ||
              (item.source.cell == cell.source.cell &&
                item.target.cell == cell.target.cell)
            ) {
              this.itemSource = cell.source.cell; //点击线的起止
              this.itemTarget = cell.target.cell; //点击线的起止
              if (item.conditionExpression!=undefined && item.conditionExpression == "false"){
                this.clickTypeCheck = false;
              } else {
                this.clickTypeCheck = true;
              }
            }
          });

          // this.clickTypeCheck=true;
        }
      });
      this.graph.on("cell:mouseleave", ({ cell }) => {
        if (cell.shape === "edge") {
          cell.removeTools();
          cell.setAttrs({
            line: {
              stroke: "#275da3"
            }
          });
          cell.zIndex = 1;
        }
      });
    },

解决步骤8:线条编辑

js 复制代码
//线条件编辑
    xianhandleOk() {
      var newArr = [];
      var newArredge = [];

      this.graph.toJSON().cells.map(item => {
        if (item.shape == "edge") {
          newArredge.push(item);
        }
      });

      newArredge.map(item => {
        if (
          (item.source == this.itemSource && item.target == this.itemTarget) ||
          (item.source.cell == this.itemSource &&
            item.target.cell == this.itemTarget)
        ) {
          item.conditionExpression = this.clickTypeCheck+"";

        }
        newArr.push(item);
      });

      this.setData();
      this.model.edges = newArr;

      this.xianVisible = false;
      this.graph.fromJSON(this.model);
    },

解决步骤9:新增模块

js 复制代码
//新增工序模块
addGXMokuai() {
  this.addHandleNode(500, 200, new Date().getTime(), "工序模块", "10");
},
//新增条件模块
addTJMokuai() {
  this.addHandleNode(500, 100, new Date().getTime(), "条件模块", "20");
},

解决步骤10:拖动节点+保存等

js 复制代码
// 拖动节点到画布中鼠标样式变为可拖动状态
    dragoverDiv(ev) {
      ev.preventDefault();
    },
    //单节点保存 不掉接口
    setData() {
      let mapArr = this.graph.toJSON().cells;
      const newNodesModel = [];
      const newEdgesModel = [];
      mapArr.map(item => {
        if (item.shape == "rect") {
          newNodesModel.push({
            id: item.id,
            x: item.position.x,
            y: item.position.y,
            type: item.type,
            width: item.size.width,
            height: item.size.height,
            label:
              item.id == this.editNodeId
                ? this.formData1.StepKey
                : item.attrs.text.text,
            extraProperties:
              this.editNodeId == item.id
                ? { ...this.formData1 }
                : { ...item.extraProperties },
            attrs: item.attrs
          });
        } else {
          newEdgesModel.push({
            source: item.source,
            target: item.target,
            ...item
          });
        }
      });
      this.model.nodes = newNodesModel;
      this.model.edges = newEdgesModel;
      this.graph.fromJSON(this.model);
    },
    //条件保存
    user_success(data) {
      let mapArr = this.graph.toJSON().cells;
      const newNodesModel = [];
      const newEdgesModel = [];
      mapArr.map(item => {
        if (item.shape == "rect") {
          if (item.id != data.id) {
            newNodesModel.push({
              id: item.id,
              x: item.position.x,
              y: item.position.y,
              type: item.type,
              width: item.size.width,
              height: item.size.height,
              label:
                item.id == this.editNodeId
                  ? this.formData1.StepKey
                  : item.attrs.text.text,
              extraProperties:
                this.editNodeId == item.id
                  ? { ...this.formData1 }
                  : { ...item.extraProperties },
              attrs: item.attrs
            });
          } else {
            newNodesModel.push(data);
          }
        } else {
          newEdgesModel.push({
            source: item.source,
            target: item.target,
            ...item
          });
        }
      });
      this.model.nodes = newNodesModel;
      this.model.edges = newEdgesModel;
      this.graph.fromJSON(this.model);
    },
    //整体提交调用接口
    setDataOk() {
      if (this.name == "" || this.remark == "") {
        return this.$message.error("请先填写名称/备注");
      }

      const params = {
        factoryid: "d4882b18-47b0-ca1f-4ad3-3a10cc22976a",
        name: this.name,
        expression: "string",
        sort: 0,
        remark: this.remark,
        elements: [
          // {
          //   code: "string",
          //   type: "BeginNode",
          //   name: "string",
          //   xCoordinate: 0,
          //   yCoordinate: 0,
          //   width: 0,
          //   height: 0,
          //   textContent: "string",
          //   expression: "string",
          //   parameters: [
          //     {
          //       name: "string",
          //       expressionType: "Constant",
          //       expression: "string",
          //       enabled: true,
          //       sort: 0
          //     }
          //   ]
          // }
        ],
        connections: [
          // {
          //   startElementCode: "string",
          //   endElementCode: "string",
          //   connectionType: 0,
          //   conditionExpression: "string"
          // }
        ]
      };

      this.graph.toJSON().cells.map(item => {
        //节点
        if (item.shape == "rect") {
          params.elements.push({
            code: item.id.toString(),
            type: item.type,
            name: item.attrs.text.text,
            xCoordinate: item.position.x,
            yCoordinate: item.position.y,
            width: item.size.width,
            height: item.size.height,
            textContent: "string",
            expression: item.expression,
            extraProperties: item.extraProperties,
            parameters: [
              {
                name: "string",
                expressionType: "Constant",
                expression: "string",
                enabled: true,
                sort: 0
              }
            ]
          });
        } else {
          //连线
          params.connections.push({
            startElementCode: item.source.cell.toString(),
            endElementCode: item.target.cell.toString(),
            extraProperties: item.extraProperties,
            // connectionType: 0,
            conditionExpression: item.conditionExpression?item.conditionExpression:"true",
            ...item
          });
        }
      });

      if (this.$route.query.id) {
        params.id = this.$route.query.id;
        plantflowSet_edit(params).then(res => {
          if (res.success == true) {
            this.$message.success(res.message);
          } else {
            this.$message.error(res.message);
          }
        });
      } else {
        plantflowSet_add(params).then(res => {
          if (res.success == true) {
            this.$message.success(res.message);
          } else {
            this.$message.error(res.message);
          }
        });
      }
    },
    UpdateData() {
      this.$forceUpdate();
    }
相关推荐
上官花雨8 分钟前
什么是axios?怎么使用axios封装Ajax?
前端·ajax·okhttp
米奇妙妙wuu10 分钟前
React中 setState 是同步的还是异步的?调和阶段 setState 干了什么?
前端·javascript·react.js
李刚大人12 分钟前
react-amap海量点优化
前端·react.js·前端框架
闹闹没有闹29 分钟前
socket连接封装
前端
qq_364371722 小时前
Vue 内置组件 keep-alive 中 LRU 缓存淘汰策略和实现
前端·vue.js·缓存
y先森2 小时前
CSS3中的弹性布局之侧轴的对齐方式
前端·css·css3
new出一个对象6 小时前
uniapp接入BMapGL百度地图
javascript·百度·uni-app
你挚爱的强哥7 小时前
✅✅✅【Vue.js】sd.js基于jQuery Ajax最新原生完整版for凯哥API版本
javascript·vue.js·jquery
y先森7 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy7 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html