一步步打造簡單的MVC電商網(wǎng)站BooksStore(3)
一步步打造一個簡單的 MVC 電商網(wǎng)站 - BooksStore(三)
本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore
《一步步打造一個簡單的 MVC 電商網(wǎng)站 - BooksStore(一)》
《一步步打造一個簡單的 MVC 電商網(wǎng)站 - BooksStore(二)》
《一步步打造一個簡單的 MVC 電商網(wǎng)站 - BooksStore(三)》
《一步步打造一個簡單的 MVC 電商網(wǎng)站 - BooksStore(四)》
簡介
上一節(jié)我們完成了兩個主要功能:添加到購物車和分類導航,這一節(jié)我們會完成整個購物車的流程,以及訂單處理。
該系列主要功能與知識點如下:
分類、產(chǎn)品瀏覽、購物車、結(jié)算、CRUD(增刪改查) 管理、發(fā)郵件、分頁、模型綁定、認證過濾器和單元測試等(預計剩余兩篇,周三(因為周二不上班)先發(fā)布一篇)。
【備注】項目使用 VS2015 + C#6 進行開發(fā),有問題請發(fā)表在留言區(qū)哦,還有,頁面長得比較丑,請見諒。
目錄
完成購物車
訂單結(jié)算
一、完成購物車
上一節(jié)其實已經(jīng)完成了移除購物車和清空購物車的方法,只是尚未將可供用戶操作的按鈕放在頁面區(qū)域。除了增加這兩個按鈕,也會在頁面頂部的位置增加購物車的摘要(用于顯示用戶的購物總額)。
下面是上一節(jié)已經(jīng)寫好的 CartController 代碼。
/// <summary>
/// 購物車
/// </summary>
public class CartController : Controller
{
private readonly IBookRepository _bookRepository;
public CartController(IBookRepository bookRepository)
{
_bookRepository = bookRepository;
}
/// <summary>
/// 首頁
/// </summary>
/// <param name="returnUrl"></param>
/// <returns></returns>
public ViewResult Index(string returnUrl)
{
return View(new CartIndexViewModel()
{
Cart = GetCart(),
ReturnUrl = returnUrl
});
}
/// <summary>
/// 添加到購物車
/// </summary>
/// <param name="id"></param>
/// <param name="returnUrl"></param>
/// <returns></returns>
public RedirectToRouteResult AddToCart(int id, string returnUrl)
{
var book = _bookRepository.Books.FirstOrDefault(x => x.Id == id);
if (book != null)
{
GetCart().AddBook(book, 1);
}
return RedirectToAction("Index", new { returnUrl });
}
/// <summary>
/// 從購物車移除
/// </summary>
/// <param name="id"></param>
/// <param name="returnUrl"></param>
/// <returns></returns>
public RedirectToRouteResult RemoveFromCart(int id, string returnUrl)
{
var book = _bookRepository.Books.FirstOrDefault(x => x.Id == id);
if (book != null)
{
GetCart().RemoveBook(book);
}
return RedirectToAction("Index", new { returnUrl });
}
/// <summary>
/// 獲取購物車
/// </summary>
/// <returns></returns>
private Cart GetCart()
{
var cart = (Cart)Session["Cart"];
if (cart != null) return cart;
cart = new Cart();
Session["Cart"] = cart;
return cart;
}
}
1.加入移除書籍和清空購物車的功能

Index.cshtml
@model Wen.BooksStore.WebUI.Models.CartIndexViewModel
<h2>我的購物車</h2>
<table class="table">
<thead>
<tr>
<th>書名</th>
<th>價格</th>
<th>數(shù)量</th>
<th>總計</th>
<th> </th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Cart.GetCartItems)
{
<tr>
<td>@item.Book.Name</td>
<td>@item.Book.Price</td>
<td>@item.Quantity</td>
<td>@((item.Book.Price * item.Quantity).ToString("C"))</td>
<td>
@using (Html.BeginForm("RemoveFromCart", "Cart"))
{
@Html.Hidden("id", item.Book.Id)
@Html.HiddenFor(x => x.ReturnUrl)
<input type="submit" value="- 移除" />
}
</td>
</tr>
}
<tr>
<td> </td>
<td> </td>
<td>總計:</td>
<td>@Model.Cart.ComputeTotalValue().ToString("C")</td>
<td>
@using (Html.BeginForm("Clear", "Cart"))
{
@Html.HiddenFor(x => x.ReturnUrl)
<input type="submit" value="清空購物車" />
}
</td>
</tr>
</tbody>
</table>
【備注】@Html.Hidden("id", item.Book.Id) 是用于生成隱藏的字段,如果直接使用@Html.HiddenFor(),生成的 name 將會是 item.Book.Id ,將和 CartController 中 RemoveFromCart(int id, string return) 的參數(shù)不匹配。
顯示的效果如下:

