正文

ASP.NET MVC实现横向展示购物车(asp.net web程序设计)不看后悔

3

文章摘要

本文介绍了使用ASP.NET MVC实现一个横向展示的购物车系统。购物车采用CartIndexVm模型,每个购物车页面显示商品列表、数量调整、总价计算及继续购物按钮。系统支持通过异步操作(使用TouchSpin插件)调整商品数量,并通过Session和URL重定向实现清空购物车功能。文章详细讨论了购物车的结构、功能实现及代码细节,展示了如何通过强类型视图和模型绑定实现高效的购物车管理。



通常,我们看到的购物车是这样的:

3

虽然这种购物车显示方式被广泛运用,但我个人觉得不够直观。如果换成这样呢?

本篇的源码放在了:https://github.com/darrenji/ShoppingCartInMVC

以上购物车页能实现的效果包括:

1、购物车明细:显示订购数量、总金额,清空购物车。

2、购物车内产品:数量可调整,对应的小计和总计动态变化。点击移除按钮移除该产品。

3、继续购物按钮:点击左下角的继续购物按钮,回到先前页。

4、使用了Bootstrap, 页面元素自适应,页面宽度调小时,页面布局动态变化。

5、每行放置4个产品,且允许高度不一致,第5个产品另起一行,且不会float到上一行的空白区域,如下图。

首先,有关产品的类。

public class Product { public int Id { get; set; } public string Name { get; set; } public string ImageUrl { get; set; } public string Description { get; set; } public decimal Price { get; set; } }

产品选购页如图:

以上,产品选购页是一个有关Product集合的强类型视图页,其对应的Model为:

public class ProductsListVm { public ProductsListVm() { this.Products=new List<Product>(); } public IEnumerable<Product> Products { get; set; } }

想像一下,我们在超市购物,在购物车内放着不同的商品对应不同的数量,在这里,可以把商品和数量抽象成一个类:

public class CartLine { public Product Product { get; set; } public int Quantity { get; set; } }

而购物车类实际上就是维护着这个CartLine集合,需要提供添加、移除、计算购物车总价、清空购物车等方法,并提供一个获取到CartLine集合的属性,另外,针对点击购物车页上的增量和减量按钮,也要提供相应的方法。

public class Cart { private List<CartLine> lineCollection=new List<CartLine>(); //添加 public void AddItem(Product product, int quantity) { CartLine line=lineCollection.Where(p=> p.Product.Id==product.Id).FirstOrDefault(); if (line==null) { lineCollection.Add(new CartLine(){Product=product, Quantity=quantity}); } else { line.Quantity +=quantity; } } //点击数量+号或点击数量-号或自己输入一个值 public void IncreaseOrDecreaseOne(Product product, int quantity) { CartLine line=lineCollection.Where(p=> p.Product.Id==product.Id).FirstOrDefault(); if (line !=null) { line.Quantity=quantity; } } //移除 public void RemoveLine(Product product) { lineCollection.RemoveAll(p=> p.Product.Id==product.Id); } //计算总价 public decimal ComputeTotalPrice() { return lineCollection.Sum(p=> p.Product.Price*p.Quantity); } //清空 public void Clear() { lineCollection.Clear(); } //获取 public IEnumerable<CartLine> Lines { get { return lineCollection; } } }

购物车页自然就是针对Cart类的一个强类型视图页,嗯,等等,购物车页还需要记录下上一个页面的url,于是,考虑到把Cart类和记录上一个页面url这2个因素,针对购物车页,给出这样的一个Model:

public class CartIndexVm { public Cart Cart { get; set; } public string ReturnUrl { get; set; } }

在HomeController中,需要用到购物车的实例,可以这样写:

private Cart GetCart() { Cart cart=(Cart)Session[“Cart”]; if (cart==null) { cart=new Cart(); Session[“Cart”]=cart; } return cart; }

Cart实例保存到Session中,并从Session中获取。当然,也可以放到ASP.NET MVC绑定机制中,需要做的就是实现IModelBinder接口。

public class CartModelBinder : IModelBinder { private const string sessionKey=”Cart”; public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { Cart cart=(Cart)controllerContext.HttpContext.Session[sessionKey]; if (cart==null) { cart=new Cart(); controllerContext.HttpContext.Session[sessionKey]=cart; } return cart; } }

自定义的ModelBinder需要在全局中注册。

public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); …… ModelBinders.Binders.Add(typeof(Cart), new CartModelBinder()); } }

在Home控制器中,首先提供了一个返回Product集合的方法。

