FineUI小技巧(1)简单的购物车页面

时间:2021-03-01 05:55:15

起因

最初是一位 FineUI 网友对购物车功能的需求,需要根据产品单价和数量来计算所有选中商品的总价。

这个逻辑最好在前台使用JavaScript实现,如果把这个逻辑移动到后台C#实现,则会导致过多的AJAX请求而影响用户体验。

FineUI小技巧(1)简单的购物车页面

最终效果

FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面

准备数据

在生成页面之前,我们需要准备购物车的数据,这里只是简单的用表格来模拟数据:

 protected DataTable GetCartDataTable()
{
DataTable table = new DataTable();
table.Columns.Add(new DataColumn("Id", typeof(int)));
table.Columns.Add(new DataColumn("Code", typeof(String)));
table.Columns.Add(new DataColumn("Name", typeof(String)));
table.Columns.Add(new DataColumn("Desc", typeof(String)));
table.Columns.Add(new DataColumn("Price", typeof(float)));
table.Columns.Add(new DataColumn("Number", typeof(int))); DataRow row = table.NewRow();
row[] = ;
row[] = "";
row[] = "商品一";
row[] = "这是商品一的介绍";
row[] = 35.5;
row[] = ;
table.Rows.Add(row); row = table.NewRow();
row[] = ;
row[] = "";
row[] = "商品二";
row[] = "这是商品二的介绍";
row[] = 18.99;
row[] = ;
table.Rows.Add(row); row = table.NewRow();
row[] = ;
row[] = "";
row[] = "商品三";
row[] = "这是商品三的介绍";
row[] = 18.99;
row[] = ;
table.Rows.Add(row); row = table.NewRow();
row[] = ;
row[] = "";
row[] = "商品四";
row[] = "这是商品四的介绍";
row[] = 22.00;
row[] = ;
table.Rows.Add(row); return table;
}

页面标签

前台页面使用了VBox布局,用来实现底部汇总面板的高度固定,顶部表格的高度自适应页面高度的布局:

 <f:PageManager ID="PageManager1" AutoSizePanelID="Panel2" runat="server" />
<f:Panel ID="Panel2" runat="server" ShowBorder="false" Layout="VBox" BoxConfigAlign="Stretch"
BoxConfigPosition="Start" BoxConfigPadding="5" BoxConfigChildMargin="0 5 0 0"
ShowHeader="false">
<Items>
<f:Grid ID="Grid1" ShowBorder="true" BoxFlex="1" ShowHeader="true" Title="购物车"
EnableCollapse="true" runat="server" EnableCheckBoxSelect="true" CheckBoxSelectOnly="true"
DataKeyNames="Id,Code,Name" EnableTextSelection="true"> </f:Grid>
<f:ContentPanel runat="server" CssClass="totalpanel" ShowBorder="true" ShowHeader="false"> </f:ContentPanel>
</Items>
</f:Panel>

VBox布局和HBox布局对于 FineUI 来说举足轻重,如果你还搞不清楚其中的参数含义,请移步FineUI教程。  

这里有个小技巧,由于上下两个面板紧贴在一起,所以中间的两个边框就显得不好看了,我们只需通过简单的CSS来调整,使得下面面板的顶部边框宽度为零:

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

下面来看表格的定义:

 <f:Grid>
<Columns>
<f:RowNumberField />
<f:BoundField Width="120px" DataField="Code" DataFormatString="{0}" HeaderText="商品代码" />
<f:BoundField DataField="Name" ExpandUnusedSpace="true" DataFormatString="{0}" HeaderText="商品名称" />
<f:BoundField Width="120px" DataField="Price" HeaderText="商品单价" DataFormatString="¥{0:F}" />
<f:TemplateField HeaderText="数量" Width="120px">
<ItemTemplate>
<input type="hidden" class="price" runat="server" value='<%# Eval("Price") %>' />
<asp:TextBox runat="server" Width="98%" ID="tbxNumber" CssClass="number"
TabIndex='<%# Container.DataItemIndex + 10 %>' Text='<%# Eval("Number") %>'></asp:TextBox>
</ItemTemplate>
</f:TemplateField>
<f:TemplateField HeaderText="小计" Width="120px">
<ItemTemplate>
<asp:Label runat="server" CssClass="xiaoji" Text='<%# "¥" + GetXiaoji(Eval("Price"), Eval("Number")) %>'></asp:Label>
</ItemTemplate>
</f:TemplateField>
</Columns>
</f:Grid>

一些小技巧:

  • DataFormatString="¥{0:F}" 将浮点数格式化为两个小数位的字符串。
  • Container.DataItemIndex 表示当前项的序号,设置TabIndex是为了启用Tab键导航
  • 隐藏字段 class="price",是为了方便客户端使用JavaScript获取产品单价
  • 数量的文本输入框的 CssClass="number" 同样是为了方便客户端调用
  • 通过后台定义的C#函数 GetXiaoji 来计算初始产品价格小计

下面来看下 GetXiaoji 的定义:

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

接下来看下汇总面板的标签定义:

 <f:ContentPanel>
