zoukankan      html  css  js  c++  java
  • AspNetCore-MVC实战系列(三)之个人中心

    AspNetCore - MVC实战系列目录

    . 爱留图网站诞生

    . git源码:https://github.com/shenniubuxing3/LovePicture.Web

    . AspNetCore - MVC实战系列(一)之Sqlserver表映射实体模型

    . AspNetCore-MVC实战系列(二)之通过绑定邮箱找回密码

    AspNetCore-MVC实战系列(三)之个人中心

    AspNetCore-MVC实战系列(四)之账户设置

    开篇唠嗑

    这实战第三章,也就是本章的时候为了响应博友们的要求,已经把爱留图网站的代码开源到了git了,也为了NetCore的美好明天贡献自己微薄的一份力,尽管已经开源了源码,我还是会坚持写完netcore实战系列的博文,希望大家多多支持;还有就是今天为了缓解51假期综合征能进入学习正规,早上习惯性的打开博客园,让我吃惊了,看着今天编辑推荐的博文还以为博客园出现什么故障了,如此不堪的文章尽然被编辑推荐,我在想怎么了是审核人员的不负责么,要说不负责为什么我写了这么久的博文,一篇都没被编辑推荐过呢呵呵,这里还是希望博客园能对内容的审核更严谨,而不是马马虎虎,毕竟我们很多人还是支持园区的;

    个人中心模块设计

    对于一个暂时只有上传,点赞,阅览的雏形网站来说,个人中心没有太多能够展示的,因此我这里把用户常用操作的记录也展示出来了,和个简单的统计模块,所以就有了以下的个人中心模块:

    由上图能够看到内容不多,但是从技术角度上来说也涉及到了几个知识点和注意事项;

    代码知识点分享

    获取客户端ip和服务端ip+port

    首先我们就从用户记录日志开始讲起,对刚刚使用netcore的朋友,对怎么获取操作人的ip有点疑惑,这里我就分享下我的经验,避免您们再做各种测试或者查资料了,这里我为了代码使用方便,直接通过静态扩展方法对Controller扩展了获取ip的方法:

    1   public static string GetUserIp(this Controller controller)
    2   {
    3       return controller.HttpContext.Connection.RemoteIpAddress.ToString();
    4   }

    没错客户端的ip信息存储于 HttpContext.Connection 中,上面是获取ip再来分享怎么获取网站的host和port吧,只需要使用 Request.Host 的代码:

    1 var appUrl = $"http://{Request.Host.Host}:{Request.Host.Port}"

    验证用户是否登录

    对于个人中心来说往往需要验证用户是否登录,也就是是否存在咋们说通session,同样为了方便我重写了一个父Controller,凡是需要登录验证的Controller都继承于她:

     1  public class BaseController : Controller
     2     {
     3         public MoUserInfo _MyUserInfo;
     4 
     5         public override void OnActionExecuting(ActionExecutingContext context)
     6         {
     7             _MyUserInfo = context.HttpContext.Session.Get<MoUserInfo>(context.HttpContext.Session.SessionKey()); //获取登录session
     8             if (_MyUserInfo == null)
     9             {
    10                 context.Result = new RedirectToActionResult(nameof(MemberController.Login), "Member", new { ReturnUrl = context.HttpContext.Request.Path });
    11             }
    12 
    13             ViewData["MyUserInfo"] = _MyUserInfo;
    14 
    15             base.OnActionExecuting(context);
    16         }
    17     }

    上面的这种方式通常也是大家对于登录验证用的最多的方式之一了,这里暂时没有用到过滤器等其他的验证方式;

    自定义分页TagHelper

    对于个人中心的登陆日志爱心积分增加记录来说,一页是展示不完的,往往需要分页来展示,为此我采用了重写TagHelper的方式,来自定义一个分页控件;首先我定义一个这样的分页属性实体:

     1 /// <summary>
     2     /// 分页option属性
     3     /// </summary>
     4     public class MoPagerOption
     5     {
     6         /// <summary>
     7         /// 当前页  必传
     8         /// </summary>
     9         public int CurrentPage { get; set; }
    10         /// <summary>
    11         /// 总条数  必传
    12         /// </summary>
    13         public int Total { get; set; }
    14 
    15         /// <summary>
    16         /// 分页记录数(每页条数 默认每页15条)
    17         /// </summary>
    18         public int PageSize { get; set; }
    19 
    20         /// <summary>
    21         /// 路由地址(格式如:/Controller/Action) 默认自动获取
    22         /// </summary>
    23         public string RouteUrl { get; set; }
    24 
    25         /// <summary>
    26         /// 样式 默认 bootstrap样式 1
    27         /// </summary>
    28         public int StyleNum { get; set; }
    29 
    30         /// <summary>
    31         /// 地址与分页数拼接符
    32         /// </summary>
    33         public string JoinOperateCode { get; set; }
    34     }

    然后自定个PagerTagHelper类来继承TagHelper,并且重写她的 public override void Process(TagHelperContext context, TagHelperOutput output) 函数,为了简洁我已经在代码注意的地方加上了注释:

     1 /// <summary>
     2     /// 分页标签
     3     /// </summary>
     4     public class PagerTagHelper : TagHelper
     5     {
     6 
     7         public MoPagerOption PagerOption { get; set; }
     8 
     9 
    10         public override void Process(TagHelperContext context, TagHelperOutput output)
    11         {
    12 
    13             output.TagName = "div";
    14 
    15             //PagerOption.JoinOperateCode = string.IsNullOrWhiteSpace(PagerOption.JoinOperateCode) ? "/" : PagerOption.JoinOperateCode;
    16             if (PagerOption.PageSize <= 0) { PagerOption.PageSize = 15; }
    17             if (PagerOption.CurrentPage <= 0) { PagerOption.CurrentPage = 1; }
    18             if (PagerOption.Total <= 0) { return; }
    19 
    20             //总页数
    21             var totalPage = PagerOption.Total / PagerOption.PageSize + (PagerOption.Total % PagerOption.PageSize > 0 ? 1 : 0);
    22             if (totalPage <= 0) { return; }
    23             else if (totalPage <= PagerOption.CurrentPage)
    24             {
    25 
    26                 PagerOption.CurrentPage = totalPage;
    27             }
    28 
    29 
    30             //当前路由地址
    31             if (string.IsNullOrEmpty(PagerOption.RouteUrl))
    32             {
    33 
    34                 //PagerOption.RouteUrl = helper.ViewContext.HttpContext.Request.RawUrl;
    35                 if (!string.IsNullOrEmpty(PagerOption.RouteUrl))
    36                 {
    37 
    38                     var lastIndex = PagerOption.RouteUrl.LastIndexOf("/");
    39                     PagerOption.RouteUrl = PagerOption.RouteUrl.Substring(0, lastIndex);
    40                 }
    41             }
    42 
    43             //构造分页样式
    44             var sbPage = new StringBuilder(string.Empty);
    45             switch (PagerOption.StyleNum)
    46             {
    47                 case 2:
    48                     {
    49 
    50                         break;
    51                     }
    52                 default:
    53                     {
    54                         #region 默认样式
    55 
    56                         PagerOption.RouteUrl = PagerOption.RouteUrl.TrimEnd('/');
    57                         sbPage.Append("<nav>");
    58                         sbPage.Append("  <ul class="pagination">");
    59                         sbPage.AppendFormat("       <li><a href="{0}{2}{1}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>",
    60                                                 PagerOption.RouteUrl,
    61                                                 PagerOption.CurrentPage - 1 <= 0 ? 1 : PagerOption.CurrentPage - 1,
    62                                                 PagerOption.JoinOperateCode);
    63 
    64                         for (int i = 1; i <= totalPage; i++)
    65                         {
    66 
    67                             sbPage.AppendFormat("       <li {1}><a href="{2}{3}{0}">{0}</a></li>",
    68                                 i,
    69                                 i == PagerOption.CurrentPage ? "class="active"" : "",
    70                                 PagerOption.RouteUrl,
    71                                 PagerOption.JoinOperateCode);
    72 
    73                         }
    74 
    75                         sbPage.Append("       <li>");
    76                         sbPage.AppendFormat("         <a href="{0}{2}{1}" aria-label="Next">",
    77                                             PagerOption.RouteUrl,
    78                                             PagerOption.CurrentPage + 1 > totalPage ? PagerOption.CurrentPage : PagerOption.CurrentPage + 1,
    79                                             PagerOption.JoinOperateCode);
    80                         sbPage.Append("               <span aria-hidden="true">&raquo;</span>");
    81                         sbPage.Append("         </a>");
    82                         sbPage.Append("       </li>");
    83                         sbPage.Append("   </ul>");
    84                         sbPage.Append("</nav>");
    85                         #endregion
    86                     }
    87                     break;
    88             }
    89 
    90             output.Content.SetHtmlContent(sbPage.ToString());
    91         }
    92 
    93     }

    到此后台封装的方法就完成了,此刻我们还需要再Views/_ViewImports.cshtml文件中增加如下内容,才能正常的只用我们自定义的标签:

    1 @using LovePicture.Web
    2 @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
    3 @addTagHelper LovePicture.Web.Extends.PagerTagHelper, LovePicture.Web  //这里是增加的内容

    到此我们分页的配置就完成了,下面就开始介绍怎么来使用封装的分页标签了;先看Action中这样来写:

     1 public IActionResult UserLogs(string id)
     2         {
     3             if (string.IsNullOrWhiteSpace(id)) { return BadRequest(); }
     4 
     5             #region  构造参数
     6             var paramArr = id.Split('-');
     7             if (paramArr.Length != 2) { return BadRequest(); }
     8             var page = Convert.ToInt32(paramArr[1]);
     9             var codeId = Convert.ToInt32(paramArr[0]);
    10             if (codeId != (int)EnumHelper.EmLogCode.登录 && codeId != (int)EnumHelper.EmLogCode.积分)
    11             {
    12                 return BadRequest();
    13             }
    14             page = page <= 0 ? 1 : page;
    15 
    16             //初始化分页类
    17             var pageOption = new MoPagerOption
    18             {
    19                 CurrentPage = page,
    20                 PageSize = 15,
    21                 Total = 0,
    22                 RouteUrl = $"/usercenter/userlogs/{codeId}",
    23                 StyleNum = 1,
    24 
    25                 JoinOperateCode = "-"
    26             };
    27             #endregion
    28 
    29             var userLogs = _db.ToUserLog.
    30                  Where(b => b.UserId == _MyUserInfo.Id && b.CodeId == codeId).AsEnumerable();
    31             pageOption.Total = userLogs.Count();
    32             userLogs = userLogs.OrderByDescending(b => b.Id).
    33                  Skip((pageOption.CurrentPage - 1) * pageOption.PageSize).
    34                  Take(pageOption.PageSize).
    35                  ToList();
    36             //通过ViewData方式分页属性数据传递到试图中
    37             ViewBag.PagerOption = pageOption;
    38 
    39             var userLog = new ToUserLog
    40             {
    41                 CodeId = codeId,
    42                 Des = $"{Enum.GetName(typeof(EnumHelper.EmLogCode), codeId)}记录"
    43             };
    44             ViewData["userLog"] = userLog;
    45 
    46             return View(userLogs);
    47         }

    最后需要再试图中绑定模型,和使用标签pager:

     1 @using LovePicture.Com
     2 @using LovePicture.Model.Models
     3 @using LovePicture.Model.MoClass
     4 @using LovePicture.Web.Extends
     5 @model IEnumerable<ToUserLog>
     6 @{
     7     var userLog = ViewData["userLog"] as ToUserLog;
     8     ViewData["Title"] = userLog.Des;
     9     var userInfo = ViewData["MyUserInfo"] as MoUserInfo;
    10     userInfo.Status = 1;
    11 }
    12 <div class="row">
    13     @await Html.PartialAsync("UserCenterGroup", userInfo)
    14     <div class="col-md-10">
    15         <h3><span class="glyphicon glyphicon-home" aria-hidden="true"></span> 个人中心</h3>
    16         <hr />
    17         <div class="row">
    18             <div class="col-md-12">
    19                 <table id="tabLog" class="table">
    20                     <thead>
    21                         <tr>
    22                             <th class="text-center" style="border:0px">@userLog.Des</th>
    23                         </tr>
    24                     </thead>
    25                     <tbody>
    26                         @foreach (var item in Model)
    27                         {
    28                             <tr>
    29                                 <td>
    30                                     @item.Des
    31                                 </td>
    32                             </tr>
    33                         }
    34                     </tbody>
    35                 </table>
    36                 <div class=" text-center">
    37                     <pager pager-option="ViewBag.PagerOption as MoPagerOption"></pager>
    38                 </div>
    39             </div>
    40         </div>
    41     </div>
    42 </div>

    为什么通过ajax获取数据来绑定数据,而不用Action返回实体模型来绑定试图呢

    由于什么统计数据,上传记录,登录记录,爱心记录等数据来源方式基本都是统一的来自数据库(暂未只用缓存),所以没有什么特别需要讲的地方,反而关注分析了为什么用ajax获取后再绑定数据到页面的方式;对于ajax的方式请求来说,通常都是前端异步的代名词,一个个人中心来说需要展示很多的信息,尤其是还有统计等信息,如果通过Action-View方式直接绑定Model来加载数据,那无疑是让用户体验变差了,因为这种方式需要等待后台把数据加载完后才能展示页面给客户,经过我对爱留图个人中心如果采用这种方式展示数据的统计,需要花10s(本地)因此在没有使用缓存的基础上,我果断的使用了前端异步展示的方式(ajax);当然对于在mvc模式中使用ajax的方式来绑定数据感觉有些异类,的确是这样,因为netcore的mvc目的就是让咋们快速构建系统的,不过有些时候还是需要更具具体的情况来选择具体的方式;来看看个人中心的统计信息对应的代码吧:

     1 #region 统计信息
     2 
     3         [HttpPost]
     4         public JsonResult UserStatis()
     5         {
     6             var data = new MoLoveData();
     7             var list = new List<dynamic>();
     8 
     9             //留图数(公有)
    10             var userContent = _db.ToContent.Where(b => b.UserId == _MyUserInfo.Id).AsEnumerable();
    11             var total1 = userContent.Count(b => b.Status == (int)EnumHelper.EmContentStatus.公有);
    12             list.Add(new
    13             {
    14                 name = "留图数(公有)",
    15                 total = $"{ total1}(张)"
    16             });
    17             //留图数(私有)
    18             var total2 = userContent.Count(b => b.Status == (int)EnumHelper.EmContentStatus.私有);
    19             list.Add(new
    20             {
    21                 name = "留图数(私有)",
    22                 total = $"{ total2}(张)"
    23             });
    24             //点赞数
    25             var total3 = userContent.Where(b => b.Status != (int)EnumHelper.EmContentStatus.删除).Sum(b => b.ZanNum);
    26             list.Add(new
    27             {
    28                 name = "点赞数",
    29                 total = $"{ total3}(个)"
    30             });
    31             //浏览数
    32             var total4 = userContent.Where(b => b.Status != (int)EnumHelper.EmContentStatus.删除).Sum(b => b.ReadNum);
    33             list.Add(new
    34             {
    35                 name = "浏览数",
    36                 total = $"{ total4}(次)"
    37             });
    38             //爱心积分
    39             var total5 = _MyUserInfo.LevelNum;
    40             list.Add(new
    41             {
    42                 name = "爱心积分",
    43                 total = $"{ total5}(分)"
    44             });
    45             data.Data = list;
    46             data.IsOk = true;
    47             return Json(data);
    48         }
    49 
    50         #endregion

    通过 return Json(data); 方法json对象给请求方;然后前端需要通过ajax来请求:

     1 getUserStatis: function (tabId) {
     2             if (tabId.length <= 0) { return; }
     3             $.post("/usercenter/UserStatis", { x: 520 }, function (data) {
     4                 if (data) {
     5 
     6                     if (!data.isOk) { $("#" + tabId + " tbody").html('<tr><td colspan="2">获取失败,稍后重试</td></tr>'); return; }
     7                     var trArr = [];
     8                     $.each(data.data, function (i, item) {
     9                         //console.log(item);
    10                         trArr.push('<tr><td>' + item.name + '</td><td>' + item.total + '</td></tr>');
    11                     });
    12                     if (trArr.length > 0) {
    13                         $("#" + tabId + " tbody").html(trArr.join(''));
    14                     } else {
    15                         $("#" + tabId + " tbody").html('<tr><td>暂无</td></tr>');
    16                     }
    17                 }
    18             });
    19         }

    为了更好的用户体验,我在ajax还没有请求加载数据前,默认在数据展示区域增加一个加载的图片背景:

     1  <div class="row">
     2             <div class="col-md-6">
     3                 <div class="panel panel-default">
     4                     <div class="panel-heading">统计信息</div>
     5                     <div class="panel-body">
     6                         <table id="tabStatis" class="table text-center">
     7                             <thead>
     8                                 <tr>
     9                                     <th class="text-center" style="border:0px">统计名称</th>
    10                                     <th class="text-center" style="border:0px">数值</th>
    11                                 </tr>
    12                             </thead>
    13                             <tbody>
    14                                 <tr>
    15                                     <td colspan="2">
    16                                         <div class="loading"></div>
    17                                     </td>
    18                                 </tr>
    19                             </tbody>
    20                         </table>
    21                     </div>
    22                 </div>
    23             </div>

    对应的loading类样式:

    1 .loading {
    2      100%;
    3     height: 32px;
    4     background: url('../images/load.gif') no-repeat center top;
    5 }  

    最后上传一个效果图吧,表示表示:

  • 相关阅读:
    Sharepoint List Attachments in a Webpart : The Solution
    MOSS 文档库操作类
    许多实用的js 正则表达式
    SharePoint 调查列表的自定义错误页面
    排序算法——插入排序
    用vs2010创建带有AssociationForm的工作流
    does not have element "configuration/configSections/sectionGroup[@name='SharePoint']" or it is invalid
    CSharp数据库使用SqlCommand进行增,删,改,查询操作
    去掉视图选择栏
    IList与Xml互相转换
  • 原文地址:https://www.cnblogs.com/wangrudong003/p/6793529.html
Copyright © 2011-2022 走看看