ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查...

前言:

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

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

微软文档: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

 

 

本章内容:分页、自定义 HtmlHelper、通过模态窗口确认是否提交表单、上传文件、获取文件、预览文件、select 元素、表单提交数组、checkbox、js 确认关闭页面

 

 

 

注:在对 EF 中的数据进行更改时,需要调用对应 Context 的 SaveChange 方法才能对更改进行保存。

 

 

 

一、视图分页视图模型

首先创建一个视图模型用于确定每页的书籍数、页数

 1     public class PagingInfo
 2     {
 3         public int TotalItems { get; set; }
 4         public int ItemsPerPage { get; set; }
 5         public int CurrentPage { get; set; }
 6 
 7         public int TotalPages
 8         {
 9             get => (int)Math.Ceiling((decimal)TotalItems / ItemsPerPage);
10         }
11     }

 

然后创建另一个视图模型用于确定该分页信息以及各分页的书籍:

1      public class BookListViewModel
2      {
3          public IEnumerable BookDetails { get; set; }
4          public PagingInfo PagingInfo { get; set; }
5      }

 

创建一个自定义 HtmlHelper 用于在视图中使用 Razor 语法获取分页,在 ASP.NET Core 中 TagBuilder 继承自 IHtmlContent,不能直接对 TagBuilder 进行赋值,需调用 MergeAttribute 方法和 InnerHtml 属性的 AppendHtml 方法编写标签;并且 TagBuilder 没有实现 ToString 方法,只能通过其 WriteTo 方法将内容写到一个 TextWriter 对象中以取出其值:

 1     public static class PagingHelper
 2     {
 3         public static HtmlString PageLinks(this IHtmlHelper html, PagingInfo pagingInfo, Func<int, string> pageUrl)
 4         {
 5             StringWriter writer=new StringWriter();
 6             for (int i = 1; i <= pagingInfo.TotalPages; i++)
 7             {
 8                 TagBuilder tag=new TagBuilder("a");
 9                 tag.MergeAttribute("href",pageUrl(i));
10                 tag.InnerHtml.AppendHtml(i.ToString());
11                 if (i==pagingInfo.CurrentPage)
12                 {
13                     tag.AddCssClass("selected");
14                     tag.AddCssClass("btn-primary");
15                 }
16                 tag.AddCssClass("btn btn-default");
17                 tag.WriteTo(writer,HtmlEncoder.Default);
18             }
19             return new HtmlString(writer.ToString());
20         }
21     }

 

 

 

二、编辑图书信息页面的首页

在此准备使用 Session 更快地获取图书信息,为了在使用时更直观,此处对 Session 类进行扩展:

 1     public static class SessionExtensions
 2     {
 3         public static void Set(this ISession session, string key, T value)
 4         {
 5             session.SetString(key, JsonConvert.SerializeObject(value));
 6         }
 7 
 8         public static T Get(this ISession session, string key)
 9         {
10             var value = session.GetString(key);
11             return value == null ? default(T) : JsonConvert.DeserializeObject(value);
12         }
13     }

 

创建一个 BookInfo 控制器:

1    public class BookInfoController : Controller
2     {
3         private LendingInfoDbContext _lendingInfoDbContext;
4 
5         public BookInfoController(LendingInfoDbContext context)
6         {
7             _lendingInfoDbContext = context;
8         }
9     }

 

创建学生浏览的首页:

在此使用 Session 获取书籍列表:

创建 BookInfo 控制器并确定其中每个分页的书籍数,使用 Session 获取更快地获取书籍信息:

 1     public class BookInfoController : Controller
 2     {
 3         private LendingInfoDbContext _context;
 4         private static int amout = 4;
 5 
 6         public BookInfoController(LendingInfoDbContext context)
 7  { 8 _context = context; 9  } 10 11 public IActionResult Index(string category, int page = 1) 12  { 13 IEnumerable books = null; 14 if (HttpContext.Session != null) 15  { 16 books = HttpContext.Session.Get>("bookDetails"); 17  } 18 if (books == null) 19  { 20 books = _context.BooksDetail; 21 HttpContext.Session?.Set>("books", books); 22  } 23 BookListViewModel model = new BookListViewModel() 24  { 25 PagingInfo = new PagingInfo() 26  { 27 ItemsPerPage = amout, 28 TotalItems = books.Count(), 29 CurrentPage = page, 30  }, 31 BookDetails = books.OrderBy(b => b.FetchBookNumber).Skip((page - 1) * amout).Take(amout) 32  }; 33 return View(model); 34  } 35 36 public FileContentResult GetImage(string isbn) 37  { 38 BookDetails target = _context.BooksDetail.FirstOrDefault(b => b.ISBN == isbn); 39 if (target != null) 40  { 41 return File(target.ImageData, target.ImageMimeType); 42  } 43 return null; 44  } 45 }

 

