ASP.NET MVC實(shí)現(xiàn)橫向展示購(gòu)物車(chē)
通常,我們看到的購(gòu)物車(chē)是這樣的:

雖然這種購(gòu)物車(chē)顯示方式被廣泛運(yùn)用,但我個(gè)人覺(jué)得不夠直觀(guān)。如果換成這樣呢?

本篇的源碼放在了:https://github.com/darrenji/ShoppingCartInMVC
以上購(gòu)物車(chē)頁(yè)能實(shí)現(xiàn)的效果包括:
1、購(gòu)物車(chē)明細(xì):顯示訂購(gòu)數(shù)量、總金額,清空購(gòu)物車(chē)。
2、購(gòu)物車(chē)內(nèi)產(chǎn)品:數(shù)量可調(diào)整,對(duì)應(yīng)的小計(jì)和總計(jì)動(dòng)態(tài)變化。點(diǎn)擊移除按鈕移除該產(chǎn)品。
3、繼續(xù)購(gòu)物按鈕:點(diǎn)擊左下角的繼續(xù)購(gòu)物按鈕,回到先前頁(yè)。
4、使用了Bootstrap, 頁(yè)面元素自適應(yīng),頁(yè)面寬度調(diào)小時(shí),頁(yè)面布局動(dòng)態(tài)變化。
5、每行放置4個(gè)產(chǎn)品,且允許高度不一致,第5個(gè)產(chǎn)品另起一行,且不會(huì)float到上一行的空白區(qū)域,如下圖。

首先,有關(guān)產(chǎn)品的類(lèi)。
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; }
}產(chǎn)品選購(gòu)頁(yè)如圖:

