PageSourceSearch

https://mao.fan/js/search.js

js mao.fan collected 2026-10-02 07:43:59 UTC 12,256 bytes, 240 lines download raw bytes

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.