private List<Product> GetAllProducts() { return new List<Product>() { new Product(){Id=1, Description=”产品描述产品描述产品描述产品描述产品描述产品描述产品描述”,ImageUrl=”/images/1.jpg”,Name=”产品1″,Price=85M}, new Product(){Id=2, Description=”产品描述产品描述产品描述产品描述产品描述产品描述产品描述”,ImageUrl=”/images/2.jpg”,Name=”产品2″,Price=95M}, new Product(){Id=3, Description=”产品描述产品描述产品描述”,ImageUrl=”/images/2.jpg”,Name=”产品3″,Price=55M}, new Product(){Id=4, Description=”产品描述产品描述产品描述产品描述产品描述产品描述产品描述”,ImageUrl=”/images/1.jpg”,Name=”产品4″,Price=65M}, new Product(){Id=5, Description=”产品描述产品描述产品描述产品描述产品描述产品描述产品描述”,ImageUrl=”/images/2.jpg”,Name=”产品5″,Price=75M} }; }

在HomeController中,有关产品选购页的如下:

//产品选购页 public ActionResult Index() { ProductsListVm productsListVm=new ProductsListVm(); productsListVm.Products=GetAllProducts(); return View(productsListVm); }

Homme/Index.cshtml是一个ProductsListVm的强类型视图页。

@model MvcApplication1.Models.ProductsListVm @{ ViewBag.Title=”Index”; Layout=”~/Views/Shared/_Layout.cshtml”; } <style type=”text/css”> .item { border-bottom: solid 1px gray; } </style> <div class=”container”> <div class=”row”> @foreach (var item in Model.Products) { Html.RenderPartial(“ProductSummary”, item); } </div> </div>

其中,遍历Product集合的时候,又去加载Views/Shared/ProductSummary.cshtml这个强类型部分视图。

@model MvcApplication1.Models.Product <div class=”item”> <h3>@Model.Name</h3> <p><img src=”https://www.jb51.net/article/@Model.ImageUrl” style=”width: 100px;height: 100px;”/></p> <p>@Model.Description</p> <h4>@Model.Price.ToString(“c”)</h4> @using (Html.BeginForm(“AddToCart”, “Home”)) { @Html.HiddenFor(p=> p.Id) @Html.Hidden(“returnUrl”, Request.Url.PathAndQuery) <input type=”submit” value=”+放入购物车”/> } </div>

点击”+放入购物车”按钮,调用HomeController中的AddToCart方法,并且需要把选购产品页的url以query string的形式传递给控制器方法。

//购物车页 public ActionResult CartIndex(Cart cart, string returnUrl) { return View(new CartIndexVm { Cart=cart, ReturnUrl=returnUrl }); } //添加到购物车 public ActionResult AddToCart(Cart cart, int id, string returnUrl) { Product product=GetAllProducts().Where(p=> p.Id==id).FirstOrDefault(); if (product !=null) { cart.AddItem(product, 1); } return RedirectToAction(“CartIndex”, new {returnUrl}); }

购物车页Home/CartIndex.cshtml是一个CartIndexVm的强类型视图页。

