Error message here!

Hide Error message here!

忘记密码?

Error message here!

请输入正确邮箱

Hide Error message here!

密码丢失?请输入您的电子邮件地址。您将收到一个重设密码链接。

Error message here!

返回登录

Close

ASP.NET Core 打造一个简单的图书馆管理系统 (修正版)(六)学生借阅/预约/查询书籍事务

NanaseRuri 2019-02-17 13:41:00 阅读数:255 评论数:0 点赞数:0 收藏数:0

 

 

前言:

本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作。

本系列文章主要参考资料:

微软文档:https://docs.microsoft.com/zh-cn/aspnet/core/getting-started/?view=aspnetcore-2.1&tabs=windows

《Pro ASP.NET MVC 5》、《锋利的 jQuery》

  

此系列皆使用 VS2017+C/# 作为开发环境。如果有什么问题或者意见欢迎在留言区进行留言。 

项目 github 地址:https://github.com/NanaseRuri/LibraryDemo

 

 

本章内容:自定义布局页、自定义 EditorFor 模板、EF 多对多数据的更新

 

 

一、自定义布局页 

在 ASP.NET 中,默认将 HTML 页面的 body 元素一部分抽出来,该部分称作 RenderBody ;然后将这部分放到一个布局即大体页面框架中即可完成对同一系列的页面进行精简的布局实现。

默认布局页为 Layout.cshtml,可在视图文件夹中根目录或各个控制器视图目录的 ViewStart.cshtml 修改默认布局页,或者在每个 Razor 页面的开头中指定布局页:@{ ViewData["Title"] = "EditLendingInfo"; Layout= "_LendingLayout"; }

 

之前一直使用的是 VS 的默认布局页,现在以该默认布局页为基础,添加自己所需要的信息:

 

1 @using Microsoft.AspNetCore.Http.Extensions2 @using Microsoft.AspNetCore.Authorization3 @inject IAuthorizationService AuthorizationService4 5 6 7 8 9 @ViewData["Title"] - LibraryDemo 10 11 12 13 14 15 18 19 20 21 22

59
60
61
62 @Html.DropDownList("keyword", new List()63 {64 new SelectListItem("书名", "Name"),65 new SelectListItem("ISBN", "ISBN"),66 new SelectListItem("索书号", "FetchBookNumber"),67 })68 69 70 71 72 @if (TempData["message"] != null)73 {74
75

@TempData["message"]

76
77 }78
79 80
81 82 @RenderBody()83
84
85 86
87
88

© 2018 - LibraryDemo

89
90
91 92 93 94 95 96 97 102 107 108 109 @RenderSection("Scripts", required: false)110 111 View Code

 

 现在大体框架:

 

 

除了默认的 RenderBody 外,可以指定特定的部分放在页面的不同地方,在布局页中使用@RenderSection("SectionName"):@RenderSection("SectionName")

且在视图页中使用指定特定的节@section SectionName{ };

1 @section SectionName{ 2 3 };

则该视图页中的 SectionName 部分会被提取出来放到布局页对应的位置。

 

 

 

 

二、管理员编辑借阅信息

动作方法:

