[.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(三)
一步步打造一个简单的 MVC 电商网站 - BooksStore(三)
本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore
《一步步打造一个简单的 MVC 电商网站 - BooksStore(一)》(发布时间:2017-03-30 )
《一步步打造一个简单的 MVC 电商网站 - BooksStore(二)》(发布时间:2017-03-31)
《一步步打造一个简单的 MVC 电商网站 - BooksStore(三)》(发布时间:2017-04-01)
《一步步打造一个简单的 MVC 电商网站 - BooksStore(四)》(发布时间:2017-04-05)
简介
上一节我们完成了两个主要功能:添加到购物车和分类导航,这一节我们会完成整个购物车的流程,以及订单处理。
该系列主要功能与知识点如下:
分类、产品浏览、购物车、结算、CRUD(增删改查) 管理、发邮件、分页、模型绑定、认证过滤器和单元测试等(预计剩余两篇,周三(因为周二不上班)先发布一篇)。
【备注】项目使用 VS2015 + C#6 进行开发,有问题请发表在留言区哦,还有,页面长得比较丑,请见谅。
目录
- 完成购物车
- 订单结算
一、完成购物车
上一节其实已经完成了移除购物车和清空购物车的方法,只是尚未将可供用户操作的按钮放在页面区域。除了增加这两个按钮,也会在页面顶部的位置增加购物车的摘要(用于显示用户的购物总额)。
下面是上一节已经写好的 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, );
} 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;
}
}
CartController.cs
1.加入移除书籍和清空购物车的功能
@model Wen.BooksStore.WebUI.Models.CartIndexViewModel <h2>我的购物车</h2> <table class="table">
<thead>
<tr>
<th>书名</th>
<th>价格</th>
<th>数量</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>
Index.cshtml
【备注】@Html.Hidden("id", item.Book.Id) 是用于生成隐藏的字段,如果直接使用 @Html.HiddenFor(),生成的 name 将会是 item.Book.Id ,将和 CartController 中 RemoveFromCart(int id, string return) 的参数不匹配。
显示的效果如下:
2.添加摘要:我们在购物车存放了许多东西,通过摘要,可以显示购物总额的缩略图,我们选择的位置在顶部右上角的一个比较显眼的位置进行显示它,当然,还需要有点击的跳转按钮方便显示所有的购物清单页面。
继续在 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("结算", "Checkout", "Cart", new { retunUrl = Request.Url.PathAndQuery }, null)</span>
</div>
对应的布局页 _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>
_Layout.cshtml
添加了新的东西,css 也要进行修改:
body {
} #header, #content, #sideBar {
display: block;
} #header {
background-color: green;
border-bottom: 2px solid #;
color: White;
} #header, .title {
font-size: .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 ;
margin-top: 1em;
} .pager A {
font-size: .1em;
color: #;
padding: .4em .4em;
} .pager A:hover {
background-color: Silver;
} .pager A.selected {
background-color: #;
color: White;
} .item input {
float: right;
color: White;
background-color: green;
} .table {
width: %;
padding: ;
margin: ;
} .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: #;
} .table th.spec, td.spec {
border-left: 1px solid #C1DAD7;
} .bookSummary {
width: %;
float: right;
margin-top: 1.5%;
}
Site.css
二、订单结算
购物完毕就是结算页面了,这里的订单结算并不涉及支付接口的调用,只是使用邮件的形式进行通知而已。
这里,我设计结算的时候需要要求用户输入一些信息,如姓名、地址和邮箱等信息,在点击确定时我再将这些输入的信息与购物清单的信息从系统的邮箱发到你所输入的邮箱当中。一个比较直观的图:
1.在 Entities 中添加一个域模型 Contact.cs 表示联系人的信息。
/// <summary>
/// 联系信息
/// </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 添加一个用于结算的 Action:
/// <summary>
/// 结算
/// </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>
_Layout.cshtml
尝试运行,会出现以下页面,如果信息不填的话会出现相关的错误提示:
2.接下来,要进入“提交”后的流程了。
现在还需要一个组件用于处理订单,创建一个用于订单处理的接口,和一个该接口的实现,再通过 Ninject 进行两者的绑定:
/// <summary>
/// 订单处理
/// </summary>
public interface IOrderProcessor
{
/// <summary>
/// 处理订单
/// </summary>
/// <param name="cart"></param>
/// <param name="contact"></param>
void ProcessOrder(Cart cart, Contact contact);
}
建立一个实现该接口用于处理订单的实体类,这里并不是调用支付接口,而是简单通过 BCL 中的进行邮件的发送。
EmailOrderProcessor.cs:
/// <summary>
/// 邮件订单处理器
/// </summary>
public class EmailOrderProcessor : IOrderProcessor
{
/// <summary>
/// 发送人
/// </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($"联系人:{contact.Name} {contact.Address}"); //设置发件人,发件人需要与设置的邮件发送服务器的邮箱一致
var fromAddr = new MailAddress(Sender.Account);
var message = new MailMessage { From = fromAddr }; //设置收件人,可添加多个,添加方法与下面的一样
message.To.Add(contact.Email);
//设置抄送人
message.CC.Add(Sender.Account);
//设置邮件标题
message.Subject = "您的订单正在出库...";
//设置邮件内容
message.Body = sb.ToString();
//设置邮件发送服务器,服务器根据你使用的邮箱而不同,可以到相应的 邮箱管理后台查看,下面是QQ的 var client = new SmtpClient("smtp.qq.com", )
{
Credentials = new NetworkCredential(Sender.Account, Sender.Password),
EnableSsl = true
}; //设置发送人的邮箱账号和密码
//启用ssl,也就是安全发送
//发送邮件
client.Send(message);
}
CartController 也需要稍作调整:
还要在 CartController 中额外添加一个带 [HttPost] 特性的名为 Checkout 方法:
/// <summary>
/// 结算
/// </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");
}
当校验成功时,会调用接口发一条信息,并且清空已有的购物车,然后跳转到指定的一个新视图页:
新建 Thanks.cshtml,内容如下:
Thanks
别忘了添加绑定哦,使用 DI 容器将两者进行绑定:
启动页面,试试效果吧:
看来,好像成功了哦:
【博主】反骨仔
【出处】http://www.cnblogs.com/liqingwen/p/6652564.html
【参考】《精通 ASP.NET MVC ...》
[.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(三)的更多相关文章
- [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(二)
一步步打造一个简单的 MVC 电商网站 - BooksStore(二) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore 前: ...
- [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(四)
一步步打造一个简单的 MVC 电商网站 - BooksStore(四) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore &l ...
- [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(一)
一步步打造一个简单的 MVC 电商网站 - BooksStore(一) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore &l ...
- [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(一) (转)
http://www.cnblogs.com/liqingwen/p/6640861.html 一步步打造一个简单的 MVC 电商网站 - BooksStore(一) 本系列的 GitHub地址:ht ...
- [.NET] 一步步打造一个简单的 MVC 网站 - BooksStore(一)
一步步打造一个简单的 MVC 网站 - BooksStore(一) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore 简介 主 ...
- 如何一步一步用DDD设计一个电商网站(三)—— 初涉核心域
一.前言 结合我们本次系列的第一篇博文中提到的上下文映射图(传送门:如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念),得知我们这个电商网站的核心域就是销售子域.因为电子商务是以信息网络 ...
- ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(三)密码修改以及密码重置
前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/as ...
- 如何一步一步用DDD设计一个电商网站(九)—— 小心陷入值对象持久化的坑
阅读目录 前言 场景1的思考 场景2的思考 避坑方式 实践 结语 一.前言 在上一篇中(如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成),有一行注释的代码: public interfa ...
- 如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成
阅读目录 前言 建模 实现 结语 一.前言 前面几篇已经实现了一个基本的购买+售价计算的过程,这次再让售价丰满一些,增加一个会员价的概念.会员价在现在的主流电商中,是一个不大常见的模式,其带来的问题是 ...
随机推荐
- 【12c】扩展数据类型(Extended Data Types)-- MAX_STRING_SIZE
[12c]扩展数据类型(Extended Data Types)-- MAX_STRING_SIZE 在12c中,与早期版本相比,诸如VARCHAR2, NAVARCHAR2以及 RAW这些数据类型的 ...
- 升级到appcompat v7 21.0.2之后遇到的问题解决方法
1)一开始是手动直接覆盖文件到对应的lib project下,提示数个style找不到.原因是新旧版本的文件命名和结构不同,所以这个问题只需要把project目录清空,重新复制文件即可解决. 2)发现 ...
- ConOS安装mysql5.7 及简单配置
安装 保证你的用户有权限 安装 没有 切换 root su root (su的意思:swich user) # rpm -ivh http://dev.mysql.com/get/mysql57- ...
- Struts2系列笔记(7)---Struts2类型转换
Struts2类型转换 struts2中内置了大量的类型转换器用来完成数据类型转换的问题,这篇随笔主要通过两个方面来写Struts类型转换 1:Struts2内置的类型转换器 2:如何自定义 ...
- Fiddler教程【转】
阅读目录 Fiddler的基本介绍 Fiddler的工作原理 同类的其它工具 Fiddler如何捕获Firefox的会话 Fiddler如何捕获HTTPS会话 Fiddler的基本界面 Fiddler ...
- ubuntu12.04下安装pptp_vpn服务器
1.下载安装apt-get install pptpd 2.配置/etc/pptpd.confvim /etc/pptpd.conf添加下面两行(在配置文件的最后取消注释修改IP即可)localip ...
- devexpress设置皮肤、字体以及折叠菜单、伸缩Panel的实现
1.为了体现系统的个性化,越来越多的系统增加了换肤功能.这里例举一个devexpress实现换肤的案例,效果图对比: 第二张图片中更换了皮肤与字体. 2.皮肤数据源绑定代码: //循环添加皮肤名称 f ...
- Sublime Text3安装、注册、插件安装教程
在前端开发中,有很多软件供我们使用:大名鼎鼎的WebStorm,老而弥坚的Dreamweaver,后起之秀Hbuilder,还有小清新Sublime Text.......等等.作为新手, ...
- XML入门知识
什么是XML? 答:指可扩展标记语言(eXtensible Markup Language),被设计用来传输和存储数据:标签没有被预定义.您需要自行定义标签:被设计为具有自我描述性. XML和HTML ...
- web性能优化 来自《web全栈工程师的自我修养》
最近在看<web全栈工程师的自我修养>一书,作者是来自腾讯的前端工程师.作者在做招聘前端的时候问应聘者web新能优化有什么了解和经验,应聘者思索后回答“在发布项目之前压缩css和 Java ...