Files
web_huangguo2_h5/app/static/js/header.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

634 lines
19 KiB
JavaScript

(function () {
var header = document.querySelector(".site-header");
var drawer = document.querySelector("[data-drawer]");
var openBtn = document.querySelector("[data-drawer-open]");
var userMenu = document.querySelector("[data-user-menu]");
if (header) {
var threshold = 48;
var ticking = false;
function measure() {
document.documentElement.style.setProperty("--site-header-height", header.getBoundingClientRect().height + "px");
}
function update() {
ticking = false;
header.classList.toggle("is-scrolled", window.scrollY > threshold);
}
function onScroll() {
if (ticking) return;
ticking = true;
window.requestAnimationFrame(update);
}
measure();
update();
window.addEventListener("scroll", onScroll, { passive: true });
window.addEventListener("resize", measure);
if ("ResizeObserver" in window) new ResizeObserver(measure).observe(header);
}
// 用户下拉菜单
if (userMenu) {
var toggle = userMenu.querySelector("[data-user-menu-toggle]");
function openUserMenu() {
userMenu.classList.add("is-open");
if (toggle) toggle.setAttribute("aria-expanded", "true");
}
function closeUserMenu() {
userMenu.classList.remove("is-open");
if (toggle) toggle.setAttribute("aria-expanded", "false");
}
function toggleUserMenu(e) {
e.stopPropagation();
if (userMenu.classList.contains("is-open")) {
closeUserMenu();
} else {
openUserMenu();
}
}
if (toggle) {
toggle.addEventListener("click", toggleUserMenu);
}
document.addEventListener("click", function (e) {
if (!userMenu.contains(e.target)) {
closeUserMenu();
}
});
document.addEventListener("keydown", function (e) {
if (e.key === "Escape") closeUserMenu();
});
}
// 左侧抽屉
if (drawer && openBtn) {
var closeEls = drawer.querySelectorAll("[data-drawer-close]");
function openDrawer() {
drawer.classList.add("is-open");
document.body.classList.add("drawer-open");
openBtn.setAttribute("aria-expanded", "true");
}
function closeDrawer() {
drawer.classList.remove("is-open");
document.body.classList.remove("drawer-open");
openBtn.setAttribute("aria-expanded", "false");
}
openBtn.setAttribute("aria-expanded", "false");
openBtn.setAttribute("aria-controls", "site-drawer");
openBtn.addEventListener("click", openDrawer);
closeEls.forEach(function (el) {
el.addEventListener("click", closeDrawer);
});
document.addEventListener("keydown", function (e) {
if (e.key === "Escape" && drawer.classList.contains("is-open")) {
closeDrawer();
}
});
}
function openAccountSwitch(event) {
if (event) event.preventDefault();
if (userMenu) {
userMenu.classList.remove("is-open");
var userToggle = userMenu.querySelector("[data-user-menu-toggle]");
if (userToggle) userToggle.setAttribute("aria-expanded", "false");
}
if (drawer) {
drawer.classList.remove("is-open");
document.body.classList.remove("drawer-open");
if (openBtn) openBtn.setAttribute("aria-expanded", "false");
}
if (typeof window.openAccountSwitchLogin === "function") {
window.openAccountSwitchLogin();
return;
}
if (typeof window.openLoginPop === "function") {
window.openLoginPop();
return;
}
window.location.href = "/login";
}
document.querySelectorAll("[data-account-switch]").forEach(function (btn) {
btn.addEventListener("click", openAccountSwitch);
});
// 搜索表单:走路径 /search/{q},不用 query;同时写入本地搜索历史
var SEARCH_HISTORY_KEY = "hgdj_search_history";
var SEARCH_HISTORY_MAX = 10;
function searchResultsPath(q) {
q = String(q || "").trim();
if (!q) return "/search";
return "/search/" + encodeURIComponent(q).replace(/%20/g, "%20");
}
function readSearchHistory() {
try {
var raw = window.localStorage.getItem(SEARCH_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, SEARCH_HISTORY_MAX);
} catch (_) {
return [];
}
}
function writeSearchHistory(list) {
try {
window.localStorage.setItem(
SEARCH_HISTORY_KEY,
JSON.stringify(list.slice(0, SEARCH_HISTORY_MAX))
);
} catch (_) {
/* ignore quota / private mode */
}
}
function pushSearchHistory(q) {
q = String(q || "").trim();
if (!q) return;
var list = readSearchHistory().filter(function (item) {
return item !== q;
});
list.unshift(q);
writeSearchHistory(list);
}
function clearSearchHistory() {
try {
window.localStorage.removeItem(SEARCH_HISTORY_KEY);
} catch (_) {
/* ignore */
}
}
document.querySelectorAll("form[data-path-search]").forEach(function (form) {
form.addEventListener("submit", function (event) {
event.preventDefault();
var input = form.querySelector('input[type="search"], input[name="q"]');
var q = input ? String(input.value || "").trim() : "";
if (!q) {
if (input) input.focus();
return;
}
pushSearchHistory(q);
window.location.href = searchResultsPath(q);
});
var input = form.querySelector("[data-header-search-input], input[type='search'], input[name='q']");
var clear = form.querySelector("[data-header-search-clear]");
if (input && clear) {
function syncClear() {
clear.hidden = !String(input.value || "").trim();
}
input.addEventListener("input", syncClear);
clear.addEventListener("click", function () {
input.value = "";
syncClear();
input.dispatchEvent(new Event("input", { bubbles: true }));
input.focus();
});
syncClear();
}
// PC header 搜索下拉:聚焦展开历史 + 热搜;输入时切换联想
var dropdown = form.querySelector("[data-header-search-dropdown]");
if (!dropdown || !input) return;
var historyBox = form.querySelector("[data-header-search-history]");
var historyChips = form.querySelector("[data-header-history-chips]");
var historyClear = form.querySelector("[data-header-history-clear]");
var idleBox = form.querySelector("[data-header-search-idle]");
var suggestBox = form.querySelector("[data-header-search-suggest]");
var closeTimer = null;
var suggestTimer = null;
var suggestSeq = 0;
var PLACEHOLDER_IMG = "/static/images/placeholder.png?v=2";
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="is-hit">' +
escapeHtml(hit) +
"</span>" +
escapeHtml(after)
);
}
function renderHistory() {
if (!historyBox || !historyChips) return;
var list = readSearchHistory();
historyChips.innerHTML = "";
if (!list.length) {
historyBox.hidden = true;
return;
}
historyBox.hidden = false;
list.forEach(function (term) {
var link = document.createElement("a");
link.className = "search-dropdown__chip";
link.href = searchResultsPath(term);
link.textContent = term;
historyChips.appendChild(link);
});
}
function showIdlePanel() {
if (idleBox) idleBox.hidden = false;
if (suggestBox) {
suggestBox.hidden = true;
suggestBox.innerHTML = "";
}
}
function showSuggestPanel() {
if (idleBox) idleBox.hidden = true;
if (suggestBox) suggestBox.hidden = false;
}
function renderSuggest(data, keyword) {
if (!suggestBox) return;
var html = "";
var feature = data && data.feature;
var items = (data && data.items) || [];
if (feature && feature.url) {
html +=
'<article class="search-dropdown__feature">' +
'<a class="search-dropdown__feature-cover" href="' +
escapeHtml(feature.url) +
'">' +
'<img decoding="async" loading="lazy" src="' +
PLACEHOLDER_IMG +
'" data-src="' +
escapeHtml(feature.cover || "") +
'" alt="' +
escapeHtml(feature.title || "") +
'">' +
"</a>" +
'<div class="search-dropdown__feature-copy">' +
'<h2 class="search-dropdown__feature-title">' +
highlightHTML(feature.title, keyword) +
"</h2>" +
(feature.meta
? '<p class="search-dropdown__feature-meta">' + escapeHtml(feature.meta) + "</p>"
: "") +
"</div>" +
'<a class="search-dropdown__feature-play" href="' +
escapeHtml(feature.url) +
'">' +
'<img decoding="async" loading="lazy" src="' +
PLACEHOLDER_IMG +
'" data-src="/static/images/play_hollow.svg" alt="播放">播放</a>' +
"</article>";
}
if (items.length) {
html += '<div class="search-dropdown__suggest-list">';
items.forEach(function (item) {
if (!item || !item.url) return;
html +=
'<a class="search-dropdown__suggest-item" href="' +
escapeHtml(item.url) +
'" data-suggest-query="' +
escapeHtml(item.query || item.title || keyword) +
'">' +
'<img decoding="async" loading="lazy" class="search-dropdown__suggest-icon" src="' +
PLACEHOLDER_IMG +
'" data-src="/static/images/search.svg" alt="">' +
'<span class="search-dropdown__suggest-text">' +
highlightHTML(item.title || item.query || "", keyword) +
"</span>" +
'<span class="search-dropdown__suggest-type">' +
escapeHtml(item.kind || "") +
"</span>" +
"</a>";
});
html += "</div>";
} else if (!feature) {
html +=
'<p class="search-dropdown__suggest-empty">暂无相关结果,回车搜索“' +
escapeHtml(keyword) +
'”</p>';
}
suggestBox.innerHTML = html;
showSuggestPanel();
if (typeof window.initLazyImages === "function") {
window.initLazyImages(suggestBox, { eager: true });
}
}
function fetchSuggest(keyword) {
var q = String(keyword || "").trim();
if (!q || !suggestBox) {
showIdlePanel();
openDropdown();
return;
}
var seq = ++suggestSeq;
showSuggestPanel();
suggestBox.innerHTML =
'<p class="search-dropdown__suggest-empty">搜索中…</p>';
openDropdown();
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 scheduleSuggest() {
var q = String(input.value || "").trim();
if (suggestTimer) clearTimeout(suggestTimer);
if (!q) {
suggestSeq += 1;
showIdlePanel();
openDropdown();
return;
}
suggestTimer = setTimeout(function () {
suggestTimer = null;
fetchSuggest(q);
}, 220);
}
function openDropdown() {
if (closeTimer) {
clearTimeout(closeTimer);
closeTimer = null;
}
var q = String(input.value || "").trim();
if (q) {
if (suggestBox && suggestBox.hidden) {
scheduleSuggest();
}
dropdown.hidden = false;
form.classList.add("is-open");
input.setAttribute("aria-expanded", "true");
return;
}
showIdlePanel();
renderHistory();
var hasHistory = historyBox && !historyBox.hidden;
var hasHot = !!(idleBox && idleBox.querySelector(".search-dropdown__hot"));
if (!hasHistory && !hasHot) {
dropdown.hidden = true;
form.classList.remove("is-open");
input.setAttribute("aria-expanded", "false");
return;
}
dropdown.hidden = false;
form.classList.add("is-open");
input.setAttribute("aria-expanded", "true");
}
function closeDropdown() {
dropdown.hidden = true;
form.classList.remove("is-open");
input.setAttribute("aria-expanded", "false");
}
function scheduleClose() {
if (closeTimer) clearTimeout(closeTimer);
closeTimer = setTimeout(function () {
closeTimer = null;
if (!form.contains(document.activeElement)) {
closeDropdown();
}
}, 120);
}
input.addEventListener("focus", openDropdown);
input.addEventListener("click", openDropdown);
input.addEventListener("input", function () {
if (clear) clear.hidden = !String(input.value || "").trim();
scheduleSuggest();
});
input.addEventListener("blur", scheduleClose);
dropdown.addEventListener("mousedown", function (event) {
// 点击下拉内链接/按钮时先阻止 input blur 抢先关闭
event.preventDefault();
});
dropdown.addEventListener("click", function (event) {
var chip = event.target.closest
? event.target.closest(
".search-dropdown__chip, .search-dropdown__link, .search-dropdown__feature-play, .search-dropdown__feature-cover, .search-dropdown__suggest-item"
)
: null;
if (chip && chip.href) {
var q = "";
if (chip.classList.contains("search-dropdown__chip")) {
q = String(chip.textContent || "").trim();
} else if (chip.classList.contains("search-dropdown__link")) {
var name = chip.querySelector(".search-dropdown__name");
if (name) q = String(name.textContent || "").trim();
} else if (chip.getAttribute("data-suggest-query")) {
q = String(chip.getAttribute("data-suggest-query") || "").trim();
} else {
q = String(input.value || "").trim();
}
if (q) pushSearchHistory(q);
window.location.href = chip.href;
}
});
if (historyClear) {
historyClear.addEventListener("click", function (event) {
event.preventDefault();
event.stopPropagation();
clearSearchHistory();
renderHistory();
openDropdown();
input.focus();
});
}
document.addEventListener("keydown", function (event) {
if (event.key === "Escape" && !dropdown.hidden) {
closeDropdown();
input.blur();
}
});
document.addEventListener("mousedown", function (event) {
if (dropdown.hidden) return;
if (form.contains(event.target)) return;
closeDropdown();
});
});
var PLACEHOLDER = "/static/images/placeholder.png?v=2";
var DEFAULT_AVATAR = "/static/images/profile/avatar.png";
function usableAvatar(url) {
var u = String(url || "").trim();
if (!u) return "";
if (/^https?:\/\//i.test(u) || u.indexOf("/static/") === 0) return u;
return "";
}
function setAvatarImg(img, url) {
if (!img) return;
var src = usableAvatar(url);
img.removeAttribute("data-decrypted");
img.removeAttribute("data-decrypt-failed");
if (src && typeof window.shouldDecryptImage === "function" && window.shouldDecryptImage(src)) {
img.classList.add("lazy");
img.src = PLACEHOLDER;
img.setAttribute("data-src", src);
img.setAttribute("data-lazy-eager", "1");
} else {
img.classList.remove("lazy");
img.removeAttribute("data-src");
img.removeAttribute("data-lazy-eager");
img.src = src || DEFAULT_AVATAR;
}
}
function refreshAvatarDecrypt(root) {
var scope = root || document;
if (typeof window.initLazyImages === "function") {
window.initLazyImages(scope, { eager: true });
return;
}
if (typeof window.loadLazyNode !== "function") return;
scope
.querySelectorAll(
"img.user-chip__avatar.lazy[data-src], img.user-menu__avatar.lazy[data-src], img.drawer__avatar.lazy[data-src], img.play-composer__avatar-img.lazy[data-src]"
)
.forEach(function (img) {
window.loadLazyNode(img);
});
}
function formatRecharge(raw) {
var n = Math.floor(Number(raw));
if (!isFinite(n) || n < 0) n = 0;
var s = String(n);
if (s.length <= 3) return s;
var out = [];
var rem = s.length % 3;
if (rem) out.push(s.slice(0, rem));
for (var i = rem; i < s.length; i += 3) out.push(s.slice(i, i + 3));
return out.join(",");
}
function syncHeaderUser() {
var session =
window.CookieUtil && typeof CookieUtil.getAuthSession === "function"
? CookieUtil.getAuthSession()
: null;
if (!session) return;
var avatar = usableAvatar(session.avatar);
document.querySelectorAll("[data-header-avatar]").forEach(function (img) {
if (avatar) setAvatarImg(img, avatar);
});
var name = String(session.nickName || session.accountNo || "").trim();
if (name) {
document.querySelectorAll(".user-chip__name, .user-menu__meta strong, .drawer__meta strong").forEach(function (el) {
el.textContent = name;
});
}
if (session.userId) {
document.querySelectorAll(".user-menu__meta span, .drawer__meta span").forEach(function (el) {
if (/^ID\b/.test(String(el.textContent || "").trim())) {
el.textContent = "ID " + session.userId;
}
});
}
if (Object.prototype.hasOwnProperty.call(session, "recharge") && session.recharge !== "") {
var text = formatRecharge(session.recharge);
document.querySelectorAll("[data-header-recharge]").forEach(function (el) {
el.textContent = text;
});
}
refreshAvatarDecrypt(document);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", syncHeaderUser);
} else {
syncHeaderUser();
}
document.addEventListener("auth:login", syncHeaderUser);
document.addEventListener("auth:logout", syncHeaderUser);
window.refreshAvatarDecrypt = refreshAvatarDecrypt;
window.setAvatarImg = setAvatarImg;
window.syncHeaderUser = syncHeaderUser;
})();