首页 > 代码库 > 使用 FineUI 制作简单的购物车页面(源代码免费下载,另附 24 张专业版截图)
使用 FineUI 制作简单的购物车页面(源代码免费下载,另附 24 张专业版截图)
起因
最初是一位 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-event44 $(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 的最新信息。