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


前言:

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

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

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

 

 

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

 

 

 

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

 

 

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

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

 1      [Authorize(Roles = "Admin")]
 2         public IActionResult BookDetails(string isbn, int page = 1)
 3         {
 4             IEnumerable<BookDetails> books = null;
 5             BookListViewModel model;
 6             if (HttpContext.Session != null)
 7             {
 8                 books = HttpContext.Session.Get<IEnumerable<BookDetails>>("bookDetails");
 9             }
10             if (books == null)
11             {
12                 books = _context.BooksDetail.AsNoTracking();
13                 HttpContext.Session?.Set<IEnumerable<BookDetails>>("books", books);
14 
15             }
16             if (isbn != null)
17             {
18                 model = new BookListViewModel()
19                 {
20                     BookDetails = new List<BookDetails>() { 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         }

 

利用 [Authorize] 特性确保只有 Admin 身份的人才能访问编辑页面:

 1       [Authorize(Roles = "Admin")]
 2         public IActionResult Edit(string isbn, int page = 1)
 3         {
 4             IEnumerable<BookDetails> books = null;
 5             BookListViewModel model;
 6             if (HttpContext.Session != null)
 7             {
 8                 books = HttpContext.Session.Get<IEnumerable<BookDetails>>("bookDetails");
 9             }
10             if (books == null)
11             {
12                 books = _context.BooksDetail;
13                 HttpContext.Session?.Set<IEnumerable<BookDetails>>("books", books);
14 
15             }
16             if (isbn != null)
17             {
18                 model = new BookListViewModel()
19                 {
20                     BookDetails = new List<BookDetails>() { 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         }

 

Edit 视图,confirmDelete 为删除按钮添加了确认的模态窗口;45 行为 glyphicon 为 Bootstrap 提供的免费图标,只能通过 span 元素使用:

 1   @using LibraryDemo.TagHelpers
 2     @model BookListViewModel
 3     @{
 4         ViewData["Title"] = "BookDetails";
 5         int i = 1;
 6     }
 7 
 8     <script>
 9         function confirmDelete() {
10             var isbns = document.getElementsByName("isbns");
11             var message="确认删除";
12             for (i in isbns) {
13                 if (isbns[i].checked) {
14                     var book = isbns[i].parentElement.nextElementSibling.nextElementSibling.firstElementChild.innerHTML;
15                     message=message+""+book+"";
16                 }
17             }
18             message = message + "?";
19             if (confirm(message) == true) {
20                 return true;
21             } else {
22                 return false;
23             }
24         }
25     </script>
26 
27     <style type="text/css">
28         tr + tr {
29             border-top: thin solid gray;
30         }
31 
32         .container {
33             width: 1200px;
34         }
35     </style>
36 
37 
38     <hr />
39     @if (TempData["message"] != null)
40     {
41         <p>@TempData["message"]</p>
42         <br />
43         <br />
44     }
45     <form class="pull-left" action="@Url.Action("Search")">
46         @Html.DropDownList("keyword",new List<SelectListItem>()
47         {
48             new SelectListItem("书名","Name"),
49             new SelectListItem("ISBN","ISBN"),
50             new SelectListItem("索书号","FetchBookNumber"),
51         })
52         <input type="text" name="value"/>
53         <button type="submit"><span class="glyphicon glyphicon-search"></span></button>
54     </form>
55     <br />
56     <br />
57 
58     <form method="post" asp-action="RemoveBooksAndBookDetails">
59         <table width="1000">
60             <tbody>
61             <tr>
62                 <th></th>
63                 <th >序号</th>
64                 <th>标题</th>
65                 <th ></th>
66                 <th style="text-align: right">ISBN</th>            
67             </tr>
68             @foreach (var book in Model.BookDetails)
69             {
70                 <tr>
71                     <td><input type="checkbox" name="isbns" value="@book.ISBN" /></td>
72                     <td style=" padding-left: 10px">@((Model.PagingInfo.CurrentPage-1)*4+i++)</td>
73                     <td><a asp-action="EditBookDetails" asp-route-isbn="@book.ISBN">@book.Name</a></td>
74                     <td></td>
75                     <td style="text-align: right;">@book.ISBN</td>                
76                 </tr>
77             }
78             </tbody>
79         </table>
80         <br/>
81         <div>
82             <a class="btn btn-primary" href="@Url.Action("AddBookDetails")">添加书籍</a>
83             <button type="submit" class="btn btn-danger" onclick="return confirmDelete()"> 删除书籍</button>        
84         </div>
85     </form>
86 
87     <br />
88     <div class="btn-group pull-right">
89         @Html.PageLinks(Model.PagingInfo, x => Url.Action("BookDetails", new { page = x }))
90     </div>

 

结果:

 

 

 

二、添加以及删除书籍信息

在此为了接受图片需要使用 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<IActionResult> 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 _context.BooksDetail.AddAsync(bookDetails);
37 
38                 _context.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     <script>
 7         function preview(file) {
 8             $(".image").addClass("hidden");
 9             $('.preview').wrap("<div></div>");
10             $(".preview").removeClass("hidden");
11             if (file.files && file.files[0]) {
12                 var reader = new FileReader();
13                 reader.onload = function (evt) {
14                     $('.preview').attr('src', evt.target.result);
15                 }
16                 reader.readAsDataURL(file.files[0]);
17             } else {
18                 $('.preview').attr('src', file.value);
19             }
20         }        
21         window.onbeforeunload = function () {
22             return "您的数据未保存,确定退出?";
23         }
24         function removeOnbeforeunload() {
25             window.onbeforeunload = "";
26         }
27     </script>
28 
29     <h2>添加书籍</h2>
30 
31     <form enctype="multipart/form-data" method="post">
32         <div class="panel-body">
33             <div class="form-group">
34                 @Html.LabelFor(m => m.ISBN)
35                 @Html.TextBoxFor(m => m.ISBN, new { @class = "form-control" })
36             </div>
37             <div class="form-group">
38                 @Html.LabelFor(m => m.Name)
39                 @Html.TextBoxFor(m => m.Name, new { @class = "form-control" })
40             </div>
41             <div class="form-group">
42                 @Html.LabelFor(m => m.Author)
43                 @Html.TextBoxFor(m => m.Author, new { @class = "form-control" })
44             </div>
45             <div class="form-group">
46                 @Html.LabelFor(m => m.Press)
47                 @Html.TextBoxFor(m => m.Press, new { @class = "form-control" })
48             </div>
49             <div class="form-group">
50                 @Html.LabelFor(m => m.FetchBookNumber)
51                 @Html.TextBoxFor(m => m.FetchBookNumber, new { @class = "form-control" })
52             </div>
53             <div class="form-group">
54                 @Html.LabelFor(m => m.SoundCassettes)
55                 @Html.TextBoxFor(m => m.SoundCassettes, new { @class = "form-control" })
56             </div>
57             <div class="form-group">
58                 @Html.LabelFor(m => m.Description)
59                 @Html.TextAreaFor(m => m.Description, new { @class = "form-control", rows = 5 })
60             </div>
61             <div class="form-group">
62                 @Html.LabelFor(m => m.PublishDateTime)
63                 <div>@Html.EditorFor(m => m.PublishDateTime)</div>
64             </div>
65             <div class="form-group">
66                 @Html.LabelFor(m => m.Version)
67                 <div>@Html.EditorFor(m => m.Version)</div>
68             </div>
69             <div class="form-group">
70                 <div style="position: relative;">
71                     <label>Image</label>
72                     <a class="btn" href="javascript:;">
73                         选择图片
74                         <input type="file" name="Image" size="40" accept="image/*"
75                                style="position: absolute; z-index: 2; top: 0; left: 0; filter: alpha(opacity=0); opacity: 0; background-color: transparent; color: transparent"
76                                onchange="preview(this)" />
77                     </a>
78                     <img style="width: 150px;" class="hidden preview img-thumbnail">
79                 </div>
80             </div>        
81             <input type="submit" onclick="removeOnbeforeunload()"/>
82         </div>
83     </form>

 

结果:

 

 

删除书籍的动作方法:

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

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

 

 

结果:

 

 

三、编辑书籍信息

 1     [Authorize(Roles = "Admin")]
 2         public async Task<IActionResult> EditBookDetails(string isbn)
 3         {
 4             BookDetails book = await _context.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<ActionResult> EditBookDetails(BookDetails model, IFormFile image)
19         {
20             BookDetails bookDetails = _context.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 _context.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     <script>
  8         function preview(file) {
  9             $(".image").addClass("hidden");
 10             $('.preview').wrap("<div></div>");
 11             $(".preview").removeClass("hidden");
 12             if (file.files && file.files[0]){  
 13                 var reader = new FileReader();  
 14                 reader.onload = function(evt){
 15                     $('.preview').attr('src' , evt.target.result);
 16                 }    
 17                 reader.readAsDataURL(file.files[0]);  
 18             }else{  
 19                 $('.preview').attr('src' , file.value);
 20             }
 21         }
 22         window.onload = function() {
 23             $("div>input").addClass("form-control");
 24             var isbn = document.getElementById("ISBN");
 25             isbn.setAttribute("readonly","true");
 26         }
 27         window.onbeforeunload = function (event) {
 28             return "您的数据未保存,确定退出?";
 29         }
 30         function removeOnbeforeunload() {
 31             window.onbeforeunload = "";
 32         }
 33     </script>
 34 
 35     <h2>编辑书籍</h2>
 36 
 37 
 38     <form enctype="multipart/form-data" method="post">
 39         <div class="panel-body">
 40             <div class="form-group">
 41                 @Html.LabelFor(m => m.ISBN)
 42                 @Html.EditorFor(m => m.ISBN)
 43             </div>
 44             <div class="form-group">
 45                 @Html.LabelFor(m => m.Name)
 46                 @Html.TextBoxFor(m => m.Name, new {@class = "form-control"})
 47             </div>
 48             <div class="form-group">
 49                 @Html.LabelFor(m => m.Author)
 50                 @Html.TextBoxFor(m => m.Author, new {@class = "form-control"})
 51             </div>
 52             <div class="form-group">
 53                 @Html.LabelFor(m => m.Press)
 54                 @Html.TextBoxFor(m => m.Press, new {@class = "form-control"})
 55             </div>
 56             <div class="form-group">
 57                 @Html.LabelFor(m => m.FetchBookNumber)
 58                 @Html.TextBoxFor(m => m.FetchBookNumber, new {@class = "form-control"})
 59             </div>
 60             <div class="form-group">
 61                 @Html.LabelFor(m => m.SoundCassettes)
 62                 @Html.TextBoxFor(m => m.SoundCassettes, new {@class = "form-control"})
 63             </div>
 64             <div class="form-group">
 65                 @Html.LabelFor(m => m.Description)
 66                 @Html.TextAreaFor(m => m.Description, new {@class = "form-control", rows = 5})
 67             </div>
 68             <div class="form-group">
 69                 @Html.LabelFor(m => m.PublishDateTime)
 70                 <div>@Html.EditorFor(m => m.PublishDateTime)</div>
 71             </div>
 72             <div class="form-group">
 73                 @Html.LabelFor(m => m.Version)
 74                 <div>@Html.EditorFor(m => m.Version)</div>
 75             </div>
 76             <div class="form-group">
 77                 <div style="position: relative;">
 78                     <label>Image</label>
 79                     <a class="btn" href="javascript:;">
 80                         选择图片
 81                         <input type="file" name="Image" size="40" accept="image/*"
 82                                style="position: absolute; z-index: 2; top: 0; left: 0; filter: alpha(opacity=0); opacity: 0; background-color: transparent; color: transparent"
 83                                onchange="preview(this)" />
 84                     </a>
 85                     <img style="width: 150px;" class="hidden preview img-thumbnail">
 86                 </div>
 87                 @if (Model.ImageData == null)
 88                 {
 89                 <div class="form-control-static image">No Image</div>
 90                 }
 91                 else
 92                 {
 93                 <img class="img-thumbnail image" style="width: 150px;" src="@Url.Action("GetImage", "BookInfo", new {Model.ISBN})" />
 94                 }
 95             </div>
 96             <br />
 97             <a class="btn btn-primary" asp-action="Books" asp-route-isbn="@Model.ISBN" onclick="return removeOnbeforeunload()">编辑外借书籍信息</a>
 98             <br />
 99             <br />
100             <input type="submit" class="btn btn-success" onclick="return removeOnbeforeunload()"/>
101         </div>
102     </form>

 

结果:

 

 

 

四、查询特定书籍

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

 1         public async Task<IActionResult> 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         }        

 

 结果:

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM