一:背景 1.講故事 前段時間有位朋友找到我,說他的程式在客戶的機器上跑著跑著會出現偶發卡死,然後就崩掉了,但在本地怎麼也沒復現,dump也抓到了,讓我幫忙看下到底怎麼回事,其實崩潰類的dump也有簡單的,也有非常複雜的,因為大多情況下都是非托管層面出現的各種故障,非常考驗對 C, C++, Win ...
篇(18)-Asp.Net Core入門實戰-文章管理之文章內容管理(下拉框二級結構遞歸實現)
文章管理是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["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)視圖中的下拉框的實現方式(遞歸和迴圈嵌套)
我想在添加文章時,實現一個具有二級層次結構的下拉框,如上圖所示。所以,在對下拉框進行數據綁定時,就要費點功夫,上個章節講文章類別管理時,的表結構就一個,分類都存在一張表中,所以要進行遞歸的獲取子菜單或者通過迴圈嵌套來實現。
遞歸的主要核心函數為:
/// <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;