1. 程式人生 > >svg 繪製流程圖 defs

svg 繪製流程圖 defs

Vue.component('flowMapGuide', {
    template: `<div>
        <svg ref="rootSvg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  style="display:block;margin: 0 auto">
            <defs> 
                <marker id="arrow" markerWidth="10" markerHeight="10" refX="2" refY="3" orient="auto" markerUnits="strokeWidth" fill="rgb(128,128,128)"> <path d="M0,0 L0,6 L9,3 z"/> 
                </marker>
            </defs>
        </svg>
    </div>`,
    props: {
        flowMapParam: {
            required: true
        }
    },
    data: function() {
        return {  
            MAP_JSON: null,
            svgSizePx: {
                width: 0,
                height: 0
            },
            size: {
                x: 0,
                y: 0
            },
            endLineStep: [],
            perMapSizePX : {
                x: 150, //寬
                y: 60,  //高
                rx: 0,
                gapX: 60,
                gapY: 28
            }
        }
    },
    mounted: function() {
        var self  = this;
        /*requestFunc("/c/userinfo/queryflowmap", {SEQ: this.flowMapParam.SEQ}, true).then(function(flowMapObj) {
            try {
                var MAP_JSON = JSON.parse(flowMapObj.MAP_JSON);
            } catch(err) {
                console.log(err);
            }*/
            var MAP_JSON = {"body":[[{"id":"pressureInteractionDevice","seq":"1","title":"壓感裝置定義","optionFlag":"must","widgetType":"rectangle","pageUrl":"pages/ydCommerce/pressureInteraction/pressureInteractionDevice.html","progCode":"pressureInteractionDevice","moduleName":"online","icon":"goodsProperty","parentId":"upBasicData-3"}],[{"id":"fodderManage","seq":"1","title":"商品視訊上傳","optionFlag":"must","widgetType":"rectangle","pageUrl":"pages/ydCommerce/putIn/fodderManage.html","progCode":"fodderManage","moduleName":"ad","icon":"upGoodsPic"},{"id":"styleVideoUpload","seq":"2","title":"商品視訊選擇","optionFlag":"must","widgetType":"rectangle","pageUrl":"pages/ydCommerce/pressureInteraction/styleVideoUpload.html","progCode":"styleVideoUpload","moduleName":"online","icon":"fodderManage"},{"id":"upGoodsPic","seq":"3","title":"商品圖片上傳","optionFlag":"must","widgetType":"rectangle","pageUrl":"pages/goods/langWan/upGoodsPic.html","progCode":"upGoodsPic","moduleName":"goods","icon":"upGoodsPic","parentId":"upBasicData-3"}],[{"id":"pressureInteractionService","seq":"1","title":"壓感商品投放","optionFlag":"must","widgetType":"rectangle","pageUrl":"pages/ydCommerce/pressureInteraction/pressureInteractionService.html","progCode":"pressureInteractionService","moduleName":"online","icon":"hotGoodsService-2","parentId":"upBasicData-3"}],[{"id":"fsAdService","seq":"1","title":"廣告投放","optionFlag":"must","widgetType":"rectangle","pageUrl":"pages/fittingSystem/putIn/adService.html","progCode":"fsAdService","moduleName":"AD","icon":"adService","parentId":"upBasicData-3"}]],"footer":[{"id":"upBasicData-3","seq":"2","title":"資料上傳-生成終端DB","optionFlag":"must","widgetType":"rectangle","pageUrl":"pages/goods/langWan/upBasicData.html","progCode":"upBasicData","moduleName":"goods","icon":"upBasicData-3","parentId":""}]}
            self.MAP_JSON = self.mapJsonFormat(MAP_JSON);
            self.computedMapSize();
            self.drawMap();
        /*});*/
    },
    methods: {
        mapJsonFormat: function(mapJson) {
            return mapJson;
        },
        computedMapSize: function() {
            var x = this.MAP_JSON.body.length;
            var y = 0;

            for(var i=0;i<this.MAP_JSON.body.length;i++) {
                if(y<this.MAP_JSON.body[i].length) {
                    y = this.MAP_JSON.body[i].length;
                }
            }
            if(x<this.MAP_JSON.footer.length) {
                x = this.MAP_JSON.footer.length;
            }
            this.size.x = x+1;
            this.size.y = y+1;

            var rootSvg = this.$refs.rootSvg;
            rootSvg.setAttribute("style", "display:block;margin: 0 auto;")
            this.svgSizePx.width = (this.size.x-1)*this.perMapSizePX.x+(this.size.x-2)*this.perMapSizePX.gapX+20;
            this.svgSizePx.height = this.size.y*this.perMapSizePX.y+this.size.y*this.perMapSizePX.gapY+20;
            rootSvg.setAttribute("width", this.svgSizePx.width);
            rootSvg.setAttribute("height", this.svgSizePx.height);
        },
        drawMap: function() {
            var rootSvg = this.$el.getElementsByTagName("svg")[0];
            //計算起點
            if(this.MAP_JSON.footer.length<this.size.x-1) {
                this.drawFooter(rootSvg, (this.size.x-1-this.MAP_JSON.footer.length)*(this.perMapSizePX.x+this.perMapSizePX.gapX)/2);
            } else {
                this.drawFooter(rootSvg, 0);
            }

            //計算起點
            if(this.MAP_JSON.body.length<this.size.x-1) {
                this.drawBody(rootSvg, (this.size.x-1-this.MAP_JSON.body.length)*(this.perMapSizePX.x+this.perMapSizePX.gapX)/2);
            } else {
                this.drawBody(rootSvg, 0);
            }
            
        },
        drawBody: function(rootSvg, offsetLeft) {
            var rows = this.MAP_JSON.body;
            for(var i=0;i<rows.length;i++) {
                var curColumns = rows[i];
                for(var j=0;j<curColumns.length;j++) {
                    var opt = {
                        x: 10+i*(this.perMapSizePX.x+this.perMapSizePX.gapX)+offsetLeft,
                        y: 10+j*(this.perMapSizePX.y+this.perMapSizePX.gapY)
                    }
                    Object.assign(opt, curColumns[j]);
                    if(j==curColumns.length-1) {
                        var g = this.createg(opt, j);
                    } else {
                        var g = this.createg(opt);
                    }
                    rootSvg.append(g);
                }
            }
        },
        createg: function(opt, endY) {
            var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
            g.setAttribute("transform", 'translate('+(opt.x+0.5)+","+(opt.y+0.5)+')');
            g.setAttribute("title", opt.title);
            g.setAttribute("name", opt.progCode);
            g.setAttribute("url", opt.pageUrl);

            //建立一個框
            if(opt.widgetType == "rectangle") {
                var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
                rect.setAttribute("width", this.perMapSizePX.x);
                rect.setAttribute("height", this.perMapSizePX.y);
                rect.setAttribute("rx", this.perMapSizePX.rx);
                rect.setAttribute("ry", this.perMapSizePX.rx);
                rect.setAttribute( "fill", "rgb(255,255,255)");
                rect.setAttribute( "stroke-width", 1);
                rect.setAttribute( "stroke", "rgb(148,148,148)");
            } else {
                var d = 'M0 0 l'+this.perMapSizePX.x+' 0 l0 '+this.perMapSizePX.y+'C'+(this.perMapSizePX.x-this.perMapSizePX.x/8)+' '+(this.perMapSizePX.y-10)+","+(this.perMapSizePX.x-3*this.perMapSizePX.x/8)
                +" "+(this.perMapSizePX.y-10)+","+this.perMapSizePX.x/2+" "+this.perMapSizePX.y+"S"+(this.perMapSizePX.x/8) +" "+(this.perMapSizePX.y+10)+",0 "+this.perMapSizePX.y+" Z";
                var rect = document.createElementNS("http://www.w3.org/2000/svg", "path");
                rect.setAttribute("d", d);
                rect.setAttribute("fill", "none");
                rect.setAttribute( "stroke-width", 1);
                rect.setAttribute( "stroke", "rgb(148,148,148)");
                rect.setAttribute("widgetType", "document");
            }
            

            var foreignObject = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject")
            foreignObject.setAttribute("width", this.perMapSizePX.x);
            foreignObject.setAttribute("height", this.perMapSizePX.y);
            var des = opt.optionFlag == "must" ? "必填":"選填";
            var foreignObjectInnerHTML = '<div class="outer">'+
                    '<div class="map-title">'+opt.title+'</div>'+
                    '<div class="tip">('+des+')</div>'+
                '</div>';
            foreignObject.innerHTML = foreignObjectInnerHTML;

            var path = document.createElementNS("http://www.w3.org/2000/svg", "path");
            if(typeof endY == "undefined") { 
                path.setAttribute("d", 'M '+this.perMapSizePX.x/2+' '+this.perMapSizePX.y+" l 0 "+(this.perMapSizePX.gapY-8));
            } else {
                var matched = false;
                for(var i=0;i<this.endLineStep.length;i++) {
                    if(this.endLineStep[i].parentId == opt.parentId) {
                        matched = true;
                    }
                }
                if(!matched) {
                    this.endLineStep.push({
                        parentId: opt.parentId,
                        endY: this.endLineStep.length*2
                    });
                }

                var endPointY;
                for(var i=0;i<this.endLineStep.length;i++) {
                    if(this.endLineStep[i].parentId == opt.parentId) {
                        endPointY = this.endLineStep[i].endY;
                        break;
                    }
                }
                var rootSvg = this.$refs.rootSvg;
                var endPointEle =rootSvg.getElementById(opt.parentId);
                var point = {};
                point.x = endPointEle.getAttribute("pointX");
                point.y = endPointEle.getAttribute("pointY");
                var d = 'M '+this.perMapSizePX.x/2+' '+this.perMapSizePX.y+' l 0 '+ (this.perMapSizePX.gapY+(this.perMapSizePX.gapY+this.perMapSizePX.y)*(this.size.y-2-endY)+this.endLineStep.length*4+endPointY) +' l '+(point.x-opt.x)+' 0'+"l 0 "+(20-endPointY*3);
                path.setAttribute("d", d);
            }
            
            path.setAttribute("marker-end", "url(#arrow)");
            path.setAttribute("style", "stroke:rgb(148,148,148);stroke-width:1;fill:none");
            path.className="line";

            var xmlns = "http://www.w3.org/2000/svg"; 
            var oImg = document.createElementNS(xmlns,"image");
            oImg.setAttributeNS('http://www.w3.org/1999/xlink','href', 'img/guidemap_icon/'+opt.icon+'.png');

            oImg.setAttributeNS(null, "width", 30);
            oImg.setAttributeNS(null, "height", 30);
            oImg.setAttributeNS(null, "x", 10);
            oImg.setAttributeNS(null, "y", 15);

            g.appendChild(rect);
            g.append(foreignObject);
            g.append(path);
            //g.appendChild(oImg);
            return g;
        },
        drawFooter: function(rootSvg, offsetLeft) {
            var curColumns = this.MAP_JSON.footer;
            for(var i=0;i<curColumns.length;i++) {
                var opt = {
                        x: 10+i*(this.perMapSizePX.x+this.perMapSizePX.gapX)+offsetLeft,
                        y: (this.size.y-1)*(this.perMapSizePX.y+this.perMapSizePX.gapY)+40
                    }
                    Object.assign(opt, curColumns[i]);
                    var g = this.createFooterCell(opt);
                    rootSvg.append(g);
            }
        },
        createFooterCell: function(opt) {
            var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
            g.setAttribute("transform", 'translate('+(opt.x+0.5)+","+(opt.y+0.5)+')');
            g.setAttribute("id", opt.id);
            g.setAttribute("title", opt.title);
            g.setAttribute("name", opt.progCode);
            g.setAttribute("url", opt.pageUrl);

            g.setAttribute("pointX", opt.x);
            g.setAttribute("pointY", opt.y);

            //建立一個框
            var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
            rect.setAttribute("width", this.perMapSizePX.x);
            rect.setAttribute("height", this.perMapSizePX.y);
            rect.setAttribute("rx", this.perMapSizePX.rx);
            rect.setAttribute("ry", this.perMapSizePX.rx);
            rect.setAttribute( "fill", "rgb(255,255,255)");
            rect.setAttribute( "stroke-width", 1);
            rect.setAttribute( "stroke", "rgb(148,148,148)");

            var foreignObject = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject")
            foreignObject.setAttribute("width", this.perMapSizePX.x);
            foreignObject.setAttribute("height", this.perMapSizePX.y);
            var des = opt.optionFlag == "must" ? "必填":"選填";
            var foreignObjectInnerHTML = '<div class="outer">'+
                    '<div class="map-title">'+opt.title+'</div>'+
                    '<div class="tip">('+des+')</div>'+
                '</div>';
            foreignObject.innerHTML = foreignObjectInnerHTML;

            var xmlns = "http://www.w3.org/2000/svg"; 
            /*var oImg = document.createElementNS(xmlns,"image");
            oImg.setAttributeNS('http://www.w3.org/1999/xlink','href', 'img/guidemap_icon/'+opt.icon+'.png');

            oImg.setAttributeNS(null, "width", 30);
            oImg.setAttributeNS(null, "height", 30);
            oImg.setAttributeNS(null, "x", 10);
            oImg.setAttributeNS(null, "y", 15);*/

            g.appendChild(rect);
            g.append(foreignObject);
            //g.appendChild(oImg);
            return g;
        }
    }
});
<div id="app">
    <flow-map-guide :flowMapParam="flowMapParam"></flow-map-guide>
</div>