欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

vue中使用gojs/jointjs的示例代码

程序员文章站 2022-10-14 21:37:16
因为公司项目需求,要画出相关业务的流程图,以便客户了解自己身处何处 搜索框输入 “前端流程图插件”,查了很多资料,总结一下有以下几种 flow-chart 代码写法繁...

因为公司项目需求,要画出相关业务的流程图,以便客户了解自己身处何处

搜索框输入 “前端流程图插件”,查了很多资料,总结一下有以下几种

flow-chart

代码写法繁琐,不是json就可以解决,效果也比较丑,pass

darge-d3

github :

效果图

vue中使用gojs/jointjs的示例代码

下载里面的demo,改一下json就可以了

// states 
var states = [ "new", "submitted","finished" ,"failed","deliver", 
    "canceled", "abolished" , "deleted","refunding","refunded"];
var map = ['新创建','已提交','已完成','提交失败',"交付中",
    '已取消','废除','已删除','退款中',"已退款"]
// automatically label each of the nodes
states.foreach(function(state,index) { g.setnode(state, { label: `${map[index]}(${state})`})});

// set up the edges
g.setedge("new", "failed",  { label: "后台接口自动"});
g.setedge("new", "submitted", { label: "后台接口自动" });
g.setedge("new", "canceled", { label: "用户取消订单" });
g.setedge("submitted","canceled",  { label: "用户取消订单" });
g.setedge("submitted", "abolished", { label: "用户超过48小时未支付,\n系统自动取消"});
g.setedge("abolished","deleted",  { label: "已删除" });
g.setedge("canceled", "deleted", { label: "已删除"});
g.setedge("failed", "submitted",  { label: "后台接口自动" });

g.setedge("submitted", "deliver",  { label: "用户支付" });
g.setedge("finished", "refunding",  { label: "用户退款" });

g.setedge("deliver", "finished",  { label: "交付完成" });
g.setedge("refunding", "refunded",  { label: "已退款" });
g.setedge("refunded", "deleted",  { label: "已删除" });
g.setedge("deliver", "refunding",  { label: "用户退款" });
g.setedge("failed", "canceled",  { label: "用户取消订单" });

不满意的地方:画出来的图是垂直方向的,我要的是水平方向,pass

gojs

gojs是一个实现交互类图表(比如流程图,树图,关系图,力导图等等)的js库。本文将介绍gojs的精华部分。
因为gojs依赖于html5,所以请保证您的浏览器版本支持html5,当然还要加载这个库。

github :https://github.com/northwoodssoftware/gojs

可以通过npm install gojs -save安装

效果图

vue中使用gojs/jointjs的示例代码

看里面的demo我自己包装了一下

<template>
<div>
 <p style="background-color:#d5d5d5;margin:0;padding:5px;">
 您当前处于 <span class="tip">用户提交资料</span> 步骤 
 下一步等待<span class="tip">供应商接单</span>
 <el-button type="text" v-if="show===false" @click="show=true">展开</el-button>
 <el-button type="text" v-else @click="show=false">收起</el-button>
 
 </p>
 <div id="mydiagramdiv" v-show="show" ></div>
 </div> 
 
</template>
<style scoped>
.tip{
 color:red;
 font-size:0.8em;
 font-weight:bold;
 padding:5px;
}
#mydiagramdiv{
 height: 200px; 
 border: solid 1px #d3d3d3;
}

</style>
<script>
window.go =require('./go.js') 
var $ = go.graphobject.make;

