105 lines
4.4 KiB
JavaScript
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());
|
|
}
|
|
});
|
|
}
|