svg 繪製流程圖 defs
阿新 • • 發佈:2018-12-12
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>