import datam from './data';
export default{
 mixins:[datam],
 data(){
 return{
  mydiagram:null,
  show:true
 }
 },
 mounted(){
 this.load();
 },
 methods:{
 load(){
  this.init();
  this.addnodetemplate(this.user);
  this.addnodetemplate(this.supplier);
  this.layout();
 },
 layout() {
  this.mydiagram.model = go.model.fromjson(this.myjson);
  this.mydiagram.layoutdiagram(true);
 },

 getoption(){
  // for conciseness in defining templates

  let options={
  yellowgrad : $(go.brush, "linear", { 0: "rgb(254, 201, 0)", 1: "rgb(254, 162, 0)" }),
  greengrad : $(go.brush, "linear", { 0: "#98fb98", 1: "#9acd32" }),
  bluegrad : $(go.brush, "linear", { 0: "#b0e0e6", 1: "#87ceeb" }),
  redgrad : $(go.brush, "linear", { 0: "#c45245", 1: "#871e1b" }),
  whitegrad : $(go.brush, "linear", { 0: "#f0f8ff", 1: "#e6e6fa" }),
  bigfont : "bold 8pt helvetica, arial, sans-serif",
  smallfont : "bold 6pt helvetica, arial, sans-serif",
  
  }

  return options;
 },

  textstyle(){
  return {
   margin: 6,
   wrap: go.textblock.wrapfit,
   textalign: "center",
   editable: true,
   font: this.getoption()['bigfont']
  }
  },
  init(){
  this.mydiagram =
   $(go.diagram, "mydiagramdiv",
    {
    isreadonly: true,
    // have mouse wheel events zoom in and out instead of scroll up and down
    "toolmanager.mousewheelbehavior": go.toolmanager.wheelnone,
    initialautoscale: go.diagram.uniform,
    "linkingtool.direction": go.linkingtool.forwardsonly,
    initialcontentalignment: go.spot.center,
    layout: $(go.layereddigraphlayout, { isinitial: false, isongoing: false, layerspacing: 50 }),
    "undomanager.isenabled": true
    });
    //默认节点模板
  this.mydiagram.nodetemplate =
   $(go.node, "auto",
    new go.binding("location", "loc", go.point.parse).maketwoway(go.point.stringify),
    // define the node's outer shape, which will surround the textblock
    $(go.shape, "rectangle",
    { fill: this.getoption()['yellowgrad'], stroke: "black",
     portid: "", fromlinkable: true, tolinkable: true, cursor: "pointer",
     toendsegmentlength: 50, fromendsegmentlength: 40 }),
    $(go.textblock, "page",
    { margin: 6,
     font: this.getoption()['bigfont'],
     editable: true },
    new go.binding("text", "text").maketwoway()));
   // replace the default link template in the linktemplatemap
   this.mydiagram.linktemplate =
    $(go.link, // the whole link panel
     new go.binding("points").maketwoway(),
     { curve: go.link.bezier, toshortlength: 15 },
     new go.binding("curviness", "curviness"),
     $(go.shape, // the link shape
     { stroke: "#2f4f4f", strokewidth: 2.5 }),
     $(go.shape, // the arrowhead
     { toarrow: "kite", fill: "#2f4f4f", stroke: null, scale: 2 })
     );
  },
  /**
  * options:{
  * category
  * shape:roundedrectangle/rectangle
  * shapeoptions:{
  * fill:bluegrad/greengrad/yellowgrad/null/redgrad/whitegrad 自定义的
  * stroke: "black",
  * portid:""
  * fromlinkable:true
  * tolinkable:
  * cursor:"pointer"
  * fromendsegmentlength:40
  * toendsegmentlength
  * strokewidth
  * 
  * }
  * textstyle:{
  *  margin: 9,
  *  maxsize: new go.size(200, nan),
  *  wrap: go.textblock.wrapfit,
  *  editable: true,
  *  textalign: "center",
  *  font: smallfont 
  * },
  * 
  * }
  */
  addnodetemplate(options){
  let fill = this.getoption()[options.shapeoptions.fill];
  options.shapeoptions.fill = fill;
  this.mydiagram.nodetemplatemap.add(options.category,
   $(go.node, "auto",
   new go.binding("location", "loc", go.point.parse).maketwoway(go.point.stringify),
   $(go.shape, options.shape,options.shapeoptions),
   $(go.textblock, this.textstyle(),
    new go.binding("text", "text").maketwoway())
   ));
  },

 }


}
</script>

不满意的地方:

  1. 免费版gojs是有水印的,百度搜索“gojs如何去水印”有一堆答案,我就不写了。
  2. 因为要自己手动去掉水印,所以我只能手动下载go.js放在我自己的组件目录下,但是这个文件太大了,800+kb,npm run dev 的时候停在这里停了好久。有时候还爆出“......maximun ....500kb”的错误,我也不知道是什么原因,不知道有什么方法,有的话麻烦通知我。
  3. 代码写法有点太繁琐

这是我自己包装的代码地址:https://github.com/lry1994/vue-lib/tree/master/src/components/process-go

jointjs

相比dagre-d3和jsplumb,jointjs的api很详细,代码量少,连接线有多种选择,封装了多种常用的形状,而且能画的图很多,官方也给了一些demo可以参考。

github : https://github.com/clientio/joint

效果图

vue中使用gojs/jointjs的示例代码

可以通过npm install jointjs -save安装

