1const Search = { 2 init() { 3 // è·åæææç´¢æ¡å ç´ 4 this.searchInputs = document.querySelectorAll('.search-input'); 5 this.searchBtns = document.querySelectorAll('.search-btn'); 6 7 // 为æ¯ä¸ªæç´¢æ¡ç»å®äºä»¶ 8 this.searchInputs.forEach((input, index) => { 9 const btn = this.searchBtns[index]; 10 const container = input.closest('.form-search-wrapper'); 11 const tip = container.querySelector('.search-tip'); 12 const searchContainer = container.querySelector('.search-container'); 13 14 // è¾å ¥äºä»¶ 15 input.addEventListener('input', this.debounce(() => { 16 const keyword = input.value.trim(); 17 if (keyword.length >= 2) { 18 this.performSearch(keyword, container); 19 } else { 20 tip.style.display = 'block'; 21 searchContainer.style.display = 'none'; 22 } 23 }, 300)); 24 25 // ç¹å»æç´¢æé® 26 if (btn) { 27 btn.addEventListener('click', () => { 28 const keyword = input.value.trim(); 29 if (keyword.length >= 2) { 30 this.performSearch(keyword, container); 31 } 32 }); 33 } 34 }); 35 }, 36 37 async performSearch(keyword, container) { 38 // ç§»é¤ç»å½æ£æ¥ï¼ç´æ¥è¿è¡æç´¢ 39 const searchContainer = container.querySelector('.search-container'); 40 const tip = container.querySelector('.search-tip'); 41 42 try { 43 const response = await fetch(`/api/search?keyword=${encodeURIComponent(keyword)}`); 44 const data = await response.json(); 45 46 if (data.success) { 47 tip.style.display = 'none'; 48 searchContainer.style.display = 'block'; 49 this.renderResults(data.results, container); 50 } 51 } catch (error) { 52 console.error('æç´¢åºé:', error); 53 } 54 }, 55 56 renderResults(results, container) { 57 const hasArticles = results.articles && results.articles.length > 0; 58 const hasRouters = results.routers && results.routers.length > 0; 59 const searchContainer = container.querySelector('.search-container'); 60 61 // å¦ææ²¡æä»»ä½ç»æ 62 if (!hasArticles && !hasRouters) { 63 searchContainer.innerHTML = ` 64 <div class="d-flex flex-column justify-content-center align-items-center p-4"> 65 <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="currentColor" class="bi bi-search" viewBox="0 0 16 16"> 66 <path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"/> 67 </svg> 68 <p class="mt-3 text-muted">ææ æç´¢ç»æ</p> 69 </div>`; 70 return; 71 } 72 73 // æå»ºæç´¢ç»æHTML 74 let resultsHTML = ''; 75 76 // æç« ç»æ 77 if (hasArticles) { 78 resultsHTML += this.renderArticles(results.articles); 79 } 80 81 // è·¯ç±å¨ç»æ 82 if (hasRouters) { 83 resultsHTML += this.renderRouters(results.routers); 84 } 85 86 // æ´æ°æç´¢ç»æå®¹å¨ 87 searchContainer.innerHTML = resultsHTML; 88 89 // 妿æè·¯ç±å¨ç»æï¼ç»å®æ·»å 对æ¯äºä»¶ 90 if (hasRouters) { 91 this.addSvgClickListeners(); 92 } 93 }, 94 95 renderArticles(articles) { 96 return ` 97 <div class="article-results"> 98 <div class="d-flex align-items-center bg-light rounded p-2 mb-3"> 99 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-file-text me-2" viewBox="0 0 16 16"> 100 <path d="M5 4a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1H5zm-.5 2.5A.5.5 0 0 1 5 6h6a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zM5 8a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1H5zm0 2a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1H5z"/> 101 <path d="M2 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2zm10-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1z"/> 102 </svg>
103 <span class="fs-5 fw-semibold text-secondary" style="font-weight: 500;padding-left: 5px;">æç« </span> 104 </div> 105 <div class="article-list"> 106 ${articles.map(article => ` 107 <div class="search-item"> 108 <a href="/article/${article.id * 3}" class="text-dark"> 109 ${article.title} 110 </a> 111 </div> 112 `).join('')} 113 </div> 114 </div>`; 115 }, 116 117 renderRouters(routers) { 118 return ` 119 <div class="router-results"> 120 <div class="d-flex align-items-center bg-light rounded p-2 mb-3"> 121 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-router me-2" viewBox="0 0 16 16"> 122 <path d="M5.525 3.025a3.5 3.5 0 0 1 4.95 0 .5.5 0 1 0 .707-.707 4.5 4.5 0 0 0-6.364 0 .5.5 0 0 0 .707.707Z"/> 123 <path d="M6.94 4.44a1.5 1.5 0 0 1 2.12 0 .5.5 0 0 0 .708-.708 2.5 2.5 0 0 0-3.536 0 .5.5 0 0 0 .707.707ZM2.5 11a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1Zm4.5-.5a.5.5 0 1 0 1 0 .5.5 0 0 0-1 0Zm2.5.5a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1Zm1.5-.5a.5.5 0 1 0 1 0 .5.5 0 0 0-1 0Zm2 0a.5.5 0 1 0 1 0 .5.5 0 0 0-1 0Z"/> 124 <path d="M2.974 2.342a.5.5 0 1 0-.948.316L3.806 8H1.5A1.5 1.5 0 0 0 0 9.5v2A1.5 1.5 0 0 0 1.5 13H2a.5.5 0 0 0 .5.5h10a.5.5 0 0 0 .5-.5.5.5 0 0 0 .5.5h.5a1.5 1.5 0 0 0 1.5-1.5v-2A1.5 1.5 0 0 0 14.5 8h-2.306l1.78-5.342a.5.5 0 1 0-.948-.316L11.14 8H4.86L2.974 2.342ZM14.5 9a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h13Z"/> 125 </svg> 126 <span class="fs-5 fw-semibold text-secondary" style="font-weight: 500;padding-left: 5px;">è·¯ç±å¨</span> 127 </div> 128 <div class="router-list"> 129 ${routers.map(router => ` 130 <div class="position-relative"> 131 <a href="/Router/${router.brand_url}/${router.link}" 132 class="d-flex flex-row p-3 bg-light border rounded-4 mb-3 shadow-sm text-decoration-none" 133 style="cursor: pointer; transition: all 0.2s ease-in-out;" 134 onmouseover="this.style.backgroundColor='#f8f9fa'; this.style.borderColor='#dee2e6';" 135 onmouseout="this.style.backgroundColor='#f8f9fa'; this.style.borderColor='#dee2e6';"> 136 <div class="col-5"> 137 <img src="${router.img}" alt="${router.model}" class="img-fluid"> 138 </div> 139 <div class="col-7 d-flex flex-column justify-content-between" style="padding-left: 0;padding-right: 0;"> 140 <div class="mr-md-2"> 141 <div class="pt-2 mb-2 fs-6 text-dark" style="font-size: .9375rem; font-weight: 500;"> 142 ${router.model} 143 </div> 144 <div class="mb-2"> 145 ${router.tags.map((tag, i) => ` 146 <span class="badge rounded-pill bg-${['primary', 'warning', 'success'][i % 3]} small" 147 style="font-size: 0.735rem;"> 148 ${tag} 149 </span> 150 `).join(' ')} 151 </div> 152 </div> 153 <div class="d-flex justify-content-between align-items-center"> 154 <span class="text-muted small"> 155 åèä»· 156 <span class="price-symbol" style="font-size: 0.8rem;">
156ï¿¥</span> 157 <span class="price-buy" style="font-size: 0.9rem;">${router.price}</span> 158 </span> 159 </div> 160 </div> 161 </a> 162 163 <!-- æ·»å å¯¹æ¯æé® --> 164 <div class="position-absolute" style="top: 20px; right: 10px;"> 165 <svg xmlns="http://www.w3.org/2000/svg" 166 width="26" height="26" 167 fill="currentColor" 168 class="bi bi-plus-circle-fill mySvg" 169 viewBox="0 0 16 16" 170 data-router-id="${router.id}" 171 style="fill: #808080; cursor: pointer;"> 172 <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8.5 4.5a.5.5 0 0 0-1 0v3h-3a.5.5 0 0 0 0 1h3v3a.5.5 0 0 0 1 0v-3h3a.5.5 0 0 0 0-1h-3v-3z"/> 173 </svg> 174 </div> 175 176 <!-- äº¬ä¸æé® - åªå¨ååæææ¶æ¾ç¤º --> 177 ${router.jd_url ? ` 178 <div class="position-absolute" style="bottom: 20px; right: 10px;"> 179 <a href="${router.jd_url}" 180 target="_blank" 181 rel="nofollow" 182 onclick="event.stopPropagation();" 183 style="display: flex; align-items: center;"> 184 <img src="/svg/jd7.svg" style="height: 24px;"> 185 </a> 186 </div> 187 ` : ''} 188 </div> 189 `).join('')} 190 </div> 191 </div>`; 192 }, 193 194 addSvgClickListeners() { 195 document.querySelectorAll('.mySvg').forEach(svg => { 196 svg.addEventListener('click', async (event) => { 197 event.preventDefault(); 198 event.stopPropagation(); 199 200 const routerId = Number(svg.getAttribute('data-router-id')); 201 try { 202 // æ£æ¥æ¯å¦å·²ç»æ·»å 203 const isAdded = svg.classList.contains('added'); 204 205 if (isAdded) { 206 // å¦æå·²æ·»å ï¼åç§»é¤ 207 await window.COMPARE.removeFromCompare(routerId); 208 window.COMPARE.updateSvgElement(routerId, 'remove'); 209 } else { 210 // å¦ææªæ·»å ï¼åæ·»å 211 await window.COMPARE.addToCompare(routerId); 212 window.COMPARE.updateSvgElement(routerId, 'added'); 213 } 214 215 // æ´æ°å¯¹æ¯å表 216 await window.COMPARE.updateCompareList(); 217 } catch (error) { 218 console.error('Error:', error); 219 } 220 }); 221 }); 222 }, 223 224 debounce(func, wait) { 225 let timeout; 226 return function executedFunction(...args) { 227 const later = () => { 228 clearTimeout(timeout); 229 func(...args); 230 }; 231 clearTimeout(timeout); 232 timeout = setTimeout(later, wait); 233 }; 234 } 235}; 236 237// åå§åæç´¢åè½ 238document.addEventListener('DOMContentLoaded', function() { 239 Search.init(); 240});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.