Files
blog-app/static/js/pages/search.js
2026-08-17 17:11:27 +08:00

105 lines
4.4 KiB
JavaScript

/* 全局搜索页逻辑: 防抖搜索 + 项目/release/issue 三段结果.
* renderProjectCard 由模板内联 module 注入.
*/
import { debounce, fetchJson, escapeHtml, truncate } from "../utils.js";
export function initSearchPage(renderProjectCard) {
function renderResults(data) {
const hint = document.getElementById("searchHint");
const results = document.getElementById("searchResults");
const total = data.projects.length + data.releases.length + data.issues.length;
if (!total) {
results.classList.add("d-none");
hint.classList.remove("d-none");
hint.textContent = "没有找到匹配结果";
return;
}
hint.classList.add("d-none");
results.classList.remove("d-none");
document.getElementById("projCount").textContent = data.projects.length;
document.getElementById("relCount").textContent = data.releases.length;
document.getElementById("issueCount").textContent = data.issues.length;
document.getElementById("resultProjects").innerHTML = data.projects.length
? data.projects
.map((p) =>
renderProjectCard(p, { showSubtitle: false, showForks: false })
)
.join("")
: '<div class="empty-hint">无匹配项目</div>';
document.getElementById("resultReleases").innerHTML = data.releases.length
? data.releases
.map(
(r) => `
<li class="list-group-item d-flex justify-content-between align-items-center">
<span>
<strong>${escapeHtml(r.tag_name || "")}</strong>
<span class="text-muted"> ${escapeHtml(truncate(r.name, 60))}</span>
</span>
<a class="btn btn-sm btn-outline-primary" href="/projects/${r.project_id}">查看项目</a>
</li>`
)
.join("")
: '<li class="list-group-item empty-hint">无匹配 release</li>';
document.getElementById("resultIssues").innerHTML = data.issues.length
? data.issues
.map(
(i) => `
<li class="list-group-item d-flex justify-content-between align-items-center">
<span>
<span class="badge ${i.state === "open" ? "badge-state-open" : "badge-state-closed"} me-1">${escapeHtml(i.state || "")}</span>
${escapeHtml(truncate(i.title, 80))}
</span>
<a class="btn btn-sm btn-outline-primary" href="/projects/${i.project_id}">查看项目</a>
</li>`
)
.join("")
: '<li class="list-group-item empty-hint">无匹配 issue</li>';
}
async function doSearch(keyword) {
const hint = document.getElementById("searchHint");
const results = document.getElementById("searchResults");
if (!keyword) {
results.classList.add("d-none");
hint.classList.remove("d-none");
hint.textContent = "输入关键词开始搜索";
return;
}
try {
hint.classList.remove("empty-hint-error");
const data = await fetchJson(`/api/search?q=${encodeURIComponent(keyword)}`);
renderResults(data);
} catch (err) {
console.error("搜索失败:", err);
results.classList.add("d-none");
hint.classList.remove("d-none");
hint.classList.add("empty-hint-error");
hint.innerHTML =
'加载失败, <button type="button" class="btn btn-sm btn-outline-danger" id="searchRetryBtn">点击重试</button>';
const retryBtn = document.getElementById("searchRetryBtn");
if (retryBtn) retryBtn.addEventListener("click", () => doSearch(keyword));
}
}
const debouncedSearch = debounce(doSearch, 300);
document.addEventListener("DOMContentLoaded", () => {
const input = document.getElementById("searchInput");
input.addEventListener("input", (e) => debouncedSearch(e.target.value.trim()));
// Support ?q= from the navbar search box.
const params = new URLSearchParams(window.location.search);
const q = params.get("q");
if (q) {
input.value = q;
doSearch(q.trim());
}
});
}