参照了很多demo和文档,用的是矩形,但是可以设置圆角的度数变成椭圆形,其他形状我就无力了。

可以自定义矩形的样式和矩形框里面的文字样式

//data.vue
<script>
export default {
 data(){
  var userclass = {//这个要参照svg属性
   /**shapestyle
    * fill:填充的背景颜色
    stroke: 边框颜色
    strokewidth: 边框宽度
    rx: 圆角
    ry: 
    */
   shapestyle:{//矩形样式
    fill:{
     type: 'lineargradient',
     stops: [
      {offset: '0%', color: '#98fb98'},
      {offset: '100%', color: '#9acd32'}
     ],
    },
    rx:150,
    ry:15
   },
   /**
    * textstyle
    * fontweight
    * fontsize
    * 
    */
   textstyle:{//文本样式
    fontweight:'bold'
   } 
  };
  return{
   graphdata :{
    node:{
     '100':{text:'用户提交资料',category:userclass},
     '101':{text:'用户完善资料',category:userclass},
     '102':{text:'用户确认完成',category:userclass},
     '103':{text:'用户撤销',category:userclass},

     '200':{text:'供应商驳回'},
     '201':{text:'供应商接单'},
     '202':{text:'供应商完工'},
     '203':{text:'等待供应商处理'},

     '300':{text:'系统交付出错'}    
    },
    edge :{//每个点都要写
     '100': ['200','201','103'], 
     '101': ['201'],
     '102':[],
     '103': ['100'],

     '200': ['101'],    
     '201': ['202','300'],
     '202': ['102'],    
     '203': ['102'], 

     '300': ['203'],
    
    },
   }
  }
 }
}
</script>
<template>
<div id="container">
 <p style="background-color:#eeeeee;margin:0;padding:5px;font-size:0.9em">
 您当前处于 <span class="tip">用户提交资料</span> 步骤 
 下一步等待<span class="tip">供应商接单</span>
 <el-button type="text" v-if="show===false" @click="show=true">展开</el-button>
 <el-button type="text" v-else @click="show=false">收起</el-button>
 
 </p>
 <div id="myholder" v-show="show"></div>
</div>
</template>
<script>
window.joint=require('jointjs');
var shape = joint.dia.element.define('default.rectangle', {  
  attrs: { 
   rect: {
    refwidth: '100%',
    refheight: '100%',
    //下面这些可以自己设置
    fill:{
     type: 'lineargradient',
     stops: [
      {offset: '0%', color: '#b0e0e6'},//渐变开始
      {offset: '100%', color: '#f0f8ff'}//渐变结束
     ]
    },
    stroke: '#b0e0e6',
    strokewidth: 1,
    rx: 5,//圆角
    ry: 5
   },
   text: {
    refx: '50%',
    refy: '50%',        
    textverticalanchor: 'middle',
    textanchor: 'middle',
    fontsize: 10   
   }
   }     
 }, 
 {
   markup: '<rect/><text/>',
   settext: function(text) {     
   return this.attr('text/text', text || '');
  },
  setshapestyle:function(shapestyle){
   let newstyle = object.assign({},this.attr('rect'),shapestyle);
   return this.attr('rect',newstyle)
  },
  
  settextstyle:function(textstyle){
   let newstyle = object.assign({},this.attr('text'),textstyle);
   return this.attr('text',newstyle)
  }
 }
);

var link = joint.dia.link.define('default.link', {
  attrs: {
   '.connection': {
    stroke: '#2f4f4f',//线
    strokewidth: 1,
    pointerevents: 'none',
    targetmarker: {//箭头
     type: 'path',
     fill: '#2f4f4f',//填充颜色
     stroke: '#2f4f4f',//边框颜色
     strokewidth:'1',
     d: 'm 2 -2 0 0 2 2 z'//形状
    }
   }
  },
  connector: {
   name: 'rounded'
  },
  z: -1,
  weight: 1,
  minlen: 1,
  labelposition: 'c',
  labeloffset: 10,
  labelsize: {
   width: 50,
   height: 30
  },
  labels: [{
   markup: '<rect/><text/>',
   attrs: {
    text: {
     fill: 'gray',
     textanchor: 'middle',
     refy: 5,
     refy2: '-50%',
     fontsize: 10,
     cursor: 'pointer'
    },
    // rect: {
    //  fill: 'lightgray',
    //  stroke: 'gray',
    //  strokewidth: 2,
    //  refwidth: '100%',
    //  refheight: '100%',
    //  refx: '-50%',
    //  refy: '-50%',
    //  rx: 5,
    //  ry: 5
    // }
   },
   size: {
    width: 50, height: 10
   }
  }]

 }, {
  markup: '<path class="connection"/><g class="labels"/>',
  
  connect: function(sourceid, targetid) {
   return this.set({
    source: { id: sourceid },
    target: { id: targetid }
   });
  },

  setlabeltext: function(text) {
   return this.prop('labels/0/attrs/text/text', text || '');
  }
 });



