一步步打造简单的MVC电商网站BooksStore(3)

一步步打造一个简单的 MVC 电商网站 - BooksStore(三)

本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore

《一步步打造一个简单的 MVC 电商网站 - BooksStore(一)》

《一步步打造一个简单的 MVC 电商网站 - BooksStore(二)》

《一步步打造一个简单的 MVC 电商网站 - BooksStore(三)》

《一步步打造一个简单的 MVC 电商网站 - BooksStore(四)》

简介

上一节我们完成了两个主要功能:添加到购物车和分类导航,这一节我们会完成整个购物车的流程,以及订单处理。

该系列主要功能与知识点如下:

分类、产品浏览、购物车、结算、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, 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>数量</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) 的参数不匹配。

显示的效果如下:

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 修改的地方为:

_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%;
}

二、订单结算

购物完毕就是结算页面了,这里的订单结算并不涉及支付接口的调用,只是使用邮件的形式进行通知而已。

这里,我设计结算的时候需要要求用户输入一些信息,如姓名、地址和邮箱等信息,在点击确定时我再将这些输入的信息与购物清单的信息从系统的邮箱发到你所输入的邮箱当中。一个比较直观的图:

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>

尝试运行,会出现以下页面,如果信息不填的话会出现相关的错误提示:

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", 25)
  {
  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 容器将两者进行绑定:

启动页面,试试效果吧:

看来,好像成功了哦:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • MVC+EasyUI+三层新闻网站建立 建站准备工作(一)

    这一次的项目是建立一个小型的新闻发布网站,所以就不需要用一些高大上的框架.三层+MVC+easyUI就足够了. 首先:搭建起项目框架 其次:到easyui官网去下载easyUI 我这里用的是我在很久的项目中用过的版本jquery-easyui-1.5.2 下载链接:http://www.jeasyui.com/download/list.php 最后就是把easyui引入到项目中去. 现在Content文件夹下建立一个叫EasyUi的文件夹,把需要用到的文件复制到其中,具体如下图所示: 以上就是

  • ASP.NET MVC验证码功能实现代码

    前台 复制代码 代码如下: <img id="vcodeimg" src="/Home/VCode" width="70"                                    height="25" />                                 <span                                    style="cursor: p

  • 一步步打造简单的MVC电商网站BooksStore(1)

    一步步打造一个简单的 MVC 电商网站 - BooksStore(一) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore <一步步打造一个简单的 MVC 电商网站 - BooksStore(一)> <一步步打造一个简单的 MVC 电商网站 - BooksStore(二)> <一步步打造一个简单的 MVC 电商网站 - BooksStore(三)> <一步步打造一个简单的 MVC 电商网站 -

  • MVC4制作网站教程第四章 更新栏目4.3

    序  一.用户  二.用户组  三.栏目 3.1添加栏目 3.2浏览栏目  3.3更新栏目 上次在树形列表里面点击栏目名称后跳转到详细信息页面~/Category/ManageDetails/id.在详细页面里点修改,来完成栏目资料修改. 先打开[CategoryController]添加[ManageDetails(int id)]action /// <summary> /// 栏目详细资料 /// </summary> /// <param name="id&

  • ASP.NET MVC5网站开发之添加\删除\重置密码\修改密码\列表浏览管理员篇2(六)

    一.安装插件. 展示层前端框架以Bootstrap为主,因为Bootstrap的js功能较弱,这里添加一些插件作补充.其实很多js插件可以通过NuGet安装,只是NuGet安装时添加的内容较多,不如自己复制来的干净,所以这里所有的插件都是下载然后复制到项目中. 1.Bootstrap 3 Datepicker 4.17.37 网址:https://eonasdan.github.io/bootstrap-datetimepicker/ 下载并解压压缩包->将bootstrap-datetimep

  • ASP.NET MVC5网站开发之登录、验证和注销管理员篇1(六)

    上次业务逻辑和展示层的架构都写了,可以开始进行具体功能的实现,这次先实现管理员的登录.验证和注销功能. 一.业务逻辑层1.实现256散列加密方法. Ninesky.Core[右键]-> 添加->文件夹,输入文件夹名General. General文件夹[右键]->添加->类,输入类名Security. 引用System.Security.Cryptography命名空间(1),并实现SHA256静态加密方法. 2.Administrator模型类 Ninesky.Core[右键]-

  • 一步步打造简单的MVC电商网站BooksStore(4)

    一步步打造一个简单的 MVC 电商网站 - BooksStore(四) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore <一步步打造一个简单的 MVC 电商网站 - BooksStore(一)> <一步步打造一个简单的 MVC 电商网站 - BooksStore(二)> <一步步打造一个简单的 MVC 电商网站 - BooksStore(三)> <一步步打造一个简单的 MVC 电商网站 -

  • MVC使用极验验证制作登录验证码学习笔记7

    在之前的项目中,如果有需要使用验证码,基本都是自己用GDI+画图出来,简单好用,但是却也存在了一些小问题,首先若较少干扰线,则安全性不是很高,验证码容易被机器识别,若多画太多干扰线条,机器人识别率下降的同时,人眼的识别率也同步下降(震惊哭).更为重要的是,GDI+绘制的验证码一般来说也不会很美观,如果做一个炫酷的登陆界面却配了这样一个验证码,画风诡异,丑到极致. 再后来浏览网页的过程中,发现很多很多网站项目中都使用了一种叫极验验证的验证码,采用移动滑块的方式进行验证,方便美观.而一番搜索之后了解

  • asp.net mvc验证码类使用

    验证码类 namespace QJW.VerifyCode { //用法: //public FileContentResult CreateValidate() //{ // ValidateCode vCode = new ValidateCode(); // string code = vCode.CreateValidateCode(5); // Session["ValidateCode"] = code; // byte[] bytes = vCode.CreateVali

  • MVC4制作网站教程第四章 添加栏目4.1

    好几天没时间写了.今天有写时间在学一点. 今天状态也不是很好,晕晕沉沉的写吧. 序 一.用户 二.用户组 三.栏目 3.1添加栏目 首先添加[CategoryController]控制器, 那么我想我的视图里,首先显示的应该是栏目类型,这里应该是一个下拉框,用户可以选择"一般栏目","单页栏目","外部链接".那么首先应该在[CategoryController]添加一个属性,用来返回栏目类型列表. #region Attribute publi

随机推荐