2.添加摘要:我們在購物車存放了許多東西,通過摘要,可以顯示購物總額的縮略圖,我們選擇的位置在頂部右上角的一個比較顯眼的位置進行顯示它,當然,還需要有點擊的跳轉(zhuǎn)按鈕方便顯示所有的購物清單頁面。
繼續(xù)在 CartController 下新增一個 Action,名為 Summary,返回值是一個分部視圖:
/// <summary>
/// 摘要
/// </summary>
/// <returns></returns>
public PartialViewResult Summary()
{
return PartialView(GetCart());
}
對應的Summary.cshtml
@model Wen.BooksStore.Domain.Entities.Cart
<div class="bookSummary">
你的購物車:@Model.ComputeTotalValue()
<span>@Html.ActionLink("結(jié)算", "Checkout", "Cart", new { retunUrl = Request.Url.PathAndQuery }, null)</span>
</div>
對應的布局頁_Layout.cshtml 修改的地方為:

_Layout.cshtml
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
<link href="~/Contents/Site.css" rel="stylesheet" />
</head>
<body>
<div id="header">
@{ Html.RenderAction("Summary", "Cart");}
<div class="title">圖書商城</div>
</div>
<div id="sideBar">
@{ Html.RenderAction("Sidebar", "Nav"); }
</div>
<div id="content">
@RenderBody()
</div>
</body>
</html>
添加了新的東西,css 也要進行修改:
Site.css
body {
}
#header, #content, #sideBar {
display: block;
}
#header {
background-color: green;
border-bottom: 2px solid #111;
color: White;
}
#header, .title {
font-size: 1.5em;
padding: .5em;
}
#sideBar {
float: left;
width: 8em;
padding: .3em;
}
#content {
border-left: 2px solid gray;
margin-left: 10em;
padding: 1em;
}
.pager {
text-align: right;
padding: .5em 0 0 0;
margin-top: 1em;
}
.pager A {
font-size: 1.1em;
color: #666;
padding: 0 .4em 0 .4em;
}
.pager A:hover {
background-color: Silver;
}
.pager A.selected {
background-color: #353535;
color: White;
}
.item input {
float: right;
color: White;
background-color: green;
}
.table {
width: 100%;
padding: 0;
margin: 0;
}
.table th {
font: bold 12px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
color: #4f6b72;
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
border-top: 1px solid #C1DAD7;
letter-spacing: 2px;
text-transform: uppercase;
text-align: left;
padding: 6px 6px 6px 12px;
background: #CAE8EA no-repeat;
}
.table td {
border-right: 1px solid #C1DAD7;
border-bottom: 1px solid #C1DAD7;
background: #fff;
font-size: 14px;
padding: 6px 6px 6px 12px;
color: #4f6b72;
}
.table td.alt {
background: #F5FAFA;
color: #797268;
}
.table th.spec, td.spec {
border-left: 1px solid #C1DAD7;
}
.bookSummary {
width: 15%;
float: right;
margin-top: 1.5%;
}

二、訂單結(jié)算
購物完畢就是結(jié)算頁面了,這里的訂單結(jié)算并不涉及支付接口的調(diào)用,只是使用郵件的形式進行通知而已。
這里,我設(shè)計結(jié)算的時候需要要求用戶輸入一些信息,如姓名、地址和郵箱等信息,在點擊確定時我再將這些輸入的信息與購物清單的信息從系統(tǒng)的郵箱發(fā)到你所輸入的郵箱當中。一個比較直觀的圖:

1.在 Entities 中添加一個域模型 Contact.cs 表示聯(lián)系人的信息。

