feat: add global search bar, fix search cover blur, shelf title search + category tags

1. Fix hdCover() to also replace s_ prefix (search results use s_ not t6_)
2. Add persistent search bar below navbar, switches to discover tab on search
3. Shelf search now supports book title and author search (new keyword param)
4. Add category tag pills above book grid, click to filter by category
5. Update /api/shelf backend: keyword param for title/author, category for exact match
6. Mobile responsive styles for search bar and category tags
This commit is contained in:
admin committed 2026-07-17 12:12:23 +08:00
1 parent 7c918140b3
commit 4c916eceb7
4 files changed
+194 -9

No files matched your search

+110
View File
@@ -271,6 +271,103 @@ body {
[data-theme="dark"] .theme-toggle .icon-moon { display: none; }
[data-theme="dark"] .theme-toggle { color: var(--toggle-icon-sun); }
/* ===== 顶部常驻搜索框 ===== */
.global-search-bar {
position: sticky;
top: 60px;
z-index: 90;
background: var(--nav-bg);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border-bottom: 1px solid var(--border);
padding: 10px 0;
transition: background-color 0.3s ease, border-color 0.3s ease;
}
.global-search-wrapper {
display: flex;
align-items: center;
gap: 0;
max-width: 600px;
margin: 0 auto;
position: relative;
}
.global-search-icon {
position: absolute;
left: 14px;
font-size: 15px;
opacity: 0.5;
pointer-events: none;
}
.global-search-input {
flex: 1;
padding: 9px 16px 9px 40px;
border: 1px solid var(--border);
border-right: none;
border-radius: var(--radius-sm) 0 0 var(--radius-sm);
font-size: 14px;
background: var(--bg-card);
color: var(--text-primary);
transition: border-color 0.2s, box-shadow 0.2s;
}
.global-search-input:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-bg);
}
.global-search-btn {
padding: 9px 20px;
border: 1px solid var(--accent);
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
background: var(--accent);
color: #fff;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: background 0.2s;
}
.global-search-btn:hover {
background: var(--accent-dark);
}
/* ===== 书架分类标签 ===== */
.shelf-categories {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 20px;
}
.shelf-category-tag {
padding: 5px 14px;
border: 1px solid var(--border);
border-radius: 20px;
font-size: 13px;
color: var(--text-secondary);
background: var(--bg-card);
cursor: pointer;
transition: all 0.2s;
white-space: nowrap;
}
.shelf-category-tag:hover {
border-color: var(--accent);
color: var(--accent);
background: var(--accent-bg);
}
.shelf-category-tag.active {
border-color: var(--accent);
color: #fff;
background: var(--accent);
font-weight: 500;
}
/* ===== Hero 区 ===== */
.hero-section {
position: relative;
@@ -1730,6 +1827,19 @@ body {
.nav-update {
display: none;
}
.global-search-wrapper {
max-width: 100%;
}
.shelf-search {
width: 140px;
}
.shelf-categories {
gap: 6px;
}
.shelf-category-tag {
font-size: 12px;
padding: 4px 10px;
}
.back-to-top {
bottom: 20px;
right: 20px;
+61 -3
View File
@@ -34,9 +34,17 @@ function getChartColors() {
// ===== 工具函数 =====
/**
* 升级微信读书封面图 URL 到高清晰度版本。
* 微信读书 CDN 的封面图通过文件名前缀区分分辨率:
* t6_ = 低清缩略图(~150px,Retina 屏模糊)
* s_ = 超低清缩略图(~90px,搜索结果常用,更模糊)
* t8_ = 高清版(~500px,适合 2x/3x 屏幕)
* 将 t6_ 和 s_ 前缀统一升级为 t8_ 高清版。
*/
function hdCover(url) {
if (!url) return "";
return url.replace("/t6_", "/t8_");
return url.replace(/\/(t6_|s_)/, "/t8_");
}
async function fetchAPI(path) {
@@ -147,6 +155,53 @@ function scrollToTop() {
window.scrollTo({ top: 0, behavior: "smooth" });
}
// ===== 全局搜索(顶部常驻搜索框) =====
function globalSearch() {
const keyword = document.getElementById("globalSearchInput").value.trim();
if (!keyword) return;
// 切换到推荐标签页
switchTab("discover");
// 切换到搜索子标签
switchDiscoverTab("search");
// 填入搜索词并执行搜索
document.getElementById("searchInput").value = keyword;
doSearch();
}
// ===== 书架分类标签 =====
let shelfCategories = [];
let shelfActiveCategory = ""; // 空字符串表示"全部"
async function loadShelfCategories() {
try {
const data = await fetchAPI("/api/shelf?page=1&per_page=1");
shelfCategories = data.categories || [];
renderShelfCategories();
} catch (e) {
console.error("加载分类失败:", e);
}
}
function renderShelfCategories() {
const container = document.getElementById("shelfCategories");
if (!container) return;
let html = `<button class="shelf-category-tag ${shelfActiveCategory === "" ? "active" : ""}" onclick="filterByCategory('')">全部</button>`;
html += shelfCategories
.map((cat) => `<button class="shelf-category-tag ${shelfActiveCategory === cat ? "active" : ""}" onclick="filterByCategory('${cat.replace(/'/g, "\\'")}')">${cat}</button>`)
.join("");
container.innerHTML = html;
}
function filterByCategory(category) {
shelfActiveCategory = category;
renderShelfCategories();
loadShelf(1);
}
// ===== 初始化 =====
document.addEventListener("DOMContentLoaded", async () => {
@@ -155,6 +210,7 @@ document.addEventListener("DOMContentLoaded", async () => {
loadOverview();
loadTopBooks();
loadShelfCategories();
loadShelf(1);
loadNotebooks();
loadRecommend(0);
@@ -500,9 +556,11 @@ let shelfCurrentPage = 1;
async function loadShelf(page) {
shelfCurrentPage = page;
const search = document.getElementById("shelfSearch").value;
const keyword = document.getElementById("shelfSearch").value.trim();
const finished = document.getElementById("finishedOnly").checked ? "1" : "0";
const params = new URLSearchParams({ page: page, per_page: 60, category: search, finished: finished });
const params = new URLSearchParams({ page: page, per_page: 60, finished: finished });
if (keyword) params.set("keyword", keyword);
if (shelfActiveCategory) params.set("category", shelfActiveCategory);
try {
const data = await fetchAPI(`/api/shelf?${params}`);