视图页面:

33 行利用 BookListViewModel 中 PagingInfo 的 CurrentPage 获取各序号,32 行中使 img 元素的 src 指向 BookInfoController 的 GetImage 方法以获取图片:

 1 @using LibraryDemo.HtmlHelpers
 2 @model BookListViewModel
 3 @{
 4     ViewData["Title"] = "Index";
 5     int i = 1;
 6     Layout = "_LendingLayout";
 7 }
 8 
18 
19 
20 2122 @foreach (var book in Model.BookDetails) 23 { 242526364849 } 5051
"width: 3%">@((@Model.PagingInfo.CurrentPage-1)*4+i++) "text-align: center; width: 150px; height: 200px;"> 27 @if (book.ImageData == null) 28 { 29 30 } 31 else 32 { 33 class="img-thumbnail pull-left" src="@Url.Action("GetImage", "BookInfo", new {book.ISBN})" /> 34 } 35 "text-align: left;"> 37 "margin-left: 1em;" href="@Url.Action("Detail",new{[email protected]})">@book.Name 38
"margin-left: 2em;margin-top: 5px"> 39 @book.Author 40
41 @book.Press 42

@book.FetchBookNumber

43
44
"text-indent: 2em"> 45

@book.Description

46
47
52
class="btn-group pull-right"> 53 @Html.PageLinks(Model.PagingInfo, x => Url.Action("Index", new { page = x})) 54

 

在此同样使用 Session 获取书籍列表:

利用 [Authorize] 特性指定 Role 属性确保只有 Admin 身份的人才能访问该页面:

 1         [Authorize(Roles = "Admin")]
 2         public IActionResult BookDetails(string isbn, int page = 1)
 3         {
 4             IEnumerable books = null;
 5             BookListViewModel model;
 6             if (HttpContext.Session != null)
 7             {
 8                 books = HttpContext.Session.Get>("bookDetails");
 9             }
10             if (books == null)
11             {
12                 books = _context.BooksDetail.AsNoTracking();
13                 HttpContext.Session?.Set>("books", books);
14 
15             }
16             if (isbn != null)
17             {
18                 model = new BookListViewModel()
19                 {
20                     BookDetails = new List() { books.FirstOrDefault(b => b.ISBN == isbn) },
21                     PagingInfo = new PagingInfo()
22                 };
23                 return View(model);
24             }
25             model = new BookListViewModel()
26             {
27 
28                 PagingInfo = new PagingInfo()
29                 {
30                     ItemsPerPage = amout,
31                     TotalItems = books.Count(),
32                     CurrentPage = page,
33                 },
34                 BookDetails = books.OrderBy(b => b.FetchBookNumber).Skip((page - 1) * amout).Take(amout)
35             };
36             return View(model);
37         }

 

BookDetails 视图,confirmDelete 为删除按钮添加了确认的模态窗口;

53 行为 glyphicon 为 Bootstrap 提供的免费图标,只能通过 span 元素使用:

 1     @using LibraryDemo.HtmlHelpers
 2     @model BookListViewModel
 3     @{
 4         ViewData["Title"] = "BookDetails";
 5         int i = 1;
 6     }
 7 
 8     
