From 4163cce3fa6b86917db12575ca4d8e4851ebbd26 Mon Sep 17 00:00:00 2001 From: jekkinoopy Date: Wed, 3 Jun 2026 15:59:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=BC=94=E5=94=B1=E6=9C=83=E6=90=9C?= =?UTF-8?q?=E5=B0=8B=E6=B8=85=E5=96=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- concert/concert.html | 53 ++++++++++++++++++- concert/search.js | 118 ++++++++++++++++++++++++++++++++----------- 2 files changed, 140 insertions(+), 31 deletions(-) diff --git a/concert/concert.html b/concert/concert.html index 6c54c44..c5d582b 100644 --- a/concert/concert.html +++ b/concert/concert.html @@ -97,15 +97,62 @@ box-shadow: 0 0 10px rgba(255, 204, 0, 0.35); } - #concert-search-result { + .concert-search-result { display: block; width: 100%; margin: 0; + } + + .concert-search-summary { + margin: 0 0 12px; text-align: center; font-size: 0.95rem; line-height: 1.6; color: #fff; } + + .concert-search-hits { + list-style: none; + margin: 0 auto; + padding: 0; + max-width: 720px; + text-align: left; + } + + .concert-search-hits li { + padding: 10px 14px; + margin-bottom: 8px; + background: rgba(255, 248, 223, 0.1); + border: 1px solid rgba(255, 204, 0, 0.35); + border-radius: 12px; + color: #fff; + font-size: 0.88rem; + line-height: 1.5; + } + + .concert-search-hits .hit-track { + display: block; + font-weight: 700; + color: #ffdd00; + } + + .concert-search-hits .hit-meta { + display: block; + margin-top: 4px; + color: rgba(255, 255, 255, 0.78); + font-size: 0.82rem; + } + + .concert-search-hits .hit-more { + text-align: center; + border-style: dashed; + color: rgba(255, 255, 255, 0.85); + } + + .track-item.is-search-hit .track { + color: #ffdd00; + font-weight: 700; + } @@ -128,7 +175,9 @@ - 輸入後按「查詢」,會顯示比對結果。 +
+

輸入後按「查詢」,會在下方列出比對到的曲目。

+
diff --git a/concert/search.js b/concert/search.js index be467e1..42ab4cd 100644 --- a/concert/search.js +++ b/concert/search.js @@ -2,59 +2,119 @@ let myQuery = document.querySelector("#concert-query"); let myMember = document.querySelector("#concert-member"); let myBtn = document.querySelector("#concert-search-btn"); -let mySpan = document.querySelector("#concert-search-result"); +let myResult = document.querySelector("#concert-search-result"); -console.log("myQuery", myQuery); -console.log("myMember", myMember); -console.log("myBtn", myBtn); -console.log("mySpan", mySpan); +const SEARCH_LIST_MAX = 12; -function countMatchingTracks(keyword, member) { +function getTrackContext(trackEl) { + const venueBlock = trackEl.closest(".track-list")?.parentElement; + const tourCard = trackEl.closest(".con-1, .con-2, .con-3, .con-4"); + return { + trackName: trackEl.textContent.trim(), + tourTitle: tourCard?.querySelector(".title")?.textContent.trim().replace(/\s+/g, " ") || "", + location: venueBlock?.querySelector(".location")?.textContent.trim() || "" + }; +} + +function findMatchingTracks(keyword, member) { const tracks = document.querySelectorAll(".track-item .track"); const k = keyword.trim().toLowerCase(); const m = member.trim().toLowerCase(); - let count = 0; + const hits = []; tracks.forEach(function (trackEl) { const text = trackEl.textContent.toLowerCase(); const matchKeyword = !k || text.includes(k); const matchMember = !m || text.includes(m); if (matchKeyword && matchMember) { - count += 1; + hits.push({ + el: trackEl.closest(".track-item"), + ...getTrackContext(trackEl) + }); } }); - return count; + return hits; +} + +function clearSearchHighlights() { + document.querySelectorAll(".track-item.is-search-hit").forEach(function (item) { + item.classList.remove("is-search-hit"); + }); +} + +function renderSearchResults(getQuery, getMember, hits) { + if (!myResult) return; + + clearSearchHighlights(); + + const summary = document.createElement("p"); + summary.className = "concert-search-summary"; + + if (!hits.length) { + summary.textContent = `關鍵字「${getQuery || "(未填)"}」${getMember.trim() ? `、成員「${getMember}」` : ""}:沒有比對到曲目,請換字再試。`; + myResult.replaceChildren(summary); + return; + } + + if (getMember.trim()) { + summary.textContent = `關鍵字「${getQuery || "(未填)"}」、成員「${getMember}」:共 ${hits.length} 筆,以下列出前 ${Math.min(hits.length, SEARCH_LIST_MAX)} 筆。`; + } else { + summary.textContent = `關鍵字「${getQuery}」:共 ${hits.length} 筆,以下列出前 ${Math.min(hits.length, SEARCH_LIST_MAX)} 筆。`; + } + + const list = document.createElement("ul"); + list.className = "concert-search-hits"; + + hits.slice(0, SEARCH_LIST_MAX).forEach(function (hit) { + if (hit.el) hit.el.classList.add("is-search-hit"); + + const li = document.createElement("li"); + const trackLine = document.createElement("span"); + trackLine.className = "hit-track"; + trackLine.textContent = hit.trackName; + + const metaLine = document.createElement("span"); + metaLine.className = "hit-meta"; + metaLine.textContent = [hit.tourTitle, hit.location].filter(Boolean).join(" · "); + + li.appendChild(trackLine); + if (metaLine.textContent) li.appendChild(metaLine); + list.appendChild(li); + }); + + if (hits.length > SEARCH_LIST_MAX) { + const more = document.createElement("li"); + more.className = "hit-more"; + more.textContent = `另有 ${hits.length - SEARCH_LIST_MAX} 筆未顯示,請縮小關鍵字。`; + list.appendChild(more); + } + + myResult.replaceChildren(summary, list); + + const firstHit = hits[0]?.el; + if (firstHit) { + firstHit.scrollIntoView({ behavior: "smooth", block: "nearest" }); + } } // 2.action -if (myBtn && mySpan) { +if (myBtn && myResult) { myBtn.addEventListener("click", function () { - console.log("myBtn", myBtn); - - // 1.抓 input let getQuery = myQuery ? myQuery.value : ""; let getMember = myMember ? myMember.value : ""; - console.log("getQuery", getQuery); - console.log("getMember", getMember); if (!getQuery.trim() && !getMember.trim()) { - mySpan.innerText = "請至少輸入曲目/場次,或成員其中一項。"; + myResult.replaceChildren(); + const summary = document.createElement("p"); + summary.className = "concert-search-summary"; + summary.textContent = "請至少輸入曲目/場次,或成員其中一項。"; + myResult.appendChild(summary); + clearSearchHighlights(); return; } - const count = countMatchingTracks(getQuery, getMember); - - // 2.組成 text - let tmpText; - if (getMember.trim()) { - tmpText = `關鍵字「${getQuery || "(未填)"}」、成員「${getMember}」:在巡迴歌單中共 ${count} 筆曲目。`; - } else { - tmpText = `關鍵字「${getQuery}」:在巡迴歌單中共 ${count} 筆曲目。`; - } - console.log("tmpText", tmpText); - - // 3.顯示在 span - mySpan.innerText = tmpText; + const hits = findMatchingTracks(getQuery, getMember); + renderSearchResults(getQuery, getMember, hits); }); }