zoukankan      html  css  js  c++  java
  • ExtJs4实战流量统计系统----系统核心是图表,曲线数量不固定怎么办?(二)

    能最直观的展示流量数据的,当属各种图表了,神马折线图、柱状图、饼图......

    这也是我选择ExtJs来重构这个项目的原因,因为ExtJs有一套图表控件,虽然功能较FusionCharts略弱~,

    但ExtJs还有其他诸如Grid、Tree、Form等一系列用于开发一个完整系统的控件,这对于一个界面要靠自己的设计盲来说......不说你们也知道。

    ------------------------------我是分隔线-----------------------------------

    刚开始动手,就发现Ext的折线图中,一条曲线就是一个字段,由该字段的多个值绘成一条曲线。

    比如说:今天的流量数据,从0点到23点,那么要实现PV一条曲线,UV一条曲线,那再简单不过了。

    创建个Chart,设置好对应的字段名就OK了,这像这样子↓。

    ------------------------------我是分隔线-----------------------------------

    但除此类图表外,我还需要的是:只显示PV数据,但要的是一天一条曲线,比如昨天一条,今天一条,都是PV数据,都是0点到23点的数据,好对比嘛,

    这就麻烦了,使劲研究API后,还是得抓后脑勺......

    问了下度娘和google,倒是查了好几个,点进去一看,其实也就一两个,其他都是复制粘贴,解决办法也一样:那就是在服务器端处理好数据,格式化为Ext能解析的样式。

    比如这样的数据:

    目的是:X轴是小时(VTime),Y轴自然是PV了,然后23号一条张,24号一条线,当然,也可能要继续增加:25号一条,26号一条......

    这样Ext原生就无法支持了,以Ext对数据格式的要求:"2013-02-23"和"2013-02-24"得是字段名,字段值是PV,主键是VTime。

    如果在服务端处理的话,"2013-02-23"这样的东西,显然不能做为对象的属性。

    那要返回json给Ext就只能拼字符串了,着实麻烦,直接放弃。

    ------------------------------我是分隔线-----------------------------------

    那就只能用脚本来重新转换数据格式了。

    原本我是希望能创建一个新的Reader,就像JsonReader一样,接受到数据后,通过此Reader转换数据,多方便;无奈以我目前的功能,实在无法参透Ext的源码。

    没办法了,只能先从服务端获取原生数据,然后再逐条格式化,最后重新组合。

    new一个新的store,设置好fields,将组合好的新格式数据,填充到store里,创建Chart,将store绑定到Chart。

    OK,虽然笨拙了点,但好歹是实现了。

    下边就是代码了,因为是就着需求来写这功能,原本看着还可以配置下参数实现通用。

    但随着需求的变化,比如,需要从返回的日期字段里,分离出日,后来又要分离出月,这下好了,着急实现效果,顾不上通用不通用了,直接改之。

    ------------------------------我是分隔线-----------------------------------

    首先,这个没疑问,Ajax获取数据。格式如上边的数据库截图。

    然后便是转换数据格式了,思路大概是这样:

      通过三个配置属性:

        属性1:需要做为X轴值的字段名(如小时);

        属性2:需要做为曲线数据的字段(如PV);

        属性3:再就是需要做为一条曲线的字段(如日期)。

      转换过程:循环所有返回的原生数据,逐条分析。

    1. 根据配置,找到属性3的值,把它放进一个数组(这个数组用来存属性3的值,也就是所有曲线,数组有多少条数据,就会有多少条曲线,不重复是肯定的),再new object(),属性3的值就是此对象的一个属性,属性的值,是以属性2为名的字段值,当然,X轴怎么能忘了呢,获取属性1为我的字段值,也做为此对象的一个属性值,属性名嘛可以通过配置也可以固定死。
    2. 这样,当第一条数据走完上述步骤后,新的数据组合里,已经有一条记录了,假设这第新记录是这样的:“VHour:5,2013-02-23:15555”,接下来再循环其他原生数据时,就得做判断了,比如如果新组合里,已经有小时=5的数据了,而当前循环到的数据,也是小时=5的数据,但日期是另一天,那么就不需要new object(),直接给已经存在的数据新增一个属性就是了,就是这样:“VHour:5,2013-02-23:15555,2013-02-24:3214”。
    3. 如此循环处理完所有原生数据,那么上边的截图数据,就会变成这样的格式,当然,新数据是json格式的。
      VHour 2013-02-23 2013-02-24
      0 30311 27480
      1 19363 17506
      2 12401 11292
      3 9064 9449
      4 7680 8631
      5 12818 9248

    代码在这(这是我根据自己的需求实现的,看不懂的可以留言):

      1 /// <reference path="../../Scripts/ext4.2/ext-all.js" />
      2 Ext.define('Yiqi.Show.Chart.MultiDiyLine', {
      3     extend: 'Ext.panel.Panel',
      4     //requires: ['Yiqi.Chart.DiyJson'],
      5 
      6     BottomTitle: '小时',
      7     BaseFieldName: 'VTime', //--基本字段,以此字段为基础,将所有该字段值一样的数据,进行处理。
      8     TurnFieldName: 'VDate', //--需要将值转为新的字段名的字段
      9     NewTurnFieldFormat: '',
     10     IsSplitTurnFieldDate: false, //--将转换字段,拆分为month及day
     11     LineFormat: 'Y-m',
     12     ReturnDateFormat: 'MS',//--MVC返回的时间值,需要这样的格式来解析
     13     xFormat: 'd',
     14     ValueToField: 'PV', //--作为新字段值的原字段。
     15     ConstFields: [], //--
     16     root: 'listdata',
     17 
     18     lineFields: [],
     19 
     20     firstLoad: true,
     21 
     22     url: '/CatagoryStatics/GetHourDataByDateBetween',
     23     catagoryId: 6,
     24     dtBegin: '',
     25     dtEnd: '',
     26 
     27     initComponent: function () {
     28         var me = this;
     29         this.loadMask = new Ext.LoadMask(this, { msg: "加载中...", removeMask: true }),
     30         Ext.apply(this, {
     31             layout: 'fit'
     32         });
     33 
     34         this.callParent(arguments);
     35     },
     36     afterRender: function () {
     37         this.loadData();
     38         this.callParent(arguments);
     39     },
     40     buildTbarItems: function () {
     41         this.beginDateField = Ext.create('Ext.form.field.Date', {
     42             name: 'dtbegin',
     43             format: 'Y-m-d',
     44             fieldLabel: '起止日期',
     45             labelWidth: 60,
     46             labelAlign: 'right',
     47              170,
     48             value: Ext.Date.add(new Date(), Ext.Date.DAY, -1)
     49         });
     50         this.endDateField = Ext.create('Ext.form.field.Date', {
     51             name: 'datefield',
     52             format: 'Y-m-d',
     53             value: new Date()
     54         });
     55     },
     56     loadData: function (dateBegin, dateEnd) {
     57         var me = this;
     58         me.loadMask.show();
     59         if (dateBegin) {
     60             me.dtBegin = dateBegin;
     61         }
     62         if (dateEnd) {
     63             me.dtEnd = dateEnd;
     64         }
     65         //--先获取数据
     66         Ext.Ajax.request({
     67             url: me.url,
     68             params: {
     69                 catagoryId: me.catagoryId,
     70                 dtBegin: me.dtBegin,
     71                 dtEnd: me.dtEnd
     72             },
     73             success: function (response) {
     74                 if (response.responseText == "unauth") {
     75                     Yiqi.Common.Tools.IsLogin(response);
     76                 }
     77                 else {
     78                     //--解析并创建图表
     79                     var allData = Ext.decode(response.responseText);
     80                     me.buildAll(allData);
     81                     me.loadMask.hide();
     82                 }
     83             },
     84             failure: function (response) {
     85                 Yiqi.Common.Tools.DealAjaxError(response);
     86             }
     87         });
     88     },
     89     getParamsValue: function () {
     90         this.params.dtBegin = this.beginDateField.getValue();
     91         this.params.dtEnd = this.endDateField.getValue();
     92         this.params.catagoryId = this.catagoryId;
     93     },
     94     buildAll: function (allData) {
     95         if (!this.firstLoad) {
     96             this.removeAll(true);
     97         }
     98         this.firstLoad = false;
     99         this.buildStore(allData);
    100         var objFields = Ext.Array.remove(this.lineFields, this.BaseFieldName);
    101         objFields = Ext.Array.sort(objFields);
    102         this.buildChart(objFields);
    103         this.add(this.lineChart);
    104     },
    105     buildStore: function (orgData) {
    106         //--重新转换数据格式
    107         var newData = this.readRecords(orgData);
    108         this.dataStore = Ext.create('Ext.data.JsonStore', {
    109             autoDestroy: true,
    110             fields: this.lineFields,
    111             data: newData
    112         });
    113     },
    114     buildChart: function (fields) {
    115         this.buildChartSeries(fields);
    116         this.lineChart = Ext.create('Ext.chart.Chart', {
    117             style: 'background:#fff',
    118             animate: true,
    119             //shadow: false,
    120             theme: 'Category2',
    121             legend: {
    122                 position: 'right'
    123             },
    124             axes: [{
    125                 type: 'Numeric',
    126                 position: 'left',
    127                 fields: fields,
    128                 title: 'PV',
    129                 grid: true
    130             }, {
    131                 type: 'Category',
    132                 position: 'bottom',
    133                 fields: [this.BaseFieldName],
    134                 title: this.BottomTitle
    135             }],
    136             series: this.series,
    137             store: this.dataStore
    138         });
    139 
    140     },
    141     buildAxes: function (fields) {
    142         this.axes = [{
    143             type: 'Numeric',
    144             position: 'left',
    145             fields: fields,
    146             grid: true
    147         }, {
    148             type: 'Category',
    149             position: 'bottom',
    150             fields: [this.BaseFieldName],
    151             title: this.BottomTitle
    152         }]
    153     },
    154     buildChartSeries: function (fields) {
    155         //--循环创建
    156         var me = this;
    157         var objSeries = [];
    158         Ext.Array.each(fields, function (obj, idx, all) {
    159             objSeries[idx] = {
    160                 type: 'line',
    161                 smooth: true,
    162                 highlight: {
    163                     size: 2,
    164                     radius: 2
    165                 },
    166                 style: {
    167                     'stroke-width': 1
    168                 },
    169                 xField: me.BaseFieldName,
    170                 yField: [String(obj)],
    171                 tips: {
    172                     trackMouse: true,
    173                     renderer: function (storeItem, item) {
    174                         this.update(storeItem.get(me.BaseFieldName) + ': ' + Ext.util.Format.number(storeItem.get(String(obj)), '0,0'));
    175                     }
    176                 }
    177             };
    178         });
    179         this.series = objSeries;
    180     },
    181     readRecords: function (data) {
    182         var me = this;
    183         //--是否需要从日期字段中分离数据
    184         if (me.IsSplitTurnFieldDate) {
    185             me.BaseFieldName = "VDay";
    186         }
    187         this.lineFields = new Array();
    188         this.lineFields.push(me.BaseFieldName);
    189         var newData = [];
    190         var listData = data[me.root];
    191         var newTurnFieldName = me.TurnFieldName;
    192         //--重新转换数据
    193         Ext.each(listData, function (obj, idx, all) {
    194             var objBaseField = obj[me.BaseFieldName];
    195             //--针对日期数据的处理,因为MVC返回的时间字段数据格式是new Data(/12345465/)
    196             if (me.IsSplitTurnFieldDate) {
    197                 objBaseField = Ext.Date.format(Ext.Date.parse(obj[me.TurnFieldName], me.ReturnDateFormat), me.xFormat);
    198             }
    199             if (!Ext.isEmpty(me.NewTurnFieldFormat)) {
    200 
    201             }
    202             //--从已分析好的数据中,查看是否已经存在
    203             var newObj = me.findItemByBaseField(newData, objBaseField);
    204             //--如果已经存在,那就删除,因为接下来的操作会修改数据
    205             newData = Ext.Array.remove(newData, newObj);
    206             for (var key in obj) {
    207                 if (key == me.BaseFieldName) {
    208                     newObj[key] = obj[key];
    209                 }
    210                 else if (key == me.TurnFieldName) {
    211                     var objVal = obj[key];
    212                     //--针对日期数据的处理
    213                     if (me.IsSplitTurnFieldDate) {
    214                         objVal = Ext.Date.parse(objVal, me.ReturnDateFormat);
    215 
    216                         newObj[me.BaseFieldName] = objBaseField;
    217                         objVal = Ext.Date.format(objVal, me.LineFormat);
    218                     }
    219                     //--将解析获取的值(如可能是一个日期2013-02-23)作为对象的一个新属性,属性值也是之前配置的一个字段值
    220                     newObj[String(objVal)] = obj[me.ValueToField];
    221                     me.InsertToModelFields(String(objVal));
    222                 }
    223             }
    224             //--修改完,重新放入数据中
    225             newData = Ext.Array.push(newData, newObj);
    226         });
    227         newData = Ext.Array.sort(newData, function (a, b) {
    228             return (a[me.BaseFieldName] - b[me.BaseFieldName]);
    229         });
    230         return newData;
    231     },
    232     findItemByBaseField: function (array, val) {
    233         var newObj = {};
    234         var me = this;
    235         Ext.Array.each(array, function (obj, idx, all) {
    236             if (obj[me.BaseFieldName] == val) {
    237                 newObj = obj;
    238                 return false;
    239             }
    240         });
    241         return newObj;
    242     },
    243     InsertToModelFields: function (modelName) {
    244         var me = this;
    245         var existsFlag = false;
    246         Ext.Array.each(this.lineFields, function (field, idx, all) {
    247             var fieldName = undefined;
    248             var typeName = typeof (field);
    249             if (typeName == "string") {
    250                 fieldName = field;
    251             }
    252             else if (typeName == "object") {
    253                 fieldName = field["name"];
    254             }
    255             if (fieldName == modelName) {
    256                 existsFlag = true;
    257                 return false;
    258             }
    259         });
    260 
    261 
    262         if (!existsFlag) {
    263             this.lineFields.push(modelName);
    264         }
    265     }
    266 });
    View Code

    ------------------------------我是分隔线-----------------------------------

    这就符合Ext图表空间要求的数据格式了。

    接下来就是创建图表了,循环步骤1里的数组,逐一创建曲线,最后绑定数据,就OK了。。。

    ------------------------------我是分隔线-----------------------------------

    PS:能实现的方法确实有很多,我这样做,也是时间所迫,来不及去想更简单方便的方法,现在项目已经上线了,却又没什么精力再返回去继续研究。

  • 相关阅读:
    javascript和jquery如何判断元素是否存在最佳。
    Sping中Bean配置的深入探讨
    Spring基于 Annotation 的简单介绍
    Sping中的配置Bean详解
    Sping框架概述
    Hibernate中的HQL语言
    Hibernate的检索策略和优化
    Hibernate表关系映射之多对多映射
    Hibernate表关系映射之一对多映射
    Hibernate表关系映射之一对一映射
  • 原文地址:https://www.cnblogs.com/uphenson/p/3565110.html
Copyright © 2011-2022 走看看