26 
27     
36 
37 
38     
39 @if (TempData["message"] != null) 40 { 41

@TempData["message"]

42
43
44 } 45
class="pull-left" action="@Url.Action("Search")"> 46 @Html.DropDownList("keyword",new List() 47 { 48 new SelectListItem("书名","Name"), 49 new SelectListItem("ISBN","ISBN"), 50 new SelectListItem("索书号","FetchBookNumber"), 51 }) 52 "text" name="value"/> 53 54 55
56
57 58
"post" asp-action="RemoveBooksAndBookDetails"> 59 "1000"> 606162636465666768 @foreach (var book in Model.BookDetails) 69 { 7071727374757677 } 7879
序号 标题 "text-align: right">ISBN
"checkbox" name="isbns" value="@book.ISBN" /> " padding-left: 10px">@((Model.PagingInfo.CurrentPage-1)*4+i++) "EditBookDetails" asp-route-isbn="@book.ISBN">@book.Name "text-align: right;">@book.ISBN
80
81
82 class="btn btn-primary" href="@Url.Action("AddBookDetails")">添加书籍 83 84
85
86 87
88
class="btn-group pull-right"> 89 @Html.PageLinks(Model.PagingInfo, x => Url.Action("BookDetails", new { page = x })) 90

 

Index 页面:

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第1张图片

 

BookDetails 页面:

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第2张图片

 

 

 

 

三、添加书籍信息

在此为了接受图片需要使用 IFormFile 接口,为了使图片以原有的格式在浏览器中显示,需要用另一个字段 ImageType 保存文件的格式;

39 页使用 TempData 传递一次性信息告知书籍添加成功,在传递完成后 TempData 将被立即释放:

 1         [Authorize(Roles = "Admin")]
 2         public IActionResult AddBookDetails(BookDetails model)
 3         {
 4             if (model == null)
 5             {
 6                 model = new BookDetails();
 7             }
 8             return View(model);
 9         }
10 
11         [HttpPost]
12         [ValidateAntiForgeryToken]
13         [Authorize(Roles = "Admin")]
14         public async Task AddBookDetails(BookDetails model, IFormFile image)
15         {
16             BookDetails bookDetails = new BookDetails();
17             if (ModelState.IsValid)
18             {
19                 if (image != null)
20                 {
21                     bookDetails.ImageMimeType = image.ContentType;
22                     bookDetails.ImageData = new byte[image.Length];
23                     await image.OpenReadStream().ReadAsync(bookDetails.ImageData, 0, (int)image.Length);
24                 }
25 
26                 bookDetails.ISBN = model.ISBN;
27                 bookDetails.Name = model.Name;
28                 bookDetails.Author = model.Author;
29                 bookDetails.Description = model.Description;
30                 bookDetails.FetchBookNumber = model.FetchBookNumber;
31                 bookDetails.Press = model.Press;
32                 bookDetails.PublishDateTime = model.PublishDateTime;
33                 bookDetails.SoundCassettes = model.SoundCassettes;
34                 bookDetails.Version = model.Version;
35 
36                 await _lendingInfoDbContext.BooksDetail.AddAsync(bookDetails);
37 
38                 _lendingInfoDbContext.SaveChanges();
39                 TempData["message"] = $"已添加书籍《{model.Name}》";
40                 return RedirectToAction("EditBookDetails");
41             }
42             return View(model);
43         }
 

AddBookDetails 视图:

为了使表单可以上传文件,需要指定表单的 enctype 属性值为 multipart/form-data,66 行中使用一个 a 元素包含用来上传文件的 input ,指定其 class 为 btn 以生成一个按钮,指定 href="javascript:;" 使该元素不会返回任何值。

指定 input 的 name 属性为 image 以在上传表单时进行模型绑定,指定其 accept 属性令其打开文件选择框时只接收图片。

JS 代码为 input 添加 onchange 事件以预览上传的图片,并为关闭或刷新页面时添加模态窗口进行确认,同时为提交按钮添加事件以重置 window.onbeforeunload 事件从而不弹出确认窗口:

 1 @model LibraryDemo.Models.DomainModels.BookDetails
 2 @{
 3     ViewData["Title"] = "AddBookDetails";
 4 }
 5 
 6 
28 
29 

添加书籍