在此对数据库的表格使用 Include 方法使 EF 应用其导航属性以获得 KeepingBooks 列表,否则使用 Student 对象 KeepingBooks 属性只会返回空。1 [Authorize(Roles = "Admin")]2 public IActionResult EditLendingInfo(stringbarcode)3 {4 if (barcode == null)5 {6 return RedirectToAction("BookDetails");7 }8 Book book = _lendingInfoDbContext.Books.FirstOrDefault(b => b.BarCode ==barcode);9 returnView(book);10 }11 12 [HttpPost]13 [Authorize(Roles = "Admin")]14 [ValidateAntiForgeryToken]15 public async Task EditLendingInfo([Bind("BarCode,ISBN,BorrowTime,KeeperId,AppointedLatestTime,State")]Book book)16 {17 if(ModelState.IsValid)18 {19 if (book.BorrowTime >DateTime.Now)20 {21 ModelState.AddModelError("", "请检查外借时间");22 returnView(book);23 }24 if(book.AppointedLatestTime.HasValue)25 {26 if (book.AppointedLatestTime s.KeepingBooks).FirstOrDefaultAsync(s => s.UserName ==book.KeeperId);40 41 Book addedBook =_lendingInfoDbContext.Books42 .Include(b => b.Keeper).ThenInclude(k =>k.KeepingBooks)43 .FirstOrDefault(b => b.BarCode ==book.BarCode);44 if (addedBook == null)45 {46 return RedirectToAction("Books", new { isbn =book.ISBN });47 }48 49 StudentInfo preStudent =addedBook.Keeper;50 AppointmentOrLending targetLending = 51 preStudent?.KeepingBooks.FirstOrDefault(b => b.BookId ==addedBook.BarCode);52 53 addedBook.AppointedLatestTime =book.AppointedLatestTime;54 addedBook.State =book.State;55 addedBook.BorrowTime =book.BorrowTime;56 addedBook.MatureTime = null;57 58 preStudent?.KeepingBooks.Remove(targetLending);59 60 if(addedBook.BorrowTime.HasValue)61 {62 if (book.KeeperId == null)63 {64 ModelState.AddModelError("", "请检查借阅者");65 returnView(book);66 }67 68 if (student == null)69 {70 ModelState.AddModelError("", "不存在该学生");71 returnView(book);72 }73 if (student != null)74 {75 if (student.KeepingBooks.Count >=student.MaxBooksNumber)76 {77 TempData["message"] = "该学生借书已超过上限";78 }79 80 addedBook.State =BookState.Borrowed;81 student.KeepingBooks.Add(newAppointmentOrLending()82 {83 BookId =addedBook.BarCode,84 StudentId =student.UserName85 });86 addedBook.Keeper =student;87 88 }89 addedBook.MatureTime = addedBook.BorrowTime + TimeSpan.FromDays(28);90 }91 92 93 TempData["message"] = "保存成功";94 await_lendingInfoDbContext.SaveChangesAsync();95 return RedirectToAction("Books", new { isbn =book.ISBN });96 }97 returnView(book);98 }

 

将 BookState 枚举提取成分部视图 _BookStatePartial:

 1 @using LibraryDemo.Models.DomainModels2 @model Book3

4 @Html.LabelFor(b =>b.State)5 @Html.DropDownListFor(b => b.State, Enum.GetValues(typeof(BookState)).Cast().Select(state => 6 {7 string enumVal = Enum.GetName(typeof(BookState), state);8 stringdisplayVal;9 switch(enumVal)10 {11 case "Normal":12 displayVal = "可借阅";13 break;14 case "Readonly":15 displayVal = "馆内阅览";16 break;17 case "Borrowed":18 displayVal = "已借出";19 break;20 case "ReBorrowed":21 displayVal = "被续借";22 break;23 case "Appointed":24 displayVal = "被预约";25 break;26 default:27 displayVal = "";28 break;29 }30 return newSelectListItem()31 {32 Text =displayVal,33 Value =enumVal,34 Selected = Model.State.ToString() ==enumVal35 };36 }))37

 

Html.DisplayFor 方法是 ASP.NET 内置对各种属性进行展示的方法,可以在项目的 Views 文件夹中的 Shared 文件夹创建对应类型的 Editor 模板供其使用:

在此创建一个 DateTime.cshtml,于是我们使用 Html.DisplayFor 用于展示 DateTime 数据时只会显示年份/月份/天数:1 @model DateTime? 2 3 @Model?.ToString("yyyy/M/dd")

 

视图中第 40 行使用 partial TagHelper 指定其 name 为 _BookStatePartial 以应用分部视图:1 @model LibraryDemo.Models.DomainModels.Book2 @{3 ViewData["Title"] = "EditLendingInfo";4 Layout="_LendingLayout";5 }6 7

@Model.BarCode

8

@Model.Name

9
10 11 22 23 @Html.ValidationSummary(false,"",new{@class="text-danger"})24 25
26 @Html.HiddenFor(b =>b.BarCode)27 @Html.HiddenFor(b =>b.ISBN)28
29 @Html.LabelFor(b =>b.KeeperId)30 @Html.EditorFor(b =>b.KeeperId)31
32
33 @Html.LabelFor(b =>b.BorrowTime)34 @Html.EditorFor(b =>b.BorrowTime)35
36
37 @Html.LabelFor(b =>b.AppointedLatestTime)38 @Html.EditorFor(b =>b.AppointedLatestTime)39
40 41 42

 

 

结果:

 

 

 

 

 

 

 

 

三、查看个人信息

这里通过 User.Identity.Name 获取当前登录人的信息以选定当前登录的学生:1 [Authorize]2 public async TaskPersonalInfo()3 {4 StudentInfo student = await _lendingInfoDbContext.Students.Include(s => s.KeepingBooks).ThenInclude(k =>k.Book)5 .FirstOrDefaultAsync(s => s.UserName ==User.Identity.Name);6 decimal fine = 0;7 foreach (var book in student.KeepingBooks.Where(b => b.Book.MatureTime < DateTime.Now && !b.AppointingDateTime.HasValue))8 {9 fine += (DateTime.Now - book.Book.MatureTime.Value).Days /* (decimal)0.2;10 book.Book.State = book.Book.State == BookState.Appointed ?BookState.Appointed : BookState.Expired;11 }12 13 student.Fine =fine;14 PersonalInfoViewModel model = newPersonalInfoViewModel()15 {16 Student =student,17 BookingBook = _lendingInfoDbContext.Books.FirstOrDefault(b => b.BarCode ==student.AppointingBookBarCode)18 };19 returnView(model);20 }

 

视图:1 @model LibraryDemo.Models.PersonalInfoViewModel2 @{3 ViewData["Title"] = "PersonalInfo";4 Layout = "_LendingLayout";5 }6 7 15 16

@Model.Student.Name

17
18 @if (Model.Student.KeepingBooks.Any(b => b.Book.MatureTime 21 22 23 过期书籍 24 25 26 27 书名 28 条形码 29 状态 30 到期时间 31 索书号 32 33 34 @foreach (var matureBook in Model.Student.KeepingBooks.Where(b => b.Book.MatureTime < DateTime.Now && !b.AppointingDateTime.HasValue))35 {36 37 @matureBook.Book.Name 38 @matureBook.Book.BarCode 39 @Html.DisplayFor(b => matureBook.Book.State) 40 @matureBook.Book.MatureTime?.ToString("yyyy/MM/dd") 41 @matureBook.Book.FetchBookNumber 42 43 }44 罚款:@Model.Student.Fine 45 46 }47
48 49 50 51 52 53 54 55 56 57 @if (!Model.Student.KeepingBooks.Any())58 {59 60 61 62 }63 else 64 {65 foreach (var keepingBook in Model.Student.KeepingBooks.Where(b=>!b.AppointingDateTime.HasValue))66 {67 68 69 70 71 72 73 74 75 }76 }77
续借书名条形码状态到期时间索书号
未借阅书本
@keepingBook.Book.Name@keepingBook.Book.BarCode@Html.DisplayFor(b=>keepingBook.Book.State)@keepingBook.Book.MatureTime?.ToString("yyyy/MM/dd")@keepingBook.Book.FetchBookNumber
78
79 80
81 82
83 @if (Model.BookingBook != null)84 {85
86 87 88 89 90 91 92 93 94 95
书名条形码状态预约时间索书号
96
97 98 99
100 }

 

结果:

 

 

 

 

四、借阅书籍

由于暂时未有获取二维码的接口,仅通过直接访问 Lending 模拟借阅:1 [Authorize]2 public async Task Lending(stringbarcode)3 {4 Book targetBook=await _lendingInfoDbContext.Books.Include(b=>b.Appointments).FirstOrDefaultAsync(b => b.BarCode ==barcode);5 if (targetBook==null)6 {7 TempData["message"] = "请重新扫描书籍";8 return RedirectToAction("PersonalInfo");9 }10 11 if (targetBook.Appointments.Any(a=>a.AppointingDateTime.HasValue))12 {13 TempData["message"] = "此书已被预约";14 return RedirectToAction("PersonalInfo");15 }16 17 if (targetBook.State==BookState.Readonly)18 {19 TempData["message"] = "此书不供外借";20 return RedirectToAction("PersonalInfo");21 }22 23 targetBook.State =BookState.Borrowed;24 targetBook.BorrowTime =DateTime.Now.Date;25 targetBook.MatureTime = DateTime.Now.Date+TimeSpan.FromDays(28);26 StudentInfo student = 27 await _lendingInfoDbContext.Students.Include(s=>s.KeepingBooks).FirstOrDefaultAsync(s => s.UserName ==User.Identity.Name);28 student.KeepingBooks.Add(newAppointmentOrLending()29 {30 BookId =targetBook.BarCode,31 StudentId =student.UserName32 });33 await_lendingInfoDbContext.SaveChangesAsync();34 TempData["message"] = "借书成功";35 return RedirectToAction("PersonalInfo");36 }

 

结果:

 

 

 

 

 六、续借书籍

动作方法:1 [Authorize]2 [HttpPost]3 public async Task ReBorrow(IEnumerablebarcodes)4 {5 StringBuilder borrowSuccess = newStringBuilder();6 StringBuilder borrowFail = newStringBuilder();7 borrowSuccess.Append("成功续借书籍:");8 borrowFail.Append("续借失败书籍:");9 foreach (var barcode inbarcodes)10 {11 Book reBorrowBook = _lendingInfoDbContext.Books.FirstOrDefault(b => b.BarCode ==barcode);12 if (reBorrowBook != null)13 {14 if (reBorrowBook.State == BookState.Borrowed && DateTime.Now-reBorrowBook.MatureTime?.Date<=TimeSpan.FromDays(3))15 {16 reBorrowBook.State =BookState.ReBorrowed;17 reBorrowBook.BorrowTime =DateTime.Now.Date;18 reBorrowBook.MatureTime = DateTime.Now.Date+TimeSpan.FromDays(28);19 borrowSuccess.Append($"《{reBorrowBook.Name}》、");20 }21 else 22 {23 borrowFail.Append($"《{reBorrowBook.Name}》、");24 }25 }26 }27 borrowSuccess.AppendLine(borrowFail.ToString());28 await_lendingInfoDbContext.SaveChangesAsync();29 TempData["message"] =borrowSuccess.ToString();30 return RedirectToAction("PersonalInfo");31 }

 

结果:

 

 

 

 

 

 

七、查询书籍

修改之前的 Search 方法使其通过当前用户的身份返回不同页面,以及在 _LendingInfoLayout 中添加搜索框部分:

19 行通过短路使未授权用户不用登录。

 1 public async Task Search(string keyWord, stringvalue)2 {3 BookDetails bookDetails = newBookDetails();4 switch(keyWord)5 {6 case "Name":7 bookDetails = await lendingInfoDbContext.BooksDetail.AsNoTracking().FirstOrDefaultAsync(b => b.Name ==value);8 break;9 case "ISBN":10 bookDetails = await lendingInfoDbContext.BooksDetail.AsNoTracking().FirstOrDefaultAsync(b => b.ISBN ==value);11 break;12 case "FetchBookNumber":13 bookDetails = await _lendingInfoDbContext.BooksDetail.AsNoTracking().FirstOrDefaultAsync(b => b.FetchBookNumber ==value);14 break;15 }16 17 if (bookDetails != null)18 {19 if (User.Identity.IsAuthenticated&& User.IsInRole("Admin"))20 {21 return RedirectToAction("EditBookDetails", new { isbn =bookDetails.ISBN });22 }23 else 24 {25 return RedirectToAction("Detail", new {isbn =bookDetails.ISBN});26 }27 }28 29 TempData["message"] = "找不到该书籍";30 return RedirectToAction("BookDetails");31 }

 

 结果:

 

 

版权声明
本文为[NanaseRuri]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/gokoururi/p/10390901.html

编程之旅,人生之路,不止于编程,还有诗和远方。
阅代码原理,看框架知识,学企业实践;
赏诗词,读日记,踏人生之路,观世界之行;

支付宝红包,每日可领