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             width: 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:能實現的方法確實有很多,我這樣做,也是時間所迫,來不及去想更簡單方便的方法,現在項目已經上線了,卻又沒什么精力再返回去繼續研究。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM