vue中使用gojs/jointjs

博客文章地址:https://lry1994.github.io/stu...
---------------------------------------------更新 2018/8/30----------------------------------------------------------
通過一番實踐,我以爲jointjs很差用了。
不滿意點:javascript

  1. 不能相對容器自動佈局,都用的是死的像素,分辨率低的屏幕,部分圖有些就看不見

圖片描述

  1. 不能滾動鼠標本身縮小放大
  2. 比較醜,也多是我不會配色

最終採用gojs,上面提到的問題解決方案是:把去掉水印的gojs放在本身的npm服務器上,這樣就能夠npm安裝了!!!
實踐過程當中遇到個問題html

<process-go style="height:250px"  v-if="show"  :model-data="dataObj.graph" :category="category"> </process-go>

像這樣,用v-if反覆切換,會報錯Cannot read property 'type' of undefined at Po (go.js?d976:1044)前端

換成v-show就不會vue

<process-go style="height:250px"  v-show="show"  :model-data="dataObj.graph" :category="category"> </process-go>

可是這樣的前提是show初始化是true.若是初始化爲false,沒有報錯,可是圖呈現不出來。
緣由我猜想是:show初始化爲false,組件高度爲0,由於默認畫布就是組件容器,因此畫布高度變成0,圖畫不出。java

最後想出個方法。v-ifv-show結合一塊兒使用,加上$once方法,代碼:node

<process-go style="height:250px" v-if="firstClick" v-show="show"  :model-data="dataObj.graph" :category="category"> </process-go>
data(){
    return{        
        show:false,
        firstClick:false,
    }
}
created(){
    this.init();
    this.$once('firstClick',()=>{
        this.firstClick = true
    })
},
methods:{
    init(){...},
    look(){//用v-if切換會報錯,只有第一次點擊查看用v-if,其餘用v-show
        this.$emit('firstClick');
        this.show = true;
    }
}

從新封裝了一下gojs 地址:https://github.com/LRY1994/vue-lib/blob/master/src/components/process-go/index.vuegit

-------------------------------------------更新線-----------------------------------------------------------------github

由於公司項目需求,要畫出相關業務的流程圖,以便客戶瞭解本身身處何處npm

搜索框輸入 「前端流程圖插件」,查了不少資料,總結一下有如下幾種json

flow-chart

代碼寫法繁瑣,不是json就能夠解決,效果也比較醜,PASS

darge-d3

github :https://github.com/dagrejs/dagre-d3

效果圖
圖片描述

下載裏面的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

github :https://github.com/NorthwoodsSoftware/GoJS

能夠經過npm install gojs -save安裝

效果圖
圖片描述

看裏面的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

github : https://github.com/clientIO/joint
效果圖
圖片描述

能夠經過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
http://www.daviddurman.com/assets/autolayout.js
http://resources.jointjs.com/demos/layout

相關文章
相關標籤/搜索