前言
童鞋们,大家好
我是专注.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代码如下:
正文HTML
其中正文的第一部分为一个BANNER,在这个区域中,可以放置一些重要的关于站点的描述信息,也可以放滚动播放的广告图片等,按自己的需要处理就可以了。
第二部分则是一个文章列表区域,其中列出了网站最近发布的20条文章列表,正文的HTML代码如下:
页脚
页面最后为页脚部分,包含比较简单的版权等信息,HTML代码如下:
首页完整的HTML代码如下:
Index.cshtml
@model IEnumerable
@{
Layout = null;
}
ASP.NET MVC 5 系列文章教程--首页 | TSBLOG
后端接口与实现
在完成了前端页面的布局与制作之后,我们需要后端程序提供接口和服务,来供前端页面调用,如首页视图中的视图模型:
@model IEnumerable
文章仓储接口和实现
打开文件[IPostRepository.cs],在其中新增接口方法: FindHomePagePosts
,代码如下:
using System.Collections.Generic;
using TsBlog.Domain.Entities;
namespace TsBlog.Repositories
{
public interface IPostRepository : IRepository
{
///
/// 查询首页文章列表
///
/// 要查询的记录数
///
IEnumerable FindHomePagePosts(int limit = 20);
}
}
打开文件[PostRepository.cs],实现对应的接口方法:FindHomePagePosts
,代码如下:
using SqlSugar;
using System.Collections.Generic;
using TsBlog.Domain.Entities;
namespace TsBlog.Repositories
{
///
/// POST表的数据库操作类
///
public class PostRepository : GenericRepository, IPostRepository
{
#region Implementation of IPostRepository
///
/// 查询首页文章列表
///
/// 要查询的记录数
///
public IEnumerable FindHomePagePosts(int limit = 20)
{
using (var db = DbFactory.GetSqlSugarClient())
{
var list = db.Queryable().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
{
///
/// 查询首页文章列表
///
/// 要查询的记录数
///
IEnumerable 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, IPostService
{
private readonly IPostRepository _repository;
public PostService(IPostRepository repository) : base(repository)
{
_repository = repository;
}
#region Implementation of IPostService
///
/// 查询首页文章列表
///
/// 要查询的记录数
///
public IEnumerable FindHomePagePosts(int limit = 20)
{
return _repository.FindHomePagePosts(limit);
}
#endregion
}
}
附加修改:重构了一下仓储接口中的 FindListByClause
方法,将orderBy
参数设置为可空参数,具体实现如下:
///
/// 根据条件查询数据
///
/// 条件表达式树
/// 排序
/// 泛型实体集合
IEnumerable FindListByClause(Expression> predicate, string orderBy = "");
对应的修改泛型仓储中的对应实现:
///
/// 根据条件查询数据
///
/// 条件表达式树
/// 排序
/// 泛型实体集合
public IEnumerable FindListByClause(Expression> predicate, string orderBy = "")
{
using (var db = DbFactory.GetSqlSugarClient())
{
var query = db.Queryable().Where(predicate);
if (!string.IsNullOrEmpty(orderBy))
{
query = query.OrderBy(orderBy);
}
var entities = query.ToList();
return entities;
}
}
同样的,服务层中也作相应的修改:
IService.cs 文件中的 FindListByClause
接口方法:
///
/// 根据条件查询数据
///
/// 条件表达式树
/// 排序
/// 泛型实体集合
IEnumerable FindListByClause(Expression> predicate, string orderBy = "");
泛型服务类:GenericService.cs 中的 FindListByClause
方法实现:
///
/// 根据条件查询数据
///
/// 条件表达式树
/// 排序
/// 泛型实体集合
public IEnumerable FindListByClause(Expression> 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中的所有标签,只留下纯文本
///
/// 去掉HTML中的所有标签,只留下纯文本
///
///
///
public static string CleanHtml(this string strHtml)
{
if (string.IsNullOrEmpty(strHtml)) return strHtml;
//删除脚本
strHtml = Regex.Replace(strHtml, "(\\