以上,產(chǎn)品選購(gòu)頁(yè)是一個(gè)有關(guān)Product集合的強(qiáng)類(lèi)型視圖頁(yè),其對(duì)應(yīng)的Model為:
public class ProductsListVm
{
public ProductsListVm()
{
this.Products = new List<Product>();
}
public IEnumerable<Product> Products { get; set; }
}想像一下,我們?cè)诔匈?gòu)物,在購(gòu)物車(chē)內(nèi)放著不同的商品對(duì)應(yīng)不同的數(shù)量,在這里,可以把商品和數(shù)量抽象成一個(gè)類(lèi):
public class CartLine
{
public Product Product { get; set; }
public int Quantity { get; set; }
}而購(gòu)物車(chē)類(lèi)實(shí)際上就是維護(hù)著這個(gè)CartLine集合,需要提供添加、移除、計(jì)算購(gòu)物車(chē)總價(jià)、清空購(gòu)物車(chē)等方法,并提供一個(gè)獲取到CartLine集合的屬性,另外,針對(duì)點(diǎn)擊購(gòu)物車(chē)頁(yè)上的增量和減量按鈕,也要提供相應(yīng)的方法。
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;
}
}
//點(diǎn)擊數(shù)量+號(hào)或點(diǎn)擊數(shù)量-號(hào)或自己輸入一個(gè)值
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);
}
//計(jì)算總價(jià)
public decimal ComputeTotalPrice()
{
return lineCollection.Sum(p => p.Product.Price*p.Quantity);
}
//清空
public void Clear()
{
lineCollection.Clear();
}
//獲取
public IEnumerable<CartLine> Lines
{
get { return lineCollection; }
}
}購(gòu)物車(chē)頁(yè)自然就是針對(duì)Cart類(lèi)的一個(gè)強(qiáng)類(lèi)型視圖頁(yè),嗯,等等,購(gòu)物車(chē)頁(yè)還需要記錄下上一個(gè)頁(yè)面的url,于是,考慮到把Cart類(lèi)和記錄上一個(gè)頁(yè)面url這2個(gè)因素,針對(duì)購(gòu)物車(chē)頁(yè),給出這樣的一個(gè)Model:
public class CartIndexVm
{
public Cart Cart { get; set; }
public string ReturnUrl { get; set; }
}在HomeController中,需要用到購(gòu)物車(chē)的實(shí)例,可以這樣寫(xiě):
private Cart GetCart()
{
Cart cart = (Cart)Session["Cart"];
if (cart == null)
{
cart = new Cart();
Session["Cart"] = cart;
}
return cart;
}Cart實(shí)例保存到Session中,并從Session中獲取。當(dāng)然,也可以放到ASP.NET MVC綁定機(jī)制中,需要做的就是實(shí)現(xiàn)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需要在全局中注冊(cè)。
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
......
ModelBinders.Binders.Add(typeof(Cart), new CartModelBinder());
}
}在Home控制器中,首先提供了一個(gè)返回Product集合的方法。
private List<Product> GetAllProducts()
{
return new List<Product>()
{
new Product(){Id = 1, Description = "產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述",ImageUrl = "/images/1.jpg",Name = "產(chǎn)品1",Price = 85M},
new Product(){Id = 2, Description = "產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述",ImageUrl = "/images/2.jpg",Name = "產(chǎn)品2",Price = 95M},
new Product(){Id = 3, Description = "產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述",ImageUrl = "/images/2.jpg",Name = "產(chǎn)品3",Price = 55M},
new Product(){Id = 4, Description = "產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述",ImageUrl = "/images/1.jpg",Name = "產(chǎn)品4",Price = 65M},
new Product(){Id = 5, Description = "產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述產(chǎn)品描述",ImageUrl = "/images/2.jpg",Name = "產(chǎn)品5",Price = 75M}
};
}在HomeController中,有關(guān)產(chǎn)品選購(gòu)頁(yè)的如下:
//產(chǎn)品選購(gòu)頁(yè)
public ActionResult Index()
{
ProductsListVm productsListVm = new ProductsListVm();
productsListVm.Products = GetAllProducts();
return View(productsListVm);
}Homme/Index.cshtml是一個(gè)ProductsListVm的強(qiáng)類(lèi)型視圖頁(yè)。
@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集合的時(shí)候,又去加載Views/Shared/ProductSummary.cshtml這個(gè)強(qiáng)類(lèi)型部分視圖。
@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="+放入購(gòu)物車(chē)"/>
}
</div>點(diǎn)擊"+放入購(gòu)物車(chē)"按鈕,調(diào)用HomeController中的AddToCart方法,并且需要把選購(gòu)產(chǎn)品頁(yè)的url以query string的形式傳遞給控制器方法。
//購(gòu)物車(chē)頁(yè)
public ActionResult CartIndex(Cart cart, string returnUrl)
{
return View(new CartIndexVm
{
Cart = cart,
ReturnUrl = returnUrl
});
}
//添加到購(gòu)物車(chē)
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});
}購(gòu)物車(chē)頁(yè)Home/CartIndex.cshtml是一個(gè)CartIndexVm的強(qiáng)類(lèi)型視圖頁(yè)。
@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個(gè)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;">單價(jià):</td>
<td>@item.Product.Price</td>
</tr>
<tr>
<td>數(shù)量:</td>
<td>
<input class="demo2" type="text" value="@item.Quantity" name="demo2" />
</td>
</tr>
<tr>
<td>小計(jì):</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>總計(jì):</span> @Model.Cart.ComputeTotalPrice().ToString("c")
</div>
<p align="left" class="actionButtons" style="width: 100%; clear: both">
<a href="@Model.ReturnUrl" rel="external nofollow" >繼續(xù)購(gòu)物</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>
}在購(gòu)物車(chē)頁(yè),用了Bootstrap TouchSpin這款插件,點(diǎn)擊其中的數(shù)量的增量和減量按鈕,就向Home控制器中的IncreaseOrDecreaseOne方法發(fā)送一個(gè)異步post請(qǐng)求,得到返回?cái)?shù)據(jù)刷新購(gòu)物車(chē)頁(yè)。
//點(diǎn)擊數(shù)量+號(hào)或點(diǎn)擊數(shù)量-號(hào)或自己輸入一個(gè)值
[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
});
}在購(gòu)車(chē)頁(yè),點(diǎn)擊"移除"按鈕,就向Home控制器的RemoveFromCart方法提交表單。
//從購(gòu)物車(chē)移除
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});
}購(gòu)物車(chē)摘要是通過(guò)在Views/Shared/_Layout.cshtml中加載部分視圖而來(lái)。
<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控制器中,對(duì)應(yīng)的Summary方法為:
//清空購(gòu)物車(chē)
public ActionResult EmptyCart(Cart cart, string returnUrl)
{
cart.Clear();
return View("Index",new ProductsListVm{Products = GetAllProducts()});
}
//顯示購(gòu)物車(chē)摘要
public ActionResult Summary(Cart cart)
{
return View(cart);
}Home/Summary.cshtml是一個(gè)有關(guān)Cart的強(qiáng)類(lèi)型部分視圖:
@model MvcApplication1.Models.Cart
@{
Layout = null;
}
<div id="cart" style="background-color: #e3e3e3;padding: 10px; text-align:center;">
<span class="caption">
<b>購(gòu)物車(chē)明細(xì):</b>
@if (Model != null)
{
@Model.Lines.Sum(x => x.Quantity) <span>件,</span>
@Model.ComputeTotalPrice().ToString("c")
}
</span>
@Html.ActionLink("結(jié)算", "CartIndex", "Home", new {returnUrl = Request.Url.PathAndQuery}, null)
@Html.ActionLink("清空", "EmptyCart", "Home", new {returnUrl = Request.Url.PathAndQuery}, null)
</div>注意:需要把Layout設(shè)置為null,否則會(huì)報(bào)錯(cuò),因?yàn)楫a(chǎn)品選購(gòu)頁(yè)和購(gòu)物車(chē)摘要同時(shí)加載Views/Shared/_Layout.cshtml就反復(fù)調(diào)用了。
到此這篇關(guān)于A(yíng)SP.NET MVC實(shí)現(xiàn)橫向展示購(gòu)物車(chē)的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
asp.net在后端動(dòng)態(tài)添加樣式表調(diào)用的方法
這篇文章主要介紹了asp.net在后端動(dòng)態(tài)添加樣式表調(diào)用的方法,涉及asp.net操作樣式表的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04
淺談.Net Core后端單元測(cè)試的實(shí)現(xiàn)
這篇文章主要介紹了淺談.Net Core后端單元測(cè)試的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
.NET下通過(guò)HttpListener實(shí)現(xiàn)簡(jiǎn)單的Http服務(wù)
這篇文章主要為大家詳細(xì)介紹了.NET下通過(guò)HttpListener實(shí)現(xiàn)簡(jiǎn)單Http服務(wù)的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-09-09
c#網(wǎng)站W(wǎng)ebConfig中域名引用示例介紹
這篇文章主要介紹了c#網(wǎng)站W(wǎng)ebConfig中域名引用,需要的朋友可以參考下2014-06-06
AntDesign Pro + .NET Core 實(shí)現(xiàn)基于JWT的登錄認(rèn)證功能
這篇文章主要介紹了AntDesign Pro + .NET Core 實(shí)現(xiàn)基于JWT的登錄認(rèn)證功能,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03

