約定:本文將不對echarts的庫文件的引入和echarts風格的代碼編寫規範作講解,如有不懂地方,請閱讀本人的博客文章http://www.javashuo.com/article/p-swipnxkc-hc.htmljavascript
該圖形分爲2個區:頂部的查詢按鈕和圖形展現區前端
咱們使用Pentaho CDE做圖時,通常不多考慮圖形聯動,由於Pentaho自身的一套傳參、監聽機制幫咱們作了不少事情,這大大提升了咱們的開發效率;一旦引入第三方可視化庫(Echarts),開發者就要本身實現 這一套機制。java
圖形聯動思路:大致分爲如下幾大步ajax
廢話很少說,直接上代碼,,,,,數據庫
下圖是代碼片斷一的位置:json
代碼片斷一的具體內容見下面編輯框:api
/** * 頁面初始化的圖形渲染 */ function initCharts(){ /********************************************* * 支付方式餅圖 * *********************************************/ var discountTypeUrl = "/pentaho/plugin/cda/api/doQuery?path=/public/dashboard/dashboard1-1.cda&dataAccessId=DiscountTypeQueryBefore"; discountTypeUrl += "¶mbutton_event=" + button_event; var discountTypeJSON = readJSONFile(discountTypeUrl).resultset; var discountTypeChart = echarts.init(document.querySelector("#DiscountTypeHtmlObj")); EchartsChartsEncapsulation( GetDiscountTypeOption( PieDataEncapsulation(discountTypeJSON).getDatas(), PieDataEncapsulation(discountTypeJSON).getDatas() ), discountTypeChart ); /********************************************* * 時段柱狀圖 * *********************************************/ var hourNoUrl = "/pentaho/plugin/cda/api/doQuery?path=/public/dashboard/dashboard1-1.cda&dataAccessId=HourNoQueryBefore"; hourNoUrl += "¶mbutton_event=" + button_event; var hourNoJSON = readJSONFile(hourNoUrl).resultset; var hourNoChart = echarts.init(document.querySelector("#HourNoHtmlObj")); EchartsChartsEncapsulation( GetHourNoOption( BarDataEncapsulation(hourNoJSON).getLegends(), BarDataEncapsulation(hourNoJSON).getDatas() ), hourNoChart ); /********************************************* * 門店柱狀圖 * *********************************************/ var outletUrl = "/pentaho/plugin/cda/api/doQuery?path=/public/dashboard/dashboard1-1.cda&dataAccessId=OutletQuery"; outletUrl += "¶mbutton_event=" + button_event; var outletJSON = readJSONFile(outletUrl).resultset; var outletChart = echarts.init(document.querySelector("#OutletHtmlObj")); var ShowSecondBarEcharts = function(){ outletChart.setOption( GetOutletOption( BarDataEncapsulation(outletJSON).getLegends(), BarDataEncapsulation(outletJSON).getDatas() ) ); //給門店柱狀圖綁定click事件 outletChart.on('click',function(param){ refreshCharts(param.name); //從新加載圖形,達到刷新效果 }); }(); AdaptionWindows(outletChart); } /** * 點擊聯動後頁面的圖形渲染 */ function refreshCharts(outletId){ //刷新參數,實現與Pentaho CDE組件的交互 Dashboards.fireChange("outletParam",outletId); /********************************************* * 支付方式餅圖 * *********************************************/ var discountTypeUrl = "/pentaho/plugin/cda/api/doQuery?path=/public/dashboard/dashboard1-1.cda&dataAccessId=DiscountTypeQuery"; discountTypeUrl += "¶mbutton_event=" + button_event + "¶moutletParam=" + outletParam; var discountTypeJSON = readJSONFile(discountTypeUrl).resultset; var discountTypeChart = echarts.init(document.querySelector("#DiscountTypeHtmlObj")); EchartsChartsEncapsulation( GetDiscountTypeOption( PieDataEncapsulation(discountTypeJSON).getDatas(), PieDataEncapsulation(discountTypeJSON).getDatas() ), discountTypeChart ); /********************************************* * 時段柱狀圖 * *********************************************/ var hourNoUrl = "/pentaho/plugin/cda/api/doQuery?path=/public/dashboard/dashboard1-1.cda&dataAccessId=HourNoQuery"; hourNoUrl += "¶mbutton_event=" + button_event + "¶moutletParam=" + outletParam; var hourNoJSON = readJSONFile(hourNoUrl).resultset; var hourNoChart = echarts.init(document.querySelector("#HourNoHtmlObj")); EchartsChartsEncapsulation( GetHourNoOption( BarDataEncapsulation(hourNoJSON).getLegends(), BarDataEncapsulation(hourNoJSON).getDatas() ), hourNoChart ); /********************************************* * 下鑽到分類柱狀圖 * *********************************************/ var itemCategoryUrl = "/pentaho/plugin/cda/api/doQuery?path=/public/dashboard/dashboard1-1.cda&dataAccessId=ItemCategoryQuery"; itemCategoryUrl += "¶mbutton_event=" + button_event + "¶moutletParam=" + outletParam; var itemCategoryJSON = readJSONFile(itemCategoryUrl).resultset; var itemCategoryChart = echarts.init(document.querySelector("#OutletHtmlObj")); EchartsChartsEncapsulation( //hourNoUrl, //option GetOutletOption GetItemCategoryOption GetOutletOption( // 這裏沿用門店柱狀圖的封裝 BarDataEncapsulation(itemCategoryJSON).getLegends(), BarDataEncapsulation(itemCategoryJSON).getDatas() ), itemCategoryChart ); } /** * 對下鑽圖形的返回原始狀態的圖形渲染 */ function goBackCharts(){ /********************************************* * 門店柱狀圖 * *********************************************/ var outletUrl = "/pentaho/plugin/cda/api/doQuery?path=/public/dashboard/dashboard1-1.cda&dataAccessId=OutletQuery"; outletUrl += "¶mbutton_event=" + button_event; var outletJSON = readJSONFile(outletUrl).resultset; var outletChart = echarts.init(document.querySelector("#OutletHtmlObj")); var ShowSecondBarEcharts = function(){ outletChart.setOption( //option GetOutletOption( BarDataEncapsulation(outletJSON).getLegends(), BarDataEncapsulation(outletJSON).getDatas() ) ); outletChart.on('click',function(param){ refreshCharts(param.name); }); }(); AdaptionWindows(outletChart); }
代碼片斷一是上述思路的最直接體現,代碼主體有2個函數:initCharts()和refreshCharts()。initCharts()用於網頁首次加載時渲染Echarts圖形的,refreshCharts()用於點擊聯動的刷新;瀏覽代碼你會發現,上述代碼的結構都差很少,是的。這裏將相關功能作了封裝,具體的實現細節將在代碼片斷二中給你們展現。上述代碼的核心是繪製門店柱狀圖的代碼片斷,接下來我將這部分帶單獨抽出來作詳細的講解,其他的繪圖代碼功能相似,就不作重複敘述了。echarts
在這裏,我將下鑽分爲2種狀況:異步
狀況二咱們以前已經說明過,此部分 只針對狀況一作詳細的講解。實現起來也很簡單,在refreshCharts()函數中添加這段代碼即可(下鑽到分類柱狀圖):async
/********************************************* * 下鑽到分類柱狀圖 * *********************************************/ var itemCategoryUrl = "/pentaho/plugin/cda/api/doQuery?path=/public/dashboard/dashboard1-1.cda&dataAccessId=ItemCategoryQuery"; itemCategoryUrl += "¶mbutton_event=" + button_event + "¶moutletParam=" + outletParam; var itemCategoryJSON = readJSONFile(itemCategoryUrl).resultset; var itemCategoryChart = echarts.init(document.querySelector("#OutletHtmlObj")); EchartsChartsEncapsulation( //hourNoUrl, //option GetOutletOption GetItemCategoryOption GetOutletOption( // 這裏沿用門店柱狀圖的封裝 BarDataEncapsulation(itemCategoryJSON).getLegends(), BarDataEncapsulation(itemCategoryJSON).getDatas() ), itemCategoryChart );
下鑽前
下鑽後
片斷一的代碼實現了咱們的業務流程,具體功能上的邏輯問題在代碼片斷二中實現;在講解代碼片斷二以前,咱們來思考幾個問題:
// 讀取CDA文件 var readJSONFile = function(url){ var jsonData; $.ajax({ type : 'GET', url : url, async : false, dataType : 'json', data : null, success : function(response){ jsonData = response; } }); return jsonData; };
所以,在片斷一的每一個繪圖代碼裏都有URL和結果集的部分,以門店柱狀圖爲例:
/********************************************* * 門店柱狀圖 * *********************************************/ var outletUrl = "/pentaho/plugin/cda/api/doQuery?path=/public/dashboard/dashboard1-1.cda&dataAccessId=OutletQuery"; outletUrl += "¶mbutton_event=" + button_event; //Pentaho CDA的URL var outletJSON = readJSONFile(outletUrl).resultset; //獲取結果集 var outletChart = echarts.init(document.querySelector("#OutletHtmlObj")); //獲取圖形的DOM var ShowSecondBarEcharts = function(){ //這次爲繪圖代碼主體,不作分析,故省略掉...... outletChart.setOption( GetOutletOption( //經過BarDataEncapsulation拼湊legend BarDataEncapsulation(outletJSON).getLegends(), //經過BarDataEncapsulation拼湊series BarDataEncapsulation(outletJSON).getDatas() ) ); }(); AdaptionWindows(outletChart); //圖形自適應
/** * 實現對CDA數據的封裝 */ // 柱狀圖 var BarDataEncapsulation = function(cdaData){ // 柱狀圖的X軸屬性 var legends = function(cdaData){ var tempArr = []; for(var i = 0; i < cdaData.length; i++){ tempArr.push({ 'name' : cdaData[i][0], selected : true }); tempArr[i] = cdaData[i][0]; } return tempArr; }; //柱狀圖的X軸的值 var data = function(){ var tempArr = []; for(var i = 0; i < cdaData.length; i++){ tempArr.push({ 'name' : cdaData[i][0], 'value' : cdaData[i][1], selected : true }); } return tempArr; }; return { getLegends : function(){ return legends(cdaData); }, getDatas : function(){ return data(cdaData); } }; };
此處咱們就作了柱狀圖的數據的拼湊,並將其封裝起來方便屢次調用。
/** * 對門店柱狀圖的option的簡單封裝 */ var GetOutletOption = function(XLegends,XDatas){ return { tooltip : { trigger : 'item', formatter: function(params) { var res = "門店:" + params.name + '<br/>' + "銷售額:" + formatSaleAmt(params.value.toFixed(2)) + "元"; return res; } }, calculable : true, xAxis : [ { name : "門店", nameLocation : "end", type : 'category', show : true, //data : SecondBarDataEncapsulation.getLegends(), data : XLegends, axisLabel : { interval : 0, rotate: 20, textStyle: { fontSize : 10 } }, axisLine : { show : true, color : "#007AC7" }, splitLine : { show: false } } ], grid : { //控制圖的大小 x : 70, x2 : 70, y2 : 70 //y2能夠控制X軸跟Zoom控件之間的間隔,避免覺得傾斜後形成label重疊到Zoom上 }, yAxis : [ { name : "營業額", nameLocation : "end", type : 'value', show : true, precision: 2, scale : true, axisLabel : { formatter : function(value){ if(value !== 0){ return value/10000 + "W"; }else if(value === ""){ return 0; }else{ return 0; } } }, splitLine : { show: false } } ], series : [ { name : "單位/元", type : 'bar', precision: 2, barWidth : 40,//柱圖寬度 itemStyle : { emphasis: { barBorderRadius: 30 }, normal : { barBorderRadius:[10, 10, 10, 10], color : function(params){ var colorList = [ "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7" ]; return colorList[params.dataIndex]; } }, label : { show : true, position : 'top', } }, data : XDatas, markPoint: { symbol : 'pin', silent : true, itemStyle : { normal : { color : "#8EC21F" } }, label : { normal : { show : true, position : "inside", //formatter : "{c}", formatter : function(params){ return (params.value/10000).toFixed(1) + "W"; }, textStyle : { //color : "red", //fontStyle : "bold", //fontWight : "bold", fontFamily : "sans-serif", fontSize : "10" } } }, data: [ {type: 'max', name: '最好門店'}, {type: 'min', name: '最差門店'} ] }, markLine : { silent : true, itemStyle : { normal : { color : "#8EC21F" } }, data : [ {type : 'average', name : '平均值'} ] } } ] }; };
調用部分見下圖:
/** * Echarts自適應屏幕 * @param chart */ function AdaptionWindows(chart) { window.addEventListener('resize',function () { chart.resize(); }); } /** * 對 option的總體封裝 * @param url CDA 數據源 * @param option Echarts繪圖屬性 * @param chartsObj 圖形Dom */ function EchartsChartsEncapsulation(option,chartsObj){ var myChart = function(){ chartsObj.setOption(option); }(); //調用屏幕應的方法 AdaptionWindows(chartsObj); // } }
以時段柱狀圖爲例(見下圖):
以上是對代碼片斷二的具體細節作的簡單描述,但願你們就這段代碼多多提提意見,具體代碼見編輯框:
// 讀取CDA 文件 var readJSONFile = function(url){ var jsonData; $.ajax({ type : 'GET', url : url, async : false, dataType : 'json', data : null, success : function(response){ jsonData = response; } }); return jsonData; }; /** * 格式化金額數值 */ var formatSaleAmt = function(nStr){ nStr += ''; x = nStr.split('.'); x1 = x[0]; x2 = x.length > 1 ? '.' + x[1] : ''; var rgx = /(\d+)(\d{3})/; while (rgx.test(x1)){ x1 = x1.replace(rgx, '$1' + ',' + '$2'); } x2 = x2.slice(0,3); return x1 + x2; }; /** * 實現對CDA數據的封裝 */ // 餅狀圖 var PieDataEncapsulation = function(cdaData){ var legends = function(cdaData){ var tempArr = []; for(var i = 0; i < cdaData.length; i++){ tempArr[i][0] = cdaData[i][0]; tempArr[i][1] = cdaData[i][1]; } return tempArr; }; var data = function(cdaData){ var tempArr = []; for(var i = 0; i < cdaData.length; i++){ tempArr.push({ 'name' : cdaData[i][0], 'value' : cdaData[i][1], selected : true }); } return tempArr; }; return { getLegends : function(){ return legends(cdaData); }, getDatas : function(){ return data(cdaData); } }; }; // 柱狀圖 var BarDataEncapsulation = function(cdaData){ // 柱狀圖的X軸屬性 var legends = function(cdaData){ var tempArr = []; for(var i = 0; i < cdaData.length; i++){ tempArr.push({ 'name' : cdaData[i][0], selected : true }); tempArr[i] = cdaData[i][0]; } return tempArr; }; //柱狀圖的X軸的值 var data = function(){ var tempArr = []; for(var i = 0; i < cdaData.length; i++){ tempArr.push({ 'name' : cdaData[i][0], 'value' : cdaData[i][1], selected : true }); } return tempArr; }; return { getLegends : function(){ return legends(cdaData); }, getDatas : function(){ return data(cdaData); } }; }; /** * 對支付方式餅狀圖的option的簡單封裝 */ var GetDiscountTypeOption = function(PLegends,PSeries){ return { tooltip : { trigger : 'item', formatter: function(params) { var res = " 支付類型:" + params.name + '<br/>' + "總金額:" + formatSaleAmt(params.value.toFixed(2)) + "元"; return res; } }, legend : { orient: 'vertical', x: 'left', data : PLegends }, toolbox : { show : true, orient : 'item', feature : { mark : { show : true }, magicType : { show : true, type : ['pie','funnel'], option : { funnel : { x : '25%', width : '50%', funnelAlign : 'bottom', max : 1548 } } } } }, calculable : true, series : [ { //name : '折扣', name : "單位/元", type :'pie', radius : '75%', center: ['60%', '55%'], data : PSeries, avoidLabelOverlap: true, itemStyle : { normal : { label : { position : 'inner', formatter: function (params){ return (params.percent - 0) + '%'; }, textStyle : { color : '#000' }, }, labelLine : { show : true }, color : function(params){ var colorList = [ '#29A7E2','#8EC21F', '#005BAC','#FED32B', '#E80014','#F4A20E', '#F4F20E' ]; return colorList[params.dataIndex]; } }, emphasis : { label : { show : true }, labelLine : { show :true } } }, label : { normal : { } }, labelLine : { normal : { lineStyle : { color : 'green' }, smooth : 0.2, length : 10, length2 : 20 } } } ] }; }; /** * 對時段柱狀圖的option的簡單封裝 */ var GetHourNoOption = function(XLegends, XDatas){ return { tooltip : { trigger : 'item', formatter: function(params) { var res = " 時段:" + params.name + "點" + '<br/>' + "銷售額:" + formatSaleAmt(params.value.toFixed(2)) + "元"; return res; } }, calculable : true, xAxis : [ { name : "時段", //name : "單位/元", nameLocation : "end", type : 'category', show : true, //data : FirstBarLegends axisLine : { show : true }, axisLabel : { /* interval : "auto", */ //rotate: 25, interval : 0 /* textStyle : { color : "#007AC7" } */ }, splitLine : { show: false }, //data : FirstBarDataEncapsulation.getLegends() data : XLegends } ], grid : { //控制圖的大小 borderWidth : 0, y : 80, //y2 : 60 x : 70, x2 : 70, y2 : 70 //y2能夠控制X軸跟Zoom控件之間的間隔,避免覺得傾斜後形成label重疊到Zoom上 }, yAxis : [ { name : "營業額", nameLocation : "end", type : 'value', show : true, axisLabel : { formatter : function(value){ if(value !== 0){ return value/10000 + "W"; }else if(value == "null"){ return 0; }else{ return 0; } } }, splitLine : { show: false } } ], series : [ { //name : '時段營業額', name : "單位/元", type : 'bar', //barWidth : 20,//柱圖寬度 barWidth : 40,//柱圖寬度 itemStyle : { normal : { barBorderRadius:[10, 10, 10, 10], color : function(params){ var colorList = [ "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7" ]; return colorList[params.dataIndex]; } }, label : { show : true, position : 'top', formatter : '{b}\n{c}' } }, //data : FirstBarData //data : FirstBarDataEncapsulation.getDatas() //data : BarDataEncapsulation(getFirstBarJSON).getDatas() data : XDatas, markPoint: { symbol : 'pin', //silent : true, itemStyle : { normal : { color : "#8EC21F" } }, label : { normal : { show : true, position : "inside", //formatter : "{c}", formatter : function(params){ if(params.value < 9999){ return (params.value/1000).toFixed(1) + "K"; }else{ return (params.value/10000).toFixed(1) + "W"; } }, textStyle : { //color : "red", //fontStyle : "bold", //fontWight : "bold", fontFamily : "sans-serif", fontSize : "10" } } }, data: [ {type: 'max', name: '最佳時段'}, {type: 'min', name: '最差時段'} ] }, markLine : { silent : true, itemStyle : { normal : { color : "#8EC21F" } }, data : [ {type : 'average', name : '平均值'} ] } } ] }; }; /** * 對門店柱狀圖的option的簡單封裝 */ var GetOutletOption = function(XLegends,XDatas){ return { tooltip : { trigger : 'item', formatter: function(params) { var res = "門店:" + params.name + '<br/>' + "銷售額:" + formatSaleAmt(params.value.toFixed(2)) + "元"; return res; } }, calculable : true, xAxis : [ { name : "門店", nameLocation : "end", type : 'category', show : true, //data : SecondBarDataEncapsulation.getLegends(), data : XLegends, axisLabel : { interval : 0, rotate: 20, textStyle: { fontSize : 10 } }, axisLine : { show : true, color : "#007AC7" }, splitLine : { show: false } } ], grid : { //控制圖的大小 x : 70, x2 : 70, y2 : 70 //y2能夠控制X軸跟Zoom控件之間的間隔,避免覺得傾斜後形成label重疊到Zoom上 }, yAxis : [ { name : "營業額", nameLocation : "end", type : 'value', show : true, precision: 2, scale : true, axisLabel : { formatter : function(value){ if(value !== 0){ return value/10000 + "W"; }else if(value === ""){ return 0; }else{ return 0; } } }, splitLine : { show: false } } ], series : [ { name : "單位/元", type : 'bar', precision: 2, barWidth : 40,//柱圖寬度 itemStyle : { emphasis: { barBorderRadius: 30 }, normal : { barBorderRadius:[10, 10, 10, 10], color : function(params){ var colorList = [ "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7", "#007AC7","#007AC7","#007AC7" ]; return colorList[params.dataIndex]; } }, label : { show : true, position : 'top', } }, data : XDatas, markPoint: { symbol : 'pin', silent : true, itemStyle : { normal : { color : "#8EC21F" } }, label : { normal : { show : true, position : "inside", //formatter : "{c}", formatter : function(params){ return (params.value/10000).toFixed(1) + "W"; }, textStyle : { //color : "red", //fontStyle : "bold", //fontWight : "bold", fontFamily : "sans-serif", fontSize : "10" } } }, data: [ {type: 'max', name: '最好門店'}, {type: 'min', name: '最差門店'} ] }, markLine : { silent : true, itemStyle : { normal : { color : "#8EC21F" } }, data : [ {type : 'average', name : '平均值'} ] } } ] }; }; /** * Echarts自適應屏幕 * @param chart */ function AdaptionWindows(chart) { window.addEventListener('resize',function () { chart.resize(); }); } /** * 對 option的總體封裝 * @param url CDA 數據源 * @param option Echarts繪圖屬性 * @param chartsObj 圖形Dom */ function EchartsChartsEncapsulation(option,chartsObj){ var myChart = function(){ chartsObj.setOption(option); }(); //調用屏幕應的方法 AdaptionWindows(chartsObj); } //載入echarts圖形 //window.onload = onload_echarts(0); window.onload = initCharts();