天天看點

一步一步建立ASP.NET MVC5程式[Repository+Autofac+Automapper+SqlSugar](九)

前言

童鞋們,大家好

我是專注.NET開發者社群建設的實踐者Rector。

首先,為自己間隔了兩個星期五再更新本系列文章找個不充分的理由:Rector最近工作,家庭的各種事務所緻,希望大家諒解。

本文知識要點

回到本文的主題,還是關于系列文章:《一步一步建立ASP.NET MVC5程式Repository+Autofac+Automapper+SqlSugar》,本文将為大家分享的主要内容有:

  • 響應式網站首頁的布局與制作
  • 文章清單的展示
  • 文章詳情頁面

前端布局與制作

在以本文之前的系列文章的頁面中,我們的網站首頁以及文章清單頁面都沒有應用樣式,本文将給大家分享首頁的制作,其中包含的内容有:

  • 頭部導航
  • 文章清單
  • Bootstrap響應式布局

最終的首頁效果圖如下:

CSS樣式

首先,在項目[TsBlog.Frontend]中建立資源檔案夾命名為:resources,在其中建立一個css樣式檔案夾,并建立一個樣式檔案,命名為:site.css,此時的目錄結構如下:

樣式代碼如下:

site.css

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td { border: 0; margin: 0; padding: 0; }

