Files
DDCZ/js/ui/SearchController.js
KQL ab50931347 初始化多多畅职企业内推平台项目
功能特性:
- 3D地球动画与中国地图可视化
- 省份/城市/企业搜索功能
- 308家企业数据展示
- 响应式设计(PC端和移动端)
- 企业详情页面与业务板块展示
- 官网新闻轮播图

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-22 19:38:14 +08:00

329 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ===================================
搜索控制器
=================================== */
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 = '<div class="search-no-result">未找到相关结果</div>';
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 ? `<span class="search-city-tag">${city}</span>` : '';
return `
<div class="search-item" data-index="${index}" data-type="${type}">
<span class="search-icon">${icons[type]}</span>
<span class="search-name">${name}</span>
${cityTag}
</div>
`;
}
// 绑定点击事件
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;
}
}