mirror of
https://gitee.com/bookshelfplus/bookshelfplus
synced 2025-09-01 22:53:29 +08:00
前端书籍搜索页面、手机详情页面;前端优化;后端异常处理;添加项目Gitee和GitHub仓库地址;搜索按钮样式美化
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
"scripts": {
|
||||
"start": "node app",
|
||||
"dev": "nodemon",
|
||||
"test": "set NODE_ENV=production & nodemon",
|
||||
"prod": "set NODE_ENV=production & pm2 start app.js --name bookshelfplus-frontend",
|
||||
"clean": "node cleanup.js"
|
||||
},
|
||||
|
1
bookshelfplus-frontend/public/assets/image/svg/gitee.svg
Normal file
1
bookshelfplus-frontend/public/assets/image/svg/gitee.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2839" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M512 1024C229.222 1024 0 794.778 0 512S229.222 0 512 0s512 229.222 512 512-229.222 512-512 512z m259.149-568.883h-290.74a25.293 25.293 0 0 0-25.292 25.293l-0.026 63.206c0 13.952 11.315 25.293 25.267 25.293h177.024c13.978 0 25.293 11.315 25.293 25.267v12.646a75.853 75.853 0 0 1-75.853 75.853h-240.23a25.293 25.293 0 0 1-25.267-25.293V417.203a75.853 75.853 0 0 1 75.827-75.853h353.946a25.293 25.293 0 0 0 25.267-25.292l0.077-63.207a25.293 25.293 0 0 0-25.268-25.293H417.152a189.62 189.62 0 0 0-189.62 189.645V771.15c0 13.977 11.316 25.293 25.294 25.293h372.94a170.65 170.65 0 0 0 170.65-170.65V480.384a25.293 25.293 0 0 0-25.293-25.267z" fill="#0056ff" p-id="2840"></path></svg>
|
After Width: | Height: | Size: 967 B |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2487" width="200" height="200"><path d="M511.542857 14.057143C228.914286 13.942857 0 242.742857 0 525.142857 0 748.457143 143.2 938.285714 342.628571 1008c26.857143 6.742857 22.742857-12.342857 22.742858-25.371429v-88.571428c-155.085714 18.171429-161.371429-84.457143-171.771429-101.6C172.571429 756.571429 122.857143 747.428571 137.714286 730.285714c35.314286-18.171429 71.314286 4.571429 113.028571 66.171429 30.171429 44.685714 89.028571 37.142857 118.857143 29.714286 6.514286-26.857143 20.457143-50.857143 39.657143-69.485715-160.685714-28.8-227.657143-126.857143-227.657143-243.428571 0-56.571429 18.628571-108.571429 55.2-150.514286-23.314286-69.142857 2.171429-128.342857 5.6-137.142857 66.4-5.942857 135.428571 47.542857 140.8 51.771429 37.714286-10.171429 80.8-15.542857 129.028571-15.542858 48.457143 0 91.657143 5.6 129.714286 15.885715 12.914286-9.828571 76.914286-55.771429 138.628572-50.171429 3.314286 8.8 28.228571 66.628571 6.285714 134.857143 37.028571 42.057143 55.885714 94.514286 55.885714 151.2 0 116.8-67.428571 214.971429-228.571428 243.314286a145.714286 145.714286 0 0 1 43.542857 104v128.571428c0.914286 10.285714 0 20.457143 17.142857 20.457143 202.4-68.228571 348.114286-259.428571 348.114286-484.685714 0-282.514286-229.028571-511.2-511.428572-511.2z" fill="#0056ff" p-id="2488"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
@@ -108,6 +108,7 @@ hr {
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
|
||||
.searchBox #searchInput:focus {
|
||||
padding: 0 18px;
|
||||
height: 35px;
|
||||
@@ -117,6 +118,32 @@ hr {
|
||||
.searchBox #searchButton {
|
||||
width: 80px;
|
||||
height: 40px;
|
||||
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
color: #000;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
margin: 0;
|
||||
padding: 0 16px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
/* width: auto; */
|
||||
|
||||
margin-left: 10px;
|
||||
transition: 0.25s;
|
||||
}
|
||||
|
||||
.searchBox #searchButton:hover {
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 网站Slogan */
|
||||
@@ -164,3 +191,12 @@ hr {
|
||||
grid-template-columns: 10vw 1fr 0 10vw;
|
||||
}
|
||||
}
|
||||
|
||||
/* 全局样式 */
|
||||
/* 超出内容显示为... */
|
||||
.overflow-omit {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
}
|
||||
|
@@ -5,8 +5,8 @@
|
||||
</head>
|
||||
<body>
|
||||
<%- include("./component/navbar.html"); %>
|
||||
<div class="main" align="center">
|
||||
<h1>书栖网</h1>
|
||||
<div class="main">
|
||||
<h1>关于 · 书栖网</h1>
|
||||
<p>一个完全免费无门槛的计算机类电子书下载网站</p>
|
||||
|
||||
<h2>随便唠唠</h2>
|
||||
|
@@ -2,20 +2,87 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<%- include("./component/header.html"); %>
|
||||
<style>
|
||||
.main {
|
||||
width: 80vw !important;
|
||||
max-width: initial !important;
|
||||
}
|
||||
|
||||
#bookImage {
|
||||
/* width: 100%; */
|
||||
height: auto;
|
||||
max-height: 300px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<%- include("./component/navbar.html"); %>
|
||||
<main class="main">
|
||||
<h1><%= title %></h1>
|
||||
<div id="container">
|
||||
|
||||
</div>
|
||||
</main>
|
||||
<%- include("./component/footer.html"); %>
|
||||
|
||||
<!-- 获取参数 -->
|
||||
<script src="./assets/javascripts/getParams.js"></script>
|
||||
<script>
|
||||
getRequest("/book/get", { id: 1 })
|
||||
var requestParams = getParams();
|
||||
var searchbox = document.getElementById("searchInput");
|
||||
var bookId = Number(requestParams["id"]) ?? "";
|
||||
if (bookId === "") {
|
||||
location.href = "/search";
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
getRequest("/book/get", { id: bookId })
|
||||
.then(function (response) {
|
||||
console.log(response.data);
|
||||
var axiosData = response.data;
|
||||
var status = axiosData.status;
|
||||
var data = axiosData.data;
|
||||
if (status === "success") {
|
||||
console.log(data)
|
||||
data.thumbnail = "https://img14.360buyimg.com/pop/jfs/t1/141705/31/25225/853702/61a85f89Ef68c838b/929ded96a4a7579e.png";
|
||||
if(data.description == ""){
|
||||
data.description = "暂无描述";
|
||||
}
|
||||
document.getElementById("container").innerHTML =`
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<img id="bookImage" src="${data.thumbnail}" alt="书籍缩略图">
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
<h1>${data.bookName}</h1>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
<a href="/download/?bookId=${data.id}">下载这本书</a>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
<p>作者:${data.author}</p>
|
||||
<p>所属分类:<a href="/category?id=${data.category.id}">${data.category.name}</a></p>
|
||||
<p>语言:${data.language}</p>
|
||||
<p>出版社:${data.publishingHouse}</p>
|
||||
<p></p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>书本介绍</h2>
|
||||
<p>${data.description}</p>
|
||||
<h2>版权信息</h2>
|
||||
<p>${data.copyright}</p>
|
||||
</div>`;
|
||||
|
||||
// 渲染后重新获取一次字体
|
||||
fontmin(getPageText());
|
||||
}
|
||||
else {
|
||||
alert(
|
||||
`查询失败
|
||||
错误码: ${data.errCode}
|
||||
错误信息: ${data.errMsg}`);
|
||||
}
|
||||
}).catch(function (error) {
|
||||
console.log(error);
|
||||
});
|
||||
|
@@ -4,9 +4,15 @@
|
||||
书栖网 • 2021-2022
|
||||
<br>
|
||||
<small>
|
||||
<br>
|
||||
❤️本项目是开源项目,项目的发展离不开大家的支持,欢迎前往项目仓库点亮Star支持我们❤️
|
||||
<br>
|
||||
<a target="_blank" href="https://gitee.com/bookshelfplus/bookshelfplus"><img src="./assets/image/svg/gitee.svg" style="height: 1.05em; vertical-align: middle;"/><span style="vertical-align: middle; margin-left: 3px;">码云</span></a>
|
||||
·
|
||||
<a target="_blank" href="https://github.com/bookshelfplus/bookshelfplus"><img src="./assets/image/svg/github.svg" style="height: 1.08em; vertical-align: middle;"/><span style="vertical-align: middle; margin-left: 3px;">GitHub</span></a>
|
||||
<br>
|
||||
<a href="/status">网站状态检测</a>
|
||||
</small>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -43,7 +49,7 @@
|
||||
|
||||
$(document).ready(function () {
|
||||
var allText = getPageText();
|
||||
console.log(allText);
|
||||
// console.log(allText);
|
||||
fontmin(allText);
|
||||
});
|
||||
</script>
|
@@ -1,13 +1,14 @@
|
||||
<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><%= title; %></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>
|
||||
<title><%= typeof title !=='undefined' ? title : "前端服务出现异常"; %></title>
|
||||
|
||||
<script src="./assets/javascripts/httpRequest.js"></script>
|
||||
<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/httpRequest.js"></script>
|
||||
<script>
|
||||
// API地址
|
||||
const APIHOST = '<%= global.site.api.prefix %>';
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<div class="searchBox">
|
||||
<input id="searchInput" type="text" placeholder="只需两步:搜索、下载 就这么简单" />
|
||||
<input id="searchButton" type="button" value="搜一下" />
|
||||
<input id="searchInput" type="text" placeholder="只需两步:搜索、下载 就这么简单" /><!--
|
||||
|
||||
--><input id="searchButton" type="button" value="搜一下" />
|
||||
</div>
|
||||
<script>
|
||||
// /**
|
||||
|
@@ -5,9 +5,11 @@
|
||||
</head>
|
||||
<body>
|
||||
<%- include("./component/navbar.html"); %>
|
||||
<h1><%= message %></h1>
|
||||
<h2><%= error.status %></h2>
|
||||
<pre><%= error.stack %></pre>
|
||||
<div class="main">
|
||||
<h1><%= message %></h1>
|
||||
<h2><%= error.status %></h2>
|
||||
<pre><%= error.stack %></pre>
|
||||
</div>
|
||||
<%- include("./component/footer.html"); %>
|
||||
</body>
|
||||
</html>
|
@@ -10,9 +10,9 @@
|
||||
|
||||
#result-table {
|
||||
width: 100%;
|
||||
border: 1px solid black;
|
||||
/* border: 1px solid black; */
|
||||
margin-top: 30px;
|
||||
line-height: 1.8em;
|
||||
line-height: 2.3em;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
@@ -23,13 +23,6 @@
|
||||
cursor: pointer;
|
||||
/* cursor: alias; */
|
||||
}
|
||||
|
||||
.overflow-omit {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -45,6 +38,8 @@
|
||||
<%- include("./component/footer.html"); %>
|
||||
<!-- 获取参数 -->
|
||||
<script src="./assets/javascripts/getParams.js"></script>
|
||||
<!-- 渲染表格 -->
|
||||
<script src="./assets/javascripts/renderTable.js"></script>
|
||||
<script>
|
||||
var requestParams = getParams();
|
||||
var searchbox = document.getElementById("searchInput");
|
||||
@@ -52,53 +47,66 @@
|
||||
searchbox.value = keyword;
|
||||
if (keyword === "") {
|
||||
searchbox.focus();
|
||||
} else {
|
||||
search(keyword);
|
||||
}
|
||||
</script>
|
||||
<!-- 渲染表格 -->
|
||||
<script src="./assets/javascripts/renderTable.js"></script>
|
||||
<script>
|
||||
getRequest("/book/search", { bookName: encodeURIComponent(searchbox.value) })
|
||||
.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 = 80/*vw*/; // 定义div的宽度(用于计算表格中的数据的显示长度)
|
||||
var columnWidth = [23, 17, 30, 10, 20];
|
||||
renderData.push({
|
||||
书名: ` <a href="/book?id=${element.category.id}">
|
||||
<span class="overflow-omit" style="max-width: ${columnWidth[0] * mainDivWidth / 100}vw; max-height: 2em;">
|
||||
${element.bookName}
|
||||
</span>
|
||||
</a>`,
|
||||
分类: ` <a href="/category?id=${element.category.id}">
|
||||
<span class="overflow-omit" style="max-width: ${columnWidth[1] * mainDivWidth / 100}vw; max-height: 2em;">
|
||||
${element.category.name}
|
||||
</span>
|
||||
</a>`,
|
||||
简介: ` <span class="overflow-omit" style="max-width: ${columnWidth[2] * mainDivWidth / 100}vw; max-height: 2em;">
|
||||
${element.description}
|
||||
</span>`,
|
||||
语言: ` <span class="overflow-omit" style="max-width: ${columnWidth[3] * mainDivWidth / 100}vw; max-height: 2em;">
|
||||
${element.language}
|
||||
</span>`,
|
||||
出版社: `<span class="overflow-omit" style="max-width: ${columnWidth[4] * mainDivWidth / 100}vw; max-height: 2em;">
|
||||
${element.publishingHouse}
|
||||
</span>`,
|
||||
})
|
||||
});
|
||||
renderTable({ data: renderData, tableId: "result-table", renderTableHead: true });
|
||||
// renderTable({ data: data, tableId: "origin-table", renderTableHead: true });
|
||||
}
|
||||
else {
|
||||
alert("搜索失败");
|
||||
}
|
||||
});
|
||||
function search(searchboxValue) {
|
||||
getRequest("/book/search", { bookName: encodeURIComponent(searchboxValue) })
|
||||
.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 = 80/*vw*/; // 定义div的宽度(用于计算表格中的数据的显示长度)
|
||||
var columnWidth = [23, 17, 30, 10, 20];
|
||||
renderData.push({
|
||||
书名: ` <a href="/book?id=${element.category.id}">
|
||||
<span class="overflow-omit" style="max-width: ${columnWidth[0] * mainDivWidth / 100}vw; max-height: 2em; margin: 0 auto;">
|
||||
${element.bookName}
|
||||
</span>
|
||||
</a>`,
|
||||
分类: ` <a 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>`,
|
||||
简介: ` <span class="overflow-omit" style="max-width: ${columnWidth[2] * mainDivWidth / 100}vw; max-height: 2em; margin: 0 auto;">
|
||||
${element.description}
|
||||
</span>`,
|
||||
语言: ` <span class="overflow-omit" style="max-width: ${columnWidth[3] * mainDivWidth / 100}vw; max-height: 2em; margin: 0 auto;">
|
||||
${element.language}
|
||||
</span>`,
|
||||
出版社: `<span class="overflow-omit" style="max-width: ${columnWidth[4] * mainDivWidth / 100}vw; max-height: 2em; margin: 0 auto;">
|
||||
${element.publishingHouse}
|
||||
</span>`,
|
||||
})
|
||||
});
|
||||
if(renderData.length == 0) {
|
||||
renderData.push({
|
||||
书名: "没有搜索到相关书籍",
|
||||
分类: "",
|
||||
简介: "",
|
||||
语言: "",
|
||||
出版社: "",
|
||||
})
|
||||
}
|
||||
renderTable({ data: renderData, tableId: "result-table", renderTableHead: true });
|
||||
// renderTable({ data: data, tableId: "origin-table", renderTableHead: true });
|
||||
|
||||
// 渲染后重新获取一次字体
|
||||
fontmin(getPageText());
|
||||
}
|
||||
else {
|
||||
alert(`出错啦!${data.errMsg} (错误码: ${data.errCode}) `);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@@ -53,16 +53,5 @@
|
||||
</main>
|
||||
<%- include("./component/footer.html"); %>
|
||||
<script async="true" src="./assets/javascripts/siteStatus.js"></script>
|
||||
<script>
|
||||
// function checkTimeOff() {
|
||||
// var oldScriptDom = document.getElementById("timeCalibrationScript");
|
||||
// if (oldScriptDom) oldScriptDom.parentNode.removeChild(oldScriptDom);
|
||||
|
||||
// oldScriptDom = document.createElement("script");
|
||||
// oldScriptDom.id = "timeCalibrationScript";
|
||||
// oldScriptDom.src = "./assets/javascripts/timeCalibration.js?" + Date.now() + "-" + new Date().getTime();
|
||||
// document.body.appendChild(oldScriptDom);
|
||||
// }
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@@ -2,9 +2,18 @@ package plus.bookshelf.Controller.Controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
// import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import plus.bookshelf.Common.Error.BusinessErrorCode;
|
||||
import plus.bookshelf.Common.Error.BusinessException;
|
||||
import plus.bookshelf.Common.Response.CommonReturnType;
|
||||
import plus.bookshelf.Common.Response.CommonReturnTypeStatus;
|
||||
import plus.bookshelf.Service.Model.UserModel;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -41,4 +50,24 @@ public class BaseController {
|
||||
// redisTemplate.opsForValue().set(uuidToken, userModel);
|
||||
return uuidToken;
|
||||
}
|
||||
|
||||
// 定义ExceptionHandler解决未被Controller层吸收的Exception
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
@ResponseBody
|
||||
public Object handlerException(HttpServletRequest request, Exception ex) {
|
||||
|
||||
HashMap<Object, Object> responseData = new HashMap<>();
|
||||
|
||||
if (ex instanceof BusinessException) {
|
||||
BusinessException businessException = (BusinessException) ex;
|
||||
responseData.put("errCode", businessException.getErrCode());
|
||||
responseData.put("errMsg", businessException.getErrMsg());
|
||||
} else {
|
||||
responseData.put("errCode", BusinessErrorCode.UNKNOWN_ERROR.getErrCode());
|
||||
responseData.put("errMsg", BusinessErrorCode.UNKNOWN_ERROR.getErrMsg());
|
||||
}
|
||||
|
||||
return CommonReturnType.create(responseData, CommonReturnTypeStatus.FAILED);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user