30 31
"multipart/form-data" method="post"> 32
class="panel-body"> 33
class="form-group"> 34 @Html.LabelFor(m => m.ISBN) 35 @Html.TextBoxFor(m => m.ISBN, new { @class = "form-control" }) 36
37
class="form-group"> 38 @Html.LabelFor(m => m.Name) 39 @Html.TextBoxFor(m => m.Name, new { @class = "form-control" }) 40
41
class="form-group"> 42 @Html.LabelFor(m => m.Author) 43 @Html.TextBoxFor(m => m.Author, new { @class = "form-control" }) 44
45
class="form-group"> 46 @Html.LabelFor(m => m.Press) 47 @Html.TextBoxFor(m => m.Press, new { @class = "form-control" }) 48
49
class="form-group"> 50 @Html.LabelFor(m => m.FetchBookNumber) 51 @Html.TextBoxFor(m => m.FetchBookNumber, new { @class = "form-control" }) 52
53
class="form-group"> 54 @Html.LabelFor(m => m.SoundCassettes) 55 @Html.TextBoxFor(m => m.SoundCassettes, new { @class = "form-control" }) 56
57
class="form-group"> 58 @Html.LabelFor(m => m.Description) 59 @Html.TextAreaFor(m => m.Description, new { @class = "form-control", rows = 5 }) 60
61
class="form-group"> 62 @Html.LabelFor(m => m.PublishDateTime) 63
@Html.EditorFor(m => m.PublishDateTime)
64
65
class="form-group"> 66 @Html.LabelFor(m => m.Version) 67
@Html.EditorFor(m => m.Version)
68
69 81 "submit" οnclick="removeOnbeforeunload()"/> 82
83

结果:

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第3张图片

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第4张图片

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第5张图片

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第6张图片

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第7张图片

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第8张图片

 

 

 

四、删除书籍信息

删除书籍的动作方法:

此处通过在之前的 BookDetails 视图中指定 input 元素的 type 为 checkbox,指定 name 为 isbns 以实现多个字符串的模型绑定:

 1         [Authorize(Roles = "Admin")]
 2         [HttpPost]
 3         [ValidateAntiForgeryToken]
 4         public async Task RemoveBooksAndBookDetails(IEnumerable<string> isbns)
 5         {
 6             StringBuilder sb = new StringBuilder();
 7             foreach (var isbn in isbns)
 8             {
 9                 BookDetails bookDetails = _lendingInfoDbContext.BooksDetail.First(b => b.ISBN == isbn);
10                 IQueryable books = _lendingInfoDbContext.Books.Where(b => b.ISBN == isbn);
11                 _lendingInfoDbContext.BooksDetail.Remove(bookDetails);
12                 _lendingInfoDbContext.Books.RemoveRange(books);
13                 sb.Append("" + bookDetails.Name + "");
14                 await _lendingInfoDbContext.SaveChangesAsync();
15             }
16             TempData["message"] = $"已移除书籍{sb.ToString()}";
17             return RedirectToAction("BookDetails");
18         }

结果:

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第9张图片

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第10张图片

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第11张图片

 

 

 

五、编辑书籍信息

动作方法:

 1         [Authorize(Roles = "Admin")]
 2         public async Task EditBookDetails(string isbn)
 3         {
 4             BookDetails book = await _lendingInfoDbContext.BooksDetail.FirstOrDefaultAsync(b => b.ISBN == isbn);
 5             if (book != null)
 6             {
 7                 return View(book);
 8             }
 9             else
10             {
11                 return RedirectToAction("BookDetails");
12             }
13         }
14 
15         [HttpPost]
16         [ValidateAntiForgeryToken]
17         [Authorize(Roles = "Admin")]
18         public async Task EditBookDetails(BookDetails model, IFormFile image)
19         {
20             BookDetails bookDetails = _lendingInfoDbContext.BooksDetail.FirstOrDefault(b => b.ISBN == model.ISBN);
21             if (ModelState.IsValid)
22             {
23                 if (bookDetails != null)
24                 {
25                     if (image != null)
26                     {
27                         bookDetails.ImageMimeType = image.ContentType;
28                         bookDetails.ImageData = new byte[image.Length];
29                         await image.OpenReadStream().ReadAsync(bookDetails.ImageData, 0, (int)image.Length);
30                     }
31 
32                     BookDetails newBookDetails = model;
33 
34                     bookDetails.Name = newBookDetails.Name;
35                     bookDetails.Author = newBookDetails.Author;
36                     bookDetails.Description = newBookDetails.Description;
37                     bookDetails.FetchBookNumber = newBookDetails.FetchBookNumber;
38                     bookDetails.Press = newBookDetails.Press;
39                     bookDetails.PublishDateTime = newBookDetails.PublishDateTime;
40                     bookDetails.SoundCassettes = newBookDetails.SoundCassettes;
41                     bookDetails.Version = newBookDetails.Version;
42 
43                     await _lendingInfoDbContext.SaveChangesAsync();
44                     TempData["message"] = $"《{newBookDetails.Name}》修改成功";
45                     return RedirectToAction("EditBookDetails");
46                 }
47             }
48             return View(model);
49         }

 

