Echarts是一款百度的开源图表库,里面提供了非常多的图表样式,我们今天要讲的内容是利用这一款开源js图表,制作一个能够动态定制的图表平台。
1)Echarts API介绍
首先我们先来看一下Echarts中的一个简单柱状图的API:
option = { color: ['#3398DB'], tooltip : { trigger: 'axis', axisPointer : { // 坐标轴指示器,坐标轴触发有效 type : 'shadow' // 默认为直线,可选为:'line' | 'shadow' } }, grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }, xAxis : [ { type : 'category', data : ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], axisTick: { alignWithLabel: true } } ], yAxis : [ { type : 'value' } ], series : [ { name:'直接访问', type:'bar', barWidth: '60%', data:[10, 52, 200, 334, 390, 330, 220] } ] };
在这个option中包含了很多图表的属性,series是图表的基础属性,包含了图表的类型、数据的值等,xAxis包含了x轴上的数据列等。在其他的图表类型中,还会包含legend、title、tooltip、toolbox等属性,这些可以通过查看Echarts API查看,这里不一一叙述。
2)根据Echarts API建模
我们既然要实现一个报表平台,就需要对这些属性需要的数据进行建模:
public class TuBiao : IAggregateRoot { public title title { get; set; } = new title(); public legend legend { get; set; } = new legend(); public xAxis xAxis { get; set; } = new xAxis(); public yAxis yAxis { get; set; } = new yAxis(); public IList<series> serieList { get; set; } = new List<series>(); public TuBiao GetTuBiaoByID(string tuBiaoID) { ITuBiaoRepository repo = IoC.Resolve<ITuBiaoRepository>(); return repo.GetByTuBiaoID(tuBiaoID); } }
其中title、legend、xAxis、series都是图表模型中的值对象,熟悉领域驱动的同学可能看起来很熟悉,每一个都对应着Echarts API中的js属性,随便贴出一个两个代码示例:
public class title { public string text { get; set; } public string subtext { get; set; } }
public class series { public string name { get; set; } public string type { get; set; } public List<string> dataList { get; set; } = new List<string>(); }
public class yAxis { /// <summary> /// 单位 /// </summary> public string danwei { get; set; } /// <summary> /// 最大值 /// </summary> public string ZuiDaZhi { get; set; } /// <summary> /// 最小值 /// </summary> public string ZuiXiaoZhi { get; set; } }
值对象的创建方式可以参考Echarts的API创建,最终我们的模型要满足一个完整Echarts所需要的所有属性。
3)根据Echarts模型加载Echarts的JavaScript脚本
在页面加载的时候,我们可以获取Echarts的模型,并加载到Echarts图表中。示例代码如下:
myChart.setOption({ title: { text: '@ViewBag.TuBiao.title.text', subtext: '@ViewBag.TuBiao.title.subtext' }, tooltip: { trigger: 'axis', }, legend: { data: [@for (;i<= ViewBag.TuBiao.legend.dataList.Count - ;i++ ) { ) { <text>'@ViewBag.TuBiao.legend.dataList[i]'</text> } else { <text>'@ViewBag.TuBiao.legend.dataList[i]',</text> } }] }, @if(ViewBag.TuBiao.serieList.Count >= && ViewBag.TuBiao.serieList[].type != ].type != "funnel") { <text> xAxis: [ { data: [@for (; i <= ViewBag.TuBiao.xAxis.dataList.Count - ; i++) { ) {<text>'@ViewBag.TuBiao.xAxis.dataList[i]'</text>} else {<text>'@ViewBag.TuBiao.xAxis.dataList[i]',</text>} }] } ], yAxis: [ { type: 'value', axisLabel: { formatter: '{value}@ViewBag.TuBiao.yAxis.danwei' }, @if(!string.IsNullOrEmpty(ViewBag.TuBiao.yAxis.ZuiXiaoZhi)) { <text>min:@ViewBag.TuBiao.yAxis.ZuiXiaoZhi,</text> } @if(!string.IsNullOrEmpty(ViewBag.TuBiao.yAxis.ZuiDaZhi)) { <text>max:@ViewBag.TuBiao.yAxis.ZuiDaZhi,</text> } boundaryGap:[, ] } ],</text>} toolbox: { show: true, feature: { mark : {show: true}, dataView : {show: true, readOnly: true}, @if (ViewBag.TuBiao.serieList.Count >= && ViewBag.TuBiao.serieList[].type != ].type != "funnel") { <text> magicType: { type: ['line', 'bar'] },</text>} restore: {}, saveAsImage: {} } }, series: [@for (; serie <= ViewBag.TuBiao.serieList.Count - ; serie++) { ) { <text> { name: '@ViewBag.TuBiao.serieList[serie].name', type: '@ViewBag.TuBiao.serieList[serie].type', data: [@for (;i<= ViewBag.TuBiao.serieList[serie].dataList.Count -;i++) { ) { <text>{value:'@ViewBag.TuBiao.serieList[serie].dataList[i]',name:'@ViewBag.TuBiao.xAxis.dataList[i]'}</text> } else { <text>{value:'@ViewBag.TuBiao.serieList[serie].dataList[i]',name:'@ViewBag.TuBiao.xAxis.dataList[i]'},</text> } }], @if(ViewBag.TuBiao.serieList.Count >= && ViewBag.TuBiao.serieList[].type != ].type != "funnel") { <text> markLine: { data: [ { type: 'average', name: '平均值' } ] }</text>} } </text> } else { <text> { name: '@ViewBag.TuBiao.serieList[serie].name', type: '@ViewBag.TuBiao.serieList[serie].type', data: [@for (;i<= ViewBag.TuBiao.serieList[serie].dataList.Count -;i++) { ) { <text>{value:'@ViewBag.TuBiao.serieList[serie].dataList[i]',name:'@ViewBag.TuBiao.xAxis.dataList[i]'}</text> } else { <text>{value:'@ViewBag.TuBiao.serieList[serie].dataList[i]',name:'@ViewBag.TuBiao.xAxis.dataList[i]'},</text> } }], @if(ViewBag.TuBiao.serieList.Count >= && ViewBag.TuBiao.serieList[].type != ].type != "funnel") { <text> markLine: { data: [ { type: 'average', name: '平均值' } ] }</text>} }, </text> } }] });
其中ViewBag.TuBiao就是我们从model层返回的报表模型,根据Echarts的API一一对应就可以了。
4)如何装载Echarts模型
前面所述的都是Echarts报表的展示部位,下面我们来看一下这个TuBiao的模型该如何生成,有心的同学可能已经注意到了TuBiao的模型中包含一个GetByTuBiaoID的方法,下面把该代码的实现分享一下:
public class TuBiaoRepository:Repository<TuBiao>,ITuBiaoRepository {public TuBiaoRepository(RepositoryContext context) : base(context) { } public TuBiao GetByTuBiaoID(string tuBiaoID) { TuBiaoShuJuYuan shujuyuan = TuBiaoShuJuYuan.GetByTuBiaoID(tuBiaoID); TuBiao result = new TuBiao(); result.title.text = shujuyuan.BiaoTi; result.title.subtext = shujuyuan.FuBiaoTi; //设置Y轴的一些信息 result.yAxis.danwei = shujuyuan.DanWei; result.yAxis.ZuiDaZhi = shujuyuan.ZuiDaZhi; result.yAxis.ZuiXiaoZhi = shujuyuan.ZuiXiaoZhi; //读取数据源 StreamReader srsjy = new StreamReader(HttpContext.Current.Request.MapPath("~/TubiaoData/") + shujuyuan.ShuJuYuan, System.Text.Encoding.UTF8); try { shujuyuan.ShuJuYuan = srsjy.ReadToEnd(); } catch (Exception ex) { throw ex; } finally { srsjy.Close(); } foreach (var item in HttpContext.Current.Request.QueryString.AllKeys) { if (!string.IsNullOrEmpty(item)) shujuyuan.ShuJuYuan = shujuyuan.ShuJuYuan.Replace("[" + item + "]", HttpContext.Current.Request.QueryString[item]); } // \{([^\{^\}]*)\} shujuyuan.ShuJuYuan = Regex.Replace(shujuyuan.ShuJuYuan, @"\[\S*\]", "", RegexOptions.Multiline); DbHelper h = new DbHelper(ConnectionName.LocalDB); DataTable dt = h.ExecuteDataTable(shujuyuan.ShuJuYuan).DataResult; //检索图例 var tuli = (from i in dt.AsEnumerable() select i.Field<string>(TuBiaoZiDuanStruct.TuLi)).Distinct().ToList<string>(); result.legend.dataList.AddRange(tuli); //检索统计单元 var tongJiDy = (from i in dt.AsEnumerable() select i.Field<string>(TuBiaoZiDuanStruct.TongJiDY)).ToList<string>(); result.xAxis.dataList.AddRange(tongJiDy); //循环为每个图例赋值 foreach (string dqtl in tuli) { //查询当前图例的图表类型 var leiXing = (from i in dt.AsEnumerable() where i.Field<string>(TuBiaoZiDuanStruct.TuLi) == dqtl select i.Field<string>(TuBiaoZiDuanStruct.LeiXing)).ToList<string>().FirstOrDefault(); //添加数据 series srs = new series(); srs.name = dqtl; srs.type = leiXing; //检索对应图例下的图表数据 var tubiaosy = (from i in dt.AsEnumerable() where i.Field<string>(TuBiaoZiDuanStruct.TuLi) == dqtl select i.Field<dynamic>(TuBiaoZiDuanStruct.Zhi)).ToList<dynamic>(); foreach (dynamic zhi in tubiaosy) { srs.dataList.Add(Convert.ToString(zhi)); } result.serieList.Add(srs); } return result; } struct TuBiaoZiDuanStruct { public static string TuLi { get { return "TULI"; } } public static string LeiXing { get { return "LEIXING"; } } public static string TongJiDY { get { return "TONGJIDY"; } } public static string Zhi { get { return "ZHI"; } } } }
这个方法的作用主要是从数据库中查询图表的数据源定义,并执行该数据源,得到图表所需要的数据,并装载TuBiao模型,示例数据源的SQL语句如下:
5)Echarts模型的数据源定义
SELECT T.XINGMING AS TULI,'line' AS LEIXING,T.TIWEN AS ZHI,T.JILUSJ AS TONGJIDY FROM TIWEN T WHERE T.ID= '[ID]'
这个SQL语句是根据一个ID获取一个人的体温变化曲线图,在这里的ID涉及到了图表的参数化查询,包括报表的其他个性化的定制(如图表的曲线的范围、计量单位的定义等),下篇文章将会继续介绍该图表平台的其他实现。