2025-11-22 19:38:14 +08:00
|
|
|
|
/* ===================================
|
|
|
|
|
|
应用主入口 - 模块编排与路由控制
|
|
|
|
|
|
=================================== */
|
|
|
|
|
|
|
|
|
|
|
|
import { SceneManager } from './scene/SceneManager.js';
|
|
|
|
|
|
import { MapInterface } from './ui/MapInterface.js';
|
|
|
|
|
|
import { ListInterface } from './ui/ListInterface.js';
|
|
|
|
|
|
import { DetailInterface } from './ui/DetailInterface.js';
|
|
|
|
|
|
import { SearchController } from './ui/SearchController.js';
|
|
|
|
|
|
import { UIUtils } from './ui/UIUtils.js';
|
|
|
|
|
|
|
|
|
|
|
|
class App {
|
|
|
|
|
|
constructor() {
|
|
|
|
|
|
// 3D平台 DOM 引用
|
2025-12-04 16:03:31 +08:00
|
|
|
|
this.appContainer = document.getElementById('app-container');
|
2025-11-22 19:38:14 +08:00
|
|
|
|
this.canvasContainer = document.getElementById('canvas-container');
|
|
|
|
|
|
this.mapInterface = document.getElementById('map-interface');
|
|
|
|
|
|
this.listInterface = document.getElementById('list-interface');
|
|
|
|
|
|
this.detailInterface = document.getElementById('detail-interface');
|
|
|
|
|
|
|
|
|
|
|
|
// UI 元素
|
|
|
|
|
|
this.uiLayer = document.getElementById('ui-layer');
|
|
|
|
|
|
this.hint = document.querySelector('.instruction-hint');
|
|
|
|
|
|
this.speedLines = document.getElementById('speed-lines');
|
|
|
|
|
|
this.cloudFog = document.getElementById('cloud-fog');
|
|
|
|
|
|
|
|
|
|
|
|
// 模块实例
|
|
|
|
|
|
this.sceneManager = null;
|
|
|
|
|
|
this.mapController = null;
|
|
|
|
|
|
this.listController = null;
|
|
|
|
|
|
this.detailController = null;
|
|
|
|
|
|
|
|
|
|
|
|
// 2D地图初始化标志,防止重复初始化导致省级地图被覆盖
|
|
|
|
|
|
this.is2DInitialized = false;
|
|
|
|
|
|
|
|
|
|
|
|
this.init();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化应用
|
|
|
|
|
|
async init() {
|
2025-12-04 16:03:31 +08:00
|
|
|
|
// 初始化 3D 平台的控制器(但不启动)
|
2025-11-22 19:38:14 +08:00
|
|
|
|
this.mapController = new MapInterface(
|
|
|
|
|
|
this.mapInterface,
|
|
|
|
|
|
(cityName) => this.showList(cityName)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
this.listController = new ListInterface(
|
|
|
|
|
|
this.listInterface,
|
|
|
|
|
|
(company) => this.showDetail(company),
|
|
|
|
|
|
() => this.backToMap()
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
this.detailController = new DetailInterface(
|
|
|
|
|
|
this.detailInterface,
|
|
|
|
|
|
() => this.backToList()
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化搜索控制器(PC端)
|
|
|
|
|
|
const searchInput = document.getElementById('search-input');
|
|
|
|
|
|
if (searchInput) {
|
|
|
|
|
|
this.searchController = new SearchController(searchInput, {
|
|
|
|
|
|
onSelectProvince: (provinceName) => {
|
|
|
|
|
|
this.mapController.init('province', provinceName);
|
|
|
|
|
|
},
|
|
|
|
|
|
onSelectCity: (cityName) => {
|
|
|
|
|
|
this.showList(cityName);
|
|
|
|
|
|
},
|
|
|
|
|
|
onSelectCompany: (company) => {
|
|
|
|
|
|
// 设置当前城市为企业所属城市,以便返回时显示正确的列表
|
|
|
|
|
|
this.listController.currentCity = company.city;
|
|
|
|
|
|
this.showDetail(company);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化搜索控制器(移动端)
|
|
|
|
|
|
const searchInputMobile = document.getElementById('search-input-mobile');
|
|
|
|
|
|
if (searchInputMobile) {
|
|
|
|
|
|
this.searchControllerMobile = new SearchController(searchInputMobile, {
|
|
|
|
|
|
onSelectProvince: (provinceName) => {
|
|
|
|
|
|
this.mapController.init('province', provinceName);
|
|
|
|
|
|
},
|
|
|
|
|
|
onSelectCity: (cityName) => {
|
|
|
|
|
|
this.showList(cityName);
|
|
|
|
|
|
},
|
|
|
|
|
|
onSelectCompany: (company) => {
|
|
|
|
|
|
// 设置当前城市为企业所属城市,以便返回时显示正确的列表
|
|
|
|
|
|
this.listController.currentCity = company.city;
|
|
|
|
|
|
this.showDetail(company);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-04 16:03:31 +08:00
|
|
|
|
// 绑定全局函数和事件
|
2025-11-22 19:38:14 +08:00
|
|
|
|
this.bindGlobalFunctions();
|
|
|
|
|
|
|
2025-12-04 16:03:31 +08:00
|
|
|
|
// 将应用实例暴露给全局,供页面切换使用
|
|
|
|
|
|
window.appInstance = this;
|
2025-11-22 19:38:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 切换到 3D 内推平台
|
|
|
|
|
|
switchToApp() {
|
|
|
|
|
|
// 显示 3D 容器
|
|
|
|
|
|
this.appContainer.classList.remove('hidden');
|
|
|
|
|
|
|
|
|
|
|
|
// 每次都重新初始化 3D 场景
|
|
|
|
|
|
this.init3DScene();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化 3D 场景(每次进入都重新加载)
|
|
|
|
|
|
init3DScene() {
|
|
|
|
|
|
// 如果有旧场景,先清理
|
|
|
|
|
|
if (this.sceneManager) {
|
|
|
|
|
|
this.sceneManager.dispose();
|
|
|
|
|
|
this.sceneManager = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重置UI状态
|
|
|
|
|
|
this.canvasContainer.style.display = 'block';
|
|
|
|
|
|
this.speedLines.style.display = 'block';
|
|
|
|
|
|
this.cloudFog.style.display = 'none'; // 云雾初始隐藏,转场时才显示
|
|
|
|
|
|
this.cloudFog.style.opacity = '0';
|
|
|
|
|
|
this.mapInterface.style.display = 'none';
|
|
|
|
|
|
|
|
|
|
|
|
// 创建场景管理器
|
|
|
|
|
|
this.sceneManager = new SceneManager(
|
|
|
|
|
|
this.canvasContainer,
|
|
|
|
|
|
() => this.onTransitionComplete()
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化转场系统
|
|
|
|
|
|
this.sceneManager.initTransition({
|
|
|
|
|
|
uiLayer: this.uiLayer,
|
|
|
|
|
|
hint: this.hint,
|
|
|
|
|
|
speedLines: this.speedLines,
|
|
|
|
|
|
cloudFog: this.cloudFog
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 启动开场动画
|
|
|
|
|
|
this.sceneManager.startIntroSequence({
|
|
|
|
|
|
uiLayer: this.uiLayer,
|
|
|
|
|
|
hint: this.hint
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 转场完成回调(3D地球 → 2D地图)
|
|
|
|
|
|
onTransitionComplete() {
|
|
|
|
|
|
this.switchTo2D();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 切换到2D地图界面
|
|
|
|
|
|
switchTo2D() {
|
|
|
|
|
|
// 防止重复初始化:如果已经初始化过,直接返回
|
|
|
|
|
|
if (this.is2DInitialized) {
|
|
|
|
|
|
console.log('⏭️ 2D地图已初始化,跳过重复初始化');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 隐藏3D容器和特效层
|
|
|
|
|
|
this.canvasContainer.style.display = 'none';
|
|
|
|
|
|
this.speedLines.style.display = 'none';
|
|
|
|
|
|
|
|
|
|
|
|
// 先显示地图容器(opacity: 0,但display: block)
|
|
|
|
|
|
this.mapInterface.style.display = 'block';
|
|
|
|
|
|
this.mapInterface.style.opacity = '0';
|
|
|
|
|
|
|
|
|
|
|
|
// 使用 requestAnimationFrame 确保浏览器完成布局渲染
|
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
|
|
requestAnimationFrame(async () => {
|
|
|
|
|
|
// 现在容器已经有了正确的尺寸,可以安全初始化ECharts
|
|
|
|
|
|
await this.mapController.init('china');
|
|
|
|
|
|
|
|
|
|
|
|
// 地图初始化完成后,立即调用resize确保尺寸正确
|
|
|
|
|
|
this.mapController.resize();
|
|
|
|
|
|
|
|
|
|
|
|
// 标记2D地图已初始化,防止后续重复初始化
|
|
|
|
|
|
this.is2DInitialized = true;
|
|
|
|
|
|
|
|
|
|
|
|
// 延迟淡入,提升视觉效果
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
gsap.to(this.mapInterface, { opacity: 1, duration: 1 });
|
|
|
|
|
|
gsap.to(this.cloudFog, {
|
|
|
|
|
|
opacity: 0,
|
|
|
|
|
|
duration: 1,
|
|
|
|
|
|
onComplete: () => {
|
|
|
|
|
|
this.cloudFog.style.display = 'none';
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}, 50);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 显示企业列表
|
|
|
|
|
|
async showList(cityName) {
|
|
|
|
|
|
await this.mapController.hide();
|
|
|
|
|
|
this.listController.show(cityName);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 显示企业详情
|
|
|
|
|
|
async showDetail(company) {
|
|
|
|
|
|
await this.listController.hide();
|
|
|
|
|
|
this.detailController.show(company);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 返回列表
|
|
|
|
|
|
async backToList() {
|
|
|
|
|
|
await this.detailController.hide();
|
|
|
|
|
|
this.listController.show(this.listController.currentCity);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 返回地图
|
|
|
|
|
|
async backToMap() {
|
|
|
|
|
|
await this.listController.hide();
|
|
|
|
|
|
this.mapController.show();
|
|
|
|
|
|
this.mapController.resize(); // 确保地图尺寸正确
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 绑定全局函数和事件
|
|
|
|
|
|
bindGlobalFunctions() {
|
2025-12-04 16:03:31 +08:00
|
|
|
|
// 地图界面logo点击返回首页
|
|
|
|
|
|
const mapLogoArea = document.getElementById('map-logo-area');
|
|
|
|
|
|
if (mapLogoArea) {
|
|
|
|
|
|
mapLogoArea.addEventListener('click', () => {
|
|
|
|
|
|
window.backToHome();
|
2025-11-22 19:38:14 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重置地图到全国视图
|
|
|
|
|
|
window.resetMapToChina = () => {
|
|
|
|
|
|
if (this.mapController) {
|
|
|
|
|
|
this.mapController.reset();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 从列表返回地图
|
|
|
|
|
|
window.backToMap = () => {
|
|
|
|
|
|
this.backToMap();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 从详情返回列表
|
|
|
|
|
|
window.backToList = () => {
|
|
|
|
|
|
this.backToList();
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 应用启动
|
|
|
|
|
|
const app = new App();
|