Files
web_huangguo2_h5/app/static/js/mobile-search.js
T
谢宇宁andClaude Opus 5.5 0fbdba5ccf Initial commit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 11:47:47 +07:00

321 lines
9.0 KiB
JavaScript

(function () {
var HISTORY_KEY = "hgdj_search_history";
var HISTORY_MAX = 10;
var PLACEHOLDER_IMG = "/static/images/placeholder.png?v=2";
var input = document.querySelector("[data-mobile-search-input]");
var form = input && input.closest("form");
var clear = document.querySelector("[data-mobile-search-clear]");
var defaultView = document.querySelector("[data-search-default]");
var suggestions = document.querySelector("[data-search-suggestions]");
var suggestList = document.querySelector("[data-search-suggest-list]");
var history = document.querySelector("[data-search-history]");
var historyChips = document.querySelector("[data-history-chips]");
var historyClear = document.querySelector("[data-history-clear]");
if (!input || !form || !clear || !defaultView || !suggestions || !suggestList) return;
var suggestTimer = null;
var suggestSeq = 0;
function searchResultsPath(q) {
q = String(q || "").trim();
if (!q) return "/search";
return "/search/" + encodeURIComponent(q);
}
function escapeHtml(text) {
return String(text || "")
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
function highlightHTML(text, keyword) {
var raw = String(text || "");
var key = String(keyword || "").trim();
if (!raw) return "";
if (!key) return escapeHtml(raw);
var lowerRaw = raw.toLowerCase();
var lowerKey = key.toLowerCase();
var idx = lowerRaw.indexOf(lowerKey);
if (idx < 0) return escapeHtml(raw);
var before = raw.slice(0, idx);
var hit = raw.slice(idx, idx + key.length);
var after = raw.slice(idx + key.length);
return (
escapeHtml(before) +
'<span class="mobile-search__highlight">' +
escapeHtml(hit) +
"</span>" +
escapeHtml(after)
);
}
function readHistory() {
try {
var raw = window.localStorage.getItem(HISTORY_KEY);
var list = raw ? JSON.parse(raw) : [];
if (!Array.isArray(list)) return [];
return list
.map(function (item) {
return String(item || "").trim();
})
.filter(Boolean)
.slice(0, HISTORY_MAX);
} catch (_) {
return [];
}
}
function writeHistory(list) {
try {
window.localStorage.setItem(HISTORY_KEY, JSON.stringify(list.slice(0, HISTORY_MAX)));
} catch (_) {
/* ignore quota / private mode */
}
}
function pushHistory(q) {
q = String(q || "").trim();
if (!q) return;
var list = readHistory().filter(function (item) {
return item !== q;
});
list.unshift(q);
writeHistory(list);
}
function clearHistory() {
try {
window.localStorage.removeItem(HISTORY_KEY);
} catch (_) {
/* ignore */
}
}
function renderHistory() {
if (!history || !historyChips) return;
var list = readHistory();
historyChips.innerHTML = "";
if (!list.length) {
history.hidden = true;
return;
}
history.hidden = false;
list.forEach(function (term) {
var link = document.createElement("a");
link.className = "mobile-search__chip";
link.href = searchResultsPath(term);
link.textContent = term;
historyChips.appendChild(link);
});
}
function renderSuggest(data, keyword) {
var html = "";
var feature = data && data.feature;
var items = (data && data.items) || [];
if (feature && feature.url) {
html +=
'<article class="mobile-search__suggestion-feature">' +
'<a class="mobile-search__feature-cover" href="' +
escapeHtml(feature.url) +
'" aria-label="' +
escapeHtml(feature.title || "") +
'">' +
'<img decoding="async" loading="lazy" src="' +
PLACEHOLDER_IMG +
'" data-src="' +
escapeHtml(feature.cover || "") +
'" alt="' +
escapeHtml(feature.title || "") +
'">' +
"</a>" +
'<div class="mobile-search__feature-copy">' +
'<h2 class="mobile-search__feature-title">' +
highlightHTML(feature.title, keyword) +
"</h2>" +
(feature.meta
? '<p class="mobile-search__feature-meta">' + escapeHtml(feature.meta) + "</p>"
: "") +
"</div>" +
'<a class="mobile-search__feature-play" href="' +
escapeHtml(feature.url) +
'">' +
'<img decoding="async" loading="lazy" src="' +
PLACEHOLDER_IMG +
'" data-src="/static/images/play_hollow.svg" alt="播放">播放</a>' +
"</article>";
}
items.forEach(function (item) {
if (!item || !item.url) return;
html +=
'<a class="mobile-search__suggestion" href="' +
escapeHtml(item.url) +
'" data-suggest-query="' +
escapeHtml(item.query || item.title || keyword) +
'">' +
'<img decoding="async" loading="lazy" class="mobile-search__suggestion-icon" src="' +
PLACEHOLDER_IMG +
'" data-src="/static/images/search.svg" alt="搜索">' +
'<span class="mobile-search__suggestion-text">' +
highlightHTML(item.title || item.query || "", keyword) +
"</span>" +
'<span class="mobile-search__suggestion-type">' +
escapeHtml(item.kind || "") +
"</span>" +
"</a>";
});
if (!html) {
html =
'<p class="mobile-search__suggest-empty">暂无相关结果,可直接搜索“' +
escapeHtml(keyword) +
'”</p>';
}
suggestList.innerHTML = html;
if (typeof window.initLazyImages === "function") {
window.initLazyImages(suggestList, { eager: true });
}
}
function fetchSuggest(keyword) {
var q = String(keyword || "").trim();
if (!q) {
suggestList.innerHTML = "";
return;
}
var seq = ++suggestSeq;
suggestList.innerHTML =
'<p class="mobile-search__suggest-empty">搜索中…</p>';
var req;
if (window.HttpRequest && typeof window.HttpRequest.get === "function") {
req = window.HttpRequest.get("/api/web/search/suggest", {
params: { keyword: q },
});
} else {
req = fetch(
"/api/web/search/suggest?keyword=" + encodeURIComponent(q),
{ credentials: "include", headers: { Accept: "application/json" } }
).then(function (res) {
return res.json();
});
}
Promise.resolve(req)
.then(function (data) {
if (seq !== suggestSeq) return;
renderSuggest(data || {}, q);
})
.catch(function () {
if (seq !== suggestSeq) return;
renderSuggest(
{
feature: null,
items: [
{
title: q,
kind: "搜索",
query: q,
url: searchResultsPath(q),
},
],
},
q
);
});
}
function syncPanels() {
var query = input.value.trim();
var hasQuery = query.length > 0;
defaultView.hidden = hasQuery;
suggestions.hidden = !hasQuery;
clear.hidden = !hasQuery;
if (!hasQuery) {
suggestSeq += 1;
suggestList.innerHTML = "";
}
}
function scheduleSuggest() {
syncPanels();
var q = input.value.trim();
if (suggestTimer) clearTimeout(suggestTimer);
if (!q) return;
suggestTimer = setTimeout(function () {
suggestTimer = null;
fetchSuggest(q);
}, 220);
}
input.addEventListener("input", scheduleSuggest);
clear.addEventListener("click", function () {
input.value = "";
input.dispatchEvent(new Event("input"));
input.focus();
});
form.addEventListener("submit", function (event) {
event.preventDefault();
var q = input.value.trim();
if (!q) {
input.focus();
return;
}
pushHistory(q);
window.location.href = searchResultsPath(q);
});
if (historyClear && history) {
historyClear.addEventListener("click", function () {
clearHistory();
renderHistory();
});
}
// 从联想 / 热搜跳转搜索结果时也写入本地历史
document.addEventListener("click", function (event) {
var link = event.target.closest("a[data-suggest-query], a[href^='/search/']");
if (!link || link.hasAttribute("data-search-close")) return;
var q = String(link.getAttribute("data-suggest-query") || "").trim();
if (!q) {
var href = link.getAttribute("href") || "";
var match = href.match(/^\/search\/([^/?#]+)/);
if (!match) return;
try {
q = decodeURIComponent(match[1]);
} catch (_) {
q = match[1];
}
}
if (q) pushHistory(q);
});
document.querySelectorAll("[data-search-close]").forEach(function (link) {
link.addEventListener("click", function (event) {
if (!document.referrer) return;
try {
if (new URL(document.referrer).origin === window.location.origin) {
event.preventDefault();
window.history.back();
}
} catch (_) {
/* Use the link destination when the referrer cannot be read. */
}
});
});
renderHistory();
syncPanels();
if (input.value.trim()) {
fetchSuggest(input.value.trim());
}
})();