篇(18)-Asp.Net Core入门实战-文章管理之文章内容管理(下拉框二级结构递归)

  • 篇(18)-Asp.Net Core入门实战-文章管理之文章内容管理(下拉框二级结构递归)已关闭评论
  • 138 次浏览
  • A+
所属分类:.NET技术
摘要

篇(18)-Asp.Net Core入门实战-文章管理之文章内容管理(下拉框二级结构递归实现)

篇(18)-Asp.Net Core入门实战-文章管理之文章内容管理(下拉框二级结构递归实现)

文章管理是CMS系统的核心表之一,存储文章内容,特点就是字段端,属性多,比如是否标识为热点、推荐等属性,是否发布,类别,SEO关键字等。我们本章讲解文章内容的增删改查。

(1).文章Sql表结构设计

篇(18)-Asp.Net Core入门实战-文章管理之文章内容管理(下拉框二级结构递归)

CREATE TABLE [dbo].[Article]( [Id] [int] IDENTITY(1,1) NOT NULL, [CategoryId] [int] NOT NULL, [Title] [varchar](128) NOT NULL, [ImageUrl] [varchar](128) NULL, [Content] [text] NULL, [ViewCount] [int] NOT NULL, [Sort] [int] NOT NULL, [Author] [varchar](64) NULL, [Source] [varchar](128) NULL, [SeoTitle] [varchar](128) NULL, [SeoKeyword] [varchar](256) NULL, [SeoDescription] [varchar](512) NULL, [AddManagerId] [int] NOT NULL, [AddTime] [datetime] NOT NULL, [ModifyManagerId] [int] NULL, [ModifyTime] [datetime] NULL, [IsTop] [bit] NOT NULL, [IsSlide] [bit] NOT NULL, [IsRed] [bit] NOT NULL, [IsPublish] [bit] NOT NULL, [IsDeleted] [bit] NOT NULL, CONSTRAINT [PK_ARTICLE] PRIMARY KEY NONCLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO SET ANSI_PADDING OFF GO ALTER TABLE [dbo].[Article] ADD DEFAULT (getdate()) FOR [AddTime] GO ALTER TABLE [dbo].[Article] ADD DEFAULT ((0)) FOR [IsTop] GO ALTER TABLE [dbo].[Article] ADD DEFAULT ((0)) FOR [IsSlide] GO ALTER TABLE [dbo].[Article] ADD DEFAULT ((0)) FOR [IsRed] GO ALTER TABLE [dbo].[Article] ADD DEFAULT ((0)) FOR [IsPublish] GO ALTER TABLE [dbo].[Article] ADD DEFAULT ((0)) FOR [IsDeleted] GO ALTER TABLE [dbo].[Article] WITH CHECK ADD CONSTRAINT [FK_ARTICLE_RELATIONS_ARTICLEC] FOREIGN KEY([CategoryId]) REFERENCES [dbo].[ArticleCategory] ([Id]) GO ALTER TABLE [dbo].[Article] CHECK CONSTRAINT [FK_ARTICLE_RELATIONS_ARTICLEC] GO

 

那么对应的Article Model代码如下:

public class Article { /// <summary> /// 主键 /// </summary> [Key] public Int32 Id { get; set; } /// <summary> /// 分类ID /// </summary> [Required] public Int32 CategoryId { get; set; } /// <summary> /// 文章标题 /// </summary> [Required] public String Title { get; set; } /// <summary> /// 图片地址 /// </summary> public String ImageUrl { get; set; } /// <summary> /// 文章内容 /// </summary> public String Content { get; set; } /// <summary> /// 浏览次数 /// </summary> [Required] public Int32 ViewCount { get; set; } /// <summary> /// 排序 /// </summary> [Required] public Int32 Sort { get; set; } /// <summary> /// 作者 /// </summary> public String Author { get; set; } /// <summary> /// 来源 /// </summary> public String Source { get; set; } /// <summary> /// SEO标题 /// </summary> public String SeoTitle { get; set; } /// <summary> /// SEO关键字 /// </summary> public String SeoKeyword { get; set; } /// <summary> /// SEO描述 /// </summary> public String SeoDescription { get; set; } /// <summary> /// 添加人ID /// </summary> [Required] public Int32 AddManagerId { get; set; } /// <summary> /// 添加时间 /// </summary> [Required] public DateTime AddTime { get; set; } /// <summary> /// 修改人ID /// </summary> public Int32? ModifyManagerId { get; set; } /// <summary> /// 修改时间 /// </summary> public DateTime? ModifyTime { get; set; } /// <summary> /// 是否置顶 /// </summary> public Boolean IsTop { get; set; } /// <summary> /// 是否轮播显示 /// </summary> public Boolean IsSlide { get; set; } /// <summary> /// 是否热门 /// </summary> public Boolean IsRed { get; set; } /// <summary> /// 是否发布 /// </summary> public Boolean IsPublish { get; set; } /// <summary> /// 是否删除 /// </summary> [Required] public Boolean IsDeleted { get; set; } }

 

(2).视图Create代码

(2.1)视图代码

考虑到要同时上传图片,注意form表单的额 enctype类型;

@{ ViewData["Title"] = "新建文章"; } @model Article <form action="/Article/Create" method="post" enctype="multipart/form-data"> @Html.AntiForgeryToken() <div> <label asp-for="Title">标题</label> <div> <input type="text" asp-for="Title" name="Title" placeholder="请输入标题"> </div> </div> <div> <label asp-for="CategoryId">文章类型</label> <div> @Html.DropDownList("ddl_CategoryId", ViewBag.database as IEnumerable<SelectListItem>) </div> </div> <div> <label>设置</label> <div> @*@Html.CheckBox("IsTop") 置顶 @Html.CheckBox("IsRed") 热点 @Html.CheckBox("IsSlide") 幻灯*@ <input type="checkbox" name="IsTop" asp-for="IsTop" />置顶 <input type="checkbox" name="IsRed" asp-for="IsRed"/>热点 <input type="checkbox" name="IsSlide" asp-for="IsSlide" />幻灯 </div> </div> <div> <label asp-for="ImageUrl">文章首页图</label> <div> <input type="file" asp-for="ImageUrl" name="ImageUrl"/> </div> </div> <div> <label asp-for="Content">内容</label> <div> <textarea placeholder="内容" asp-for="Content" name="Content" cols="30" rows="10"></textarea> </div> </div> <div> <label asp-for="Sort">排序</label> <div> <input type="text" placeholder="排序" asp-for="Sort" name="Sort" /> </div> </div> <div> <label asp-for="ViewCount">点击量</label> <div> <input type="text" placeholder="点击量" asp-for="ViewCount" name="ViewCount" /> </div> </div> <div> <label asp-for="IsPublish">是否发布</label> <div> <select asp-for="IsPublish" name="IsPublish" class="IsPublish"> <option value="False"></option> <option value="True" selected></option> </select> </div> </div> <div> <label asp-for="Author">作者</label> <div> <input type="text" asp-for="Author" name="Author" placeholder="作者名"> </div> </div> <div> <label asp-for="Source">来源</label> <div> <input type="text" asp-for="Source" name="Source" placeholder="文章来源"> </div> </div> <div> <label asp-for="SeoTitle">SEO标题</label> <div> <input type="text" asp-for="SeoTitle" name="SeoTitle" placeholder="SEO标题"> </div> </div> <div> <label asp-for="SeoKeyword">SEO关键词</label> <div> <input type="text" asp-for="SeoKeyword" name="SeoKeyword" placeholder="SEO关键词"> </div> </div> <div> <label asp-for="SeoDescription">SEO摘要描述</label> <div> <input type="text" asp-for="SeoDescription" name="SeoDescription" placeholder="SEO摘要描述"> </div> </div> <div> <div> <button type="submit">确定</button> <button type="reset">重置</button> </div> </div> </form>

 

(2.2)视图中的下拉框的实现方式(递归和循环嵌套)

篇(18)-Asp.Net Core入门实战-文章管理之文章内容管理(下拉框二级结构递归)

我想在添加文章时,实现一个具有二级层次结构的下拉框,如上图所示。所以,在对下拉框进行数据绑定时,就要费点功夫,上个章节讲文章类别管理时,的表结构就一个,分类都存在一张表中,所以要进行递归的获取子菜单或者通过循环嵌套来实现。

递归的主要核心函数为:

/// <summary> /// 递归函数,实现获取子菜单 /// </summary> /// <param name="lists">递归前的列表</param> /// <param name="newlists">递归后的新列表</param> /// <param name="parentId">父Id</param> /// <returns></returns> public static List<CategorySelectItemListView> GetChildCategory(List<CategorySelectItemListView> lists, List<CategorySelectItemListView> newlists, int parentId) { newlists = new List<CategorySelectItemListView>(); List<CategorySelectItemListView> tempList = lists.Where(c => c.ParentId == parentId).ToList(); for (int i = 0; i < tempList.Count; i++) { CategorySelectItemListView category = new CategorySelectItemListView(); category.Id = tempList[i].Id; category.ParentId = tempList[i].ParentId; category.Title = tempList[i].Title; category.Children = GetChildCategory(lists, newlists, category.Id); newlists.Add(category); } return newlists; }   /// <summary> /// 循环嵌套,实现获取子菜单 /// </summary> /// <param name="lists">循环遍历前的列表</param> /// <returns></returns> public static List<CategorySelectItemListView> GetChildCategory(List<CategorySelectItemListView> lists) { List<CategorySelectItemListView> categorylist = new List<CategorySelectItemListView>(); for (int i = 0; i < lists.Count; i++) { if (0 == lists[i].ParentId) categorylist.Add(lists[i]); for (int j = 0; j < lists.Count; j++) { if (lists[j].ParentId == lists[i].Id) lists[i].Children.Add(lists[j]); } } return categorylist; }

 

然后在Create和Edit的Action中去绑定对应的下拉菜单即可。

注意:List<CategorySelectItemListView> 集合的CategorySelectItemListView,这个是新建的ViewModel对象,用来专门绑定下拉菜单使用,其代码如下:

public class CategorySelectItemListView { public int Id { get; set; } public string Title { get; set; } public int ParentId { get; set; } public List<CategorySelectItemListView> Children { get; set; } public CategorySelectItemListView() { Children = new List<CategorySelectItemListView>(); }  public CategorySelectItemListView(int id,string title,int parentid) { this.Id = id; this.Title = title; this.ParentId = parentid; Children = new List<CategorySelectItemListView>(); }  public CategorySelectItemListView(int id, string title, CategorySelectItemListView parent) { this.Id = id; this.Title = title; this.ParentId = parent.Id; Children = new List<CategorySelectItemListView>(); }

 

(3).视图Edit代码,注解部分的代码可以参考,我尝试用过,也可以达到目的,演练代码最好是用多种方式实现,查看其区别,这样掌握的牢固一些

@{ ViewData["Title"] = "编辑文章"; } @model Article @section Scripts{ <script type="text/javascript" src="~/js/jquery-3.6.1.min.js"></script> <script type="text/javascript"> $(document).ready(function () { $.ajax({ type: "GET", url: "/ArticleCategory/GetCategory", data: "{}", success: function (data) { console.log(data); var s = '<option value="0">请选择</option>'; for (var i = 0; i < data.length; i++) { s += '<option value="' + data[i].Title + '"+>' + data[i].Id + '</option>'; } $("#ArticleCategory").html(s); } }); }); </script> }  <form action="/Article/Edit" method="post" enctype="multipart/form-data"> @Html.AntiForgeryToken() <div> <label asp-for="Title">标题</label> <div> <input type="text" asp-for="Title" name="Title" placeholder="请输入标题"> <input type="hidden" asp-for="Id" /> </div> </div> <div> <label asp-for="CategoryId">文章类型</label> <div> @Html.DropDownList("ddl_CategoryId", ViewBag.database as IEnumerable<SelectListItem>) </div> </div> <div> <label>设置</label> <div> @*@Html.CheckBox("IsTop", Model.IsTop,new { value = Model.IsTop}) 置顶 @Html.CheckBox("IsRed", Model.IsRed, new { value = Model.IsRed }) 热点 @Html.CheckBox("IsSlide", Model.IsSlide, new { value = Model.IsSlide }) 幻灯*@ <input asp-for="IsTop" />置顶 <input asp-for="IsRed" />热点 <input asp-for="IsSlide" />幻灯 @*<input type="checkbox" name="IsTop" @(Html.Raw(@Model.IsTop ? "checked="checked"" : "")) asp-for="IsTop" />置顶 <input type="checkbox" name="IsRed" @(Html.Raw(@Model.IsRed ? "checked="checked"" : "")) asp-for="IsRed" />热点 <input type="checkbox" name="IsSlide" @(Html.Raw(@Model.IsSlide ? "checked="checked"" : "")) asp-for="IsSlide" />幻灯*@ @*<input type="checkbox" name="IsTop" asp-for="IsTop" />置顶 <input type="checkbox" name="IsRed" asp-for="IsRed"/>热点 <input type="checkbox" name="IsSlide" asp-for="IsSlide"/>幻灯*@ </div> </div> <div> <label asp-for="ImageUrl">文章首页图</label> <div> <input type="file" asp-for="ImageUrl" name="ImageUrl" /> <label asp-for="ImageUrl">@Model.ImageUrl</label> </div> </div> <div> <label asp-for="Content">内容</label> <div> <textarea placeholder="内容" asp-for="Content" name="Content" cols="30" rows="10"></textarea> </div> </div> <div> <label asp-for="Sort">排序</label> <div> <input type="text" placeholder="排序" asp-for="Sort" name="Sort" /> </div> </div> <div> <label asp-for="ViewCount">点击量</label> <div> <input type="text" placeholder="点击量" asp-for="ViewCount" name="ViewCount" /> </div> </div> <div> <label asp-for="IsPublish">是否发布</label> <div> <select asp-for="IsPublish" name="IsPublish" class="IsPublish"> <option value="False"></option> <option value="True" selected></option> </select> </div> </div> <div> <label asp-for="Author">作者</label> <div> <input type="text" asp-for="Author" name="Author" placeholder="作者名"> </div> </div> <div> <label asp-for="Source">来源</label> <div> <input type="text" asp-for="Source" name="Source" placeholder="文章来源"> </div> </div> <div> <label asp-for="SeoTitle">SEO标题</label> <div> <input type="text" asp-for="SeoTitle" name="SeoTitle" placeholder="SEO标题"> </div> </div> <div> <label asp-for="SeoKeyword">SEO关键词</label> <div> <input type="text" asp-for="SeoKeyword" name="SeoKeyword" placeholder="SEO关键词"> </div> </div> <div> <label asp-for="SeoDescription">SEO摘要描述</label> <div> <input type="text" asp-for="SeoDescription" name="SeoDescription" placeholder="SEO摘要描述"> </div> </div> <div> <div> <button type="submit">确定</button> <button type="reset">重置</button> </div> </div> </form>

 

(4).视图Index列表的代码

针对列表的显示,又专门编写了ArticeView的这个ViewModel。

篇(18)-Asp.Net Core入门实战-文章管理之文章内容管理(下拉框二级结构递归)

public class ArticleView { public int Id { get; set; } public int CategoryId { get; set; } public string CategoryName { get; set; } public string Title { get; set; } public int ViewCount { get; set; } public int Sort { get; set; } public string Author { get; set; } public string Source { get; set; } public int AddManagerId { get; set; } public DateTime AddTime { get; set; } }

 

@using Humanizer; @using RjWebCms.Db; @using RjWebCms.Models.Articles; @model PaginatedList<ArticleView> @{ ViewData["Title"] = "文章列表"; } @section Scripts{ <script src="~/js/jquery-2.1.0.min.js"></script> <script type="text/javascript"> function DelAll() { var ids = document.getElementsByName("#chk_ids"); var arrIds = ""; var n = 0; for (var i = 0; i < ids.length; i++) { if (ids[i].checked == true) { arrIds += ids[i].value + ","; n++; } } if (n == 0) { alert("请选择要删除的信息"); return; } arrIds = arrids.substr(0, arrIds.length - 1); // if (confirm("确定要全部删除选择信息吗")) { $.ajax({ type: "post", url: "/Article/DeleteAll", data: { ids: arrIds }, success: function (data, state) { alert('删除成功!'); window.location.href = ""; },  error: function (data, state) { alert('删除失败'); } }); } } </script> } <div class="panel panel-default todo-panel"> <div class="panel-heading">@ViewData["Title"]</div> @Html.AntiForgeryToken() <form asp-action="Index" method="get"> <table> <tr><td><a asp-controller="Article" asp-action="Create">添加</a></td></tr> <tr> <td>查询关键词:<input type="text" name="SearchString" value="@ViewData["CurrentFilter"]" /></td> <td><input type="submit" value="查询" /></td> <td><a asp-action="Index">Back</a></td> <td><a asp-action="DeleteAll">批量删除</a></td> </tr> </table> </form> <table class="table table-hover"> <thead> <tr> <td>&#x2714;</td> <td><a asp-action="Index" asp-route-sortOrder="@ViewData["NameSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">标题</a></td> <td>类别</td> <td><a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">添加时间</a></td> <td>作者</td> <td>操作</td> </tr> @foreach (var item in Model) { <tr> <td><input type="checkbox" class="done-checkbox" name="chk_ids" value="@item.Id"></td> <td>@item.Title</td> <td>@item.CategoryName</td> <td>@item.AddTime</td> <td>@item.Author</td> <td> <a asp-action="Details" asp-route-id="@item.Id">Details</a> <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> <a asp-action="Delete" asp-route-id="@item.Id">Delete</a> </td> </tr> } </thead> </table> @{ var prevDisabled = !Model.HasPreviousPage ? "disabled" : ""; var nextDisabled = !Model.HasNextPage ? "disabled" : ""; ; }  <a asp-action="Index" asp-route-sortOrder="@ViewData["CurrentSort"]" asp-route-pageNumber="@(Model.PageIndex - 1)" asp-route-currentFilter="@ViewData["CurrentFilter"]" class="btn btn-default @prevDisabled"> 上一页 </a> <a asp-action="Index" asp-route-sortOrder="@ViewData["CurrentSort"]" asp-route-pageNumber="@(Model.PageIndex + 1)" asp-route-currentFilter="@ViewData["CurrentFilter"]" class="btn btn-default @nextDisabled"> 下一页 </a> <div class="panel-footer add-item-form"> <!--TODO: Add item form --> </div> </div>

 

(5).Controller部分的全部代码,注意看代码注释

public class ArticleController : Controller     {         private readonly IHostEnvironment _hostEnvironment;         private readonly IArticleService _articleService;         private readonly IArticleCategoryService _articleCategoryService;         private readonly AppDbContext _appDbContext;         public ArticleController(IArticleService articleService, IArticleCategoryService articleCategoryService,AppDbContext appDbContext,IHostEnvironment hostEnvironment)         {             _hostEnvironment = hostEnvironment;             _appDbContext = appDbContext;             _articleService = articleService;             _articleCategoryService = articleCategoryService;         }          public async Task<IActionResult> Index(string sortOrder, string currentFilter, string searchString, int? pageNumber)         {             ViewData["CurrentSort"] = sortOrder;             ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";             ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";              if (searchString != null)             {                 pageNumber = 1;             }             else             {                 searchString = currentFilter;             }             ViewData["CurrentFilter"] = searchString;             var article = from s in _appDbContext.Article                           join p in _appDbContext.ArticleCategory on s.CategoryId equals p.Id                           select new ArticleView {                              Id = s.Id,                             CategoryId = s.CategoryId,                             CategoryName = p.Title,                             Title = s.Title,                             Sort = s.Sort,                             AddManagerId = s.AddManagerId,                             AddTime = s.AddTime,                             Author = s.Author,                             Source = s.Source,                             ViewCount = s.ViewCount,                           };             if (!string.IsNullOrEmpty(searchString))             {                 article = article.Where(s => s.Title.Contains(searchString));             }             switch (sortOrder)             {                 case "name_desc":                     article = article.OrderByDescending(s => s.Title) ;                     break;                 case "Date":                     article = article.OrderBy(s => s.AddTime);                     break;                 case "date_desc":                     article = article.OrderByDescending(s => s.AddTime);                     break;                 default:                     article = article.OrderBy(s => s.Title);                     break;             }             int pageSize = 4;             return View(await PaginatedList<ArticleView>.CreateAsync(article.AsNoTracking(), pageNumber ?? 1, pageSize));         }           [HttpGet]         public async Task<IActionResult> CreateAsync()         {             #region 绑定类别下拉框             var categories = await _articleCategoryService.GetArticleCategory();//列出文章类别字典             var categoryItems = new List<SelectListItem>()             {                 new SelectListItem(){ Value="0",Text="全部",Selected=true}             };             //全部列出并转成DropDownList对象             List<CategorySelectItemListView> list = new List<CategorySelectItemListView>();             foreach (var category in categories)             {                 list.Add(new CategorySelectItemListView {                      Id=category.Id,                     Title = category.Title,                     ParentId = category.ParentId                 });             }              #region 循环嵌套调用             //List<CategorySelectItemListView> list1 = GetChildCategory(list);             //foreach (var li in list1)             //{             //    categoryItems.Add(new SelectListItem { Value = li.Id.ToString(), Text = li.Title });             //    if (li.Children.Count > 0)             //    {              //        foreach(var t in li.Children)             //            categoryItems.Add(new SelectListItem { Value = t.Id.ToString(),Text= "|-" + t.Title });             //    }             //}             #endregion              #region  递归调用             List<CategorySelectItemListView> list1 = GetChildCategory(list, new List<CategorySelectItemListView>(), 0);             foreach (var li in list1)             {                 categoryItems.Add(new SelectListItem { Value = li.Id.ToString(), Text = li.Title });                 if (li.Children.Count > 0)                 {                     foreach (var t in li.Children)                         categoryItems.Add(new SelectListItem { Value = t.Id.ToString(), Text = "  |-" + t.Title });                 }             }             #endregion              ViewBag.database = categoryItems;             #endregion             return View();         }          [HttpPost]         [ValidateAntiForgeryToken]         public async Task<IActionResult> CreateAsync(Article article,[FromForm]IFormCollection fromData)         {             //去掉对字段IsSystem的验证,IsSystem在数据库是bool类型,而前端是0和1,ModelState的验证总是报false,所以去掉对其验证             //ModelState.Remove("IsSystem");//在View端已经解决了了bool类型,那么此行代码可以不用             #region 下拉菜单             string strCategoryId = Request.Form["ddl_CategoryId"];             if (!string.IsNullOrEmpty(strCategoryId))                 article.CategoryId = int.Parse(strCategoryId);             else                 article.CategoryId = 0;             #endregion              #region 复选框             article.IsTop = fromData["IsTop"] != "false";//使用FormCollection时,可以这样             article.IsRed = fromData["IsRed"] != "false";             article.IsSlide = fromData["IsSlide"] != "false";             //也可以这样取值,但要注意View内的写法             //if (!string.IsNullOrEmpty(fromData["IsTop"]))             //    article.IsTop = true;             //else             //    article.IsTop = false;             #endregion              #region 上传文件             IFormFileCollection files = fromData.Files;            foreach(var formFile in files)             {                 //var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');                 string webContentPath = _hostEnvironment.ContentRootPath;                 var fileExt = formFile.FileName.Substring(formFile.FileName.LastIndexOf('.'));//文件扩展名                 var fileNew = DateTime.Now.ToString("yyyyMMddHHmmss") + fileExt; //给文件重新命名                                  //string upLoadPath = webContentPath + $@"{fileName}";                 string upLoadPath = webContentPath + $@"UpFiles";                 var fileUrl = upLoadPath + $@"{fileNew}";                  if (formFile.Length > 0)                 {                     using (var stream = new FileStream(fileUrl,FileMode.Create))                     {                         await formFile.CopyToAsync(stream);                     }                 }                 article.ImageUrl = "../UpFiles/" + fileNew;               }                    #endregion               if (ModelState.IsValid)             {                 var successful = await _articleService.AddArticleAysnc(article);                 if (successful)                     return RedirectToAction("Index");                 else                     return BadRequest("失败");             }             return View(article);         }           [HttpGet]         public async Task<IActionResult> Edit(int id)         {              if (string.IsNullOrEmpty(id.ToString()))                 return NotFound();              var article = await _articleService.FindArticleAsync(id);             if (article == null)                 return NotFound();              #region 绑定角色下拉框             var categories = await _articleCategoryService.GetArticleCategory();//列出文章类别字典             var categoryItems = new List<SelectListItem>()             {                 new SelectListItem(){ Value="0",Text="全部",Selected=true}             };             //全部列出并转成DropDownList对象             List<CategorySelectItemListView> list = new List<CategorySelectItemListView>();             foreach (var category in categories)             {                 list.Add(new CategorySelectItemListView                 {                     Id = category.Id,                     Title = category.Title,                     ParentId = category.ParentId                 });             }             #region  递归调用             List<CategorySelectItemListView> list1 = GetChildCategory(list, new List<CategorySelectItemListView>(), 0);             foreach (var li in list1)             {                 categoryItems.Add(new SelectListItem { Value = li.Id.ToString(), Text = li.Title });                 if (li.Children.Count > 0)                 {                     foreach (var t in li.Children)                         categoryItems.Add(new SelectListItem { Value = t.Id.ToString(), Text = "  |-" + t.Title });                 }             }             #endregion              #region 遍历并选中             foreach (SelectListItem item in categoryItems)             {                 if (item.Value == article.CategoryId.ToString())                     item.Selected = true;             }             #endregion              ViewBag.database = categoryItems;             #endregion              return View(article);         }          [HttpPost]         [ValidateAntiForgeryToken]         public async Task<IActionResult> Edit(int id, [FromForm]Article article)         {             if (id != article.Id)             {                 return NotFound();             }             #region 下拉菜单             string strCategoryId = Request.Form["ddl_CategoryId"];             if (!string.IsNullOrEmpty(strCategoryId))                 article.CategoryId = int.Parse(strCategoryId);             else                 article.CategoryId = 0;             #endregion             #region 复选框             if (Request.Form["IsTop"].Contains("true"))                 article.IsTop = true;             else                 article.IsTop = false;             if (Request.Form["IsRed"].Contains("true"))                 article.IsRed = true;             else                 article.IsRed = false;             if (Request.Form["IsSlide"].Contains("true"))                 article.IsSlide = true;             else                 article.IsSlide = false;             #endregion              //ModelState.Remove("IsTop");             //ModelState.Remove("IsRed");             //ModelState.Remove("IsSlide");             if (ModelState.IsValid)             {                 try                 {                     var result = await _articleService.UpdateArticleAsync(id, article);                     //跳转                     if (result)                         return RedirectToAction("Index");                     else                         return BadRequest("编辑失败");                 }                 catch (Exception ex)                 {                     return BadRequest("编辑失败");                 }             }             else             {                 return BadRequest("数据输入有误!");             }         }          /// <summary>         /// 递归函数,实现获取子菜单         /// </summary>         /// <param name="lists">递归前的列表</param>         /// <param name="newlists">递归后的新列表</param>         /// <param name="parentId">父Id</param>         /// <returns></returns>         public static List<CategorySelectItemListView> GetChildCategory(List<CategorySelectItemListView> lists, List<CategorySelectItemListView> newlists, int parentId)         {             newlists = new List<CategorySelectItemListView>();             List<CategorySelectItemListView> tempList = lists.Where(c => c.ParentId == parentId).ToList();             for (int i = 0; i < tempList.Count; i++)             {                 CategorySelectItemListView category = new CategorySelectItemListView();                 category.Id = tempList[i].Id;                 category.ParentId = tempList[i].ParentId;                 category.Title = tempList[i].Title;                 category.Children = GetChildCategory(lists, newlists, category.Id);                 newlists.Add(category);             }             return newlists;         }          /// <summary>         /// 循环嵌套,实现获取子菜单         /// </summary>         /// <param name="lists">循环遍历前的列表</param>         /// <returns></returns>         public static List<CategorySelectItemListView> GetChildCategory(List<CategorySelectItemListView> lists)         {             List<CategorySelectItemListView> categorylist = new List<CategorySelectItemListView>();             for (int i = 0; i < lists.Count; i++)             {                 if (0 == lists[i].ParentId)                     categorylist.Add(lists[i]);                  for (int j = 0; j < lists.Count; j++)                 {                     if (lists[j].ParentId == lists[i].Id)                         lists[i].Children.Add(lists[j]);                 }             }             return categorylist;         }       }

 

(6).Service应用层代码

篇(18)-Asp.Net Core入门实战-文章管理之文章内容管理(下拉框二级结构递归)

 

    public class ArticleService : IArticleService     {         private readonly AppDbContext _appDbContext;         public ArticleService(AppDbContext appDbContext)         {             _appDbContext = appDbContext;         }         /// <summary>         /// 添加文章         /// </summary>         /// <param name="article"></param>         /// <returns></returns>         public async Task<bool> AddArticleAysnc(Article article)         {             article.IsDeleted = false;             article.AddManagerId = 1;//用户id             article.AddTime = DateTime.Now;             article.IsPublish = true;             await _appDbContext.Article.AddAsync(article);             var result = await _appDbContext.SaveChangesAsync();             return result == 1;         }          /// <summary>         /// 删除文章         /// </summary>         /// <param name="Id"></param>         /// <returns></returns>         public async Task<bool> DeleteArticleAsync(int Id)         {             var article = await _appDbContext.Article.FirstOrDefaultAsync(x => x.Id == Id);             if (article != null)             {                 _appDbContext.Article.Remove(article);             }             var result = await _appDbContext.SaveChangesAsync();             return result == 1; //注意(result==1 如果等式成立,则返回true,说明删除成功)         }          /// <summary>         /// 按Id查询文章         /// </summary>         /// <param name="Id"></param>         /// <returns></returns>         public async Task<Article> FindArticleAsync(int Id)         {             var item = await _appDbContext.Article.Where(x => x.Id == Id).FirstOrDefaultAsync();             return item;         }          /// <summary>         /// 按标题查询文章         /// </summary>         /// <param name="title"></param>         /// <returns></returns>         public async Task<Article[]> GetArtcleByTitle(string title)         {             var items = await _appDbContext.Article.Where(x => x.Title.Contains(title)).ToArrayAsync();             return items;         }          /// <summary>         /// 查询文章         /// </summary>         /// <returns></returns>         public async Task<Article[]> GetArticles()         {             var items = await _appDbContext.Article.Where(x => x.IsDeleted==false).ToArrayAsync();             return items;         }          /// <summary>         /// 更新文章         /// </summary>         /// <param name="id"></param>         /// <param name="article"></param>         /// <returns></returns>         public async Task<bool> UpdateArticleAsync(int id, Article article)         {             var oldArticle = await  FindArticleAsync(id); //找出旧对象              //将新值赋到旧对象上             oldArticle.Title = article.Title;             oldArticle.CategoryId = article.CategoryId;             oldArticle.SeoDescription = article.SeoDescription;             oldArticle.SeoTitle = article.SeoTitle;             oldArticle.SeoKeyword = article.SeoKeyword;             oldArticle.Content = article.Content;             oldArticle.Sort = article.Sort;             oldArticle.Source = article.Source;             oldArticle.IsSlide = article.IsSlide;             oldArticle.IsPublish = article.IsPublish;             oldArticle.IsRed = article.IsRed;             oldArticle.IsTop = article.IsTop;             oldArticle.ViewCount = article.ViewCount;             oldArticle.Author = article.Author;             oldArticle.ImageUrl = article.ImageUrl;              oldArticle.ModifyManagerId = 11;//             oldArticle.ModifyTime = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));              //对旧对象执行更新             _appDbContext.Entry(oldArticle).State = EntityState.Modified;             var result = await _appDbContext.SaveChangesAsync();             return result == 1;         }     }

 

 

再谈CheckBox的使用

1.在View视图页增加的代码格式如果为:

<input type="checkbox" name="IsTop" asp-for="IsTop" />置顶

或者是这样:

<input asp-for="IsTop" />置顶

那么在生成的html代码中,都会自动成id,name,type=“checkbox” value的属性。

2.在Controller中进行取值时的代码为:

if (Request.Form["IsTop"].Contains("true"))

article.IsTop = true;

else

article.IsTop = false;

跟踪时发现,View中Checkbox选中是,会产生true和false两个值,如图跟踪变量发现:

篇(18)-Asp.Net Core入门实战-文章管理之文章内容管理(下拉框二级结构递归)

如此,取值时,就用了Contains功能,因为View中CheckBox没选中,这只有一个false值;

3.在View视图页增加代码的格式如果为:

<input type="checkbox" name="IsTop" @(Html.Raw(@Model.IsTop ? "checked="checked"" : "")) asp-for="IsTop" />置顶

4.在Controller中进行取值时的代码为:

if (!string.IsNullOrEmpty(Request.Form["IsTop"]))

article.IsTop = true;

else

article.IsTop = false;

跟踪时发现,View中的CheckBox选中是,取到的值为“on”,如图跟踪发现:

篇(18)-Asp.Net Core入门实战-文章管理之文章内容管理(下拉框二级结构递归)

所以,才用了IsNullOrEmpty这个函数,依据判空来确定是否选中。

但是这样写有个问题,在ModelState.IsValid()的模型验证中,一直无法通过,IsTop一直为false,为此,我干脆就把其去除掉验证:

ModelState.Remove("IsTop");//去除name=IsTop的checkbox的模型验证

5.使用Checkbox,还是要看给在数据表中为其定义的字段类型,Model中的指定类型和验证属性,如果你赋予了Value值,那么就在Controller中取值,Asp.Net Core中Checkbox默认是True和False的值,网上关于@Html.CheckBox()形式也行,你可以尝试跟踪变量值来判断如何处理,其宗旨就是根据具体条件来处理。