文章出處

起因

最初是一位 FineUI 網友對購物車功能的需求,需要根據產品單價和數量來計算所有選中商品的總價。

這個邏輯最好在前臺使用JavaScript實現,如果把這個邏輯移動到后臺C#實現,則會導致過多的AJAX請求而影響用戶體驗。

 

最終效果

 

準備數據

在生成頁面之前,我們需要準備購物車的數據,這里只是簡單的用表格來模擬數據:

 1 protected DataTable GetCartDataTable()
 2 {
 3     DataTable table = new DataTable();
 4     table.Columns.Add(new DataColumn("Id", typeof(int)));
 5     table.Columns.Add(new DataColumn("Code", typeof(String)));
 6     table.Columns.Add(new DataColumn("Name", typeof(String)));
 7     table.Columns.Add(new DataColumn("Desc", typeof(String)));
 8     table.Columns.Add(new DataColumn("Price", typeof(float)));
 9     table.Columns.Add(new DataColumn("Number", typeof(int)));
10     
11     DataRow row = table.NewRow();
12     row[0] = 101;
13     row[1] = "100022";
14     row[2] = "商品一";
15     row[3] = "這是商品一的介紹";
16     row[4] = 35.5;
17     row[5] = 1;
18     table.Rows.Add(row);
19 
20     row = table.NewRow();
21     row[0] = 102;
22     row[1] = "100023";
23     row[2] = "商品二";
24     row[3] = "這是商品二的介紹";
25     row[4] = 18.99;
26     row[5] = 2;
27     table.Rows.Add(row);
28 
29     row = table.NewRow();
30     row[0] = 103;
31     row[1] = "100024";
32     row[2] = "商品三";
33     row[3] = "這是商品三的介紹";
34     row[4] = 18.99;
35     row[5] = 2;
36     table.Rows.Add(row);
37 
38     row = table.NewRow();
39     row[0] = 104;
40     row[1] = "100025";
41     row[2] = "商品四";
42     row[3] = "這是商品四的介紹";
43     row[4] = 22.00;
44     row[5] = 1;
45     table.Rows.Add(row);
46 
47     return table;
48 }

 

頁面標簽

前臺頁面使用了VBox布局,用來實現底部匯總面板的高度固定,頂部表格的高度自適應頁面高度的布局:

 1 <f:PageManager ID="PageManager1" AutoSizePanelID="Panel2" runat="server" />
 2 <f:Panel ID="Panel2" runat="server" ShowBorder="false" Layout="VBox" BoxConfigAlign="Stretch"
 3     BoxConfigPosition="Start" BoxConfigPadding="5" BoxConfigChildMargin="0 5 0 0"
 4     ShowHeader="false">
 5     <Items>
 6         <f:Grid ID="Grid1" ShowBorder="true" BoxFlex="1" ShowHeader="true" Title="購物車"
 7             EnableCollapse="true" runat="server" EnableCheckBoxSelect="true" CheckBoxSelectOnly="true"
 8             DataKeyNames="Id,Code,Name" EnableTextSelection="true">
 9             
10         </f:Grid>
11         <f:ContentPanel runat="server" CssClass="totalpanel" ShowBorder="true" ShowHeader="false">
12             
13         </f:ContentPanel>
14     </Items>
15 </f:Panel>

 

VBox布局和HBox布局對于 FineUI 來說舉足輕重,如果你還搞不清楚其中的參數含義,請移步FineUI教程。  

這里有個小技巧,由于上下兩個面板緊貼在一起,所以中間的兩個邊框就顯得不好看了,我們只需通過簡單的CSS來調整,使得下面面板的頂部邊框寬度為零:

1 <style>
2     .totalpanel .x-panel-body {
3         border-top-width: 0 !important;
4     }
5 </style>

 

下面來看表格的定義:

 1 <f:Grid>
 2     <Columns>
 3         <f:RowNumberField />
 4         <f:BoundField Width="120px" DataField="Code" DataFormatString="{0}" HeaderText="商品代碼" />
 5         <f:BoundField DataField="Name" ExpandUnusedSpace="true" DataFormatString="{0}" HeaderText="商品名稱" />
 6         <f:BoundField Width="120px" DataField="Price" HeaderText="商品單價" DataFormatString="¥{0:F}" />
 7         <f:TemplateField HeaderText="數量" Width="120px">
 8             <ItemTemplate>
 9                 <input type="hidden" class="price" runat="server" value='<%# Eval("Price") %>' />
