从零开始,搭建博客系统MVC5+EF6搭建框架(5),博客详情页、留言、轮播图管理、右侧统计博文
一、博客系统进度回顾
上一遍博客介绍到,系统已经实现到了发布以及前台布局展示,接下来就是实现一些,详情页,留言、轮播图管理、右侧博文统计信息实现。
二、博客系统详情页实现
2.1先来看看详情页展示的效果
2.2实现控制器在前台控制器中创建一个Blog的控制器,主要是展示博客分类以及详情页
Action详情页实现:
1 /// <summary> 2 /// 详情页 3 /// </summary> 4 /// <param name="id"></param> 5 /// <returns></returns> 6 public ActionResult Detail(int id) 7 { 8 //获取控制器名称 9 ViewBag.controllername = RouteData.Values["controller"].ToString().ToLower(); 10 var model = BlogArticleServive.getBlogDetails(id); 11 ViewBag.gblist = GuestbookServices.QueryOrderBy(c => c.blogId == id, c => c.createdate, false).ToPagedList(1, 5); 12 13 //发布时间排序 14 ViewBag.blogtimelist = BlogArticleServive.QueryOrderBy(c => true, c => c.bCreateTime, false); 15 //评论排序 16 ViewBag.blogtrafficlist = BlogArticleServive.QueryOrderBy(c => true, c => c.btraffic, false); 17 //留言排序 18 string sql = @"select a.*,b.btitle from (select blogId,count(1) as counts from Guestbook group by blogId) as a 19 inner join BlogArticle as b 20 on 21 b.bID=a.blogId order by counts desc"; 22 23 ViewBag.blogguestbooklist = GuestbookServices.RunProc<TopgbViewModels>(sql); 24 25 return View(model); 26 }
由于展示的内容比实际存储在数据库中的信息多,所以把所有能展示的信息都放在BlogViewModels中,在blog业务逻辑层单独实现了一个包含上一页 下一页,以及摘要赋值的方法。
由于这一部分需要从详情内容中只保留部分文字,所以这里需要对索取的详情信息做处理,由于详情信息是通过富文本编辑来添加的里面包含了很多html代码信息,所以在common程序集中,创建一个工具类,Tools类来存放,辅助的相关方法。
ReplaceHtmlTag方法:
1 #region 去除富文本中的HTML标签 2 /// <summary> 3 /// 去除富文本中的HTML标签 4 /// </summary> 5 /// <param name="html"></param> 6 /// <param name="length"></param> 7 /// <returns></returns> 8 public static string ReplaceHtmlTag(string html, int length = 0) 9 { 10 string strText = System.Text.RegularExpressions.Regex.Replace(html, "<[^>]+>", ""); 11 strText = System.Text.RegularExpressions.Regex.Replace(strText, "&[^;]+;", ""); 12 13 if (length > 0 && strText.Length > length) 14 return strText.Substring(0, length); 15 16 return strText; 17 } 18 #endregion
然后在获取到多有信息,要处理的地方就是用三元运算符来判断,截取前面的文字部分
2.3详情页视图代码
1 @model Wchl.WMBlog.Model.VeiwModels.BlogViewModels 2 3 @{ 4 ViewBag.Title = "博客详情页"; 5 } 6 <link href="~/Content/lib/zui/doc.css" rel="stylesheet" /> 7 <section> 8 <article> 9 <div spellcheck="false" class="example"> 10 <br> 11 <ul class="breadcrumb"> 12 <li><i class="icon-location-arrow icon-muted"></i></li> 13 <li><a href="/home/index">首页</a></li> 14 <li><a href="/blog/index">博客</a></li> 15 <li class="active">Data</li> 16 </ul> 17 <hr> 18 <article class="article"> 19 <header> 20 <h1 class="text-center">@Model.btitle</h1> 21 <dl class="dl-inline"> 22 <dt>发布时间:</dt> 23 <dd>@Model.bCreateTime</dd> 24 <dt>作者:</dt> 25 <dd>@Model.bsubmitter</dd> 26 <dt></dt> 27 <dd class="pull-right"><span class="label label-success">新</span> <span class="label label-warning">火爆</span> <span class="label label-info">原创</span> <span class="label label-danger"><i class="icon-eye-open"></i> @Model.btraffic</span></dd> 28 </dl> 29 <section class="abstract"> 30 <p><strong>摘要:</strong>@Model.digest</p> 31 </section> 32 </header> 33 <section class="article-content"> 34 @Html.Raw(Model.bcontent) 35 </section> 36 <footer> 37 <p class="pull-right text-muted"> 38 发布时间:@Model.bCreateTime.ToString("yyyy年MM月dd日 HH:mm:ss") 点击数:@Model.btraffic 39 </p> 40 <p class="text-important">本文版权所有归<a href="###">@Model.bsubmitter</a></p> 41 <ul class="pager pager-justify"> 42 @if (Model.previous == null) 43 { 44 <li class="previous disabled"><a href="#" title=""><i class="icon-arrow-left"></i> 上一篇: </a></li> 45 } 46 else 47 { 48 <li class="previous"><a href="/blog/Detail/@Model.previousID " title="@Model.previous"><i class="icon-arrow-left"></i> 上一篇: @Model.previous.Substring(0, 5) </a></li> 49 } 50 @if (Model.next == null) 51 { 52 <li class="next disabled"><a href="#">下一篇: <i class="icon-arrow-right"></i></a></li> 53 } 54 else 55 { 56 <li class="next"><a href="/blog/Detail/@Model.nextID " title="=@Model.next">下一篇:@Model.next.Substring(0, 5) <i class="icon-arrow-right"></i></a></li> 57 } 58 59 60 </ul> 61 </footer> 62 </article> 63 </div> 64 </article> 65 <div id="comment"> 66 @Html.Partial("_GuestbookPage") 67 </div> 68 69 </section> 70 @section scripts 71 { 72 <script src="~/Content/lib/laypage/laypage.js"></script> 73 <script type="text/javascript"> 74 var blogid; 75 var curr; 76 $(function () { 77 $("#blogId").val(@Model.bID); 78 //运行分页 79 blogid = $("#blogId").val(); 80 guestbookpage(curr,blogid); 81 }) 82 83 function su(data) { 84 $("#comment").html(data); 85 $("#blogId").val(@Model.bID); 86 //setTimeout(function () { 87 // if (document.getElementById('textcenter') != null) { 88 // //运行分页 89 // guestbookpage(1, blogid); 90 // } 91 //}, 3000); 92 if (document.getElementById('textcenter') != null) { 93 //运行分页 94 guestbookpage(1, blogid); 95 } 96 97 98 99 } 100 101 //以下将以jquery.ajax为例,演示一个异步分页 102 function guestbookpage(curr,blogid) { 103 $.getJSON('/blog/getGuestbook', { 104 page: curr || 1, 105 blogId: blogid//向服务端传的参数,此处只是演示 106 }, function (res) { 107 //此处仅仅是为了演示变化的内容 108 $("#comments-list").html(res.content); 109 //显示分页 110 laypage({ 111 cont: document.getElementById('textcenter'), //容器。值支持id名、原生dom对象,jquery对象。【如该容器为】:<div id="page1"></div> 112 pages: res.pages, //通过后台拿到的总页数 113 curr: curr || 1, //当前页 114 jump: function (obj, first) { //触发分页后的回调 115 if (!first) { //点击跳页触发函数自身,并传递当前页:obj.curr 116 curr = obj.curr; 117 guestbookpage(curr, blogid); 118 } else { 119 curr = first; 120 guestbookpage(curr, blogid); 121 } 122 123 } 124 }); 125 }); 126 }; 127 </script> 128 } 129
这里博客的详情页也是通过布局页来实现的。
三、博客系统详情页留言实现
3.1留言展示页面效果
先要创建一个类在数据库中创建一个表:
Guestbook类
1 namespace Wchl.WMBlog.Model.Models 2 { 3 public class Guestbook 4 { 5 6 /// <summary>留言表 7 /// 8 /// </summary> 9 public int id { get; set; } 10 11 /// <summary>博客ID 12 /// 13 /// </summary> 14 public int? blogId { get; set; } 15 /// <summary>创建时间 16 /// 17 /// </summary> 18 public DateTime createdate { get; set; } 19 public string username { get; set; } 20 21 /// <summary>手机 22 /// 23 /// </summary> 24 public string phone { get; set; } 25 /// <summary>qq 26 /// 27 /// </summary> 28 public string QQ { get; set; } 29 30 /// <summary>留言内容 31 /// 32 /// </summary> 33 public string body { get; set; } 34 /// <summary>ip地址 35 /// 36 /// </summary> 37 public string ip { get; set; } 38 39 /// <summary>是否显示在前台,0否1是 40 /// 41 /// </summary> 42 public bool isshow { get; set; } 43 44 public BlogArticle blogarticle { get; set; } 45 } 46 }
GuestbookViewModels展示类:
1 namespace Wchl.WMBlog.Model.VeiwModels 2 { 3 /// <summary> 4 /// 留言信息展示类 5 /// </summary> 6 public class GuestbookViewModels 7 { 8 /// <summary>留言表 9 /// 10 /// </summary> 11 public int id { get; set; } 12 13 /// <summary>博客ID 14 /// 15 /// </summary> 16 public int? blogId { get; set; } 17 /// <summary>创建时间 18 /// 19 /// </summary> 20 public DateTime createdate { get; set; } 21 public string username { get; set; } 22 23 /// <summary>手机 24 /// 25 /// </summary> 26 public string phone { get; set; } 27 /// <summary>qq 28 /// 29 /// </summary> 30 public string QQ { get; set; } 31 32 /// <summary>留言内容 33 /// 34 /// </summary> 35 public string body { get; set; } 36 /// <summary>ip地址 37 /// 38 /// </summary> 39 public string ip { get; set; } 40 41 /// <summary>是否显示在前台,0否1是 42 /// 43 /// </summary> 44 public bool isshow { get; set; } 45 46 public BlogArticle blogarticle { get; set; } 47 } 48 }
GuestbookMap字段约束:
1 2 namespace Wchl.WMBlog.Model.Maps 3 { 4 public class GuestbookMap: EntityTypeConfiguration<Guestbook> 5 { 6 public GuestbookMap() { 7 this.HasKey(p => p.id); 8 //设置外键 9 this.HasRequired(p => p.blogarticle).WithMany().HasForeignKey(p => p.blogId); 10 } 11 } 12 }
接下来就是创建相应的仓储接口、实现,服务接口、实现,这里就不一一介绍了,跟上一篇实现博客类的是一样的。
我在实现这个功能的时候,想使用ajax来提交:
这里我把所有留言的内容放在一个部分视图页
_GuestbookPage代码:
1 <article> 2 <div spellcheck="false" class="example"> 3 <div class="comments"> 4 <header> 5 <div class="pull-right"><a href="#commentReplyForm2" class="btn btn-primary"><i class="icon-comment-alt"></i> 发表评论</a></div> 6 <h3>所有评论</h3> 7 </header> 8 <section class="comments-list" id="comments-list"> 9 @{ if (ViewBag.gblist != null) 10 { 11 foreach (var list in ViewBag.gblist) 12 { 13 <div class="comment"> 14 <a href="###" class="avatar"> 15 <i class="icon-user icon-2x"></i> 16 </a> 17 <div class="content"> 18 <div class="pull-right text-muted">@list.createdate</div> 19 <div><a href="###"><strong>@list.username</strong></a></div> 20 <div class="text">@list.body</div> 21 <div class="actions"> 22 <a href="##">回复</a> 23 </div> 24 </div> 25 </div> 26 } 27 } 28 29 } 30 </section> 31 <br /> 32 <div class="text-center" id="textcenter"> 33 34 </div> 35 <footer> 36 <div class="reply-form" id="commentReplyForm2"> 37 <a href="###" class="avatar"><i class="icon-user icon-2x"></i></a> 38 @using (Ajax.BeginForm("addGuestbook", "Blog", new AjaxOptions() 39 { 40 HttpMethod = "post", 41 OnSuccess="su" 42 43 }, new { @class = "form-horizontal" })) 44 { 45 <input type="hidden" id="blogId" name="blogId" value=""/> 46 <div class="form-group col-md-4 form-inline"> 47 <label for="username" class="col-xs-3">昵称</label> 48 <div> 49 <input type="text" class="form-control" name="username" id="username" placeholder="昵称"> 50 </div> 51 </div> 52 <div class="form-group col-md-4 form-inline"> 53 <label for="phone" class="col-xs-3">手机</label> 54 <div> 55 <input type="text" class="form-control" name="phone" id="phone" placeholder="手机"> 56 </div> 57 </div> 58 <div class="form-group col-md-4 form-inline"> 59 <label for="QQ" class="col-xs-3">Q Q</label> 60 <div> 61 <input type="text" class="form-control" name="QQ" id="QQ" placeholder="Q Q"> 62 </div> 63 </div> 64 <div class="form-group"></div> 65 <div class="form-group"> 66 <textarea class="form-control new-comment-text" id="body" name="body" rows="4" placeholder="撰写留言..."></textarea> 67 </div> 68 <div class="form-group comment-user"> 69 <div class="row"> 70 <div class="col-md-2 col-md-offset-10"><button type="submit" class="btn btn-block btn-primary " value="提交">提交</button></div> 71 </div> 72 </div> 73 } 74 </div> 75 </footer> 76 </div> 77 </div> 78 </article> 79
其中分为两部分,一部分是输入留言,一部分是展示信息。
留言提交的表单如下:
在blog控制器中实现添加数据方法:
1 /// <summary> 2 ///提交评论 3 /// </summary> 4 /// <param name="model"></param> 5 /// <returns></returns> 6 [HttpPost] 7 public ActionResult addGuestbook(GuestbookViewModels model) 8 { 9 model.createdate = DateTime.Now; 10 model.ip = Request.UserHostAddress; 11 //AutoMapper自动映射 12 Mapper.Initialize(cfg => cfg.CreateMap<GuestbookViewModels, Guestbook > ()); 13 Guestbook models = Mapper.Map< GuestbookViewModels, Guestbook> (model); 14 BlogArticle blogArticle = BlogArticleServive.QueryWhere(a => a.bID == model.blogId).FirstOrDefault(); 15 blogArticle.bcommentNum += 1; 16 BlogArticleServive.SaverChanges(); 17 GuestbookServices.Add(models); 18 GuestbookServices.SaverChanges(); 19 ViewBag.gblist = GuestbookServices.QueryOrderBy(c => c.blogId == model.blogId, c => c.createdate, false).ToPagedList(1, 5); 20 return PartialView("_GuestbookPage"); 21 }
执行成功之后就把信息加载到分页信息的位置。
3.2添加实现之后就是展示数据,展示数据使用的是一个js分页插件laypage.js
在mvc的视图中只能在页面有一个model类,所以就只能使用ViewBag.gblist来传值了,本来这个打算用mvcpager插件,结果发现不行,就只能使用js来分页,实现如下:
后台blog控制器下,实现分页,然后拼接数据:
1 /// <summary> 2 /// js分页实现 3 /// </summary> 4 /// <param name="page"></param> 5 /// <param name="blogId"></param> 6 /// <returns></returns> 7 public ActionResult getGuestbook(int page,int blogId) 8 { 9 StringBuilder sb = new StringBuilder(); 10 int pages = 0; 11 var modelsLists = GuestbookServices.QueryByPage(page, 5,out pages, c => c.blogId == blogId, c => c.createdate, false); 12 foreach (var item in modelsLists) 13 { 14 sb.AppendFormat(@"<div class='comment'> 15 <a href = '###' class='avatar'> 16 <i class='icon-user icon-2x'></i> 17 </a> 18 <div class='content'> 19 <div class='pull-right text-muted'>{0}</div> 20 <div><a href = '###' ><strong > {1}</strong ></a ></div> 21 <div class='text'>{2}</div> 22 <div class='actions'> 23 <a href = '##' > 回复 </a> 24 </div > 25 </div > 26 </div >",item.createdate,item.username,item.body); 27 } 28 if (pages % 5 == 0) 29 { 30 pages = pages / 5; 31 } 32 else 33 { 34 pages = (pages / 5)+1; 35 } 36 37 38 return Json(new { 39 content = sb.ToString(), 40 pages= pages 41 }); 42 }
在详情页中直接调用就可以了实现分页了
四、博客系统广告轮播图管理实现
4.2主页轮播图视图中的代码实现
4.3轮播图管理后台页面实现展示
这里上传图片使用的是百度的上传控件
视图页代码实现:
1 2 @{ 3 ViewBag.Title = "广告管理"; 4 } 5 @section stylesheet{ 6 <link href="~/Content/CSS/blogArticleStyle.css" rel="stylesheet" /> 7 <link href="~/Content/lib/DataTables/css/jquery.dataTables.css" rel="stylesheet" /> 8 <link href="~/Content/lib/DataTables/css/dataTables.bootstrap.css" rel="stylesheet" /> 9 <link href="~/Content/lib/webuploader/dist/webuploader.css" rel="stylesheet" /> 10 <style type="text/css"> 11 .uploader-list { 12 width: 100%; 13 overflow: hidden; 14 } 15 16 .file-item { 17 float: left; 18 position: relative; 19 margin: 0 20px 20px 0; 20 padding: 4px; 21 } 22 23 .file-item .info { 24 position: absolute; 25 left: 4px; 26 bottom: 4px; 27 right: 4px; 28 height: 20px; 29 line-height: 20px; 30 text-indent: 5px; 31 background: rgba(0, 0, 0, 0.6); 32 color: white; 33 overflow: hidden; 34 white-space: nowrap; 35 text-overflow: ellipsis; 36 font-size: 12px; 37 z-index: 10; 38 } 39 40 .file-item .error { 41 position: absolute; 42 top: 4px; 43 left: 4px; 44 right: 4px; 45 background: red; 46 color: white; 47 text-align: center; 48 height: 20px; 49 font-size: 14px; 50 line-height: 23px; 51 } 52 53 .upload-state-done:after { 54 content: "\f00c"; 55 font-family: FontAwesome; 56 font-style: normal; 57 font-weight: normal; 58 line-height: 1; 59 -webkit-font-smoothing: antialiased; 60 -moz-osx-font-smoothing: grayscale; 61 font-size: 32px; 62 position: absolute; 63 bottom: 0; 64 right: 4px; 65 color: #4cae4c; 66 z-index: 99; 67 } 68 69 .thumbnail { 70 } 71 /*表格信息对齐重写*/ 72 .dataTables_wrapper .dataTables_length { 73 float: left; 74 margin-left: 15px; 75 margin-top: 5px; 76 } 77 </style> 78 } 79 80 @*<link href="~/Content/CSS/userStyle.css" rel="stylesheet" />*@ 81 <script src="~/Content/lib/DataTables/js/jquery.dataTables.js"></script> 82 <script src="~/Content/lib/DataTables/js/dataTables.bootstrap.js"></script> 83 <script src="~/Content/lib/webuploader/dist/webuploader.js"></script> 84 <script type="text/javascript"> 85 var table; 86 // 图片上传demo 87 $(function () { 88 //百度上传方法 89 baiduuploader(); 90 //创建表格 91 creatabel(); 92 }); 93 //初始化表格 94 function creatabel() { 95 table = $('#adtabel').DataTable({ 96 "processing": true, 97 "serverSide": true, 98 //"paging": true, 99 "scrollY": 366, 100 "ajax": { 101 url: '@Url.Action("getData", "Advertisement")' 102 //"dataType": "json" 103 }, 104 // "pagingType": "full_numbers", 105 // "sLoadingRecords": "正在加载数据...", 106 // "sZeroRecords": "暂无数据", 107 //"stateSave": true, 108 "searching": false, 109 "aaSorting": [[1, "desc"]],//默认第几个排序 110 "dom": 'rt<"bottom"iflp><"clear">', 111 "columns": [ 112 { 113 "data": "count", 114 "width": "28px", 115 }, 116 { 117 "data": "imgUrl", 118 "width": "60px", 119 render: function (data, type, row, meta) { 120 //这里是主题 把url变成超链接、把图片路径显示为图片 121 //return "<a href='" + data + "'>" + data + "</a>"; 122 return "<img src='" + data + "' style='width:50px;height:50px' />"; 123 } 124 }, 125 { "data": "title" }, 126 { "data": "url" }, 127 { "data": "createdate" }, 128 { "data": "remark" }, 129 { 130 data: null 131 } 132 ], 133 "columnDefs": [ 134 { 135 // 指定第一列,从0开始,0表示第一列,1表示第二列…… 136 targets: -1, 137 "width": "100px", 138 render: function (data, type, row, meta) { 139 return '<a type="button" class="btn btn-danger" href="#" onclick="del(\'' + row.id + '\')" ><i class="icon icon-trash"></i>删除</a>' 140 } 141 } 142 ], 143 "language": { 144 "processing": "玩命加载中...", 145 "lengthMenu": "显示 _MENU_ 项结果", 146 "zeroRecords": "没有匹配结果", 147 "info": "显示第 _START_ 至 _END_ 项结果,共 _TOTAL_ 项", 148 "infoEmpty": "显示第 0 至 0 项结果,共 0 项", 149 "infoFiltered": "(由 _MAX_ 项结果过滤)", 150 "infoPostFix": "", 151 "url": "", 152 "paginate": { 153 "first": "首页", 154 "previous": "上一页", 155 "next": "下一页", 156 "last": "末页" 157 } 158 } 159 }); 160 }; 161 162 //删除轮播数据 163 function del(adid) { 164 if (confirm("是否确认删除?")) 165 { 166 $.post('@Url.Action("del", "Advertisement")', { id: adid },function(ajaxobj){ 167 if(ajaxobj.status=="0") 168 { 169 //如果后台删除成功,则刷新表格,并提示用户删除成功 170 //保留分页信息 171 table.ajax.reload(null,false); 172 alert("删除成功"); 173 } 174 }) 175 } 176 } 177 178 //百度上传 179 function baiduuploader() { 180 var $ = jQuery, 181 $list = $('#fileList'), 182 // 优化retina, 在retina下这个值是2 183 ratio = window.devicePixelRatio || 1, 184 185 // 缩略图大小 186 thumbnailWidth = 80 * ratio, 187 thumbnailHeight = 80 * ratio, 188 189 // Web Uploader实例 190 uploader; 191 192 // 初始化Web Uploader 193 uploader = WebUploader.create({ 194 195 // 自动上传。 196 auto: true, 197 198 // swf文件路径 199 swf: '/Content/lib/webuploader/dist/Uploader.swf', 200 201 // 文件接收服务端。 202 server: '@Url.Action("UpLoadProcess", "Advertisement")', 203 204 // 选择文件的按钮。可选。 205 // 内部根据当前运行是创建,可能是input元素,也可能是flash. 206 pick: '#filePicker', 207 208 // 只允许选择文件,可选。 209 accept: { 210 title: 'Images', 211 extensions: 'gif,jpg,jpeg,bmp,png', 212 mimeTypes: 'image/*' 213 } 214 }); 215 216 // 当有文件添加进来的时候 217 uploader.on('fileQueued', function (file) { 218 var $li = $( 219 '<div id="' + file.id + '" class="file-item thumbnail">' + 220 '<img>' + 221 '<div class="info">' + file.name + '</div>' + 222 '</div>' 223 ), 224 $img = $li.find('img'); 225 226 $list.append($li); 227 228 // 创建缩略图 229 uploader.makeThumb(file, function (error, src) { 230 if (error) { 231 $img.replaceWith('<span>不能预览</span>'); 232 return; 233 } 234 235 $img.attr('src', src); 236 }, thumbnailWidth, thumbnailHeight); 237 }); 238 239 // 文件上传过程中创建进度条实时显示。 240 uploader.on('uploadProgress', function (file, percentage) { 241 var $li = $('#' + file.id), 242 $percent = $li.find('.progress span'); 243 244 // 避免重复创建 245 if (!$percent.length) { 246 $percent = $('<p class="progress"><span></span></p>') 247 .appendTo($li) 248 .find('span'); 249 } 250 251 $percent.css('width', percentage * 100 + '%'); 252 }); 253 254 // 文件上传成功,给item添加成功class, 用样式标记上传成功。 255 uploader.on('uploadSuccess', function (file, response) { 256 $('#' + file.id).addClass('upload-state-done'); 257 //alert(response.filePath); 258 $('#ImgUrl').val(response.filePath); 259 }); 260 261 // 文件上传失败,现实上传出错。 262 uploader.on('uploadError', function (file) { 263 var $li = $('#' + file.id), 264 $error = $li.find('div.error'); 265 266 // 避免重复创建 267 if (!$error.length) { 268 $error = $('<div class="error"></div>').appendTo($li); 269 } 270 271 $error.text('上传失败'); 272 }); 273 274 // 完成上传完了,成功或者失败,先删除进度条。 275 uploader.on('uploadComplete', function (file) { 276 $('#' + file.id).find('.progress').remove(); 277 }); 278 }; 279 //上传轮播信息成功之后调用 280 function afterSu(ajaxobj) { 281 if (ajaxobj.status == "0") { 282 alert("上传成功!"); 283 window.location.reload(); 284 } else if (ajaxobj.status == "1") { 285 alert("上传失败!"); 286 window.location.reload(); 287 } 288 } 289 </script> 290 291 <!-- head star --> 292 <div class="tnav row border-bottom white-bg page-heading"> 293 <div class="col-sm-4"> 294 <h2 class="fl">博客后台</h2> 295 <ol class="breadcrumb fl"> 296 <li><a href="/admin/Home">广告管理</a></li> 297 <li><strong>轮播图管理</strong></li> 298 </ol> 299 </div> 300 </div> 301 <!-- head end --> 302 303 <div class="example scrollbar-hover" style="max-height: 600px; overflow: scroll;"> 304 <!--table star--> 305 <table id="adtabel" class="table table-striped table-bordered table-hover dataTables-example dataTable" cellspacing="0"> 306 <thead> 307 <tr> 308 <th>编号</th> 309 <th>图片</th> 310 <th>标题</th> 311 <th>链接</th> 312 <th>创建时间</th> 313 <th>备注</th> 314 <th>操作</th> 315 </tr> 316 </thead> 317 <!-- tbody是必须的 --> 318 319 </table> 320 <!--table end--> 321 <hr /> 322 <!--form star--> 323 @using (Ajax.BeginForm("add", "Advertisement", new AjaxOptions() 324 { 325 HttpMethod = "post", 326 OnSuccess = "afterSu" 327 }, new { @class = "form-horizontal" })) 328 { 329 <input type="hidden" id="ImgUrl" name="ImgUrl" value="" /> 330 <div class="form-group"> 331 <div class="form-group"> 332 <label for="Title" class="col-sm-2">标题:</label> 333 <div class="col-md-4 col-sm-10"> 334 <input type="text" class="form-control" id="Title" name="Title" placeholder="标题"> 335 </div> 336 </div> 337 <div class="form-group"> 338 <label for="Url" class="col-sm-2">链接:</label> 339 <div class="col-md-4 col-sm-10"> 340 <input type="text" class="form-control" name="Url" id="Url" placeholder="http://"> 341 </div> 342 </div> 343 <div class="form-group"> 344 <label for="Remark" class="col-sm-2">备注:</label> 345 <div class="col-md-4 col-sm-10"> 346 <textarea rows="2" type="text" class="form-control" name="Remark" id="Remark" placeholder="说明"></textarea> 347 </div> 348 </div> 349 </div> 350 <div class="form-group"> 351 <label for="exampleInputPassword4" class="col-sm-2">上传图片:</label> 352 <div class="col-md-10 col-sm-10"> 353 <!--dom结构部分--> 354 <div id="uploader-demo"> 355 <!--用来存放item--> 356 <div id="fileList" class="uploader-list"></div> 357 <div id="filePicker">选择图片</div> 358 </div> 359 </div> 360 </div> 361 <div class="form-group"> 362 <div class="col-sm-offset-2 col-sm-10"> 363 <button type="submit" class="btn btn-info">提交</button> 364 </div> 365 </div> 366 } 367 <!--form end--> 368 </div> 369 370 371
控制器实现后台代码
1 namespace Wchl.WMBlog.WebUI.Areas.admin.Controllers 2 { 3 public class AdvertisementController : BaseController 4 { 5 IAdvertisementServices AdvertisementServices; 6 public AdvertisementController(IAdvertisementServices AdvertisementServices) 7 { 8 this.AdvertisementServices = AdvertisementServices; 9 } 10 11 // GET: admin/Advertisement 12 public ActionResult Index() 13 { 14 return View(); 15 } 16 17 /// <summary> 18 /// 获取广告信息 19 /// </summary> 20 /// <returns></returns> 21 public ActionResult getData() 22 { 23 int pageIndex = Request["start"] != null ? int.Parse(Request["start"]) : 1; 24 int pageSize = Request["length"] != null ? int.Parse(Request["length"]) : 5; 25 int draw = Request["draw"] != null ? int.Parse(Request["draw"]) : 1; 26 int totalCount; 27 int count = 0; 28 var adInfoList = AdvertisementServices.QueryByBeginPage(pageIndex, pageSize, out totalCount, r =>true , r => r.Createdate, false); 29 var temp = from u in adInfoList 30 select new { count= count+=1, ID = u.Id, Title = u.Title, ImgUrl = u.ImgUrl, Createdate = u.Createdate,url=u.Url, Remark = u.Remark }; 31 return Json(new { 32 data = temp.ToList(), 33 draw = draw, 34 recordsTotal = totalCount, 35 recordsFiltered = totalCount 36 },JsonRequestBehavior.AllowGet); 37 } 38 39 /// <summary> 40 /// 删除广告 41 /// </summary> 42 /// <param name="id"></param> 43 /// <returns></returns> 44 [HttpPost] 45 public ActionResult del(int id) 46 { 47 Advertisement model = new Advertisement() {Id=id }; 48 AdvertisementServices.Delete(model, false); 49 AdvertisementServices.SaverChanges(); 50 return Json(new { status = "0" },JsonRequestBehavior.AllowGet); 51 } 52 53 /// <summary> 54 /// 添加广告信息 55 /// </summary> 56 /// <returns></returns> 57 [HttpPost] 58 public ActionResult Add(AdvertisementViewModels model) 59 { 60 model.Createdate = DateTime.Now; 61 //AutoMapper自动映射 62 Mapper.Initialize(cfg => cfg.CreateMap<AdvertisementViewModels, Advertisement>()); 63 Advertisement models = Mapper.Map<AdvertisementViewModels, Advertisement>(model); 64 AdvertisementServices.Add(models); 65 AdvertisementServices.SaverChanges(); 66 return Json(new { status="0" },JsonRequestBehavior.AllowGet); 67 } 68 69 /// <summary> 70 /// 百度上传图片 71 /// </summary> 72 /// <param name="id">百度插件自定义对图片的命名id</param> 73 /// <param name="name">图片名称</param> 74 /// <param name="type">图片类型</param> 75 /// <param name="lastModifiedDate">图片本身的修改时间</param> 76 /// <param name="size">图片大小</param> 77 /// <param name="file">文件流</param> 78 /// <returns></returns> 79 public ActionResult UpLoadProcess(string id, string name, string type, string lastModifiedDate, int size, HttpPostedFileBase file) 80 { 81 string filePathName = string.Empty; 82 83 string localPath = Server.MapPath("/upload/"); 84 if (Request.Files.Count == 0) 85 { 86 return Json(new { jsonrpc = 2.0, error = new { code = 102, message = "保存失败" }, id = "id" }); 87 } 88 89 string ex = Path.GetExtension(file.FileName); 90 filePathName = Guid.NewGuid().ToString("N") + ex; 91 92 string datedir = DateTime.Now.ToString("yyyyMMdd"); 93 if (!Directory.Exists(localPath + datedir)) 94 { 95 Directory.CreateDirectory(localPath + datedir); 96 } 97 string path = localPath + datedir; 98 try 99 { 100 file.SaveAs(Path.Combine(path, filePathName)); 101 } 102 catch (Exception) 103 { 104 return Json(new { jsonrpc = 2.0, error = new { code = 103, message = "保存失败" }, id = "id" }); 105 } 106 return Json(new 107 { 108 jsonrpc = "2.0", 109 id = id, 110 filePath = "/Upload/"+datedir + "/" + filePathName 111 }); 112 113 } 114 } 115 }
广告对应的表的类Advertisement:
1 namespace Wchl.WMBlog.Model.Models 2 { 3 public class Advertisement 4 { 5 /// <summary> 6 /// 分类ID 7 /// </summary> 8 public int Id { get; set; } 9 /// <summary> 10 /// 创建时间 11 /// </summary> 12 public DateTime Createdate { get; set; } 13 14 /// <summary> 15 /// 广告图片 16 /// </summary> 17 public string ImgUrl { get; set; } 18 19 /// <summary> 20 /// 广告标题 21 /// </summary> 22 public string Title { get; set; } 23 24 /// <summary> 25 /// 广告链接 26 /// </summary> 27 public string Url { get; set; } 28 29 /// <summary> 30 /// 备注 31 /// </summary> 32 public string Remark { get; set; } 33 } 34 }
展示类AdvertisementViewModels:
1 namespace Wchl.WMBlog.Model.VeiwModels 2 { 3 /// <summary> 4 /// 广告类 5 /// </summary> 6 public class AdvertisementViewModels 7 { 8 /// <summary> 9 /// 分类ID 10 /// </summary> 11 public int Id { get; set; } 12 /// <summary> 13 /// 创建时间 14 /// </summary> 15 public DateTime Createdate { get; set; } 16 17 /// <summary> 18 /// 广告图片 19 /// </summary> 20 public string ImgUrl { get; set; } 21 22 /// <summary> 23 /// 广告标题 24 /// </summary> 25 public string Title { get; set; } 26 27 /// <summary> 28 /// 广告链接 29 /// </summary> 30 public string Url { get; set; } 31 32 /// <summary> 33 /// 备注 34 /// </summary> 35 public string Remark { get; set; } 36 } 37 }
表约束AdvertisementMap:
1 namespace Wchl.WMBlog.Model.Maps 2 { 3 public class AdvertisementMap: EntityTypeConfiguration<Advertisement> 4 { 5 public AdvertisementMap() 6 { 7 this.HasKey(p => p.Id); 8 this.Property(p => p.ImgUrl).HasMaxLength(512); 9 this.Property(p => p.Title).HasMaxLength(64); 10 this.Property(p => p.Url).HasMaxLength(256); 11 } 12 } 13 }
这里实现的是添加图片自动上传:
五、博客系统右侧统计
5.1效果显示,这里的统计主要是对发布时间、阅读排行、评论排行做了统计。
后台实现代码:
部分布局视图代码:
1 <!--右边栏部分--> 2 <aside> 3 <div class="col-md-4"> 4 <section class="youbianlan"> 5 <div class="panel-group"> 6 <div class="panel"> 7 <div class="panel-heading"> 8 <div class="panel-title panel-info"> 9 <h4>最新发布</h4> 10 </div> 11 </div> 12 <div class="panel-body"> 13 <ul> 14 @{ 15 if (ViewBag.blogtimelist != null) 16 { 17 for (int i = 0; i < 10; i++) 18 { 19 <li><a href="/blog/Detail/@ViewBag.blogtimelist[i].bID">@ViewBag.blogtimelist[i].btitle</a></li> 20 } 21 } 22 23 } 24 </ul> 25 </div> 26 </div> 27 </div> 28 </section> 29 <section class="youbianlan"> 30 <div class="panel-group"> 31 <div class="panel"> 32 <div class="panel-heading"> 33 <div class="panel-title panel-info"> 34 <h4>阅读排行榜</h4> 35 </div> 36 </div> 37 <div class="panel-body"> 38 <ul> 39 @{ 40 if (ViewBag.blogtrafficlist != null) 41 { 42 for (int i = 0; i < 10; i++) 43 { 44 <li><a href="/blog/Detail/@ViewBag.blogtrafficlist[i].bID">@ViewBag.blogtrafficlist[i].btitle</a></li> 45 } 46 } 47 48 } 49 </ul> 50 </div> 51 </div> 52 </div> 53 </section> 54 <section class="youbianlan"> 55 <div class="panel-group"> 56 <div class="panel"> 57 <div class="panel-heading"> 58 <div class="panel-title panel-info"> 59 <h4>评论排行榜</h4> 60 </div> 61 </div> 62 <div class="panel-body"> 63 <ul> 64 @{ 65 List<TopgbViewModels> list = ViewBag.blogguestbooklist as List<TopgbViewModels>; 66 if (list != null && list.Any()) 67 { 68 if (list.Count < 10) 69 { 70 for (int i = 0; i < list.Count; i++) 71 { 72 <li><a href="/blog/Detail/@list[i].blogId">@list[i].btitle</a></li> 73 } 74 } 75 else 76 { 77 for (int i = 0; i < 10; i++) 78 { 79 <li><a href="/blog/Detail/@list[i].blogId">@list[i].btitle</a></li> 80 } 81 } 82 83 } 84 85 } 86 </ul> 87 </div> 88 </div> 89 </div> 90 </section> 91 </div> 92 </aside> 93 <!--右边栏部分结束--> 94 95
这里重点讲讲怎么在EF中执行SQL语句:
在仓储父类的实现中这里传入的实体就是需要展示表内容的类:
六、博客系统小结
现阶段博客系统已经完成了前台展示,后台发布基本功能,接下来打算在后台添加一个权限模块,个人能力有限,代码水平不够好,也许我这样的代码还入不了大神的法眼,等我把权限做完之后,我会一点一点的来修改代码。
其实我写代码的时间不长,主要是每次想功能怎么实现的时候,要把每一步都想到,感觉编程,真正实现功能的开发编码的时间只有40%左右。
如果大家对这个有什么意见和想法,可以提出来,我会尽量,去修改的,谢谢了。
补充内容:
根据园友提出的补漏部分说明:
这个QueryByBeginPage是在仓储层写的一个查询分页的功能,是为了配合DataTables使用的。
1 /// <summary> 2 /// 从第几条开始用于DataTables 3 /// </summary> 4 /// <typeparam name="TKey"></typeparam> 5 /// <param name="pageIndex">第几页开始</param> 6 /// <param name="pagesize">一页几条</param> 7 /// <param name="rowcount">一共多少条</param> 8 /// <param name="predicate">条件</param> 9 /// <param name="keySelector">排序关键字</param> 10 /// <param name="IsQueryOrderBy">升序还是降序</param> 11 /// <returns></returns> 12 public List<TEntity> QueryByBeginPage<TKey>(int pageIndex, int pagesize, out int rowcount, Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, TKey>> keySelector, bool IsQueryOrderBy) 13 { 14 rowcount = _dbSet.Count(predicate); 15 if (IsQueryOrderBy) 16 { 17 return _dbSet.Where(predicate).OrderBy(keySelector).Skip(pageIndex).Take(pagesize).ToList(); 18 } 19 else 20 { 21 return _dbSet.Where(predicate).OrderByDescending(keySelector).Skip(pageIndex).Take(pagesize).ToList(); 22 } 23 }
这个TopgbViewModels是查询统计留言排行用的一个类,是根据你数据库查询出来表建的相应的类
1 /// <summary> 2 /// 留言排名展示类 3 /// </summary> 4 public class TopgbViewModels 5 { 6 /// <summary>博客ID 7 /// 8 /// </summary> 9 public int? blogId { get; set; } 10 11 /// <summary> 12 /// 评论数量 13 /// </summary> 14 public int counts { get; set; } 15 16 /// <summary>博客标题 17 /// 18 /// </summary> 19 public string btitle { get; set; } 20 }
有什么不明白的地方都可以跟我留言,我会尽量写清楚点,让大家看的明白点。