ASP.NETMVC实现横向展示购物车


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

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="@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="~/Content/shopitem.css" rel="external nofollow"  rel="stylesheet" />    <link href="~/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="@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="#" 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="@Model.ReturnUrl" rel="external nofollow" >继续购物</a>        </p>    </div></div>@section scripts{    <script src="~/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="~/bootstrap/css/bootstrap.min.css" rel="external nofollow"  rel="stylesheet" />    @RenderSection("styles", required: false)    @Scripts.Render("~/bundles/jquery")    <script src="~/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)    &nbsp;    @Html.ActionLink("清空", "EmptyCart", "Home", new {returnUrl = Request.Url.PathAndQuery}, null)</div>

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

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