generated from dellevin/template
407 lines
16 KiB
JavaScript
407 lines
16 KiB
JavaScript
/**
|
||
* ECharts 地图足迹模块 (支持钻取功能 - 点击省份查看下一级, 点击按钮返回)
|
||
*/
|
||
const MapFootprintModule = (() => {
|
||
let myChart = null; // ECharts 实例
|
||
let currentMapLevel = 'country'; // 当前地图层级 ('country', 'province')
|
||
let currentProvinceCode = null; // 当前显示的省份编码 (如果有的话)
|
||
|
||
// --- 配置 ---
|
||
const COUNTRY_MAP_NAME = 'china_custom';
|
||
const COUNTRY_GEOJSON_PATH = './static/js/echarts/china.geojson';
|
||
const PROVINCE_GEOJSON_BASE_PATH = './static/js/echarts/province/'; // 假设省份 GeoJSON 存放在这个目录
|
||
|
||
// --- 数据 ---
|
||
// 注意:这里的坐标可能需要根据你实际访问的城市进行微调
|
||
const visitedPlaces = [
|
||
{ name: "济南市", value: [117.024961, 36.682788], province: "山东省" },
|
||
{ name: "青岛市", value: [120.384423, 36.065918], province: "山东省" },
|
||
{ name: "淄博市", value: [118.055915, 36.813547], province: "山东省" },
|
||
{ name: "潍坊市", value: [119.162775, 36.705759], province: "山东省" },
|
||
{ name: "北京市", value: [116.407395, 39.904211], province: "北京" },
|
||
{ name: "杭州市", value: [120.153576, 30.287459], province: "浙江" },
|
||
{ name: "苏州市", value: [120.585316, 31.298886], province: "江苏" },
|
||
{ name: "扬州市", value: [119.421003, 32.393159], province: "江苏" },
|
||
{ name: "保定市", value: [115.482331, 38.867657], province: "河北" },
|
||
{ name: "石家庄市", value: [114.514891, 38.042309], province: "河北" },
|
||
{ name: "衡水市", value: [115.665996, 37.739574], province: "河北" },
|
||
{ name: "洛阳市", value: [112.454174, 34.618139], province: "河南" },
|
||
{ name: "南京市", value: [118.796877, 32.060255], province: "江苏" },
|
||
{ name: "无锡市", value: [120.311987, 31.490920], province: "江苏" },
|
||
{ name: "沧州市", value: [116.838581, 38.308094], province: "河北" },
|
||
{ name: "镇江市", value: [119.452753, 32.204402], province: "江苏" },
|
||
];
|
||
|
||
// 省份名称到其 GeoJSON 文件名的映射 (需要根据你的文件名调整)
|
||
const provinceNameToGeoFile = {
|
||
"山东省": "shandong.json",
|
||
// 你可以继续添加其他省份的映射,例如:
|
||
// "江苏省": "jiangsu.json",
|
||
// "河北省": "hebei.json",
|
||
// ...
|
||
};
|
||
|
||
// --- 工具函数 ---
|
||
|
||
/**
|
||
* 根据省份中文名获取其对应的 GeoJSON 文件完整路径
|
||
* @param {string} provinceName - 省份中文名
|
||
* @returns {string|null} - 文件路径或 null
|
||
*/
|
||
const getProvinceGeoPath = (provinceName) => {
|
||
const fileName = provinceNameToGeoFile[provinceName];
|
||
if (fileName) {
|
||
return ` ${PROVINCE_GEOJSON_BASE_PATH}${fileName}`;
|
||
}
|
||
console.warn(`未找到省份 " ${provinceName}" 对应的 GeoJSON 文件。`);
|
||
return null;
|
||
};
|
||
|
||
/**
|
||
* 根据地图层级和可选的省份编码生成 ECharts 配置
|
||
* @param {string} level - 地图层级 ('country' or 'province')
|
||
* @param {string} [provinceCode] - 省份编码 (当 level='province' 时使用)
|
||
* @returns {Object} - ECharts 配置对象
|
||
*/
|
||
const getEChartsOption = (level, provinceCode = null) => {
|
||
let geoMapName, roamSetting, zoomLevel, centerCoord, titleText;
|
||
let filteredVisitedPlaces = []; // 用于过滤足迹点
|
||
|
||
if (level === 'country') {
|
||
geoMapName = COUNTRY_MAP_NAME;
|
||
roamSetting = false; // 启用缩放和平移
|
||
zoomLevel = 1.1;
|
||
centerCoord = [104.06, 30]; // 中国中心
|
||
titleText = '';
|
||
filteredVisitedPlaces = visitedPlaces; // 显示所有足迹
|
||
} else if (level === 'province' && provinceCode) {
|
||
geoMapName = `province_ ${provinceCode}`; // 为省份地图创建唯一名称
|
||
roamSetting = false; // 省份地图也启用缩放平移
|
||
zoomLevel = 1.0; // 可以根据需要调整
|
||
centerCoord = [118.5, 36.5]; // 示例:山东中心
|
||
titleText = ` ${provinceCode} - 我的足迹`;
|
||
// 过滤出属于当前省份的足迹点
|
||
filteredVisitedPlaces = visitedPlaces.filter(place => place.province === provinceCode);
|
||
} else {
|
||
console.error("无效的地图层级或省份代码");
|
||
return {}; // 返回空配置
|
||
}
|
||
|
||
return {
|
||
title: {
|
||
text: titleText,
|
||
// subtext: level === 'country' ? '点击省份进入详情' : '点击下方返回按钮返回全国地图',
|
||
subtext: '读万卷书,不如行万里路。见多才会识广~',
|
||
left: 'center',
|
||
textStyle: {
|
||
fontSize: 18
|
||
},
|
||
subtextStyle: {
|
||
fontSize: 12
|
||
}
|
||
},
|
||
tooltip: {
|
||
trigger: 'item',
|
||
formatter: function(params) {
|
||
if (params.componentType === 'series' && params.seriesType === 'scatter') {
|
||
// 足迹点的 tooltip
|
||
if (params.value && params.value.length >= 2) {
|
||
return params.name + '<br/>经度: ' + params.value[0].toFixed(4) + '<br/>纬度: ' + params.value[1].toFixed(4);
|
||
}
|
||
return params.name;
|
||
} else if (params.componentType === 'geo') {
|
||
// 地图区域的 tooltip
|
||
return params.name;
|
||
}
|
||
return params.name;
|
||
}
|
||
},
|
||
geo: {
|
||
map: geoMapName,
|
||
roam: roamSetting,
|
||
zoom: zoomLevel,
|
||
center: centerCoord,
|
||
emphasis: {
|
||
itemStyle: {
|
||
areaColor: '#f0f0f0',
|
||
borderColor: '#409EFF',
|
||
borderWidth: 1
|
||
}
|
||
},
|
||
itemStyle: {
|
||
areaColor: '#eef2ff',
|
||
borderColor: '#333'
|
||
}
|
||
},
|
||
series: [
|
||
{
|
||
name: '足迹',
|
||
type: 'scatter',
|
||
coordinateSystem: 'geo',
|
||
data: filteredVisitedPlaces,
|
||
symbol: 'pin',
|
||
symbolSize: 18,
|
||
itemStyle: {
|
||
color: '#FF6B6B',
|
||
borderColor: '#fff',
|
||
borderWidth: 1,
|
||
},
|
||
emphasis: {
|
||
itemStyle: {
|
||
color: '#dd4444'
|
||
}
|
||
},
|
||
tooltip: {
|
||
formatter: function(params) {
|
||
return ` ${params.data.name}<br/>坐标: [ ${params.data.value[0].toFixed(4)}, ${params.data.value[1].toFixed(4)}]`;
|
||
}
|
||
}
|
||
}
|
||
]
|
||
};
|
||
};
|
||
|
||
|
||
/**
|
||
* 加载并注册指定路径的 GeoJSON 地图数据
|
||
* @param {string} path - GeoJSON 文件路径
|
||
* @param {string} mapName - 注册到 ECharts 的地图名称
|
||
* @returns {Promise<boolean>} - 成功或失败
|
||
*/
|
||
const loadAndRegisterSingleMap = async (path, mapName) => {
|
||
try {
|
||
console.log(`正在加载 GeoJSON: ${path}`);
|
||
const response = await fetch(path);
|
||
if (!response.ok) {
|
||
throw new Error(`加载 GeoJSON 失败: ${response.status} ${response.statusText}`);
|
||
}
|
||
const geoJsonData = await response.json();
|
||
|
||
// 注册地图
|
||
echarts.registerMap(mapName, geoJsonData);
|
||
console.log(`GeoJSON 地图数据 ${mapName} 加载并注册成功。`);
|
||
return true;
|
||
} catch (error) {
|
||
console.error('加载或注册 GeoJSON 地图时出错:', error);
|
||
return false;
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 切换到指定的地图层级和区域
|
||
* @param {string} targetLevel - 目标层级 ('country' or 'province')
|
||
* @param {string} [targetProvinceCode] - 目标省份编码 (当 targetLevel='province' 时使用)
|
||
*/
|
||
const switchMap = async (targetLevel, targetProvinceCode = null) => {
|
||
if (!myChart) {
|
||
console.error("ECharts 实例不存在,无法切换地图。");
|
||
return;
|
||
}
|
||
|
||
let mapNameToUse, geoPathToLoad;
|
||
|
||
if (targetLevel === 'country') {
|
||
mapNameToUse = COUNTRY_MAP_NAME;
|
||
geoPathToLoad = COUNTRY_GEOJSON_PATH;
|
||
currentMapLevel = 'country';
|
||
currentProvinceCode = null;
|
||
} else if (targetLevel === 'province' && targetProvinceCode) {
|
||
mapNameToUse = `province_ ${targetProvinceCode}`;
|
||
const path = getProvinceGeoPath(targetProvinceCode);
|
||
if (!path) {
|
||
console.error(`无法切换到省份 ${targetProvinceCode}: 找不到对应的 GeoJSON 文件。`);
|
||
return;
|
||
}
|
||
geoPathToLoad = path;
|
||
currentMapLevel = 'province';
|
||
currentProvinceCode = targetProvinceCode;
|
||
} else {
|
||
console.error("无效的目标层级或省份代码。");
|
||
return;
|
||
}
|
||
|
||
// 尝试加载并注册目标地图数据
|
||
const loadSuccess = await loadAndRegisterSingleMap(geoPathToLoad, mapNameToUse);
|
||
if (!loadSuccess) {
|
||
console.error(`切换地图失败:无法加载 ${geoPathToLoad}`);
|
||
return;
|
||
}
|
||
|
||
// 生成新配置并应用
|
||
const newOption = getEChartsOption(targetLevel, targetProvinceCode);
|
||
myChart.setOption(newOption, true); // 使用 notMerge=true 确保完全替换
|
||
|
||
// 更新返回按钮的可见性
|
||
updateBackButtonVisibility();
|
||
};
|
||
|
||
// --- 新增函数:管理返回按钮 ---
|
||
|
||
/**
|
||
* 获取返回按钮的DOM元素
|
||
* @returns {HTMLElement|null}
|
||
*/
|
||
const getBackButtonElement = () => {
|
||
// 假设按钮ID是固定的,可以根据需要调整
|
||
return document.getElementById('map-back-button');
|
||
};
|
||
|
||
/**
|
||
* 创建返回按钮并将其添加到图表容器中
|
||
* @param {HTMLElement} chartContainer - ECharts 图表容器
|
||
*/
|
||
const createBackButton = (chartContainer) => {
|
||
const button = document.createElement('button');
|
||
button.id = 'map-back-button';
|
||
button.textContent = '⬅返回全国地图';
|
||
button.style.position = 'absolute';
|
||
button.style.top = '10px';
|
||
button.style.left = '10px';
|
||
button.style.zIndex = 1000; // 确保按钮在图表之上
|
||
button.style.padding = '2px 5px'; // 减少内边距以更接近纯文本
|
||
button.style.fontSize = '14px'; // 根据需要调整字体大小
|
||
button.style.cursor = 'pointer';
|
||
button.style.border = 'none'; // 去掉边框
|
||
button.style.backgroundColor = 'transparent'; // 设置背景透明
|
||
button.style.display = 'none'; // 初始隐藏
|
||
|
||
|
||
button.onclick = () => {
|
||
console.log("点击了返回按钮。");
|
||
switchMap('country');
|
||
};
|
||
|
||
chartContainer.appendChild(button);
|
||
};
|
||
|
||
/**
|
||
* 根据当前地图层级更新返回按钮的可见性
|
||
*/
|
||
const updateBackButtonVisibility = () => {
|
||
const button = getBackButtonElement();
|
||
if (!button) {
|
||
console.warn("返回按钮 DOM 元素未找到,无法更新其可见性。");
|
||
return;
|
||
}
|
||
// 当前在省份地图时显示按钮,否则隐藏
|
||
button.style.display = (currentMapLevel === 'province') ? 'block' : 'none';
|
||
};
|
||
|
||
// --- /新增函数 ---
|
||
/**
|
||
* 初始化 ECharts 地图 (初始加载国家地图)
|
||
* @param {HTMLElement} container - 图表容器DOM元素
|
||
*/
|
||
const initEChartsMap = async (container) => {
|
||
if (!container) {
|
||
console.error('地图容器未找到');
|
||
return;
|
||
}
|
||
|
||
// 初始化 ECharts 实例
|
||
myChart = echarts.init(container);
|
||
|
||
// 创建并添加返回按钮
|
||
createBackButton(container);
|
||
|
||
// 首先加载并注册国家地图
|
||
const countryLoadSuccess = await loadAndRegisterSingleMap(COUNTRY_GEOJSON_PATH, COUNTRY_MAP_NAME);
|
||
if (!countryLoadSuccess) {
|
||
console.error("初始化失败:无法加载国家 GeoJSON。");
|
||
return; // 或者显示错误信息给用户
|
||
}
|
||
|
||
// 应用初始配置 (国家地图)
|
||
const initialOption = getEChartsOption('country');
|
||
myChart.setOption(initialOption);
|
||
|
||
// --- 添加事件监听 ---
|
||
|
||
// 监听地图区域点击事件
|
||
myChart.on('click', 'geo', function (params) {
|
||
if (currentMapLevel === 'country') {
|
||
// 当前是国家地图,点击省份则进入
|
||
const clickedProvinceName = params.name;
|
||
console.log(`点击了省份: ${clickedProvinceName}`);
|
||
|
||
// 尝试获取省份对应的 GeoJSON 文件名
|
||
const fileName = Object.keys(provinceNameToGeoFile).find(key => key === clickedProvinceName);
|
||
console.log(fileName)
|
||
if (fileName) {
|
||
const provinceCode = fileName; // 这里假设文件名就是省份简称
|
||
console.log(`即将切换到省份: ${provinceCode}`);
|
||
switchMap('province', provinceCode);
|
||
} else {
|
||
console.log(`未配置省份 " ${clickedProvinceName}" 的详细地图。`);
|
||
}
|
||
}
|
||
// 如果在省份地图上点击,可以有其他逻辑,比如不处理
|
||
});
|
||
|
||
// 监听窗口大小变化,自动适配
|
||
window.addEventListener('resize', function () {
|
||
if (myChart) {
|
||
myChart.resize();
|
||
}
|
||
});
|
||
};
|
||
|
||
|
||
/**
|
||
* 获取地图容器ID
|
||
* @returns {string}
|
||
*/
|
||
const getMapContainerId = () => {
|
||
return 'echarts-map-container';
|
||
};
|
||
|
||
/**
|
||
* 渲染 ECharts 地图内容到指定容器
|
||
* @param {HTMLElement} contentDiv - 标签页的内容容器
|
||
*/
|
||
const renderMap = (contentDiv) => {
|
||
// 清空内容区域
|
||
// contentDiv.innerHTML = '';
|
||
// 创建地图容器
|
||
const mapDiv = document.createElement('div');
|
||
mapDiv.id = getMapContainerId();
|
||
mapDiv.style.width = '100%';
|
||
mapDiv.style.height = '600px';
|
||
mapDiv.style.marginTop = '20px';
|
||
mapDiv.style.position = 'relative'; // 为绝对定位的按钮提供参考
|
||
contentDiv.appendChild(mapDiv);
|
||
|
||
// 加载 GeoJSON 并初始化地图
|
||
setTimeout(async () => {
|
||
const container = document.getElementById(getMapContainerId());
|
||
if (container) {
|
||
await initEChartsMap(container); // 使用 await 确保初始化完成
|
||
} else {
|
||
console.error('地图容器 DOM 元素未找到,无法初始化图表。');
|
||
}
|
||
}, 100);
|
||
};
|
||
|
||
/**
|
||
* 销毁 ECharts 实例 (可选,用于性能优化或切换时清理)
|
||
*/
|
||
const destroyMap = () => {
|
||
if (myChart) {
|
||
myChart.dispose();
|
||
myChart = null;
|
||
console.log('ECharts 地图已销毁');
|
||
// 可以考虑移除返回按钮
|
||
const button = getBackButtonElement();
|
||
if (button && button.parentNode) {
|
||
button.parentNode.removeChild(button);
|
||
}
|
||
}
|
||
};
|
||
|
||
// 返回公共方法
|
||
return {
|
||
renderMap,
|
||
destroyMap
|
||
};
|
||
|
||
})(); |