/// <summary>
/// 聯(lián)系信息
/// </summary>
public class Contact
{
[Required(ErrorMessage = "姓名不能為空")]
public string Name { get; set; }
[Required(ErrorMessage = "地址不能為空")]
public string Address { get; set; }
[Required(ErrorMessage = "郵箱不能為空")]
[RegularExpression(@"(\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*\w\w)", ErrorMessage = "輸入的郵箱地址不合法")]
public string Email { get; set; }
}
CartController.cs 添加一個用于結(jié)算的 Action:
/// <summary>
/// 結(jié)算
/// </summary>
/// <returns></returns>
public ViewResult Checkout()
{
return View(new Contact());
}
Checkout.cshtml 中的:
@model Wen.BooksStore.Domain.Entities.Contact
<div>
@using (Html.BeginForm())
{
<div class="error">@Html.ValidationSummary()</div>
<div>姓名: @Html.TextBoxFor(x => x.Name)</div>
<div>地址: @Html.TextBoxFor(x => x.Address)</div>
<div>郵箱: @Html.TextBoxFor(x => x.Email)</div>
<div><input type="submit" value="提交" /></div>
}
</div>
這里使用的是模型校驗,_Layout.cshtml 布局頁需要引入js:
<script src="~/Scripts/jquery-1.10.2.js"></script> <script src="~/Scripts/jquery.validate.js"></script> <script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
<link href="~/Contents/Site.css" rel="stylesheet" />
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
</head>
<body>
<div id="header">
@{ Html.RenderAction("Summary", "Cart");}
<div class="title">圖書商城</div>
</div>
<div id="sideBar">
@{ Html.RenderAction("Sidebar", "Nav"); }
</div>
<div id="content">
@RenderBody()
</div>
</body>
</html>
嘗試運行,會出現(xiàn)以下頁面,如果信息不填的話會出現(xiàn)相關(guān)的錯誤提示:

2.接下來,要進入“提交”后的流程了。
現(xiàn)在還需要一個組件用于處理訂單,創(chuàng)建一個用于訂單處理的接口,和一個該接口的實現(xiàn),再通過 Ninject 進行兩者的綁定:

/// <summary>
/// 訂單處理
/// </summary>
public interface IOrderProcessor
{
/// <summary>
/// 處理訂單
/// </summary>
/// <param name="cart"></param>
/// <param name="contact"></param>
void ProcessOrder(Cart cart, Contact contact);
}
建立一個實現(xiàn)該接口用于處理訂單的實體類,這里并不是調(diào)用支付接口,而是簡單通過 BCL 中的進行郵件的發(fā)送。