10                 <asp:TextBox runat="server" Width="98%" ID="tbxNumber" CssClass="number"
11                     TabIndex='<%# Container.DataItemIndex + 10 %>' Text='<%# Eval("Number") %>'></asp:TextBox>
12             </ItemTemplate>
13         </f:TemplateField>
14         <f:TemplateField HeaderText="小計" Width="120px">
15             <ItemTemplate>
16                 <asp:Label runat="server" CssClass="xiaoji" Text='<%# "¥" + GetXiaoji(Eval("Price"), Eval("Number")) %>'></asp:Label>
17             </ItemTemplate>
18         </f:TemplateField>
19     </Columns>
20 </f:Grid>

 

一些小技巧:

  • DataFormatString="¥{0:F}" 將浮點數格式化為兩個小數位的字符串。
  • Container.DataItemIndex 表示當前項的序號,設置TabIndex是為了啟用Tab鍵導航
  • 隱藏字段 class="price",是為了方便客戶端使用JavaScript獲取產品單價
  • 數量的文本輸入框的 CssClass="number" 同樣是為了方便客戶端調用
  • 通過后臺定義的C#函數 GetXiaoji 來計算初始產品價格小計

下面來看下 GetXiaoji 的定義:

1 protected string GetXiaoji(object priceobj, object numberobj)
2 {
3     float price = Convert.ToSingle(priceobj);
4     int number = Convert.ToInt32(numberobj);
5 
6     return String.Format("{0:F}", price * number);
7 }

 

接下來看下匯總面板的標簽定義:

 1 <f:ContentPanel>
 2     <div style="text-align: right; margin: 10px;">
 3         <div style="margin-bottom: 10px;">
 4             <input type="hidden" id="TOTAL_NUMBER" name="TOTAL_NUMBER" />
 5             <span id="totalNumber" style="color: red;"></span>
 6             件商品
 7         </div>
 8         <div style="margin-bottom: 10px;">
 9             <input type="hidden" id="TOTAL_PRICE" name="TOTAL_PRICE" />
10             總計:<span id="totalPrice" style="color: red; font-size: 1.5em; font-weight: bold;"></span>
11         </div>
12         <div>
13             <f:Button runat="server" Text="去結算" Enabled="false" Size="Large" ID="btnGotoPay" OnClick="btnGotoPay_Click"></f:Button>
14         </div>
15     </div>
16 </f:ContentPanel>

 

這里面的幾個小技巧:

  • 隱藏字段 TOTAL_NUMBER 和 TOTAL_PRICE 是為了方便在后臺獲取總價和商品總數
  • 默認設置提交按鈕的 Enabled="false",在用戶更改選中商品數量時來決定是否禁用  

前臺JavaScript邏輯

FineUI 雖然號稱 No JavaScript,但這里的真正意思是 80% 的應用場景不需要使用 JavaScript 就能輕松實現。

