/* =================================== 搜索控制器 =================================== */ import { companiesData, activeCities, activeProvinces } from '../data.js'; import { UIUtils } from './UIUtils.js'; export class SearchController { constructor(inputElement, callbacks) { this.input = inputElement; this.callbacks = callbacks; // { onSelectProvince, onSelectCity, onSelectCompany } // 创建下拉容器 this.suggestionsContainer = this.createSuggestionsContainer(); this.input.parentElement.appendChild(this.suggestionsContainer); // 当前选中的索引 this.selectedIndex = -1; this.currentResults = []; // 初始化事件监听 this.init(); } // 创建下拉建议容器 createSuggestionsContainer() { const container = document.createElement('div'); container.className = 'search-suggestions hidden'; return container; } // 初始化事件监听 init() { // 输入事件 - 使用防抖 const debouncedSearch = UIUtils.debounce((e) => { const keyword = e.target.value.trim(); if (keyword) { this.performSearch(keyword); } else { this.hideSuggestions(); } }, 300); this.input.addEventListener('input', debouncedSearch); // 键盘事件 this.input.addEventListener('keydown', (e) => this.handleKeyboard(e)); // 失去焦点时延迟关闭(给点击建议留时间) this.input.addEventListener('blur', () => { setTimeout(() => this.hideSuggestions(), 200); }); // 获得焦点时如果有内容则重新搜索 this.input.addEventListener('focus', () => { const keyword = this.input.value.trim(); if (keyword) { this.performSearch(keyword); } }); } // 执行搜索 performSearch(keyword) { const results = this.search(keyword); this.currentResults = results; this.selectedIndex = -1; this.renderSuggestions(results); } // 模糊搜索算法 search(keyword) { const lowerKeyword = keyword.toLowerCase(); const results = { provinces: [], cities: [], companies: [] }; // 搜索省份 activeProvinces.forEach(province => { const score = this.calculateMatchScore(lowerKeyword, province.toLowerCase()); if (score > 0) { results.provinces.push({ name: province, score }); } }); // 搜索城市 activeCities.forEach(city => { const score = this.calculateMatchScore(lowerKeyword, city.toLowerCase()); if (score > 0) { results.cities.push({ name: city, score }); } }); // 搜索企业 companiesData.forEach(company => { const nameScore = this.calculateMatchScore(lowerKeyword, company.name.toLowerCase()); const shortNameScore = this.calculateMatchScore(lowerKeyword, company.shortName.toLowerCase()); const score = Math.max(nameScore, shortNameScore); if (score > 0) { results.companies.push({ name: company.shortName, fullName: company.name, city: company.city, data: company, score }); } }); // 按分数排序 results.provinces.sort((a, b) => b.score - a.score); results.cities.sort((a, b) => b.score - a.score); results.companies.sort((a, b) => b.score - a.score); // 限制结果数量:省份2条、城市3条、企业5条,总计最多8条 results.provinces = results.provinces.slice(0, 2); results.cities = results.cities.slice(0, 3); results.companies = results.companies.slice(0, 5); // 再次确保总数不超过8条 const total = results.provinces.length + results.cities.length + results.companies.length; if (total > 8) { // 优先保证省份和城市,然后企业 const remaining = 8 - results.provinces.length - results.cities.length; if (remaining < results.companies.length) { results.companies = results.companies.slice(0, remaining); } } return results; } // 计算匹配分数 calculateMatchScore(keyword, target) { // 去除"省"、"市"等后缀进行匹配 const cleanTarget = target.replace(/省$|市$/g, ''); const cleanKeyword = keyword.replace(/省$|市$/g, ''); // 完全匹配 if (cleanTarget === cleanKeyword || target === keyword) { return 100; } // 开头匹配 if (cleanTarget.startsWith(cleanKeyword) || target.startsWith(keyword)) { return 80; } // 包含匹配 if (cleanTarget.includes(cleanKeyword) || target.includes(keyword)) { return 60; } return 0; } // 渲染搜索建议 renderSuggestions(results) { const { provinces, cities, companies } = results; const total = provinces.length + cities.length + companies.length; if (total === 0) { this.suggestionsContainer.innerHTML = '
未找到相关结果
'; this.showSuggestions(); return; } let html = ''; // 渲染省份 provinces.forEach((item, index) => { html += this.createSuggestionItem('province', item.name, null, index); }); // 渲染城市 cities.forEach((item, index) => { const globalIndex = provinces.length + index; html += this.createSuggestionItem('city', item.name, null, globalIndex); }); // 渲染企业 companies.forEach((item, index) => { const globalIndex = provinces.length + cities.length + index; html += this.createSuggestionItem('company', item.name, item.city, globalIndex); }); this.suggestionsContainer.innerHTML = html; this.showSuggestions(); // 绑定点击事件 this.bindClickEvents(); } // 创建单个建议项 createSuggestionItem(type, name, city, index) { const icons = { province: '🗺️', city: '🏙️', company: '🏢' }; const cityTag = city ? `${city}` : ''; return `
${icons[type]} ${name} ${cityTag}
`; } // 绑定点击事件 bindClickEvents() { const items = this.suggestionsContainer.querySelectorAll('.search-item'); items.forEach(item => { item.addEventListener('click', () => { const index = parseInt(item.dataset.index); this.selectItem(index); }); }); } // 选择项目 selectItem(index) { const { provinces, cities, companies } = this.currentResults; const provincesCount = provinces.length; const citiesCount = cities.length; let type, item; if (index < provincesCount) { type = 'province'; item = provinces[index]; } else if (index < provincesCount + citiesCount) { type = 'city'; item = cities[index - provincesCount]; } else { type = 'company'; item = companies[index - provincesCount - citiesCount]; } this.executeSelection(type, item); this.hideSuggestions(); this.input.value = ''; } // 执行选择 executeSelection(type, item) { switch (type) { case 'province': if (this.callbacks.onSelectProvince) { this.callbacks.onSelectProvince(item.name); } break; case 'city': if (this.callbacks.onSelectCity) { this.callbacks.onSelectCity(item.name); } break; case 'company': if (this.callbacks.onSelectCompany) { this.callbacks.onSelectCompany(item.data); } break; } } // 键盘事件处理 handleKeyboard(e) { const items = this.suggestionsContainer.querySelectorAll('.search-item'); const totalItems = items.length; if (totalItems === 0) return; switch (e.key) { case 'ArrowDown': e.preventDefault(); this.selectedIndex = (this.selectedIndex + 1) % totalItems; this.updateSelection(items); break; case 'ArrowUp': e.preventDefault(); this.selectedIndex = (this.selectedIndex - 1 + totalItems) % totalItems; this.updateSelection(items); break; case 'Enter': e.preventDefault(); if (this.selectedIndex >= 0 && this.selectedIndex < totalItems) { this.selectItem(this.selectedIndex); } break; case 'Escape': this.hideSuggestions(); this.input.blur(); break; } } // 更新选中状态 updateSelection(items) { items.forEach((item, index) => { if (index === this.selectedIndex) { item.classList.add('active'); item.scrollIntoView({ block: 'nearest' }); } else { item.classList.remove('active'); } }); } // 显示建议 showSuggestions() { this.suggestionsContainer.classList.remove('hidden'); } // 隐藏建议 hideSuggestions() { this.suggestionsContainer.classList.add('hidden'); this.selectedIndex = -1; } }