body { color: #333; font-size: 14px; font-family: -apple-system,'helvetica neue', helvetica,"Helvetica Neue",Helvetica,Arial,"PingFang SC","Hiragino Sans GB","WenQuanYi Micro Hei","Microsoft Yahei",sans-serif; line-height: 22px; width: 100%; height: 100%; }
h1, h2, h3, h4, h5, h6 { clear: both; font-weight: normal; }
ol, ul { list-style: none; }
blockquote { quotes: none; border-left: 5px solid #eee; font-size: 14px; margin: 10px 0; padding: 10px 20px; }
    blockquote:before, blockquote:after { content: ''; content: none; }

a { color: #4484ce; }

/*bootstrap override*/
.btn { border-radius: 2px !important; }
.btn-primary { background-color: #4484CE !important; }
    .btn-primary:hover { background-color: #3A77BE !important; }

.navbar-nav a { color: #333 !important; }
    .navbar-nav > .active > a, .navbar-nav > .active > a:focus, .navbar-nav > .active > a:hover, .navbar-nav a:hover { border-radius: 2px; background-color: #e7e7e7 !important; }
/*site begin*/
.ts-navbar .navbar-nav { height: 50px; vertical-align: middle; line-height: 50px; }
    .ts-navbar .navbar-nav li { vertical-align: middle; line-height: 50px; float: none; display: inline-block; }
.ts-navbar .dropdown li { display: block; }
.ts-navbar .navbar-nav li a { padding: 8px 15px; }
.navbar-brand { height: auto; }
a.nav-btn-login { color: #fff !important; }
    a.nav-btn-login:hover { background-color: #3A77BE !important; color: #fff !important; }
.navbar-profile { margin-right: 0; }
/*home begin*/
.jumbotron h1 { margin-bottom: 15px; }
.jumbotron p { line-height: 28px; }
.post-title { display: block; font-size: 16px; font-weight: 600; border-bottom: 2px solid #e7e7e7; padding-bottom: 8px; }
.post-item-box { margin-bottom: 15px; }
    .post-item-box li { margin-top: 15px; margin-bottom: 15px; padding-top: 10px; padding-bottom: 10px; }
        .post-item-box li h2 { font-size: 16px; font-weight: 500; margin-bottom: 10px; }
.post-item-summary { color: #555; }
.footer-box { padding: 15px; margin-top: 15px; border-top: 1px solid #e7e7e7; }


/*post details*/

.article-content { padding-top: 15px; padding-bottom: 15px; }
    .article-content p { margin-top: 20px; margin-bottom: 20px; }
.article-fixed p { margin-top: 0; margin-bottom: 5px; }
.article-content h1, .article-content h2, .article-content h3, .article-content h4, .article-content h5, .article-content h6 { margin: 15px 0 10px; }
.article-content h1, .article-content h2 { border-bottom: 1px solid #eee; padding-bottom: 10px; }
.article-content h2 { font-size: 1.75em; line-height: 1.2 }
.article-content h3 { font-size: 1.5em; line-height: 1.2 }
.article-content blockquote { background: #f6f6f6 none repeat scroll 0 0; border-left: 2px solid #009a61; color: #555; font-size: 1em; }
.cloud-tags .cloud-tag-item { border: 1px solid #efefef; background-color: #f7f7f7; padding: 5px 10px; }
.cloud-tags .cloud-tag-item, .side-bar-article-list, .article-content { word-break: break-all; word-wrap: break-word; white-space: normal; }
    .article-content pre { background-attachment: scroll; background-clip: border-box; background-color: #f6f6f6; border: medium none; line-height: 1.45; max-height: 35em; overflow: auto; padding: 1em; position: relative; margin-bottom: 15px; margin-top: 15px; }
    .article-content ul li { padding-left: 15px; list-style: inside; }
    .article-content ul li { padding-left: 15px; list-style: inside; }
    .article-content ul, .article-content ol { margin-left: 3em; padding-left: 0; }
        .article-content ul li, .article-content ol li { margin: .3em 0; }
           

以上的樣式表是本文中所用到的,你隻需要複制即可。

打開視圖檔案[...TsBlog\src\Presentation\TsBlog.Frontend\Views\Home\Index.cshtml],首先制作頭部導覽列,其中導覽列的HTML代碼如下:

<nav class="navbar navbar-default navbar-static-top ts-navbar">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="~/">TSBLOG</a>
            </div>
            <div id="navbar" class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li class="active"><a href="~/">網站首頁</a></li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">分類導航 <span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li class="dropdown-header">後端開發</li>
                            <li><a href="http://2sharings.com/category/csharp-development">C#程式設計</a></li>
                            <li><a href="http://2sharings.com/category/dot-net">.NET程式設計</a></li>
                            <li><a href="http://2sharings.com/category/asp-dot-net">ASP.NET</a></li>
                            <li><a href="http://2sharings.com/category/asp-net-mvc">ASP.NET MVC</a></li>
                            <li><a href="http://2sharings.com/category/asp-dotnet-core">ASP.NET Core</a></li>
                            <li><a href="http://2sharings.com/category/winform">Winform</a></li>
                            <li role="separator" class="divider"></li>
                            <li class="dropdown-header">資料庫</li>
                            <li><a href="http://2sharings.com/category/mysql">MySQL</a></li>
                            <li><a href="http://2sharings.com/category/sql-server">SQL Server</a></li>
                            <li><a href="http://2sharings.com/category/sqlite">SqLite</a></li>
                        </ul>
                    </li>
                    <li><a href="~/home/about">關于我們</a></li>
                    <li><a href="~/home/contact">聯系我們</a></li>
                </ul>
                <ul class="nav navbar-nav navbar-right navbar-profile">
                    <li><a href="~/account/register">免費注冊</a></li>
                    <li><a class="btn btn-primary nav-btn-login" href="~/account/login">立即登入</a></li>
                </ul>
            </div><!--/.nav-collapse -->
        </div>
    </nav>
           

正文HTML

其中正文的第一部分為一個BANNER,在這個區域中,可以放置一些重要的關于站點的描述資訊,也可以放滾動播放的廣告圖檔等,按自己的需要處理就可以了。

第二部分則是一個文章清單區域,其中列出了網站最近釋出的20條文章清單,正文的HTML代碼如下:

<div class="container">
        <div class="jumbotron">
            <h1>小夥伴,你好</h1>
            <p>歡迎來到 Rector 的ASP.NET MVC 5 系列文章教程。在這裡,Rector将和你一起一步一步建立一個內建Repository+Autofac+Automapper+SqlSugar的WEB應用程式。</p>
            <p>你準備好了嗎?</p>
            <p>......</p>
            <p>讓我們開始ASP.NET MVC 5 應用程式的探索之旅吧!!!</p>
        </div>
        <strong class="post-title">文章清單(@(Model.Count())篇)</strong>
        <ul class="list-unstyled post-item-box">
            @foreach (var p in Model)
            {
                <li>
                    <h2><a href="~/post/details/@p.Id">@p.Title</a></h2>
                    <p class="post-item-summary">@p.Summary ... <a href="~/post/details/@p.Id">閱讀全文</a></p>
                </li>
            }
        </ul>
    </div>
           

頁腳

頁面最後為頁腳部分,包含比較簡單的版權等資訊,HTML代碼如下:

<footer class="footer-box">
        <div class="container">
            版權所有 &copy; @(DateTime.Now.Year)
        </div>
    </footer>
           

首頁完整的HTML代碼如下:

Index.cshtml

@model IEnumerable<TsBlog.ViewModel.Post.PostViewModel>
@{
    Layout = null;
}
<!doctype html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>ASP.NET MVC 5 系列文章教程--首頁 | TSBLOG</title>
    <link href="~/Content/bootstrap.min.css" rel="stylesheet" />
    <link href="~/resources/css/site.css" rel="stylesheet" />

</head>
<body>
    <nav class="navbar navbar-default navbar-static-top ts-navbar">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="~/">TSBLOG</a>
            </div>
            <div id="navbar" class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li class="active"><a href="~/">網站首頁</a></li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">分類導航 <span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li class="dropdown-header">後端開發</li>
                            <li><a href="http://2sharings.com/category/csharp-development">C#程式設計</a></li>
                            <li><a href="http://2sharings.com/category/dot-net">.NET程式設計</a></li>
                            <li><a href="http://2sharings.com/category/asp-dot-net">ASP.NET</a></li>
                            <li><a href="http://2sharings.com/category/asp-net-mvc">ASP.NET MVC</a></li>
                            <li><a href="http://2sharings.com/category/asp-dotnet-core">ASP.NET Core</a></li>
                            <li><a href="http://2sharings.com/category/winform">Winform</a></li>
                            <li role="separator" class="divider"></li>
                            <li class="dropdown-header">資料庫</li>
                            <li><a href="http://2sharings.com/category/mysql">MySQL</a></li>
                            <li><a href="http://2sharings.com/category/sql-server">SQL Server</a></li>
                            <li><a href="http://2sharings.com/category/sqlite">SqLite</a></li>
                        </ul>
                    </li>
                    <li><a href="~/home/about">關于我們</a></li>
                    <li><a href="~/home/contact">聯系我們</a></li>
                </ul>
                <ul class="nav navbar-nav navbar-right navbar-profile">
                    <li><a href="~/account/register">免費注冊</a></li>
                    <li><a class="btn btn-primary nav-btn-login" href="~/account/login">立即登入</a></li>
                </ul>
            </div><!--/.nav-collapse -->
        </div>
    </nav>
    <div class="container">
        <div class="jumbotron">
            <h1>小夥伴,你好</h1>
            <p>歡迎來到 Rector 的ASP.NET MVC 5 系列文章教程。在這裡,Rector将和你一起一步一步建立一個內建Repository+Autofac+Automapper+SqlSugar的WEB應用程式。</p>
            <p>你準備好了嗎?</p>
            <p>......</p>
            <p>讓我們開始ASP.NET MVC 5 應用程式的探索之旅吧!!!</p>
        </div>
        <strong class="post-title">文章清單(@(Model.Count())篇)</strong>
        <ul class="list-unstyled post-item-box">
            @foreach (var p in Model)
            {
                <li>
                    <h2><a href="~/post/details/@p.Id">@p.Title</a></h2>
                    <p class="post-item-summary">@p.Summary ... <a href="~/post/details/@p.Id">閱讀全文</a></p>
                </li>
            }
        </ul>
    </div>
    <footer class="footer-box">
        <div class="container">
            版權所有 &copy; @(DateTime.Now.Year)
        </div>
    </footer>
    <script src="~/Scripts/jquery-3.2.1.min.js"></script>
    <script src="~/Scripts/bootstrap.min.js"></script>
</body>
</html>
           

後端接口與實作

在完成了前端頁面的布局與制作之後,我們需要後端程式提供接口和服務,來供前端頁面調用,如首頁視圖中的視圖模型:

@model IEnumerable<TsBlog.ViewModel.Post.PostViewModel>
           

文章倉儲接口和實作

打開檔案[IPostRepository.cs],在其中新增接口方法:

FindHomePagePosts

,代碼如下:

using System.Collections.Generic;
using TsBlog.Domain.Entities;

namespace TsBlog.Repositories
{
    public interface IPostRepository : IRepository<Post>
    {
        /// <summary>
        /// 查詢首頁文章清單
        /// </summary>
        /// <param name="limit">要查詢的記錄數</param>
        /// <returns></returns>
        IEnumerable<Post> FindHomePagePosts(int limit = 20);
    }
}
           

打開檔案[PostRepository.cs],實作對應的接口方法:

FindHomePagePosts

using SqlSugar;
using System.Collections.Generic;
using TsBlog.Domain.Entities;

namespace TsBlog.Repositories
{
    /// <summary>
    /// POST表的資料庫操作類
    /// </summary>
    public class PostRepository : GenericRepository<Post>, IPostRepository
    {
        #region Implementation of IPostRepository

        /// <summary>
        /// 查詢首頁文章清單
        /// </summary>
        /// <param name="limit">要查詢的記錄數</param>
        /// <returns></returns>
        public IEnumerable<Post> FindHomePagePosts(int limit = 20)
        {
            using (var db = DbFactory.GetSqlSugarClient())
            {
                var list = db.Queryable<Post>().OrderBy(x => x.Id, OrderByType.Desc).Take(limit).ToList();
                return list;
            }
        }
    }
    #endregion
}
           

文章服務接口和實作

打開檔案[IPostService.cs],在其中新增接口方法:

FindHomePagePosts

using System.Collections.Generic;
using TsBlog.Domain.Entities;

namespace TsBlog.Services
{
    public interface IPostService : IService<Post>
    {
        /// <summary>
        /// 查詢首頁文章清單
        /// </summary>
        /// <param name="limit">要查詢的記錄數</param>
        /// <returns></returns>
        IEnumerable<Post> FindHomePagePosts(int limit = 20);
    }
}
           

打開檔案[PostService.cs],實作對應的接口方法:

FindHomePagePosts

using System.Collections.Generic;
using TsBlog.Domain.Entities;
using TsBlog.Repositories;

namespace TsBlog.Services
{
    public class PostService : GenericService<Post>, IPostService
    {
        private readonly IPostRepository _repository;
        public PostService(IPostRepository repository) : base(repository)
        {
            _repository = repository;
        }


        #region Implementation of IPostService

        /// <summary>
        /// 查詢首頁文章清單
        /// </summary>
        /// <param name="limit">要查詢的記錄數</param>
        /// <returns></returns>
        public IEnumerable<Post> FindHomePagePosts(int limit = 20)
        {
            return _repository.FindHomePagePosts(limit);
        }

        #endregion
    }
}
           

附加修改:重構了一下倉儲接口中的

FindListByClause

方法,将

orderBy

參數設定為可空參數,具體實作如下:

/// <summary>
/// 根據條件查詢資料
/// </summary>
/// <param name="predicate">條件表達式樹</param>
/// <param name="orderBy">排序</param>
/// <returns>泛型實體集合</returns>
IEnumerable<T> FindListByClause(Expression<Func<T, bool>> predicate, string orderBy = "");
           

對應的修改泛型倉儲中的對應實作:

/// <summary>
/// 根據條件查詢資料
/// </summary>
/// <param name="predicate">條件表達式樹</param>
/// <param name="orderBy">排序</param>
/// <returns>泛型實體集合</returns>
public IEnumerable<T> FindListByClause(Expression<Func<T, bool>> predicate, string orderBy = "")
{
    using (var db = DbFactory.GetSqlSugarClient())
    {
        var query = db.Queryable<T>().Where(predicate);
        if (!string.IsNullOrEmpty(orderBy))
        {
            query = query.OrderBy(orderBy);
        }
        var entities = query.ToList();
        return entities;
    }
}
           

同樣的,服務層中也作相應的修改:

IService.cs 檔案中的

FindListByClause

接口方法:

/// <summary>
/// 根據條件查詢資料
/// </summary>
/// <param name="predicate">條件表達式樹</param>
/// <param name="orderBy">排序</param>
/// <returns>泛型實體集合</returns>
IEnumerable<T> FindListByClause(Expression<Func<T, bool>> predicate, string orderBy = "");
           

泛型服務類:GenericService.cs 中的

FindListByClause

方法實作:

/// <summary>
/// 根據條件查詢資料
/// </summary>
/// <param name="predicate">條件表達式樹</param>
/// <param name="orderBy">排序</param>
/// <returns>泛型實體集合</returns>
public IEnumerable<T> FindListByClause(Expression<Func<T, bool>> predicate, string orderBy = "")
{
    return _repository.FindListByClause(predicate, orderBy);
}
           

在開始處理HomeController控制器之前 ,我們先在項目[TsBlog.Core]中建立兩個幫助類,分别為:

HtmlHelper.cs

StringHelper.cs

。其中代碼分别為:

HtmlHelper.cs

:

using System.Text.RegularExpressions;

namespace TsBlog.Core
{
    public static class HtmlHelper
    {
        #region 去掉HTML中的所有标簽,隻留下純文字
        /// <summary>
        /// 去掉HTML中的所有标簽,隻留下純文字
        /// </summary>
        /// <param name="strHtml"></param>
        /// <returns></returns>
        public static string CleanHtml(this string strHtml)
        {
            if (string.IsNullOrEmpty(strHtml)) return strHtml;
            //删除腳本
            strHtml = Regex.Replace(strHtml, "(\\<script(.+?)\\</script\\>)|(\\<style(.+?)\\</style\\>)", "", RegexOptions.IgnoreCase | RegexOptions.Singleline);
            //删除标簽
            var r = new Regex(@"<\/?[^>]*>", RegexOptions.IgnoreCase);
            Match m;
            for (m = r.Match(strHtml); m.Success; m = m.NextMatch())
            {
                strHtml = strHtml.Replace(m.Groups[0].ToString(), "");
            }
            return strHtml.Trim();
        }

        #endregion
    }
}
           

StringHelper.cs

using System;

namespace TsBlog.Core
{
    public static class StringHelper
    {
        #region
        /// <summary>
        /// 截取指定長度的字元串
        /// </summary>
        /// <param name="str">原始字元串</param>
        /// <param name="strLength">要保留的字元串長度</param>
        /// <returns></returns>
        public static string CutStrLength(this string str, int strLength)
        {
            var strNew = str;
            if (string.IsNullOrEmpty(strNew)) return strNew;
            var strOriginalLength = strNew.Length;
            if (strOriginalLength > strLength)
            {
                strNew = strNew.Substring(0, strLength) + "...";
            }
            return strNew;
        }

        #endregion

        #region
        /// <summary>
        /// 截取指定長度的字元串
        /// </summary>
        /// <param name="str">原始字元串</param>
        /// <param name="strLength">要保留的字元串長度</param>
        /// <param name="endWithEllipsis">是或以省略号(...)結束</param>
        /// <returns></returns>
        public static string CutStrLength(string str, int strLength, bool endWithEllipsis)
        {
            string strNew = str;
            if (!strNew.Equals(""))
            {
                int strOriginalLength = strNew.Length;
                if (strOriginalLength > strLength)
                {
                    strNew = strNew.Substring(0, strLength);
                    if (endWithEllipsis)
                    {
                        strNew += "...";
                    }
                }
            }
            return strNew;
        }

        #endregion

        #region 截斷字元串(可保留完整單詞)
        /// <summary>
        /// 截斷字元串(可保留完整單詞)
        /// </summary>
        /// <param name="valueToTruncate">需處理的字元串</param>
        /// <param name="maxLength">字元數</param>
        /// <param name="options">截斷選項</param>
        /// <returns></returns>
        public static string TruncateString(this string valueToTruncate, int maxLength, TruncateOptions options)
        {
            if (valueToTruncate == null)
            {
                return "";
            }

            if (valueToTruncate.Length <= maxLength)
            {
                return valueToTruncate;
            }

            var includeEllipsis = (options & TruncateOptions.IncludeEllipsis) ==
                    TruncateOptions.IncludeEllipsis;
            var finishWord = (options & TruncateOptions.FinishWord) ==
                    TruncateOptions.FinishWord;
            var allowLastWordOverflow =
              (options & TruncateOptions.AllowLastWordToGoOverMaxLength) ==
              TruncateOptions.AllowLastWordToGoOverMaxLength;

            var retValue = valueToTruncate;

            if (includeEllipsis)
            {
                maxLength -= 1;
            }

            var lastSpaceIndex = retValue.LastIndexOf(" ",
              maxLength, StringComparison.CurrentCultureIgnoreCase);

            if (!finishWord)
            {
                retValue = retValue.Remove(maxLength);
            }
            else if (allowLastWordOverflow)
            {
                var spaceIndex = retValue.IndexOf(" ",
                  maxLength, StringComparison.CurrentCultureIgnoreCase);
                if (spaceIndex != -1)
                {
                    retValue = retValue.Remove(spaceIndex);
                }
            }
            else if (lastSpaceIndex > -1)
            {
                retValue = retValue.Remove(lastSpaceIndex);
            }

            if (includeEllipsis && retValue.Length < valueToTruncate.Length)
            {
                retValue += "...";
            }
            return retValue;
        }

        #endregion
    }

    #region 截斷字元串用的枚舉
    /// <summary>
    /// 截斷字元串用的枚舉
    /// </summary>
    [Flags]
    public enum TruncateOptions
    {
        /// <summary>
        /// 不作任何處理
        /// </summary>
        None = 0x0,
        /// <summary>
        /// 保留完整單詞
        /// </summary>
        FinishWord = 0x1,
        /// <summary>
        /// 允許最後一個單詞超過最大長度限制
        /// </summary>
        AllowLastWordToGoOverMaxLength = 0x2,
        /// <summary>
        /// 字元串最後跟省略号
        /// </summary>
        IncludeEllipsis = 0x4
    }
    #endregion
}

           

在項[TsBlog.ViewModel]中的文章視圖檔案[...TsBlog\src\Libraries\TsBlog.ViewModel\Post\PostViewModel.cs]中添加一個新的屬性:

Summary

,此時的

PostViewModel

是這樣的:

namespace TsBlog.ViewModel.Post
{
    /// <summary>
    /// 博文視圖實體類
    /// </summary>
    public class PostViewModel
    {
        /// <summary>
        /// ID
        /// </summary>
        public int Id { get; set; }
        /// <summary>
        /// 标題
        /// </summary>
        public string Title { get; set; }
        /// <summary>
        /// 内容
        /// </summary>
        public string Content { get; set; }
        /// <summary>
        /// 作者ID
        /// </summary>
        public string AuthorId { get; set; }
        /// <summary>
        /// 作者姓名
        /// </summary>
        public string AuthorName { get; set; }
        /// <summary>
        /// 建立時間
        /// </summary>
        public string CreatedAt { get; set; }
        /// <summary>
        /// 釋出時間
        /// </summary>
        public string PublishedAt { get; set; }
        /// <summary>
        /// 是否辨別已删除
        /// </summary>
        public string IsDeleted { get; set; }
        /// <summary>
        /// 是否允許展示
        /// </summary>
        public bool AllowShow { get; set; }
        /// <summary>
        /// 浏覽量
        /// </summary>
        public int ViewCount { get; set; }

        /// <summary>
        /// 摘要
        /// </summary>
        public string Summary { get; set; }
    }
}
           

在項目[TsBlog.Frontend]中建立一個名為:

Extensions

檔案夾,并在其中建立一個文章的靜态擴充類[...\TsBlog.Frontend\Extensions\PostExtension.cs],同時實作以下靜态擴充方法:

using TsBlog.Core;
using TsBlog.ViewModel.Post;

namespace TsBlog.Frontend.Extensions
{
    public static class PostExtension
    {
        /// <summary>
        /// 格式化文章的視圖實體
        /// </summary>
        /// <param name="model">文章視圖實體類</param>
        /// <returns></returns>
        public static PostViewModel FormatPostViewModel(this PostViewModel model)
        {
            if (model == null)
            {
                return null;
            }

            model.Summary = model.Content
                .CleanHtml()            //去掉所有HTML标簽
                .TruncateString(200, TruncateOptions.FinishWord | TruncateOptions.AllowLastWordToGoOverMaxLength);     //截斷指定長度作為文章摘要
            return model;
        }
    }
}
           

網站首頁[HomeController]

在首頁的控制器[...TsBlog.Frontend\Controllers\HomeController.cs]中,利用文章服務接口的方法實作首頁文章清單的查詢,代碼如下:

using System.Linq;
using System.Web.Mvc;
using TsBlog.AutoMapperConfig;
using TsBlog.Frontend.Extensions;
using TsBlog.Services;

namespace TsBlog.Frontend.Controllers
{
    public class HomeController : Controller
    {
        /// <summary>
        /// 文章服務接口
        /// </summary>
        private readonly IPostService _postService;
        public HomeController(IPostService postService)
        {
            _postService = postService;
        }
        /// <summary>
        /// 首頁
        /// </summary>
        /// <returns></returns>
        public ActionResult Index()
        {
            var list = _postService.FindHomePagePosts();
            var model = list.Select(x => x.ToModel().FormatPostViewModel());
            return View(model);
        }
    }
}
           

好了,到此我們的首頁制作與資料綁定等到完成了,按F5運作,我們即可看到本文開篇所示的首頁效果。

文章詳情頁[PostController]

建立一個名為:

PostController

的控制器,并添加如下代碼:

using System.Web.Mvc;
using TsBlog.AutoMapperConfig;
using TsBlog.Services;

namespace TsBlog.Frontend.Controllers
{
    public class PostController : Controller
    {
        /// <summary>
        /// 文章服務接口
        /// </summary>
        private readonly IPostService _postService;

        public PostController(IPostService postService)
        {
            _postService = postService;
        }

        /// <summary>
        /// 文章詳情
        /// </summary>
        /// <param name="id">文章ID</param>
        /// <returns></returns>
        public ActionResult Details(int id)
        {
            var post = _postService.FindById(id);
            var model = post.ToModel();
            return View(model);
        }
    }
}
           

再添加文章詳情頁的視圖[...\TsBlog.Frontend\Views\Post\Details.cshtml],添加如下視圖HTML代碼:

@model TsBlog.ViewModel.Post.PostViewModel
@{
    Layout = null;
}
<!doctype html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>@(Model.Title) | TSBLOG</title>
    <link href="~/Content/bootstrap.min.css" rel="stylesheet" />
    <link href="~/resources/css/site.css" rel="stylesheet" />
</head>
<body>
    <nav class="navbar navbar-default navbar-static-top ts-navbar">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="~/">網站名稱</a>
            </div>
            <div id="navbar" class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a href="~/">網站首頁</a></li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">分類導航 <span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li class="dropdown-header">後端開發</li>
                            <li><a href="http://2sharings.com/category/csharp-development">C#程式設計</a></li>
                            <li><a href="http://2sharings.com/category/dot-net">.NET程式設計</a></li>
                            <li><a href="http://2sharings.com/category/asp-dot-net">ASP.NET</a></li>
                            <li><a href="http://2sharings.com/category/asp-net-mvc">ASP.NET MVC</a></li>
                            <li><a href="http://2sharings.com/category/asp-dotnet-core">ASP.NET Core</a></li>
                            <li><a href="http://2sharings.com/category/winform">Winform</a></li>
                            <li role="separator" class="divider"></li>
                            <li class="dropdown-header">資料庫</li>
                            <li><a href="http://2sharings.com/category/mysql">MySQL</a></li>
                            <li><a href="http://2sharings.com/category/sql-server">SQL Server</a></li>
                            <li><a href="http://2sharings.com/category/sqlite">SqLite</a></li>
                        </ul>
                    </li>
                    <li><a href="~/home/about">關于我們</a></li>
                    <li><a href="~/home/contact">聯系我們</a></li>
                </ul>
                <ul class="nav navbar-nav navbar-right navbar-profile">
                    <li><a href="~/account/register">免費注冊</a></li>
                    <li><a class="btn btn-primary nav-btn-login" href="~/account/login">立即登入</a></li>
                </ul>
            </div>
        </div>
    </nav>
    <div class="container">
        <h1 class="post-title">@Model.Title</h1>
        <article class="article-content">
            @Html.Raw(Model.Content)
        </article>
    </div>
    <footer class="footer-box">
        <div class="container">
            版權所有 &copy; @(DateTime.Now.Year)
        </div>
    </footer>
    <script src="~/Scripts/jquery-3.2.1.min.js"></script>
    <script src="~/Scripts/bootstrap.min.js"></script>
</body>
</html>
           

OK,今天這期的關于網站首頁及文章詳情頁面的布局與制作就分享到這裡,希望對你了解ASP.NET MVC WEB應用程式開發有所幫助。

本期源碼托管位址:請至文章首發位址擷取《一步一步建立ASP.NET MVC5程式[Repository+Autofac+Automapper+SqlSugar](九) 》

資料庫腳本檔案請到目錄下檢視:TsBlog\document\scripts\mysql\v1.9\

如果你喜歡Rector的本系列文章,請為我點個大大的贊。

**看完教程如果覺得還不過瘾的,想“勾對”的,歡迎加入圖享網官方QQ群:483350228,如果你按照教程還原出來的程式運作有問題,請參照本期源碼對應調整與修改遇到問題的,也歡迎加入QQ群。有什麼,你懂的。。。_ **

謝謝你的耐心閱讀,本系列未完待續,我們下期再見……

本文首發于:圖享網 《一步一步建立ASP.NET MVC5程式[Repository+Autofac+Automapper+SqlSugar](九) 》

----------------------------------------------------

專注.NET開發的愛好者知識社群--【https://codedefault.com】。