|
入门实战-文章管理之文章内容管理(下拉框二级结构递归实现)
文章管理是CMS系统的核心表之一,存储文章内容,特点就是字段端,属性多,比如是否标识为热点、推荐等属性,是否发布,类别,SEO关键字等。我们本章讲解文章内容的增删改查。
(1).文章Sql表结构设计

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

我想在添加文章时,实现一个具有二级层次结构的下拉框,如上图所示。所以,在对下拉框进行数据绑定时,就要费点功夫,上个章节讲文章类别管理时,的表结构就一个,分类都存在一张表中,所以要进行递归的获取子菜单或者通过循环嵌套来实现。
递归的主要核心函数为:
/// <summary>
/// 递归函数,实现获取子菜单
/// </summary>
/// <param name=&#34;lists&#34;>递归前的列表</param>
/// <param name=&#34;newlists&#34;>递归后的新列表</param>
/// <param name=&#34;parentId&#34;>父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.Id;
category.ParentId = tempList.ParentId;
category.Title = tempList.Title;
category.Children = GetChildCategory(lists, newlists, category.Id);
newlists.Add(category);
}
return newlists;
}
循环嵌套的主要函数为:
/// <summary>
/// 循环嵌套,实现获取子菜单
/// </summary>
/// <param name=&#34;lists&#34;>循环遍历前的列表</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.ParentId)
categorylist.Add(lists);
for (int j = 0; j < lists.Count; j++)
{
if (lists[j].ParentId == lists.Id)
lists.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[&#34;Title&#34;] = &#34;编辑文章&#34;; }
@model Article
@section Scripts{
<script type=&#34;text/javascript&#34; src=&#34;~/js/jquery-3.6.1.min.js&#34;></script>
<script type=&#34;text/javascript&#34;>
$(document).ready(function () {
$.ajax({
type: &#34;GET&#34;,
url: &#34;/ArticleCategory/GetCategory&#34;,
data: &#34;{}&#34;,
success: function (data) {
console.log(data);
var s = &#39;<option value=&#34;0&#34;>请选择</option>&#39;;
for (var i = 0; i < data.length; i++) {
s += &#39;<option value=&#34;&#39; + data.Title + &#39;&#34;+>&#39; + data.Id + &#39;</option>&#39;;
}
$(&#34;#ArticleCategory&#34;).html(s);
}
});
});
</script>
}
<form action=&#34;/Article/Edit&#34; method=&#34;post&#34; enctype=&#34;multipart/form-data&#34;>
@Html.AntiForgeryToken()
<div>
<label asp-for=&#34;Title&#34;>标题</label>
<div>
<input type=&#34;text&#34; asp-for=&#34;Title&#34; name=&#34;Title&#34; placeholder=&#34;请输入标题&#34;>
<input type=&#34;hidden&#34; asp-for=&#34;Id&#34; />
</div>
</div>
<div>
<label asp-for=&#34;CategoryId&#34;>文章类型</label>
<div>
@Html.DropDownList(&#34;ddl_CategoryId&#34;, ViewBag.database as IEnumerable<SelectListItem>)
</div>
</div>
<div>
<label>设置</label>
<div>
@*@Html.CheckBox(&#34;IsTop&#34;, Model.IsTop,new { value = Model.IsTop}) 置顶
@Html.CheckBox(&#34;IsRed&#34;, Model.IsRed, new { value = Model.IsRed }) 热点
@Html.CheckBox(&#34;IsSlide&#34;, Model.IsSlide, new { value = Model.IsSlide }) 幻灯*@
<input asp-for=&#34;IsTop&#34; />置顶
<input asp-for=&#34;IsRed&#34; />热点
<input asp-for=&#34;IsSlide&#34; />幻灯
@*<input type=&#34;checkbox&#34; name=&#34;IsTop&#34; @(Html.Raw(@Model.IsTop ? &#34;checked=\&#34;checked\&#34;&#34; : &#34;&#34;)) asp-for=&#34;IsTop&#34; />置顶
<input type=&#34;checkbox&#34; name=&#34;IsRed&#34; @(Html.Raw(@Model.IsRed ? &#34;checked=\&#34;checked\&#34;&#34; : &#34;&#34;)) asp-for=&#34;IsRed&#34; />热点
<input type=&#34;checkbox&#34; name=&#34;IsSlide&#34; @(Html.Raw(@Model.IsSlide ? &#34;checked=\&#34;checked\&#34;&#34; : &#34;&#34;)) asp-for=&#34;IsSlide&#34; />幻灯*@
@*<input type=&#34;checkbox&#34; name=&#34;IsTop&#34; asp-for=&#34;IsTop&#34; />置顶
<input type=&#34;checkbox&#34; name=&#34;IsRed&#34; asp-for=&#34;IsRed&#34;/>热点
<input type=&#34;checkbox&#34; name=&#34;IsSlide&#34; asp-for=&#34;IsSlide&#34;/>幻灯*@
</div>
</div>
<div>
<label asp-for=&#34;ImageUrl&#34;>文章首页图</label>
<div>
<input type=&#34;file&#34; asp-for=&#34;ImageUrl&#34; name=&#34;ImageUrl&#34; />
<label asp-for=&#34;ImageUrl&#34;>@Model.ImageUrl</label>
</div>
</div>
<div>
<label asp-for=&#34;Content&#34;>内容</label>
<div>
<textarea placeholder=&#34;内容&#34; asp-for=&#34;Content&#34; name=&#34;Content&#34; cols=&#34;30&#34; rows=&#34;10&#34;></textarea>
</div>
</div>
<div>
<label asp-for=&#34;Sort&#34;>排序</label>
<div>
<input type=&#34;text&#34; placeholder=&#34;排序&#34; asp-for=&#34;Sort&#34; name=&#34;Sort&#34; />
</div>
</div>
<div>
<label asp-for=&#34;ViewCount&#34;>点击量</label>
<div>
<input type=&#34;text&#34; placeholder=&#34;点击量&#34; asp-for=&#34;ViewCount&#34; name=&#34;ViewCount&#34; />
</div>
</div>
<div>
<label asp-for=&#34;IsPublish&#34;>是否发布</label>
<div>
<select asp-for=&#34;IsPublish&#34; name=&#34;IsPublish&#34; class=&#34;IsPublish&#34;>
<option value=&#34;False&#34;>否</option>
<option value=&#34;True&#34; selected>是</option>
</select>
</div>
</div>
<div>
<label asp-for=&#34;Author&#34;>作者</label>
<div>
<input type=&#34;text&#34; asp-for=&#34;Author&#34; name=&#34;Author&#34; placeholder=&#34;作者名&#34;>
</div>
</div>
<div>
<label asp-for=&#34;Source&#34;>来源</label>
<div>
<input type=&#34;text&#34; asp-for=&#34;Source&#34; name=&#34;Source&#34; placeholder=&#34;文章来源&#34;>
</div>
</div>
<div>
<label asp-for=&#34;SeoTitle&#34;>SEO标题</label>
<div>
<input type=&#34;text&#34; asp-for=&#34;SeoTitle&#34; name=&#34;SeoTitle&#34; placeholder=&#34;SEO标题&#34;>
</div>
</div>
<div>
<label asp-for=&#34;SeoKeyword&#34;>SEO关键词</label>
<div>
<input type=&#34;text&#34; asp-for=&#34;SeoKeyword&#34; name=&#34;SeoKeyword&#34; placeholder=&#34;SEO关键词&#34;>
</div>
</div>
<div>
<label asp-for=&#34;SeoDescription&#34;>SEO摘要描述</label>
<div>
<input type=&#34;text&#34; asp-for=&#34;SeoDescription&#34; name=&#34;SeoDescription&#34; placeholder=&#34;SEO摘要描述&#34;>
</div>
</div>
<div>
<div>
<button type=&#34;submit&#34;>确定</button>
<button type=&#34;reset&#34;>重置</button>
</div>
</div>
</form>(4).视图Index列表的代码
针对列表的显示,又专门编写了ArticeView的这个ViewModel。

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[&#34;Title&#34;] = &#34;文章列表&#34;;
}
@section Scripts{
<script src=&#34;~/js/jquery-2.1.0.min.js&#34;></script>
<script type=&#34;text/javascript&#34;>
function DelAll() {
var ids = document.getElementsByName(&#34;#chk_ids&#34;);
var arrIds = &#34;&#34;;
var n = 0;
for (var i = 0; i < ids.length; i++)
{
if (ids.checked == true) {
arrIds += ids.value + &#34;,&#34;;
n++;
}
}
if (n == 0) {
alert(&#34;请选择要删除的信息&#34;);
return;
}
arrIds = arrids.substr(0, arrIds.length - 1);
//
if (confirm(&#34;确定要全部删除选择信息吗&#34;)) {
$.ajax({
type: &#34;post&#34;,
url: &#34;/Article/DeleteAll&#34;,
data: { ids: arrIds },
success: function (data, state) {
alert(&#39;删除成功!&#39;);
window.location.href = &#34;&#34;;
},
error: function (data, state) {
alert(&#39;删除失败&#39;);
}
});
}
}
</script>
}
<div class=&#34;panel panel-default todo-panel&#34;>
<div class=&#34;panel-heading&#34;>@ViewData[&#34;Title&#34;]</div>
@Html.AntiForgeryToken()
<form asp-action=&#34;Index&#34; method=&#34;get&#34;>
<table>
<tr><td><a asp-controller=&#34;Article&#34; asp-action=&#34;Create&#34;>添加</a></td></tr>
<tr>
<td>查询关键词:<input type=&#34;text&#34; name=&#34;SearchString&#34; value=&#34;@ViewData[&#34;CurrentFilter&#34;]&#34; /></td>
<td><input type=&#34;submit&#34; value=&#34;查询&#34; /></td>
<td><a asp-action=&#34;Index&#34;>Back</a></td>
<td><a asp-action=&#34;DeleteAll&#34;>批量删除</a></td>
</tr>
</table>
</form>
<table class=&#34;table table-hover&#34;>
<thead>
<tr>
<td>&#x2714;</td>
<td><a asp-action=&#34;Index&#34; asp-route-sortOrder=&#34;@ViewData[&#34;NameSortParm&#34;]&#34; asp-route-currentFilter=&#34;@ViewData[&#34;CurrentFilter&#34;]&#34;>标题</a></td>
<td>类别</td>
<td><a asp-action=&#34;Index&#34; asp-route-sortOrder=&#34;@ViewData[&#34;DateSortParm&#34;]&#34; asp-route-currentFilter=&#34;@ViewData[&#34;CurrentFilter&#34;]&#34;>添加时间</a></td>
<td>作者</td>
<td>操作</td>
</tr>
@foreach (var item in Model)
{
<tr>
<td><input type=&#34;checkbox&#34; class=&#34;done-checkbox&#34; name=&#34;chk_ids&#34; value=&#34;@item.Id&#34;></td>
<td>@item.Title</td>
<td>@item.CategoryName</td>
<td>@item.AddTime</td>
<td>@item.Author</td>
<td>
<a asp-action=&#34;Details&#34; asp-route-id=&#34;@item.Id&#34;>Details</a>
<a asp-action=&#34;Edit&#34; asp-route-id=&#34;@item.Id&#34;>Edit</a>
<a asp-action=&#34;Delete&#34; asp-route-id=&#34;@item.Id&#34;>Delete</a>
</td>
</tr>
}
</thead>
</table>
@{
var prevDisabled = !Model.HasPreviousPage ? &#34;disabled&#34; : &#34;&#34;;
var nextDisabled = !Model.HasNextPage ? &#34;disabled&#34; : &#34;&#34;; ;
}
<a asp-action=&#34;Index&#34;
asp-route-sortOrder=&#34;@ViewData[&#34;CurrentSort&#34;]&#34;
asp-route-pageNumber=&#34;@(Model.PageIndex - 1)&#34;
asp-route-currentFilter=&#34;@ViewData[&#34;CurrentFilter&#34;]&#34;
class=&#34;btn btn-default @prevDisabled&#34;>
上一页
</a>
<a asp-action=&#34;Index&#34;
asp-route-sortOrder=&#34;@ViewData[&#34;CurrentSort&#34;]&#34;
asp-route-pageNumber=&#34;@(Model.PageIndex + 1)&#34;
asp-route-currentFilter=&#34;@ViewData[&#34;CurrentFilter&#34;]&#34;
class=&#34;btn btn-default @nextDisabled&#34;>
下一页
</a>
<div class=&#34;panel-footer add-item-form&#34;>
<!-- 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[&#34;CurrentSort&#34;] = sortOrder;
ViewData[&#34;NameSortParm&#34;] = String.IsNullOrEmpty(sortOrder) ? &#34;name_desc&#34; : &#34;&#34;;
ViewData[&#34;DateSortParm&#34;] = sortOrder == &#34;Date&#34; ? &#34;date_desc&#34; : &#34;Date&#34;;
if (searchString != null)
{
pageNumber = 1;
}
else
{
searchString = currentFilter;
}
ViewData[&#34;CurrentFilter&#34;] = 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 &#34;name_desc&#34;:
article = article.OrderByDescending(s => s.Title) ;
break;
case &#34;Date&#34;:
article = article.OrderBy(s => s.AddTime);
break;
case &#34;date_desc&#34;:
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=&#34;0&#34;,Text=&#34;全部&#34;,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= &#34;|-&#34; + 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 = &#34; |-&#34; + 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(&#34;IsSystem&#34;);//在View端已经解决了了bool类型,那么此行代码可以不用
#region 下拉菜单
string strCategoryId = Request.Form[&#34;ddl_CategoryId&#34;];
if (!string.IsNullOrEmpty(strCategoryId))
article.CategoryId = int.Parse(strCategoryId);
else
article.CategoryId = 0;
#endregion
#region 复选框
article.IsTop = fromData[&#34;IsTop&#34;] != &#34;false&#34;;//使用FormCollection时,可以这样
article.IsRed = fromData[&#34;IsRed&#34;] != &#34;false&#34;;
article.IsSlide = fromData[&#34;IsSlide&#34;] != &#34;false&#34;;
//也可以这样取值,但要注意View内的写法
//if (!string.IsNullOrEmpty(fromData[&#34;IsTop&#34;]))
// 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(&#39;&#34;&#39;);
string webContentPath = _hostEnvironment.ContentRootPath;
var fileExt = formFile.FileName.Substring(formFile.FileName.LastIndexOf(&#39;.&#39;));//文件扩展名
var fileNew = DateTime.Now.ToString(&#34;yyyyMMddHHmmss&#34;) + fileExt; //给文件重新命名
//string upLoadPath = webContentPath + $@&#34;\{fileName}&#34;;
string upLoadPath = webContentPath + $@&#34;\UpFiles&#34;;
var fileUrl = upLoadPath + $@&#34;\{fileNew}&#34;;
if (formFile.Length > 0)
{
using (var stream = new FileStream(fileUrl,FileMode.Create))
{
await formFile.CopyToAsync(stream);
}
}
article.ImageUrl = &#34;../UpFiles/&#34; + fileNew;
}
#endregion
if (ModelState.IsValid)
{
var successful = await _articleService.AddArticleAysnc(article);
if (successful)
return RedirectToAction(&#34;Index&#34;);
else
return BadRequest(&#34;失败&#34;);
}
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=&#34;0&#34;,Text=&#34;全部&#34;,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 = &#34; |-&#34; + 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[&#34;ddl_CategoryId&#34;];
if (!string.IsNullOrEmpty(strCategoryId))
article.CategoryId = int.Parse(strCategoryId);
else
article.CategoryId = 0;
#endregion
#region 复选框
if (Request.Form[&#34;IsTop&#34;].Contains(&#34;true&#34;))
article.IsTop = true;
else
article.IsTop = false;
if (Request.Form[&#34;IsRed&#34;].Contains(&#34;true&#34;))
article.IsRed = true;
else
article.IsRed = false;
if (Request.Form[&#34;IsSlide&#34;].Contains(&#34;true&#34;))
article.IsSlide = true;
else
article.IsSlide = false;
#endregion
//ModelState.Remove(&#34;IsTop&#34;);
//ModelState.Remove(&#34;IsRed&#34;);
//ModelState.Remove(&#34;IsSlide&#34;);
if (ModelState.IsValid)
{
try
{
var result = await _articleService.UpdateArticleAsync(id, article);
//跳转
if (result)
return RedirectToAction(&#34;Index&#34;);
else
return BadRequest(&#34;编辑失败&#34;);
}
catch (Exception ex)
{
return BadRequest(&#34;编辑失败&#34;);
}
}
else
{
return BadRequest(&#34;数据输入有误!&#34;);
}
}
/// <summary>
/// 递归函数,实现获取子菜单
/// </summary>
/// <param name=&#34;lists&#34;>递归前的列表</param>
/// <param name=&#34;newlists&#34;>递归后的新列表</param>
/// <param name=&#34;parentId&#34;>父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.Id;
category.ParentId = tempList.ParentId;
category.Title = tempList.Title;
category.Children = GetChildCategory(lists, newlists, category.Id);
newlists.Add(category);
}
return newlists;
}
/// <summary>
/// 循环嵌套,实现获取子菜单
/// </summary>
/// <param name=&#34;lists&#34;>循环遍历前的列表</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.ParentId)
categorylist.Add(lists);
for (int j = 0; j < lists.Count; j++)
{
if (lists[j].ParentId == lists.Id)
lists.Children.Add(lists[j]);
}
}
return categorylist;
}
}(6).Service应用层代码

public class ArticleService : IArticleService
{
private readonly AppDbContext _appDbContext;
public ArticleService(AppDbContext appDbContext)
{
_appDbContext = appDbContext;
}
/// <summary>
/// 添加文章
/// </summary>
/// <param name=&#34;article&#34;></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=&#34;Id&#34;></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=&#34;Id&#34;></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=&#34;title&#34;></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=&#34;id&#34;></param>
/// <param name=&#34;article&#34;></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(&#34;yyyy-MM-dd HH:mm:ss&#34;));
//对旧对象执行更新
_appDbContext.Entry(oldArticle).State = EntityState.Modified;
var result = await _appDbContext.SaveChangesAsync();
return result == 1;
}
}再谈CheckBox的使用
1.在View视图页增加的代码格式如果为:
<input type=&#34;checkbox&#34; name=&#34;IsTop&#34; asp-for=&#34;IsTop&#34; />置顶 或者是这样:
<input asp-for=&#34;IsTop&#34; />置顶那么在生成的html代码中,都会自动成id,name,type=“checkbox” value的属性。
2.在Controller中进行取值时的代码为:
if (Request.Form[&#34;IsTop&#34;].Contains(&#34;true&#34;))
article.IsTop = true;
else
article.IsTop = false;跟踪时发现,View中Checkbox选中是,会产生true和false两个值,如图跟踪变量发现:

如此,取值时,就用了Contains功能,因为View中CheckBox没选中,这只有一个false值;
3.在View视图页增加代码的格式如果为:
<input type=&#34;checkbox&#34; name=&#34;IsTop&#34; @(Html.Raw(@Model.IsTop ? &#34;checked=\&#34;checked\&#34;&#34; : &#34;&#34;)) asp-for=&#34;IsTop&#34; />置顶4.在Controller中进行取值时的代码为:
if (!string.IsNullOrEmpty(Request.Form[&#34;IsTop&#34;]))
article.IsTop = true;
else
article.IsTop = false;跟踪时发现,View中的CheckBox选中是,取到的值为“on”,如图跟踪发现:

所以,才用了IsNullOrEmpty这个函数,依据判空来确定是否选中。
但是这样写有个问题,在ModelState.IsValid()的模型验证中,一直无法通过,IsTop一直为false,为此,我干脆就把其去除掉验证:
ModelState.Remove(&#34;IsTop&#34;);//去除name=IsTop的checkbox的模型验证
5.使用Checkbox,还是要看给在数据表中为其定义的字段类型,Model中的指定类型和验证属性,如果你赋予了Value值,那么就在Controller中取值,http://Asp.Net Core中Checkbox默认是True和False的值,网上关于@Html.CheckBox()形式也行,你可以尝试跟踪变量值来判断如何处理,其宗旨就是根据具体条件来处理。 |
|