[.NET] 一步步打造一個簡單的 MVC 電商網站 - BooksStore(三)

一步步打造一個簡單的 MVC 電商網站 - BooksStore(三)

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

  《一步步打造一個簡單的 MVC 電商網站 - BooksStore(一)》(發佈時間:2017-03-30 )html

      《一步步打造一個簡單的 MVC 電商網站 - BooksStore(二)》(發佈時間:2017-03-31)jquery

  《一步步打造一個簡單的 MVC 電商網站 - BooksStore(三)》(發佈時間:2017-04-01)git

  《一步步打造一個簡單的 MVC 電商網站 - BooksStore(四)》(發佈時間:2017-04-05)github

 

簡介

  上一節咱們完成了兩個主要功能:添加到購物車和分類導航,這一節咱們會完成整個購物車的流程,以及訂單處理。安全

  該系列主要功能與知識點以下:服務器

    分類、產品瀏覽、購物車、結算、CRUD(增刪改查) 管理、發郵件、分頁、模型綁定、認證過濾器和單元測試等(預計剩餘兩篇,週三(由於週二不上班)先發布一篇)。ide

     【備註】項目使用 VS2015 + C#6 進行開發,有問題請發表在留言區哦,還有,頁面長得比較醜,請見諒。佈局

 

目錄

  • 完成購物車
  • 訂單結算

 

1、完成購物車

  上一節其實已經完成了移除購物車和清空購物車的方法,只是還沒有將可供用戶操做的按鈕放在頁面區域。除了增長這兩個按鈕,也會在頁面頂部的位置增長購物車的摘要(用於顯示用戶的購物總額)。post

  下面是上一節已經寫好的 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;
        }
    }
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 #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%;
}
Site.css

 

2、訂單結算

  購物完畢就是結算頁面了,這裏的訂單結算並不涉及支付接口的調用,只是使用郵件的形式進行通知而已。

  這裏,我設計結算的時候須要要求用戶輸入一些信息,如姓名、地址和郵箱等信息,在點擊肯定時我再將這些輸入的信息與購物清單的信息從系統的郵箱發到你所輸入的郵箱當中。一個比較直觀的圖:

  

  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", 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 容器將二者進行綁定:

 

  啓動頁面,試試效果吧:

   看來,好像成功了哦:

 

 

 


【博主】反骨仔

【出處】http://www.cnblogs.com/liqingwen/p/6652564.html 

【參考】《精通 ASP.NET MVC ...》

相關文章
相關標籤/搜索