今天來完成排序問題,這個在書中樣本不少,不過,在這裡囉嗦一下也是有好處的。要實現遠程排序,首先要清楚的是排序資訊是如何提交到伺服器的,而這個,利用Firebug相當簡單。在VS,切換到PicManager.js檔案,找到filestore的定義,先將renmoteSort修改為true。然後添加sorters配置項,代碼如下:sorters: [ { property:"modify" , direction : "DESC" }], 這段代碼的意思就是預設排序的欄位為modify,排序方向為順序排序。現在,在瀏覽器開啟圖片管理,注意觀察Firebug中的請求。35,會看到提交的請求中,排序資訊是以JArray數組形式提交的,要將排序資訊提取出來,就要先將字串轉換為JArray,然後再提取。現在來完成這個。因為,排序資訊會在很多地方使用,因而可以在MyFunction中寫一個通用方法處理這個。650) this.width=650;" src="http://www.bkjia.com/uploads/allimg/131228/1234501F3-0.PNG" alt="" />圖34 Firebug中顯示的排序資訊
那麼,這個方法要怎麼寫呢?首先,要讓該方法知道,它要提取的欄位包括那些,只有符合要求的欄位才會被提取。其次,就是要將要處理的字串傳遞給該方法。最後,如果處理過程找不到符合要求的排序資訊,就返回一個預設的排序,因而,需要給該方法傳遞一個預設值。最後,要考慮方法怎麼返回排序資訊。新的LINQ版本支援動態查詢,它的排序資訊可以是以下格式:it.欄位1 排序方向1, it.欄位2 排序方向2,...因而,方法只需要返回以上形式組成的字串就可以了。目標確定了,切換到MyFunction.cs來完成這個方法。在MyFunction.cs內建立一個名為ProcessSorterString的靜態方法,代碼如下:public static string ProcessSorterString(string[]fields, string sortinfo,string defaultSort){ if(string.IsNullOrEmpty(sortinfo)) return defaultSort; JArray ja= JArray.Parse(sortinfo); stringresult = ""; foreach(JObject c in ja) { string field = (string)c["property"]; if(fields.Contains(field)) { result += string.Format("it.{0} {1},", field,(string)c["direction"] == "ASC" ? "" :"DESC"); } } if(result.Length > 0) { result = result.Substring(0, result.Length - 1); } else { result = defaultSort; } returnresult;} 代碼中,先檢查要處理的排序資訊是否為空白或Null 字元串,如果是,返回預設值。接著,將字串轉換為JArray對象,然後一個個提取排序對象,如果在指定的欄位數組內包含該欄位,就根據格式組合字元串。最後,檢查組合的字串是否有符合要求的排序資訊,如果沒有,返回預設值。在實現前,先開啟管理NuGet程式包視窗,搜尋DynamicQuery,然後安裝Dynamic Expression API程式包以實現動態排序。如果是使用Entity Framework,其內部已經包含了動態查詢,不需要安裝該包,在這裡,因為返回的是FileInfo集合,沒有使用到Entity Framework,因而要安裝該程式包。現在切換到File控制器,修改代碼如下以實現排序功能:[AjaxAuthorize(Roles = "普通使用者,系統管理員")]public JObject List(){ boolsuccess = false; stringmsg = ""; JArray ja= new JArray(); int total= 0; try { intstart = 0; int.TryParse(Request["start"], out start); string path = Request["path"] ?? "/"; string sort = Request["sort"] ?? ""; sort= Helper.MyFunction.ProcessSorterString(new string[]{"filename","modify","size" }, sort,"it.LastWriteTime ASC"); DirectoryInfo dir = new DirectoryInfo(Server.MapPath(root + path)); total= dir.GetFiles().Count(); var q= dir.GetFiles().Select(m=>new { filename=m.Name, modify=m.LastWriteTime, size=m.Length }).AsQueryable().OrderBy(sort).Skip(start).Take(50); foreach (var c in q) { ja.Add(new JObject { new JProperty("path",path), new JProperty("filename",c.filename), new JProperty("modify",c.modify.ToString("yyyy-MM-ddhh:mm")), new JProperty("size",c.size) }); } success = true; } catch(Exception e) { msg =e.Message; } returnHelper.MyFunction.WriteJObjectResult(success, total, msg, ja);} 代碼中,添加了處理提交參數sort的代碼。因為用戶端的欄位名和伺服器端的欄位名不對應,因而在查詢時使用Select方法將其欄位名轉換一下。還有一個要注意的地方,就是需要AsQueryable將集合類型轉換IQueryable類型,因為動態查詢只支援該類型的資料。因為修改了欄位名,所以foreach迴圈中也要做相應修改。現在,要在用戶端加一個排序菜單以實現排序功能。切換到PicManager.js檔案,找到me.items的定義,在圖片檔案的配置項中添加tbar配置項,用來放置一個分頁工具條,並在工具條上放一個SplitButton用來實現排序功能。在SplitButton下定義一個由6個子功能表組成的菜單。這6個子功能表必須是單選的,也就是一次只能選擇一個。具體代碼如下:tbar: { xtype:"pagingtoolbar", pageSize:20, displayInfo: true, store: me.filestore, items: [ '-', {xtype:"splitbutton",iconCls:"sort",tooltip:"排序",text:"排序", scope:me, menu :{ items:[ { text: '檔案名稱順序',group: 'sort',checked:false,checkHandler: me.onSort,scope:me, fieldname:"filename",sortdir:"ASC" }, { text: '檔案名稱降序',group: 'sort',checked:false,checkHandler: me.onSort,scope:me, fieldname:"filename",sortdir:"DESC" }, { text: '修改日期順序',group: 'sort',checked:false,checkHandler: me.onSort,scope:me, fieldname:"modify",sortdir:"ASC" }, { text: '修改日期降序',checked: true,group:'sort',checkHandler: me.onSort,scope:me, fieldname:"modify",sortdir:"DESC" }, { text: '檔案大小順序',group: 'sort',checked:false,checkHandler: me.onSort,scope:me, fieldname:"size",sortdir:"ASC" }, { text: '檔案大小降序',group: 'sort',checked:false,checkHandler: me.onSort,scope:me, fieldname:"size",sortdir:"DESC" } ] } } ] 注意代碼中子功能表的定義。每個子功能表都有一個group配置項,且它們的值是相同的,這樣就可將6個子功能表組合為一組了。配置項checked是必不可少的,該配置項決定了菜單的是一個單選功能的子功能表。因為剛才在Store的定義中,預設情況下是以修改日期降序排序的,因而該子功能表的checked的值被設定為true。還有一個地方比較特別,就是把子功能表相關的欄位和排序方向都以配置項形式定義好了,這樣在編寫onSort方法的時候,處理起來就很方便,以下就是onSort的代碼:onSort: function(item, checked){ varme=this; if(checked){ me.filestore.sort({property: item.fieldname , direction : item.sortdir}); me.filestore.load(); }} 因為每個子功能表都包含了欄位和排序方向資訊,因而,這裡就不需要做判斷了,直接調用Store的sort方法進行排序就行了。重新設定排序後,調用load方法重新就可以了。這裡唯一要注意的地方是,子功能表在取消選擇和選擇的時候都會觸發該方法,因而需要檢查checked的值,當它為true的時候才進行處理。在定義排序按鈕的時候使用iconCls配置項為圖片添加了一個圖片,因而要在app.css中添加它的樣式,代碼如下:.sort{ background:url("../images/sort.png")!important;}好了,現在產生一下解決方案,然後重新整理一下頁面,在瀏覽器將會看到36所示的效果。650) this.width=650;" src="http://www.bkjia.com/uploads/allimg/131228/1234502914-1.PNG" alt="" />
圖36 根據修改日期降序排序看到的結果
至此,排序功能就實現了。現在考慮一下視圖的選擇問題,在作業系統中,一般都可以使用拖動的方式選擇檔案,這個功能相當實用,而在Ext JS,要實現該功能也很簡單,只有使用Ext JS包中的使用者外掛程式Ext.ux.DataView.DragSelector就可簡單實現。先在解決方案ExtJS\ux目錄下建立一個DataView目錄,然後在Ext JS包中examples\ux\DataView目錄下,將DragSelector.js檔案複製到該目錄。為什麼要這樣?因為動態載入是根據類名來找檔案的,注意Ext.ux.DataView.DragSelector的類名,在ux目錄下,多了一個DataView,因而需要添加DataView目錄。在DataView目錄下還有一個DragSelector.css檔案,定義了DragSelector要用到的一些樣式,把檔案裡的樣式複製到app.css就行了。切換到PicManager.js檔案,先在layout配置項下加一個requires配置項,來聲明該類需要使用到DragSelector類,代碼如下:requires:["Ext.ux.DataView.DragSelector"], 接著在me.dataview的定義中添加以下代碼來建立外掛程式:plugins: [ Ext.create('Ext.ux.DataView.DragSelector', {})], 因為DragSelector類沒有定義別名,所以不能使用xtype進行定義,只能直接建立了。重新整理一下瀏覽器,然後在視圖中任意點按下滑鼠左鍵,然後拖動滑鼠,就可以看到類似圖37所示的效果,通過拖動方式選擇圖片了。650) this.width=650;" src="http://www.bkjia.com/uploads/allimg/131228/12345060a-2.PNG" alt="" />圖37 拖動選擇圖片
現在來完成圖片的刪除功能。先在分頁工具條上添加一個刪除按鈕,代碼如下:{ iconCls: "picture-delete", handler:me.onDelete, scope: me, disabled: true, tooltip: "刪除圖片" } 注意,目前按鈕的狀態是禁用狀態的。因而需要在視圖選擇了圖片的時候開啟它。同檔案夾刪除按鈕一樣,這裡也不能使用id,只能使用查詢方式擷取按鈕。在dataview的定義中,添加以下代碼監聽視圖的selectionchange事件:listeners: { scope:me, selectionchange: me.onPictureSelect} 接著完成onPictureSelect方法,代碼如下:onPictureSelect: function (model, sels) { this.down("button[tooltip=刪除圖片]").setDisabled(sels.length == 0);} 因為沒有合適的上一層組件,因而只能從組件本身開始往下找了。 現在完成刪除圖片的onDelete方法,代碼如下:onDelete: function () { var me =this, rs= me.dataview.getSelectionModel().getSelection(); if(rs.length > 0) { content = ["確定刪除以片?"]; for(var i = 0; ln = rs.length, i < ln; i++) { content.push(rs[i].data.filename); } Ext.Msg.confirm("刪除圖片",content.join("<br/>"), function (btn) { if (btn == "yes") { var me = this, store = me.dataview.store, rs = me.dataview.getSelectionModel().getSelection(); store.remove(rs); store.sync({ success: function (e, opt) { this.store.commitChanges(); }, failure: function (e, opt) { this.store.rejectChanges() Ext.Msg.alert("發生錯誤", e.exceptions[0].error); }, scope: me.dataview }); } },me) } else { Ext.Msg.alert('刪除記錄','請選擇要刪除的記錄。'); }} 代碼與之前刪除操作的代碼沒什麼不同,因而有興趣,可以研究一下將這些代碼統一起來,這樣就不用粘貼複製了。這裡的焦點還是使用Store的remove方法刪除資料,然後調用sync同步,如果伺服器端刪除成功,就調用commitChanges方法確認修改,否則調用rejectChanges方法取消刪除。如果是希望在刪除後重新載入頁面,可以將commitChanges方法修改為load方法,重新載入資料。現在切換到File控制器完成刪除操作,方法與檔案夾的刪除差不多,代碼如下:[AjaxAuthorize(Roles = "普通使用者,系統管理員")]public JObject Delete(){ boolsuccess = false; stringmsg = ""; JArray ja= null; try { string data = Request["data"] ?? ""; if(string.IsNullOrEmpty(data)) { msg = "錯誤的提交資料。"; } else { ja = JArray.Parse(data); if (ja.Count > 0) { foreach(JObject jo in ja) { string path = Server.MapPath(root + (string) jo["path"] +(string) jo["filename"]); FileInfo file = new FileInfo(path); if (file.Exists) { file.Delete(); } } success = true; } else { msg = "錯誤的提交資料。"; } } } catch(Exception e) { msg =e.Message; } returnHelper.MyFunction.WriteJObjectResult(success, 0, msg, ja);} 至此,刪除操作就完成了。圖片管理就餘下最麻煩的上傳操作了,這個在下一篇文章再說。
原始碼地址: http://vdisk.weibo.com/s/gKlcP
本文出自 “黃燈橋的部落格” 部落格,請務必保留此出處http://dqhuang.blog.51cto.com/2522725/1039975