此处视图与之前 AddBookDetails 大致相同,但在此对一些视图中的 ISBN 字段添加了 readonly 属性使它们不能被直接编辑:

  1 @model LibraryDemo.Models.DomainModels.BookDetails
  2 
  3 @{
  4     ViewData["Title"] = "EditBookDetails";
  5 }
  6 
  7 
 34 
 35 

编辑书籍

36 37 @section Scripts 38 { 39 40 } 41 42
"multipart/form-data" method="post"> 43
class="panel-body"> 44
class="form-group"> 45 @Html.LabelFor(m => m.ISBN) 46 @Html.EditorFor(m => m.ISBN) 47
48
class="form-group"> 49 @Html.LabelFor(m => m.Name) 50 @Html.TextBoxFor(m => m.Name, new {@class = "form-control"}) 51
52
class="form-group"> 53 @Html.LabelFor(m => m.Author) 54 @Html.TextBoxFor(m => m.Author, new {@class = "form-control"}) 55
56
class="form-group"> 57 @Html.LabelFor(m => m.Press) 58 @Html.TextBoxFor(m => m.Press, new {@class = "form-control"}) 59
60
class="form-group"> 61 @Html.LabelFor(m => m.FetchBookNumber) 62 @Html.TextBoxFor(m => m.FetchBookNumber, new {@class = "form-control"}) 63
64
class="form-group"> 65 @Html.LabelFor(m => m.SoundCassettes) 66 @Html.TextBoxFor(m => m.SoundCassettes, new {@class = "form-control"}) 67
68
class="form-group"> 69 @Html.LabelFor(m => m.Description) 70 @Html.TextAreaFor(m => m.Description, new {@class = "form-control", rows = 5}) 71
72
class="form-group"> 73 @Html.LabelFor(m => m.PublishDateTime) 74
@Html.EditorFor(m => m.PublishDateTime)
75
76
class="form-group"> 77 @Html.LabelFor(m => m.Version) 78
@Html.EditorFor(m => m.Version)
79
80
class="form-group"> 81 91 @if (Model.ImageData == null) 92 { 93
class="form-control-static image">No Image
94 } 95 else 96 { 97 class="img-thumbnail image" style="width: 150px;" src="@Url.Action("GetImage", "BookInfo", new {Model.ISBN})" /> 98 } 99
100
101 class="btn btn-primary" asp-action="Books" asp-route-isbn="@Model.ISBN" οnclick="return removeOnbeforeunload()">编辑外借书籍信息 102
103
104 "submit" class="btn btn-success" οnclick="return removeOnbeforeunload()"/> 105
106

 

结果:

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第12张图片

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第13张图片

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第14张图片

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第15张图片

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第16张图片

 

 

 

六、查询特定书籍

此处和之前的账号登录处一样使用 switch 对不同的关键词进行检索:

 1         public async Task Search(string keyWord, string value)
 2         {
 3             BookDetails bookDetails = new BookDetails();
 4             switch (keyWord)
 5             {
 6                 case "Name":
 7                     bookDetails =await _context.BooksDetail.FirstOrDefaultAsync(b => b.Name == value);
 8                     break;
 9                 case "ISBN":
10                     bookDetails =await _context.BooksDetail.FirstOrDefaultAsync(b => b.ISBN == value);
11                     break;
12                 case "FetchBookNumber":
13                     bookDetails =await _context.BooksDetail.FirstOrDefaultAsync(b => b.FetchBookNumber == value);
14                     break;
15             }
16 
17             if (bookDetails!=null)
18             {
19                 return RedirectToAction("EditBookDetails", new {isbn = bookDetails.ISBN});
20             }
21 
22             TempData["message"] = "找不到该书籍";
23             return RedirectToAction("BookDetails");
24         }

 

 结果:

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第17张图片

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第18张图片

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第19张图片

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查..._第20张图片

转载于:https://www.cnblogs.com/gokoururi/p/10382292.html

你可能感兴趣的:(ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查...)