<div style="text-align: right; margin: 10px;">
<div style="margin-bottom: 10px;">
<input type="hidden" id="TOTAL_NUMBER" name="TOTAL_NUMBER" />
<span id="totalNumber" style="color: red;"></span>
件商品
</div>
<div style="margin-bottom: 10px;">
<input type="hidden" id="TOTAL_PRICE" name="TOTAL_PRICE" />
总计:<span id="totalPrice" style="color: red; font-size: 1.5em; font-weight: bold;"></span>
</div>
<div>
<f:Button runat="server" Text="去结算" Enabled="false" Size="Large" ID="btnGotoPay" OnClick="btnGotoPay_Click"></f:Button>
</div>
</div>
</f:ContentPanel>

这里面的几个小技巧:

  • 隐藏字段 TOTAL_NUMBER 和 TOTAL_PRICE 是为了方便在后台获取总价和商品总数
  • 默认设置提交按钮的 Enabled="false",在用户更改选中商品数量时来决定是否禁用  

前台JavaScript逻辑

FineUI 虽然号称 No JavaScript,但这里的真正意思是 80% 的应用场景不需要使用 JavaScript 就能轻松实现。

对于购物车这种需要前台交互的页面,还是需要开发者有一定的脚本编写功底。下面先罗列一下全部的JavaScript代码:

 var gridClientID = '<%= Grid1.ClientID %>';
var btnGotoPayClientID = '<%= btnGotoPay.ClientID %>';
var numberSelector = '.f-grid-tpl input.number';
var priceSelector = '.f-grid-tpl input.price'; function getRowNumber(row) {
return parseInt(row.find(numberSelector).val(), 10);
}
function getRowPrice(row) {
return parseFloat(row.find(priceSelector).val());
} function updateTotal() {
var grid = F(gridClientID);
var selection = grid.getSelectionModel().getSelection();
var store = grid.getStore(); var total = 0;
$.each(selection, function (index, item) {
var rowIndex = store.indexOf(item);
var row = $(grid.body.el.dom).find('.x-grid-row').eq(rowIndex);
total += getRowNumber(row) * getRowPrice(row);
}); $('#totalNumber').text(selection.length);
$('#totalPrice').text("¥" + total.toFixed(2)); $('#TOTAL_NUMBER').val(selection.length);
$('#TOTAL_PRICE').val(total.toFixed(2)); var gotoPayBtn = F(btnGotoPayClientID);
if (total === 0) {
gotoPayBtn.disable();
} else {
gotoPayBtn.enable();
}
} function registerNumberChangeEvents() {
var grid = F(gridClientID); // 数量改变事件
// http://*.com/questions/17384218/jquery-input-event
$(grid.el.dom).find(numberSelector).on('input propertychange', function (evt) {
var $this = $(this); var row = $this.parents('.x-grid-row');
var number = getRowNumber(row);
var price = getRowPrice(row);
var resultNode = row.find('.f-grid-tpl span.xiaoji'); resultNode.text("¥" + (number * price).toFixed(2)); updateTotal();
});
} function registerSelectionChangeEvents() {
var grid = F(gridClientID); grid.on('selectionchange', function (cmp, selected) {
updateTotal();
});
} // 页面第一次加载完成后调用的函数
F.ready(function () {
registerNumberChangeEvents();
registerSelectionChangeEvents();
updateTotal();
});

这里只给出一些小技巧的提醒:

  • F.ready 用来初始化所有需要的JavaScript代码,包含对 updateTotal 的调用
  • registerNumberChangeEvents 注册数量文本框改变的处理函数
  • 文本框的 input 事件用来监视文本框的内容变化,包含键盘输入、拷贝粘贴等,IE8不支持此事件但可以使用 propertychange 代替
  • registerSelectionChangeEvents 注册用户选中商品行改变的事件处理函数
  • updateTotal 中根据总价来决定是否启用提交按钮  

后台C#逻辑

后台来显示汇总信息,对熟悉 FineUI 的网友应该来说很简单:

 protected void btnGotoPay_Click(object sender, EventArgs e)
{
StringBuilder sb = new StringBuilder();
sb.Append("<ol>");
foreach(int rowIndex in Grid1.SelectedRowIndexArray) {
System.Web.UI.WebControls.TextBox tbxNumber = (System.Web.UI.WebControls.TextBox)Grid1.Rows[rowIndex].FindControl("tbxNumber"); sb.AppendFormat("<li>{0}({1})</li>", Grid1.DataKeys[rowIndex][], tbxNumber.Text);
}
sb.Append("</ol><hr/>"); sb.AppendFormat("共 {0} 件商品,总计 ¥{1}", Request.Form["TOTAL_NUMBER"], Request.Form["TOTAL_PRICE"]); Alert.Show(sb.ToString(), MessageBoxIcon.Information);
}

源代码免费下载

这个简直就是废话!

这个示例会出现在下个版本的 FineUI(开源版)中,不过目前你可以直接从微软的 codeplex 网站下载全部源代码:

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

24 张专业版截图

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

FineUI小技巧(1)简单的购物车页面FineUI小技巧(1)简单的购物车页面

推荐本文

如果本文对你有一定的启发或帮助,请点击好文要顶。你也可以通过关注本博客来及时获取 FineUI 的最新信息。

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

  1. FineUI小技巧(1)简单的购物车页面
  2. FineUI小技巧(2)将表单内全部字段禁用、只读、设置无效标识
  3. FineUI小技巧(3)表格导出与文件下载
  4. FineUI小技巧(4)关闭窗体那些事
  5. FineUI小技巧(5)向子窗口传值,向父窗口传值