@model MvcApplication1.Models.CartIndexVm @{ ViewBag.Title=”CartIndex”; Layout=”~/Views/Shared/_Layout.cshtml”; } @section styles { <link href=”https://www.jb51.net/article/~/Content/shopitem.css” rel=”external nofollow” rel=”stylesheet” /> <link href=”https://www.jb51.net/article/~/Content/jquery.bootstrap-touchspin.min.css” rel=”external nofollow” rel=”stylesheet” /> } <div class=”container”> <div class=”row”> @for (int i=0; i < Model.Cart.Lines.Count(); i++) { var item=(Model.Cart.Lines.ToList())[i]; if (i !=0 && i%4==0) //每行有4个div { <div style=”clear:both;”></div> } <div class=”col-md-3 column productbox”> <img src=”https://www.jb51.net/article/@item.Product.ImageUrl” style=”width: 460px; height: 250px;” class=”img-responsive”> <div class=”producttitle”> <div class=”productname”>@item.Product.Name</div> <div class=”productdes”>@item.Product.Description</div> <div> <table> <tr> <td style=”width:50px;”>单价:</td> <td>@item.Product.Price</td> </tr> <tr> <td>数量:</td> <td> <input class=”demo2″ type=”text” value=”@item.Quantity” name=”demo2″ /> </td> </tr> <tr> <td>小计:</td> <td>@((item.Quantity * item.Product.Price).ToString(“c”))</td> </tr> </table> </div> </div> <div class=”productprice”> <div class=”text-center”> @using (Html.BeginForm(“RemoveFromCart”, “Home”)) { @Html.Hidden(“Id”, item.Product.Id) @Html.HiddenFor(x=> x.ReturnUrl) <input class=”btn btn-default btn-sm” type=”submit” value=”移除”/> <a href=”https://www.jb51.net/article/262373.htm#” rel=”external nofollow” class=”btn btn-danger btn-sm” role=”button”>查看</a> } </div> </div> </div> } </div> </div> <hr/> <div class=”container”> <div class=”row”> <div class=”text-center” style=”font-size: 55px;font-weight: bold;color: red;”> <span>总计:</span> @Model.Cart.ComputeTotalPrice().ToString(“c”) </div> <p align=”left” class=”actionButtons” style=”width: 100%; clear: both”> <a href=”https://www.jb51.net/article/@Model.ReturnUrl” rel=”external nofollow” >继续购物</a> </p> </div> </div> @section scripts { <script src=”https://www.jb51.net/article/~/Scripts/jquery.bootstrap-touchspin.min.js”></script> <script type=”text/javascript”> $(function () { var i=$(“input[class=’demo2′]”); i.TouchSpin({ min: 1, max: 100, step: 1//增量或减量 }); i.on(“touchspin.on.stopupspin”, function () { $.post(‘@Url.Action(“IncreaseOrDecreaseOne”, “Home”)’, { “id”: $(this).closest(“div.productbox”).find(‘#Id’).val(), “quantity”: $(this).val() }, function (data) { if (data.msg) { location.reload(); } }); //var temp=$(this).val(); //alert(temp); //var temp=$(this).closest(“div.productbox”).find(‘#Id’).val(); //alert(temp); }); i.on(“touchspin.on.stopdownspin”, function () { $.post(‘@Url.Action(“IncreaseOrDecreaseOne”, “Home”)’, { “id”: $(this).closest(“div.productbox”).find(‘#Id’).val(), “quantity”: $(this).val() }, function (data) { if (data.msg) { location.reload(); } }); }); }); </script> }

在购物车页,用了Bootstrap TouchSpin这款插件,点击其中的数量的增量和减量按钮,就向Home控制器中的IncreaseOrDecreaseOne方法发送一个异步post请求,得到返回数据刷新购物车页。

//点击数量+号或点击数量-号或自己输入一个值 [HttpPost] public ActionResult IncreaseOrDecreaseOne(Cart cart, int id, int quantity) { Product product=GetAllProducts().Where(p=> p.Id==id).FirstOrDefault(); if (product !=null) { cart.IncreaseOrDecreaseOne(product, quantity); } return Json(new { msg=true }); }

在购车页,点击”移除”按钮,就向Home控制器的RemoveFromCart方法提交表单。

//从购物车移除 public ActionResult RemoveFromCart(Cart cart, int id, string returnUrl) { Product product=GetAllProducts().Where(p=> p.Id==id).FirstOrDefault(); if (product !=null) { cart.RemoveLine(product); } return RedirectToAction(“CartIndex”, new {returnUrl}); }

购物车摘要是通过在Views/Shared/_Layout.cshtml中加载部分视图而来。

<head> <meta charset=”utf-8″ /> <meta name=”viewport” content=”width=device-width” /> <title>@ViewBag.Title</title> @Styles.Render(“~/Content/css”) <link href=”https://www.jb51.net/article/~/bootstrap/css/bootstrap.min.css” rel=”external nofollow” rel=”stylesheet” /> @RenderSection(“styles”, required: false) @Scripts.Render(“~/bundles/jquery”) <script src=”https://www.jb51.net/article/~/bootstrap/js/bootstrap.min.js”></script> </head> <body> @{Html.RenderAction(“Summary”, “Home”);} @RenderBody() @RenderSection(“scripts”, required: false) </body>

在Home控制器中,对应的Summary方法为:

//清空购物车 public ActionResult EmptyCart(Cart cart, string returnUrl) { cart.Clear(); return View(“Index”,new ProductsListVm{Products=GetAllProducts()}); } //显示购物车摘要 public ActionResult Summary(Cart cart) { return View(cart); }

Home/Summary.cshtml是一个有关Cart的强类型部分视图:

@model MvcApplication1.Models.Cart @{ Layout=null; } <div id=”cart” style=”background-color: #e3e3e3;padding: 10px; text-align:center;”> <span class=”caption”> <b>购物车明细:</b> @if (Model !=null) { @Model.Lines.Sum(x=> x.Quantity) <span>件,</span> @Model.ComputeTotalPrice().ToString(“c”) } </span> @Html.ActionLink(“结算”, “CartIndex”, “Home”, new {returnUrl=Request.Url.PathAndQuery}, null)   @Html.ActionLink(“清空”, “EmptyCart”, “Home”, new {returnUrl=Request.Url.PathAndQuery}, null) </div>

注意:需要把Layout设置为null,否则会报错,因为产品选购页和购物车摘要同时加载Views/Shared/_Layout.cshtml就反复调用了。

到此这篇关于ASP.NET MVC实现横向展示购物车的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

您可能感兴趣的文章:asp.net基于HashTable实现购物车的方法asp.net基于session实现购物车的方法ASP.NET购物车实现过程详解asp.net 购物车的实现浅析asp.net 购物车实现详细代码