var elementview = joint.dia.elementview.extend({
  pointerdown: function () {

   // this._click = true;
   // joint.dia.elementview.prototype.pointerdown.apply(this, arguments);
  },
  pointermove: function(evt, x, y) {
   // this._click = false;
   // joint.dia.elementview.prototype.pointermove.apply(this, arguments);
  },
  pointerup: function (evt, x, y) {
   // if (this._click) {
   //  // triggers an event on the paper and the element itself
   //  this.notify('cell:click', evt, x, y); 
   // } else {
   //  joint.dia.elementview.prototype.pointerup.apply(this, arguments);
   // }
  }
 });
var linkview = joint.dia.linkview.extend({
 addvertex: function(evt, x, y) {},
 removevertex: function(endtype) {},
 pointerdown:function(evt, x, y) {}
});


export default { 
 data(){
  return{
   graph:null,
   paper:null,
   show:true
  }  
 },
 props:{
  graphdata:{
   type:object,
   required:true
  }
 },
 mounted(){
  let w = document.getelementbyid('container').width ; 
  this.graph = new joint.dia.graph;
  this.paper = new joint.dia.paper({
   el: document.getelementbyid('myholder'),
   width: w,
   height: 250,   
   model: this.graph,
   elementview: elementview,//禁止拖拽
   linkview:linkview//禁止拖拽
  });
  this.layout();
 },
 methods:{
  getwidthandheight(label){
   let maxlinelength = _.max(label.split('\n'), function(l) { return l.length; }).length,

   // compute width/height of the rectangle based on the number
   // of lines in the label and the letter size. 0.6 * lettersize is
   // an approximation of the monospace font letter width.
    lettersize = 8,
    width = 2 * (lettersize * (0.6 * maxlinelength + 1)),
    height = 2 * ((label.split('\n').length + 1) * lettersize);
   return {width,height}
  },
  getlayoutoptions() {
   return {
    // setvertices: false,
    // setlabels: false,
    // ranker:'longer-path',//'tight-tree'/'network-simplex',
    rankdir: 'lr',
    align: 'ur',
    ranksep:0,
    edgesep:0,
    nodesep:0,
   };
  },
  buildgraphfromadjacencylist(adjacencylist) {
   let elements = [],links = [],obj,size,node;
   const _this=this;
   const map=this.graphdata.node;

   object.keys(adjacencylist).foreach(function(parentid) {
    // add element

    obj =map[parentid];
    size = _this.getwidthandheight(obj.text);
    node =new shape({id:parentid,size:size}).settext(obj.text);

    if(obj.category&&obj.category.shapestyle){
     node = node.setshapestyle(obj.category.shapestyle);
    }
    if(obj.category&&obj.category.textstyle){
     node = node.settextstyle(obj.category.textstyle);
    }

    elements.push(node);
    
    // add links
    adjacencylist[parentid].foreach(function(childid) {
     links.push(
      new link().connect(parentid, childid)// .setlabeltext(parentlabel + '-' + childlabel)           
     );
    });
   });

   return elements.concat(links);
  },
  layout() {   
   let cells = this.buildgraphfromadjacencylist(this.graphdata.edge); 
   this.graph.resetcells(cells);
   joint.layout.directedgraph.layout(this.graph, this.getlayoutoptions());
  },
 }
}
</script>

<style>
#myholder {
 border: 1px solid lightgray;
 margin-bottom:20px;
 padding-left:20px
}
.tip{
 color:#9acd32;
 font-size:0.9em;
 font-weight:bold;
 padding:5px;
}
</style>

这是我自己包装的代码地址:https://github.com/lry1994/vue-lib/tree/master/src/components/process-joint

这个目前看来还算满意

jsplumb

这个看了官网,不太友好,而且下载只有一个js文件,没有demo代码,不知如何下手

参考资料:

https://gojs.net/latest/samples/pageflow.html

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。