感谢广大博问博友的帮助和共同研究讨论,终于实现了一个无缓存无刷新仿购物车的小功能:
一、实现效果简述:
有一种列表,是由双层Repeater嵌套,第一层用来显示类别,第二层用来显示类别下的商品数据,
其显示效果如下:
且每一个菜名都可以单独点开进行配餐选择,效果如下:
现在要在页面无刷新不计入缓存(在最后提交选餐数据的时候计入缓存,类似购物车,但这里不希望页面刷新)
的情况下将所选择的商品名称和数量无重复的添加到一个叫做“餐饮盒”的容器里:
最终实现结果:
在修改订购数量的时候只修改餐饮盒中该商品的总价格,商品名称不能重复,并重新计算所有商品的总价格:
二、实现代码:
1.前台界面由2个Repeater组成:
<asp:Repeater ID="rptFoodKindList" runat="server" OnItemDataBound="rptFoodKindList_OnItemDataBound"> <ItemTemplate> <div class="Cp_fl"> <%#Eval("CategoryTwoName") %><asp:HiddenField ID="hidCategoryTwoID" Value=‘<%#Eval("CategoryTwoID") %>‘ runat="server" /> </div> <asp:Repeater ID="rptFoodList" runat="server" OnItemDataBound="rptFoodList_OnItemDataBound"> <ItemTemplate> <div class="CP_con"> <ul> <li class="CP_xh"><span id="spIndex" runat="server"></span></li> <li class="CP_name"><span id="spProName"> <%#Eval("ProductName") %></span></li> <li class="CP_jg">¥<%#decimal.Parse(Eval("Price").ToString()).ToString("f2") %></li> <li class="CP_fs">(已选<span id="spCount">0</span>份)<asp:HiddenField ID="hidProductID" runat="server" Value=‘<%#Eval("ProductID") %>‘ /> </li> </ul> <div class="CP_mx" style="display: none;"> <div class="img"> <img src="../ProductImg/160x120/201403011616.png" /> <a href="#">点击查看大图</a> </div> <div class="right"> <div class="R_name"> 菜名:<%#Eval("ProductName") %> </div> <div class="R_dj"> 单价:<span id="spPrice" runat="server">¥<%#decimal.Parse(Eval("Price").ToString()).ToString("f2") %></span> 元/份</div> <div class="R_numer"> <span>订购数量:</span><a id="aRed" class="Redu1" runat="server"></a><input id="txtBuyNum" type="text" value="1" runat="server" /><a class="Add1" id="aAdd" runat="server"></a></div> <div class="R_but"> <a class="Add_CD" id="aAddTo" runat="server">加到我的餐饮盒</a><a class="Redu_CD">取消</a></div> </div> </div> </div> </ItemTemplate> </asp:Repeater> </ItemTemplate> </asp:Repeater>
<asp:HiddenField ID="hidProInfo" runat="server" />
2.主要Jquery代码:
(1)控制商品详情的显示和隐藏:
$(function(){ var n = 0; //商品详情和订购选择区域默认隐藏 $(".CP_con").each(function () { var t = $(this);//获取当前的行 t.find("ul").click(function () { if (n == 0) {//如果隐藏则显示 t.find(".CP_mx").show(); n = 1; //设置为显示 } else {//如果显示则隐藏 t.find(".CP_mx").hide(); n = 0; } }); var $cancel = $(".Redu_CD");//取消 var $CP_mx = $(".CP_mx"); t.find($cancel).click(function () { t.find($CP_mx).hide(); });
//鼠标放置和离开时当前行的样式控制,这里是淡蓝色; $(".CP_con>ul").mouseover(function () { $(this).attr("style", "background-color:#F5F9FD"); }).mouseout(function () { $(this).removeAttr("style"); }); }); }); })
(2)修改商品订购数量以及更新餐饮盒情况:
//修改订购产品数量,type=1:添加;type=2:减少 function UpdateProductNum(btn, type, price) { var text = $(btn).parent().find(":text"); if (type == 1) { text.val(parseInt(text.val()) + 1); if (parseInt(text.val()) <= 0) text.val("1"); } else { text.val(parseInt(text.val()) - 1); if (parseInt(text.val()) <= 0) text.val("1"); } } //添加到我的餐饮盒,修改其商品数量,商品详情变动和价格计算 function AddProductNum(btn, price, proid, index) { var proAmount = 0;//每一种商品的总价格 var text = $(btn).parent().parent().find(":text");//查找修改数量的文本框 var count = $(".CP_fs>span")[index];//获取当前商品已选择份数控件 var proname = $(".CP_name>span")[index];//获取当前商品名称控件 var spCount = $(btn).parent().parent().parent().parent().find(count); var spProName = $(btn).parent().parent().parent().parent().find(proname); spCount.html(text.val());//将当前份数设置为文本框中选择的份数 var curAmount = price * text.val();//计算当前总订购商品的价格 if ($("#ul" + proid).length > 0) {//如果已经存在该商品 $("#ul" + proid).attr("class", proid + "|" + text.val());//样式妙用,class用来存放商品id和订购数量,便于以下隐藏域使用 $("#ul" + proid).attr("title", "单价:¥"+price+".00;数量:"+text.val()+"份");//设置提示信息 $("#ul" + proid).html("<li class=‘li_l‘ >" + spProName.html() + "</li><li class=‘li_r‘>¥" + curAmount + ".00</li>");//修改该商品的总价格信息 } else {//不存在则添加一行 $("#divFoodBox").html($("#divFoodBox").html() + "<ul id=‘ul" + proid + "‘ class=‘" + proid + "|" + text.val() + "‘ title=‘单价:¥"+price+".00;数量:"+text.val()+"份‘‘><li class=‘li_l‘ >" + spProName.html() + "</li><li class=‘li_r‘>¥" + curAmount + ".00</li><ul>"); } $("#divFoodBox .li_r").each(function () {//循环查找显示总价格的li控件 proAmount += parseFloat($(this).html().replace("¥", ""));//计算当前行商品总价格并追加到临时变量中; }); var arrClass="";//初始化样式字符串 $("#divFoodBox>ul").each(function () { arrClass+=$(this).attr("class")+","; }); $("#hidProInfo").val(arrClass); $("#liSum").html("¥" + proAmount+ ".00");//更新所有订购商品总价格 }
3.后台第一个Reapeter绑定时追加前台js方法:
/// <summary> /// 菜谱分类列表绑定事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void rptFoodKindList_OnItemDataBound(object sender, RepeaterItemEventArgs e) { if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) { Repeater rptFoodList = e.Item.FindControl("rptFoodList") as Repeater;//第一个Reapeter的每行绑定时查找第二层里面的Reapeter HiddenField hidCategoryTwoID = e.Item.FindControl("hidCategoryTwoID") as HiddenField; rptFoodList.DataSource = HSSM_Public_DB.PblicQuery("ProductID,ProductName,Price", "Product", "CategoryTwoID = " + hidCategoryTwoID.Value); rptFoodList.DataBind();//绑定里面的Reapeter //循环里面Reapeter的项,并给相应控件追加前台js方法 foreach (RepeaterItem item in rptFoodList.Items) { HtmlInputText txtBuyNum = item.FindControl("txtBuyNum") as HtmlInputText;//查找订购数量输入框 HtmlGenericControl spPrice = item.FindControl("spPrice") as HtmlGenericControl;//查找显示价格的控件 HtmlGenericControl spIndex = item.FindControl("spIndex") as HtmlGenericControl;//查找显示该商品所属分类的序号控件 string Price = spPrice.InnerText.Replace("¥", ""); spIndex.InnerText = item.ItemIndex + 1 + ".";//设置序号 HtmlAnchor aAdd = item.FindControl("aAdd") as HtmlAnchor;//添加订购数量的控件 HtmlAnchor aRed = item.FindControl("aRed") as HtmlAnchor;//减少订购数量的控件 HtmlAnchor aAddTo = item.FindControl("aAddTo") as HtmlAnchor;//添加到我的餐饮盒的控件 HiddenField hidProductID = item.FindControl("hidProductID") as HiddenField;//存放商品id的隐藏域 aAdd.Attributes.Add("onclick", string.Format("UpdateProductNum(this,1,{0})", Price));//追加添加订购数量的js方法 aRed.Attributes.Add("onclick", string.Format("UpdateProductNum(this,2,{0})", Price));//追加减少订购数量的js方法 aAddTo.Attributes.Add("onclick", string.Format("AddProductNum(this,{0},{1},{2})", Price, hidProductID.Value, proIndex));//追加更新我的餐饮盒详情的js方法 proIndex++;//每绑定一行商品让这个全局变量值自加1,用于前台js调用find方法寻找当前商品已选择份数和商品名称时使用, } } } /// <summary> /// 产品记录索引(全局变量) /// </summary> public int proIndex = 0;
4.最后填写好订购人信息后计入缓存:
/// <summary> /// 设置Cookie /// </summary> protected void SetFoodList() { string ProInfos = hidProInfo.Value.Substring(0, hidProInfo.Value.Length - 1);//获取最后所有的商品的id和数量信息,这里需要去掉最后一个逗号 string[] arrProInfos = ProInfos.Split(‘,‘); HttpCookie cookieFood = new HttpCookie("MyShoppingCart");//初始化一个Cookie对象 foreach (string strProInfo in arrProInfos) { string strProID = strProInfo.Split(‘|‘)[0];//商品id string strProCount = strProInfo.Split(‘|‘)[1];//该商品订购数量 ProModel = GetProModel(strProID); ImagesModel = GetProImageModel(strProID); //DateTime dt = DateTime.Now; //TimeSpan ts = new TimeSpan(0, 0, 1, 0, 0);//过期时间为1分钟 //cookie.Expires = dt.Add(ts);//设置过期时间 //设置cookie的值 cookieFood.Values.Add("Company", ComModel.Company); cookieFood.Values.Add("ProductID", strProID); cookieFood.Values.Add("ProductName", ProModel.ProductName); cookieFood.Values.Add("SaleUserID", SaleUserID.ToString()); cookieFood.Values.Add("ProductImg", ImagesModel.ImageUrl); cookieFood.Values.Add("Price", ProModel.Price.Value.ToString("f2")); cookieFood.Values.Add("Bonus", "1"); cookieFood.Values.Add("Count", strProCount); Response.Cookies.Add(cookieFood); } } /// <summary> /// 保存配送信息 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void btnDispatch_Click(object sender, EventArgs e) { SetFoodList(); Response.Redirect("OrderInfo.aspx"); }
小插曲→计入到关键隐藏域hidProInfo的值是这样的:
ok,感谢主,这个功能就此结束了,之前有博问博友建议加到餐饮盒时就加入缓存,但是考虑到需要后台处理,页面会刷新,如果用ajax也可以实现局部异步刷新,过后我再尝试一下看看,不过我这种实现方法是另一种思路,我选择最后点击“OK,下一步”的时候计入缓存到那边选餐订单确认页面获取缓存显示商品情况。其实这当中关键点就是一个class的妙用(本来class是用来指定样式表,这里用来存放数据未尝不可)和隐藏域的使用。