初始化多多畅职企业内推平台项目

功能特性:
- 3D地球动画与中国地图可视化
- 省份/城市/企业搜索功能
- 308家企业数据展示
- 响应式设计(PC端和移动端)
- 企业详情页面与业务板块展示
- 官网新闻轮播图

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
KQL
2025-11-22 19:38:14 +08:00
commit ab50931347
41 changed files with 56412 additions and 0 deletions

328
js/ui/SearchController.js Normal file
View File

@@ -0,0 +1,328 @@
/* ===================================
搜索控制器
=================================== */
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;
}
}