约定:本文将不对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();