EmailOrderProcessor.cs:
/// <summary>
/// 郵件訂單處理器
/// </summary>
public class EmailOrderProcessor : IOrderProcessor
{
/// <summary>
/// 發(fā)送人
/// </summary>
public static class Sender
{
/// <summary>
/// 賬號
/// </summary>
public static string Account = "你的@qq.com";
/// <summary>
/// 密碼
/// </summary>
public static string Password = "xxx";
}
/// <summary>
/// 處理訂單
/// </summary>
/// <param name="cart"></param>
/// <param name="contact"></param>
public void ProcessOrder(Cart cart, Contact contact)
{
if (string.IsNullOrEmpty(contact.Email))
{
throw new Exception("Email 不能為空!");
}
var sb = new StringBuilder();
foreach (var item in cart.GetCartItems)
{
sb.AppendLine($"《{item.Book.Name}》:{item.Book.Price} * {item.Quantity} = {item.Book.Price * item.Quantity}");
}
sb.AppendLine($"總額:{cart.GetCartItems.Sum(x => x.Quantity * x.Book.Price)}");
sb.AppendLine();
sb.AppendLine($"聯(lián)系人:{contact.Name} {contact.Address}");
//設(shè)置發(fā)件人,發(fā)件人需要與設(shè)置的郵件發(fā)送服務器的郵箱一致
var fromAddr = new MailAddress(Sender.Account);
var message = new MailMessage { From = fromAddr };
//設(shè)置收件人,可添加多個,添加方法與下面的一樣
message.To.Add(contact.Email);
//設(shè)置抄送人
message.CC.Add(Sender.Account);
//設(shè)置郵件標題
message.Subject = "您的訂單正在出庫...";
//設(shè)置郵件內(nèi)容
message.Body = sb.ToString();
//設(shè)置郵件發(fā)送服務器,服務器根據(jù)你使用的郵箱而不同,可以到相應的 郵箱管理后臺查看,下面是QQ的
var client = new SmtpClient("smtp.qq.com", 25)
{
Credentials = new NetworkCredential(Sender.Account, Sender.Password),
EnableSsl = true
};
//設(shè)置發(fā)送人的郵箱賬號和密碼
//啟用ssl,也就是安全發(fā)送
//發(fā)送郵件
client.Send(message);
}
CartController 也需要稍作調(diào)整:

還要在 CartController 中額外添加一個帶[HttPost] 特性的名為 Checkout 方法:

/// <summary>
/// 結(jié)算
/// </summary>
/// <param name="contact"></param>
/// <returns></returns>
[HttpPost]
public ViewResult Checkout(Contact contact)
{
if (!ModelState.IsValid)
return View(contact);
var cart = GetCart();
_orderProcessor.ProcessOrder(cart, contact);
cart.Clear();
return View("Thanks");
}
當校驗成功時,會調(diào)用接口發(fā)一條信息,并且清空已有的購物車,然后跳轉(zhuǎn)到指定的一個新視圖頁:

新建 Thanks.cshtml,內(nèi)容如下:
Thanks
別忘了添加綁定哦,使用 DI 容器將兩者進行綁定:

啟動頁面,試試效果吧:

看來,好像成功了哦:

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
- 一步步打造簡單的MVC電商網(wǎng)站BooksStore(4)
- 一步步打造簡單的MVC電商網(wǎng)站BooksStore(1)
- MVC4制作網(wǎng)站教程第四章 更新欄目4.3
- MVC4制作網(wǎng)站教程第四章 添加欄目4.1
- asp.net mvc驗證碼類使用
- MVC使用極驗驗證制作登錄驗證碼學習筆記7
- ASP.NET MVC驗證碼功能實現(xiàn)代碼
- ASP.NET?MVC5網(wǎng)站開發(fā)之添加、刪除、重置密碼、修改密碼、列表瀏覽管理員篇2(六)
- ASP.NET MVC5網(wǎng)站開發(fā)之登錄、驗證和注銷管理員篇1(六)
- MVC+EasyUI+三層新聞網(wǎng)站建立 建站準備工作(一)
相關(guān)文章
asp.net Gridview數(shù)據(jù)列中實現(xiàn)鼠標懸浮變色
Gridview一般朋友們都比較常用,因為它可以方便快捷的實現(xiàn)我們所需的很多功能,代碼也比較簡潔。平時的項目中這個控件我也比較常用,其中有個功能用到的頻率也比較多。所以記錄下備忘。2010-06-06
ASP.NET MVC5網(wǎng)站開發(fā)概述(一)
這篇文章主要內(nèi)容是ASP.NET MVC5網(wǎng)站開發(fā)實踐的整體概述,分析了開發(fā)環(huán)境、使用的技術(shù)以及項目的整體結(jié)構(gòu),感興趣的小伙伴們可以參考一下2015-09-09
ASP.NET2.0使用Enter Key作為默認提交問題分析(附源碼)
這篇文章主要介紹了ASP.NET2.0使用Enter Key作為默認提交,結(jié)合實例形式分析了ASP.NET2.0使用Enter Key默認提交的注意事項與相關(guān)實現(xiàn)技巧,并附上源碼供讀者參考,具有一定參考借鑒價值,需要的朋友可以參考下2015-11-11
Visual Studio 2015和 .NET Core安裝教程
這篇文章主要為大家詳細介紹了Visual Studio Community 2015和 .NET Core安裝圖文教程,感興趣的小伙伴們可以參考一下2016-07-07
ASP.NET網(wǎng)頁打印(只打印相關(guān)內(nèi)容/自寫功能)
朋友要求在前段時間完成的新聞的網(wǎng)站上加上一個功能,就是在每篇新聞瀏覽的頁面, 加一個打印銨鈕。讓用戶一點打印,能把整篇文章打印2013-01-01
ASP.NET WebService中使用ASP.NET_SessionId的問題說明
proxy.CookieContainer存儲了客戶端的 ASP.NET_SessionId。這樣以后每次通過webservice 方法調(diào)用時,都會將ASP.NET_SessionId傳遞到服務器端。2011-09-09

