利用Echarts设计一个图表平台(一)

时间:2023-01-12 13:03:14

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涉及到了图表的参数化查询,包括报表的其他个性化的定制(如图表的曲线的范围、计量单位的定义等),下篇文章将会继续介绍该图表平台的其他实现。