mirror of
				https://gitee.com/bookshelfplus/bookshelfplus
				synced 2025-10-31 18:53:09 +08:00 
			
		
		
		
	文件关联书籍功能完成
This commit is contained in:
		| @@ -89,6 +89,12 @@ router.get('/callback/:platform', function (req, res) { | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| router.get('/dashboard/iframe/:page', function (req, res) { | ||||
|     res.render(`dashboard/component/iframe/${req.params.page}`, { | ||||
|         pageUrl: (req._parsedUrl.pathname + "/").replace("//", "/"), | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| router.get('/dashboard/:group/:page/:subpage?', function (req, res) { | ||||
|     // baseTemplate 基于哪个html模板渲染页面 | ||||
|     // pageTemplate 引入这个文件中的页面脚本 | ||||
|   | ||||
| @@ -3,6 +3,12 @@ | ||||
| </p> | ||||
| <h3>文件详情</h3> | ||||
| <div id="file-detail-container"></div> | ||||
| <div id="book-selector-container" style="display: none;"> | ||||
|     <p> | ||||
|         请选择需要绑定的书籍 | ||||
|     </p> | ||||
|     <iframe id="book-selector-iframe" src="" style="width: 100%; height: 55vh;"></iframe> | ||||
| </div> | ||||
| <hr> | ||||
| <h3>关联文件对象</h3> | ||||
| <div id="file-object-container"></div> | ||||
| @@ -43,7 +49,7 @@ | ||||
|                             <tr><td>文件大小</td><td>${stringifyFileSize(data.fileSize)}</td></tr> | ||||
|                             <tr><td>SHA1</td><td>${data.fileSha1}</td></tr> | ||||
|                             <tr><td>文件Id</td><td>${data.id}</td></tr> | ||||
|                             <tr><td>关联书籍Id</td><td>${data.bookId == 0 ? "未关联书籍" : data.bookId}</td></tr> | ||||
|                             <tr><td>关联书籍Id</td><td>${data.bookId == 0 ? "未关联书籍" : data.bookId}  <button onclick="toggleSelectBook();">关联书籍</button></td></tr> | ||||
|                             <tr><td>是否有广告</td><td>${data.advertising ? "是" : "否"}</td></tr> | ||||
|                             <tr><td>是否有水印</td><td>${data.watermark ? "是" : "否"}</td></tr> | ||||
|                             <tr><td>文件创建日期</td><td>${data.fileCreateAt}</td></tr> | ||||
| @@ -109,4 +115,52 @@ | ||||
|             }); | ||||
|     } | ||||
|     getFileObjectInfo(); | ||||
| </script> | ||||
| <script> | ||||
|     window.addEventListener('message', function (event) { | ||||
|         var data = JSON.parse(event.data); | ||||
|         console.log("子页面消息:", data); | ||||
|         var bookId = data.id; | ||||
|         if (data.iframe != "book-selector") return; | ||||
|         if (data.id == null) { | ||||
|             toggleSelectBook(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // 用户选择了书籍,现在需要绑定到文件 | ||||
|         postRequest("/file/bindBook", { token: localStorageUtils.getToken(), fileId: fileId, bookId: bookId }) | ||||
|             .then(function (responseData) { | ||||
|                 var axiosData = responseData.data; | ||||
|                 var status = axiosData.status; | ||||
|                 var data = axiosData.data; | ||||
|                 if (status === "success") { | ||||
|                     console.log(data); | ||||
|                     if(data == "success") { | ||||
|                         // alert("绑定成功!"); | ||||
|                     } else { | ||||
|                         alert("绑定失败!"); | ||||
|                     } | ||||
|                     getFileInfo(); | ||||
|                 } else { | ||||
|                     alert(`出错啦!${data.errMsg}(错误码: ${data.errCode}) `); | ||||
|                 } | ||||
|             }).catch(function (error) { | ||||
|                 console.log(error); | ||||
|                 alert("无法连接到服务器,请检查网络连接!"); | ||||
|             }).finally(function () { | ||||
|                 toggleSelectBook(); | ||||
|             }); | ||||
|     }, false); | ||||
|  | ||||
|     function toggleSelectBook() { | ||||
|         if($("#book-selector-container").css("display") === "none") { | ||||
|             document.getElementById("book-selector-iframe").src = "/dashboard/iframe/book-selector"; | ||||
|             $("#book-selector-container").slideDown(); | ||||
|         } else { | ||||
|             $("#book-selector-container").slideUp(); | ||||
|         } | ||||
|     } | ||||
|     function selectBook() { | ||||
|         $("#book-selector-container").slideDown(); | ||||
|     } | ||||
| </script> | ||||
| @@ -0,0 +1,151 @@ | ||||
| <!DOCTYPE html> | ||||
| <html xmlns="http://www.w3.org/1999/xhtml"> | ||||
| <head> | ||||
|     <% htmlTitle = "选择书籍" %> | ||||
|     <%- include("./header.html"); %> | ||||
|     <style> | ||||
|         .main { | ||||
|             width: 92vw !important; | ||||
|             max-width: initial !important; | ||||
|         } | ||||
|  | ||||
|         #book-table { | ||||
|             width: 100%; | ||||
|             margin-top: 30px; | ||||
|             line-height: 2.3em; | ||||
|         } | ||||
|  | ||||
|         table, tr, th, td { | ||||
|             border: 1px solid black; | ||||
|         } | ||||
|  | ||||
|         tr { | ||||
|             white-space: nowrap; | ||||
|         } | ||||
|  | ||||
|         tr:hover { | ||||
|             background-color: #f5f5f5; | ||||
|         } | ||||
|  | ||||
|         tr a { | ||||
|             cursor: pointer; | ||||
|         } | ||||
|     </style> | ||||
| </head> | ||||
| <body> | ||||
|     <!-- 获取参数 --> | ||||
|     <script src="/assets/javascripts/getParams.js"></script> | ||||
|     <!-- 渲染表格 --> | ||||
|     <script src="/assets/javascripts/renderTable.js"></script> | ||||
|     <main class="main"> | ||||
|         <input id="searchInput" type="text" /> | ||||
|         <input id="searchButton" type="button" value="搜索" /> | ||||
|             <table id="book-table"></table> | ||||
|         </div> | ||||
|         <button onclick="closeWindow();" style="position: fixed; right: 0; top: 0;">×</button> | ||||
|     </main> | ||||
|     <!-- 搜索书籍 --> | ||||
|     <script> | ||||
|         var requestParams = getParams(); | ||||
|         var searchbox = document.getElementById("searchInput"); | ||||
|         var keyword = (requestParams["keyword"] || "").trim(); | ||||
|         search({ | ||||
|             tableElementId: "book-table", | ||||
|             searchText: null, | ||||
|             categoryId: null | ||||
|         }); | ||||
|  | ||||
|         $("#searchButton").click(function () { | ||||
|             search({ | ||||
|                 tableElementId: "book-table", | ||||
|                 searchText: $("#searchInput").val(), | ||||
|                 categoryId: null | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         function search({ tableElementId = "", searchText = "", categoryId = 0 }) { | ||||
|             getRequest("/book/search", { bookName: searchText, categoryId: categoryId }) | ||||
|                 .then(function (responseData) { | ||||
|                     var axiosData = responseData.data; | ||||
|                     var status = axiosData.status; | ||||
|                     var data = axiosData.data; | ||||
|                     if (status === "success") { | ||||
|                         // console.log(data) | ||||
|  | ||||
|                         // 数据进行转换 | ||||
|                         var renderData = []; | ||||
|                         data.forEach(element => { | ||||
|                             var mainDivWidth = 96/*vw*/; // 定义div的宽度(用于计算表格中的数据的显示长度) | ||||
|                             var columnWidth = [20, 15, 10, 15, 5, 35]; | ||||
|                             renderData.push({ | ||||
|                                 编号: `${element.id}`, | ||||
|                                 书名: `<a target="_blank" href="/book?id=${element.id}"> | ||||
|                                             <span class="overflow-omit" style="max-width: ${columnWidth[0] * mainDivWidth / 100}vw; max-height: 2em; margin: 0 auto;"> | ||||
|                                                 ${element.bookName} | ||||
|                                             </span> | ||||
|                                         </a>`, | ||||
|                                 分类: `<a target="_blank" href="/category?id=${element.category.id}"> | ||||
|                                             <span class="overflow-omit" style="max-width: ${columnWidth[1] * mainDivWidth / 100}vw; max-height: 2em; margin: 0 auto;"> | ||||
|                                                 ${element.category.name} | ||||
|                                             </span> | ||||
|                                         </a>`, | ||||
|                                 作者: `${element.author}`, | ||||
|                                 语言: `<span class="overflow-omit" style="max-width: ${columnWidth[2] * mainDivWidth / 100}vw; max-height: 2em; margin: 0 auto;"> | ||||
|                                             ${element.language} | ||||
|                                         </span>`, | ||||
|                                 出版社: `<span class="overflow-omit" style="max-width: ${columnWidth[3] * mainDivWidth / 100}vw; max-height: 2em; margin: 0 auto;"> | ||||
|                                             ${element.publishingHouse} | ||||
|                                         </span>`, | ||||
|                                 来源: `<span class="overflow-omit" style="max-width: ${columnWidth[4] * mainDivWidth / 100}vw; max-height: 2em; margin: 0 auto;"> | ||||
|                                             ${element.publishingHouse} | ||||
|                                         </span>`, | ||||
|                                 管理: `<span class="overflow-omit" style="max-width: ${columnWidth[5] * mainDivWidth / 100}vw; max-height: 2em; margin: 0 auto;"> | ||||
|                                             <a href="javascript:selectItem(${element.id});">选择</a> | ||||
|                                         </span>`, | ||||
|                             }) | ||||
|                         }); | ||||
|  | ||||
|                         if (renderData.length == 0) { | ||||
|                             console.log("没有搜索到相关书籍"); | ||||
|                             function htmlEncode(str) { | ||||
|                                 // refer: https://stackoverflow.com/questions/4183801/escape-html-chracters | ||||
|                                 var div = document.createElement('div'); | ||||
|                                 var txt = document.createTextNode(str); | ||||
|                                 div.appendChild(txt); | ||||
|                                 return div.innerHTML; | ||||
|                             } | ||||
|                             if (searchText && searchText != "") { | ||||
|                                 // | ||||
|                                 renderTable({ data: `没有搜索到与 <span style="color: red;">${htmlEncode(searchText)}</span> 相关的书籍,请换个关键词再试试吧`, tableId: tableElementId, renderTableHead: true }); | ||||
|                             } else if (categoryId && categoryId != 0) { | ||||
|                                 // | ||||
|                                 renderTable({ data: `该分类下暂无电子书`, tableId: tableElementId, renderTableHead: true }); | ||||
|                             } | ||||
|                         } else { | ||||
|                             renderTable({ data: renderData, tableId: tableElementId, renderTableHead: true }); | ||||
|                         } | ||||
|                     } else { | ||||
|                         alert(`出错啦!${data.errMsg} (错误码: ${data.errCode}) `); | ||||
|                     } | ||||
|                 }).catch(function (error) { | ||||
|                     console.log(error); | ||||
|                     alert("无法连接到服务器,请检查网络连接!"); | ||||
|                 }); | ||||
|         } | ||||
|  | ||||
|         function selectItem(id) { | ||||
|             window.parent.postMessage(JSON.stringify({ | ||||
|                 id: id, | ||||
|                 iframe: "book-selector" | ||||
|             }), "*"); | ||||
|         } | ||||
|  | ||||
|         function closeWindow() { | ||||
|             window.parent.postMessage(JSON.stringify({ | ||||
|                 id: null, | ||||
|                 iframe: "book-selector" | ||||
|             }), "*"); | ||||
|         } | ||||
|     </script> | ||||
| </body> | ||||
| </html> | ||||
| @@ -0,0 +1,17 @@ | ||||
| <meta charset="UTF-8"> | ||||
| <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||
| <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> | ||||
|  | ||||
| <title><%= htmlTitle %></title> | ||||
|  | ||||
| <link rel="stylesheet" href="/assets/stylesheets/style.css"> | ||||
| <script src="/assets/lib/jquery/3.6.0/jquery.min.js"></script> | ||||
| <script src="/assets/lib/axios/0.26.1/axios.min.js"></script> | ||||
|  | ||||
| <script src="/assets/javascripts/httpRequestUtils.js"></script> | ||||
| <script src="/assets/javascripts/localStorageUtils.js"></script> | ||||
| <script> | ||||
|     // API地址 | ||||
|     const APIHOST = '<%= global.site.api.prefix %>'; | ||||
|     axios.defaults.baseURL = APIHOST; | ||||
| </script> | ||||
| @@ -30,6 +30,7 @@ public enum BusinessErrorCode implements CommonError { | ||||
|  | ||||
|     // 60000开头为文件、文件对象相关错误定义 | ||||
|     FILE_ALREADY_EXIST(60001, "文件已存在"), | ||||
|     FILE_NOT_EXIST(60002, "文件不存在"), | ||||
|  | ||||
|     // 占位 | ||||
|     PLACE_HOLDER(99999, "这是一个占位符错误"); | ||||
|   | ||||
| @@ -16,6 +16,7 @@ import plus.bookshelf.Config.QCloudCosConfig; | ||||
| import plus.bookshelf.Controller.VO.FileObjectVO; | ||||
| import plus.bookshelf.Controller.VO.FileVO; | ||||
| import plus.bookshelf.Service.Impl.*; | ||||
| import plus.bookshelf.Service.Model.BookModel; | ||||
| import plus.bookshelf.Service.Model.FileModel; | ||||
| import plus.bookshelf.Service.Model.FileObjectModel; | ||||
| import plus.bookshelf.Service.Model.UserModel; | ||||
| @@ -140,6 +141,44 @@ public class FileController extends BaseController { | ||||
|         return CommonReturnType.create(fileVO); | ||||
|     } | ||||
|  | ||||
|     @Autowired | ||||
|     BookServiceImpl bookService; | ||||
|  | ||||
|     @ApiOperation(value = "【管理员】将书籍和文件进行绑定", notes = "") | ||||
|     @RequestMapping(value = "bindBook", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED}) | ||||
|     @ResponseBody | ||||
|     public CommonReturnType bindBook(@RequestParam(value = "token", required = false) String token, | ||||
|                                      @RequestParam(value = "fileId", required = true) Integer fileId, | ||||
|                                      @RequestParam(value = "bookId", required = true) Integer bookId) throws BusinessException { | ||||
|  | ||||
|         UserModel userModel = userService.getUserByToken(redisTemplate, token); | ||||
|         if (userModel == null || !Objects.equals(userModel.getGroup(), "ADMIN")) { | ||||
|             throw new BusinessException(BusinessErrorCode.OPERATION_NOT_ALLOWED, "非管理员用户无权进行此操作"); | ||||
|         } | ||||
|  | ||||
|         if (fileId == null || bookId == null) { | ||||
|             throw new BusinessException(BusinessErrorCode.PARAMETER_VALIDATION_ERROR); | ||||
|         } | ||||
|  | ||||
|         FileModel fileModel = fileService.getFileById(fileId); | ||||
|         if (fileModel == null) { | ||||
|             throw new BusinessException(BusinessErrorCode.FILE_NOT_EXIST); | ||||
|         } | ||||
|  | ||||
|         BookModel bookModel = bookService.getBookById(bookId); | ||||
|         if (bookModel == null) { | ||||
|             throw new BusinessException(BusinessErrorCode.BOOK_NOT_EXIST); | ||||
|         } | ||||
|  | ||||
|         fileModel.setBookId(bookModel.getId()); | ||||
|         Integer affectRows = fileService.updateSelective(fileModel); | ||||
|         if (affectRows > 0) { | ||||
|             return CommonReturnType.create("success"); | ||||
|         } else { | ||||
|             throw new BusinessException(BusinessErrorCode.UNKNOWN_ERROR, "绑定失败,未知错误"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private FileVO convertFileVOFromModel(FileModel fileModel) { | ||||
|         if (fileModel == null) { | ||||
|             return null; | ||||
|   | ||||
| @@ -164,6 +164,22 @@ public class FileServiceImpl implements FileService { | ||||
|         return fileDO; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 更新文件 | ||||
|      * | ||||
|      * @return | ||||
|      */ | ||||
|     @Override | ||||
|     public Integer updateSelective(FileModel fileModel) { | ||||
|         FileDO fileDO = convertFromFileModel(fileModel); | ||||
|  | ||||
|         // 如果文件 id 不对,那么不能更新 | ||||
|         if (fileDO.getId() == 0 || fileDO.getId() == null) { | ||||
|             return 0; | ||||
|         } | ||||
|         return fileDOMapper.updateByPrimaryKeySelective(fileDO); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 取消文件和书籍的关联 | ||||
|      * | ||||
|   | ||||
| @@ -60,6 +60,14 @@ public interface FileService { | ||||
|      */ | ||||
|     Boolean addFile(FileModel fileModel) throws InvocationTargetException, IllegalAccessException; | ||||
|  | ||||
|     /** | ||||
|      * 更新文件对象 | ||||
|      * | ||||
|      * @param fileModel | ||||
|      * @return | ||||
|      */ | ||||
|     Integer updateSelective(FileModel fileModel); | ||||
|  | ||||
|     /** | ||||
|      * 取消文件和书籍的关联 | ||||
|      * | ||||
|   | ||||
		Reference in New Issue
	
	Block a user