對于購物車這種需要前臺交互的頁面,還是需要開發者有一定的腳本編寫功底。下面先羅列一下全部的JavaScript代碼:

 1 var gridClientID = '<%= Grid1.ClientID %>';
 2 var btnGotoPayClientID = '<%= btnGotoPay.ClientID %>';
 3 var numberSelector = '.f-grid-tpl input.number';
 4 var priceSelector = '.f-grid-tpl input.price';
 5 
 6 function getRowNumber(row) {
 7     return parseInt(row.find(numberSelector).val(), 10);
 8 }
 9 function getRowPrice(row) {
10     return parseFloat(row.find(priceSelector).val());
11 }
12 
13 function updateTotal() {
14     var grid = F(gridClientID);
15     var selection = grid.getSelectionModel().getSelection();
16     var store = grid.getStore();
17 
18     var total = 0;
19     $.each(selection, function (index, item) {
20         var rowIndex = store.indexOf(item);
21         var row = $(grid.body.el.dom).find('.x-grid-row').eq(rowIndex);
22         total += getRowNumber(row) * getRowPrice(row);
23     });
24 
25     $('#totalNumber').text(selection.length);
26     $('#totalPrice').text("¥" + total.toFixed(2));
27 
28     $('#TOTAL_NUMBER').val(selection.length);
29     $('#TOTAL_PRICE').val(total.toFixed(2));
30 
31     var gotoPayBtn = F(btnGotoPayClientID);
32     if (total === 0) {
33         gotoPayBtn.disable();
34     } else {
35         gotoPayBtn.enable();
36     }
37 }
38 
39 function registerNumberChangeEvents() {
40     var grid = F(gridClientID);
41 
42     // 數量改變事件
43     // http://stackoverflow.com/questions/17384218/jquery-input-event
44     $(grid.el.dom).find(numberSelector).on('input propertychange', function (evt) {
45         var $this = $(this);
46 
47         var row = $this.parents('.x-grid-row');
48         var number = getRowNumber(row);
49         var price = getRowPrice(row);
50         var resultNode = row.find('.f-grid-tpl span.xiaoji');
51 
52         resultNode.text("¥" + (number * price).toFixed(2));
53 
54         updateTotal();
55     });
56 }
57 
58 function registerSelectionChangeEvents() {
59     var grid = F(gridClientID);
60 
61     grid.on('selectionchange', function (cmp, selected) {
62         updateTotal();
63     });
64 }
65 
66 // 頁面第一次加載完成后調用的函數
67 F.ready(function () {
68     registerNumberChangeEvents();
69     registerSelectionChangeEvents();
70     updateTotal();
71 });

 

這里只給出一些小技巧的提醒:

  • F.ready 用來初始化所有需要的JavaScript代碼,包含對 updateTotal 的調用
  • registerNumberChangeEvents 注冊數量文本框改變的處理函數
  • 文本框的 input 事件用來監視文本框的內容變化,包含鍵盤輸入、拷貝粘貼等,IE8不支持此事件但可以使用 propertychange 代替
  • registerSelectionChangeEvents 注冊用戶選中商品行改變的事件處理函數
  • updateTotal 中根據總價來決定是否啟用提交按鈕  

后臺C#邏輯

后臺來顯示匯總信息,對熟悉 FineUI 的網友應該來說很簡單:

 1 protected void btnGotoPay_Click(object sender, EventArgs e)
 2 {
 3     StringBuilder sb = new StringBuilder();
 4     sb.Append("<ol>");
 5     foreach(int rowIndex in Grid1.SelectedRowIndexArray) {
 6         System.Web.UI.WebControls.TextBox tbxNumber = (System.Web.UI.WebControls.TextBox)Grid1.Rows[rowIndex].FindControl("tbxNumber");
 7 
 8         sb.AppendFormat("<li>{0}({1})</li>", Grid1.DataKeys[rowIndex][2], tbxNumber.Text);
 9     }
10     sb.Append("</ol><hr/>");
11 
12     sb.AppendFormat("共 {0} 件商品,總計 ¥{1}", Request.Form["TOTAL_NUMBER"], Request.Form["TOTAL_PRICE"]);
13 
14     Alert.Show(sb.ToString(), MessageBoxIcon.Information);
15 }

 

源代碼免費下載

這個簡直就是廢話!

這個示例會出現在下個版本的 FineUI(開源版)中,不過目前你可以直接從微軟的 codeplex 網站下載全部源代碼:

https://fineui.codeplex.com/SourceControl/list/changesets

24 張專業版截圖

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

推薦本文

如果本文對你有一定的啟發或幫助,請點擊好文要頂。你也可以通過關注本博客來及時獲取 FineUI 的最新信息。

 

 

《FineUI小技巧》系列文章目錄

  1. FineUI小技巧(1)簡單的購物車頁面
  2. FineUI小技巧(2)將表單內全部字段禁用、只讀、設置無效標識
  3. FineUI小技巧(3)表格導出與文件下載
  4. FineUI小技巧(4)關閉窗體那些事
  5. FineUI小技巧(5)向子窗口傳值,向父窗口傳值

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()