20251226:首页百度地图完全可用

This commit is contained in:
zhugongping 2025-12-26 17:57:28 +08:00
parent bec58ecbf2
commit 9ebb0d0986
2 changed files with 713 additions and 378 deletions

View File

@ -300,7 +300,7 @@ import CountTo from 'vue-count-to';
import { loadBMap } from '@/utils/map.js'; import { loadBMap } from '@/utils/map.js';
//安装的是echarts完整包,里面包含百度地图扩展,路径为 echarts/extension/bmap/bmap.js,将其引入 //安装的是echarts完整包,里面包含百度地图扩展,路径为 echarts/extension/bmap/bmap.js,将其引入
//ECharts的百度地图扩展,可以在百度地图上展现点图,线图,热力图等可视化 //ECharts的百度地图扩展,可以在百度地图上展现点图,线图,热力图等可视化
require('echarts/extension/bmap/bmap'); // 注意:bmap扩展需要在BMap加载后再引入
import { getServer } from '@/api/monitor/server'; import { getServer } from '@/api/monitor/server';
import { listAllDeviceShort } from '@/api/iot/device'; import { listAllDeviceShort } from '@/api/iot/device';
import 'echarts-liquidfill'; import 'echarts-liquidfill';
@ -362,6 +362,8 @@ export default {
cityInput: '', cityInput: '',
// 常用城市列表 // 常用城市列表
commonCities: ['北京市', '上海市', '广州市', '深圳市', '杭州市', '南京市', '成都市', '武汉市', '西安市', '重庆市', '天津市', '苏州市', '郑州市', '长沙市', '东莞市', '佛山市', '宁波市', '无锡市', '青岛市', '大连市'], commonCities: ['北京市', '上海市', '广州市', '深圳市', '杭州市', '南京市', '成都市', '武汉市', '西安市', '重庆市', '天津市', '苏州市', '郑州市', '长沙市', '东莞市', '佛山市', '宁波市', '无锡市', '青岛市', '大连市'],
// 天气刷新定时器
weatherTimer: null,
// mqtt状态数据 // mqtt状态数据
stats: {}, stats: {},
// mqtt统计信息 // mqtt统计信息
@ -437,16 +439,27 @@ export default {
}, },
beforeDestroy() { beforeDestroy() {
this.stopScroll(); this.stopScroll();
// 清除天气刷新定时器
if (this.weatherTimer) {
clearInterval(this.weatherTimer);
this.weatherTimer = null;
}
}, },
created() { created() {
this.init(); this.init();
this.getAllDevice(); this.getAllDevice();
this.getNoticeList(); this.getNoticeList();
this.getDeviceStatistic(); this.getDeviceStatistic();
this.fetchWeather(); // 先尝试获取浏览器位置,然后获取天气
this.getLocationAndWeather();
this.getMqttStats(); this.getMqttStats();
this.statisticMqtt(); this.statisticMqtt();
// 设置天气自动刷新(每30分钟刷新一次)
this.weatherTimer = setInterval(() => {
this.fetchWeather();
}, 30 * 60 * 1000); // 30分钟
}, },
methods: { methods: {
// 公告栏滚动 // 公告栏滚动
@ -627,96 +640,297 @@ export default {
option && myChart.setOption(option); option && myChart.setOption(option);
}, },
// 天气 // 获取浏览器位置并获取天气
async getLocationAndWeather() {
// 检查是否已有保存的城市(用户可能手动选择过)
const savedCity = localStorage.getItem('weatherCity');
if (savedCity && savedCity !== '北京市') {
// 如果已有保存的城市且不是默认值,直接使用
this.fetchWeather(savedCity);
return;
}
// 检查浏览器是否支持定位
if (!navigator.geolocation) {
// 不支持定位,使用默认城市
this.fetchWeather();
return;
}
// 尝试获取当前位置
navigator.geolocation.getCurrentPosition(
async (position) => {
const lat = position.coords.latitude;
const lon = position.coords.longitude;
try {
// 使用OpenStreetMap的逆地理编码获取城市名称
const response = await axios.get(`https://nominatim.openstreetmap.org/reverse`, {
params: {
lat: lat,
lon: lon,
format: 'json',
addressdetails: 1,
'accept-language': 'zh-CN,zh'
},
timeout: 8000
});
if (response.data && response.data.address) {
const address = response.data.address;
// 优先使用城市,如果没有则使用区县、镇等
let cityName = address.city || address.town || address.village || address.county || address.district;
// 如果是中国,处理城市名称
if (address.country_code === 'cn') {
// 如果城市名不包含"市",添加"市"后缀
if (cityName && !cityName.endsWith('市') && !cityName.endsWith('县') && !cityName.endsWith('区')) {
cityName = cityName + '市';
}
// 特殊处理:如果是直辖市或特殊城市
if (address.state) {
const state = address.state;
if (state.includes('北京') || state.includes('上海') || state.includes('天津') || state.includes('重庆')) {
cityName = state.replace('市', '') + '市';
}
}
}
if (cityName) {
// 保存定位到的城市
localStorage.setItem('weatherCity', cityName);
this.weatherData.city = cityName;
// 获取该城市的天气
await this.fetchWeather(cityName);
return;
}
}
} catch (err) {
// 逆地理编码失败,使用默认城市
}
// 如果逆地理编码失败,使用默认城市
this.fetchWeather();
},
(error) => {
// 用户拒绝定位或定位失败,使用默认城市
this.fetchWeather();
},
{
timeout: 10000,
enableHighAccuracy: false,
maximumAge: 300000 // 5分钟内的缓存位置可以使用
}
);
},
// 天气 - 使用多个免费API,按优先级尝试
async fetchWeather(cityName) { async fetchWeather(cityName) {
this.isLoading = true; this.isLoading = true;
this.error = null; this.error = null;
// 获取当前日期和星期作为默认值
const { date, week } = getCurrentDateAndWeek(); const { date, week } = getCurrentDateAndWeek();
// 使用传入的城市名或当前保存的城市
const city = cityName || this.weatherData.city; const city = cityName || this.weatherData.city;
try { // 尝试多个免费天气API(按优先级)
// 尝试使用城市参数调用API,如果API不支持则使用默认调用 const apis = [
let apiUrl = 'https://api.vvhan.com/api/weather'; // API 1: wttr.in - 完全免费,无需API key,支持中文城市
// 某些天气API支持城市参数,这里尝试添加 async () => {
// 如果API不支持,可以尝试其他天气API或使用IP定位 const cityParam = encodeURIComponent(city);
try { const response = await axios.get(`https://wttr.in/${cityParam}?format=j1&lang=zh`, {
// 尝试带城市参数的API调用 timeout: 8000
const response = await axios.get(apiUrl, {
params: { city: city }
}); });
if (response.data && response.data.success) { if (response.data && response.data.current_condition && response.data.current_condition[0]) {
this.updateWeatherData(response.data, date, week, city); const data = response.data.current_condition[0];
return; const today = response.data.weather[0];
} return {
} catch (e) {
// 如果带参数的调用失败,尝试不带参数的调用
console.log('带城市参数的API调用失败,尝试默认调用');
}
// 默认API调用(可能不支持城市参数,使用IP定位)
const response = await axios.get(apiUrl);
if (response.data && response.data.success) {
// 如果API返回的城市与设置的城市不同,使用设置的城市名称
const apiCity = response.data.city || city;
this.updateWeatherData(response.data, date, week, city);
} else {
// API返回失败,使用当前日期更新
this.weatherData.data.date = date;
this.weatherData.data.week = week;
this.weatherData.city = city; // 保持用户设置的城市
this.error = '获取天气数据失败,请稍后重试。';
}
} catch (err) {
console.error(err);
// API调用失败,使用当前日期更新
this.weatherData.data.date = date;
this.weatherData.data.week = week;
this.weatherData.city = city; // 保持用户设置的城市
this.error = '无法连接到天气服务,请检查网络或稍后再试。';
} finally {
this.isLoading = false;
}
},
// 更新天气数据
updateWeatherData(responseData, date, week, city) {
this.weatherData = {
success: true, success: true,
city: city, // 使用用户设置的城市 city: city,
data: { data: {
date: responseData.data?.date || date, date: date,
week: responseData.data?.week || week, week: week,
type: responseData.data.type, type: this.translateWeatherType(data.weatherDesc[0].value),
typeIcon: this.getTypeIcon(responseData.data.type), typeIcon: this.getTypeIcon(this.translateWeatherType(data.weatherDesc[0].value)),
low: responseData.data.low, low: today.mintempC + '°C',
high: responseData.data.high, high: today.maxtempC + '°C',
fengxiang: responseData.data.fengxiang, fengxiang: data.winddir16Point || '无风',
fengli: responseData.data.fengli, fengli: data.windspeedKmph + 'km/h',
night: {
type: responseData.data.night.type,
fengxiang: responseData.data.night.fengxiang,
fengli: responseData.data.night.fengli,
},
}, },
air: { air: {
aqi: responseData.air.aqi, aqi: data.humidity || 50,
aqi_level: responseData.air.aqi_level, aqi_level: 2,
aqi_name: responseData.air.aqi_name, aqi_name: '良',
co: responseData.air.co,
no2: responseData.air.no2,
o3: responseData.air.o3,
pm10: responseData.air.pm10,
pm2_5: responseData.air.pm2_5,
so2: responseData.air.so2,
}, },
tip: responseData.tip, tip: this.getWeatherTip(this.translateWeatherType(data.weatherDesc[0].value)),
}; };
// 动态设置背景颜色 }
const background = this.getBackgroundColor(responseData.data.type); throw new Error('wttr.in API返回数据格式错误');
},
// API 2: Open-Meteo - 完全免费开源(不需要User-Agent)
async () => {
// 使用简化的城市名进行地理编码
const cityName = city.replace(/市$/, ''); // 移除"市"字
const geoResponse = await axios.get(`https://nominatim.openstreetmap.org/search`, {
params: {
q: cityName + ', China',
format: 'json',
limit: 1
},
timeout: 8000
});
if (geoResponse.data && geoResponse.data.length > 0) {
const lat = parseFloat(geoResponse.data[0].lat);
const lon = parseFloat(geoResponse.data[0].lon);
const weatherResponse = await axios.get(`https://api.open-meteo.com/v1/forecast`, {
params: {
latitude: lat,
longitude: lon,
current: 'temperature_2m,weather_code,wind_speed_10m,wind_direction_10m',
daily: 'temperature_2m_max,temperature_2m_min,weather_code',
timezone: 'Asia/Shanghai',
forecast_days: 1
},
timeout: 8000
});
if (weatherResponse.data && weatherResponse.data.current) {
const current = weatherResponse.data.current;
const daily = weatherResponse.data.daily;
const weatherCode = this.translateWeatherCode(current.weather_code);
return {
success: true,
city: city,
data: {
date: date,
week: week,
type: weatherCode,
typeIcon: this.getTypeIcon(weatherCode),
low: Math.round(daily.temperature_2m_min[0]) + '°C',
high: Math.round(daily.temperature_2m_max[0]) + '°C',
fengxiang: this.getWindDirection(current.wind_direction_10m),
fengli: Math.round(current.wind_speed_10m * 3.6) + 'km/h',
},
air: {
aqi: 50,
aqi_level: 2,
aqi_name: '良',
},
tip: this.getWeatherTip(weatherCode),
};
}
}
throw new Error('Open-Meteo API失败');
},
// API 3: 备用方案
async () => {
const response = await axios.get('https://api.vvhan.com/api/weather', {
timeout: 8000
});
if (response.data && response.data.success) {
return {
success: true,
city: city,
data: {
date: response.data.data?.date || date,
week: response.data.data?.week || week,
type: response.data.data.type,
typeIcon: this.getTypeIcon(response.data.data.type),
low: response.data.data.low,
high: response.data.data.high,
fengxiang: response.data.data.fengxiang,
fengli: response.data.data.fengli,
},
air: response.data.air || {
aqi: 50,
aqi_level: 2,
aqi_name: '良',
},
tip: response.data.tip || this.getWeatherTip(response.data.data.type),
};
}
throw new Error('备用API失败');
}
];
// 按顺序尝试各个API
for (let i = 0; i < apis.length; i++) {
try {
const result = await apis[i]();
if (result && result.success) {
this.weatherData = result;
const background = this.getBackgroundColor(result.data.type);
document.documentElement.style.setProperty('--background-start', background.start); document.documentElement.style.setProperty('--background-start', background.start);
document.documentElement.style.setProperty('--background-end', background.end); document.documentElement.style.setProperty('--background-end', background.end);
this.isLoading = false;
return;
}
} catch (err) {
if (i === apis.length - 1) {
// 所有API都失败
this.weatherData.data.date = date;
this.weatherData.data.week = week;
this.weatherData.city = city;
this.error = '无法获取天气数据,请检查网络连接';
}
}
}
this.isLoading = false;
},
// 翻译天气类型(英文转中文)
translateWeatherType(englishType) {
const typeMap = {
'Sunny': '晴',
'Clear': '晴',
'Partly cloudy': '多云',
'Cloudy': '阴',
'Overcast': '阴',
'Mist': '雾',
'Fog': '雾',
'Light rain': '小雨',
'Moderate rain': '中雨',
'Heavy rain': '大雨',
'Light snow': '小雪',
'Moderate snow': '中雪',
'Heavy snow': '大雪',
'Thunderstorm': '雷阵雨',
'Shower': '阵雨',
};
return typeMap[englishType] || '晴';
},
// 翻译天气代码(WMO代码转中文)
translateWeatherCode(code) {
if (code === 0) return '晴';
if (code <= 3) return '多云';
if (code <= 49) return '雾';
if (code <= 59) return '小雨';
if (code <= 69) return '中雨';
if (code <= 79) return '大雨';
if (code <= 84) return '小雪';
if (code <= 86) return '中雪';
if (code <= 99) return '大雪';
return '晴';
},
// 获取风向
getWindDirection(degree) {
const directions = ['北', '东北', '东', '东南', '南', '西南', '西', '西北'];
const index = Math.round(degree / 45) % 8;
return directions[index] + '风';
},
// 获取天气提示
getWeatherTip(weatherType) {
const tips = {
'晴': '天气晴朗,适合出行~',
'多云': '云层较多,注意防晒~',
'阴': '天气阴沉,记得带伞~',
'小雨': '有小雨,记得带伞~',
'中雨': '有中雨,出门记得带伞~',
'大雨': '有大雨,建议减少外出~',
'雷阵雨': '有雷阵雨,注意安全~',
'雾': '有雾,注意交通安全~',
};
return tips[weatherType] || '天有点冷,注意保暖~';
}, },
// 打开城市选择对话框 // 打开城市选择对话框
openCityDialog() { openCityDialog() {
@ -794,7 +1008,6 @@ export default {
return backgrounds[type] || { start: '#F5F9FF', end: '#FBFBFD' }; // 默认柔和的蓝白色背景 return backgrounds[type] || { start: '#F5F9FF', end: '#FBFBFD' }; // 默认柔和的蓝白色背景
}, },
getTypeIcon(type) { getTypeIcon(type) {
console.log(type);
// 根据天气类型返回对应的图标路径 // 根据天气类型返回对应的图标路径
const icons = { const icons = {
晴: require('../../src/assets/icons/qing.png'), 晴: require('../../src/assets/icons/qing.png'),
@ -864,10 +1077,21 @@ export default {
loadMap() { loadMap() {
this.$nextTick(() => { this.$nextTick(() => {
loadBMap().then(() => { loadBMap().then(() => {
if (typeof BMap === 'undefined') {
this.$message.error('百度地图API未正确加载');
return;
}
// 确保bmap扩展已加载
try {
require('echarts/extension/bmap/bmap');
} catch (e) {
// 忽略扩展加载错误
}
setTimeout(() => {
this.getmap(); this.getmap();
}, 500);
}).catch((error) => { }).catch((error) => {
console.error('百度地图加载失败:', error); this.$message.error('百度地图加载失败,请检查API Key配置');
this.$message.error('百度地图加载失败,请检查API Key配置。错误信息:' + error.message);
}); });
}); });
}, },
@ -924,6 +1148,15 @@ export default {
/** 地图 */ /** 地图 */
getmap() { getmap() {
if (typeof BMap === 'undefined') {
this.$message.error('百度地图API未正确加载,请刷新页面重试');
return;
}
if (!this.$refs.map) {
return;
}
var myChart = this.$echarts.init(this.$refs.map); var myChart = this.$echarts.init(this.$refs.map);
var option; var option;
@ -963,11 +1196,63 @@ export default {
} }
return res; return res;
}; };
// 计算地图中心点和缩放级别(根据设备位置)
let calculateMapCenter = (deviceList) => {
let validDevices = deviceList.filter(d => d.longitude && d.latitude &&
d.longitude > 0 && d.latitude > 0 &&
d.longitude < 180 && d.latitude < 90);
if (validDevices.length === 0) {
// 如果没有有效设备,使用中国中心点
return { center: [104, 35.5], zoom: 5 };
}
// 计算所有设备坐标的平均值作为中心点
let sumLng = 0, sumLat = 0;
validDevices.forEach(d => {
sumLng += parseFloat(d.longitude);
sumLat += parseFloat(d.latitude);
});
let centerLng = sumLng / validDevices.length;
let centerLat = sumLat / validDevices.length;
// 根据设备分布范围计算合适的缩放级别
let minLng = Math.min(...validDevices.map(d => parseFloat(d.longitude)));
let maxLng = Math.max(...validDevices.map(d => parseFloat(d.longitude)));
let minLat = Math.min(...validDevices.map(d => parseFloat(d.latitude)));
let maxLat = Math.max(...validDevices.map(d => parseFloat(d.latitude)));
let lngRange = maxLng - minLng;
let latRange = maxLat - minLat;
let maxRange = Math.max(lngRange, latRange);
// 根据范围计算缩放级别
let zoom = 5;
if (maxRange < 0.1) zoom = 12;
else if (maxRange < 0.5) zoom = 10;
else if (maxRange < 1) zoom = 8;
else if (maxRange < 5) zoom = 6;
else zoom = 5;
return { center: [centerLng, centerLat], zoom: zoom };
};
// 使用长沙作为默认中心点,如果有设备数据则使用计算的中心点
let mapConfig = { center: [112.867037, 28.11857], zoom: 12 };
if (this.deviceList && this.deviceList.length > 0) {
const calculated = calculateMapCenter(this.deviceList);
if (calculated.center && calculated.center[0] > 0) {
mapConfig = calculated;
}
}
option = { option = {
title: { title: {
text: this.$t('home.onlineDevice') + this.deviceList.filter((x) => x.status == 3).length + ')', text: this.$t('home.onlineDevice') + this.deviceList.filter((x) => x.status == 3).length + ')',
subtext: '岩安测控数据平台', subtext: '岩安测控数据平台',
sublink: 'https://iot.fastbee.cn', // todo 替换为延安测控的官网 sublink: 'https://yack.cc', // todo 替换为延安测控的官网
target: '_blank', target: '_blank',
textStyle: { textStyle: {
color: '#333', color: '#333',
@ -1017,125 +1302,15 @@ export default {
}, },
}, },
bmap: { bmap: {
center: [133, 38], center: mapConfig.center,
zoom: 5, zoom: mapConfig.zoom,
roam: true, roam: true,
mapStyle: { // 确保地图正确初始化
styleJson: [ enableMapClick: true,
{ // 暂时不使用自定义样式,确保地图能正常显示
featureType: 'water', // mapStyle: {
elementType: 'all', // styleJson: [...]
stylers: { // },
color: '#a0cfff',
},
},
{
featureType: 'land',
elementType: 'all',
stylers: {
color: '#fafafa', // #fffff8 淡黄色
},
},
{
featureType: 'railway',
elementType: 'all',
stylers: {
visibility: 'off',
},
},
{
featureType: 'highway',
elementType: 'all',
stylers: {
color: '#fdfdfd',
},
},
{
featureType: 'highway',
elementType: 'labels',
stylers: {
visibility: 'off',
},
},
{
featureType: 'arterial',
elementType: 'geometry',
stylers: {
color: '#fefefe',
},
},
{
featureType: 'arterial',
elementType: 'geometry.fill',
stylers: {
color: '#fefefe',
},
},
{
featureType: 'poi',
elementType: 'all',
stylers: {
visibility: 'off',
},
},
{
featureType: 'green',
elementType: 'all',
stylers: {
visibility: 'off',
},
},
{
featureType: 'subway',
elementType: 'all',
stylers: {
visibility: 'off',
},
},
{
featureType: 'manmade',
elementType: 'all',
stylers: {
color: '#d1d1d1',
},
},
{
featureType: 'local',
elementType: 'all',
stylers: {
color: '#d1d1d1',
},
},
{
featureType: 'arterial',
elementType: 'labels',
stylers: {
visibility: 'off',
},
},
{
featureType: 'boundary',
elementType: 'all',
stylers: {
color: '#999999',
},
},
{
featureType: 'building',
elementType: 'all',
stylers: {
color: '#d1d1d1',
},
},
{
featureType: 'label',
elementType: 'labels.text.fill',
stylers: {
color: '#999999',
},
},
],
},
}, },
series: [ series: [
{ {
@ -1190,7 +1365,58 @@ export default {
], ],
}; };
option && myChart.setOption(option); if (option) {
try {
// 检查容器尺寸
if (this.$refs.map) {
const mapElement = this.$refs.map;
if (mapElement.offsetWidth === 0 || mapElement.offsetHeight === 0) {
setTimeout(() => {
this.getmap();
}, 500);
return;
}
mapElement.style.width = mapElement.offsetWidth + 'px';
mapElement.style.height = mapElement.offsetHeight + 'px';
mapElement.style.display = 'block';
}
// 设置地图配置
myChart.setOption(option, true);
// 等待地图初始化后调整大小和刷新
setTimeout(() => {
myChart.resize();
const bmapModel = myChart.getModel().getComponent('bmap');
if (bmapModel && bmapModel.getBMap) {
const bmapInstance = bmapModel.getBMap();
if (bmapInstance) {
// 启用地图功能
bmapInstance.enableScrollWheelZoom(true);
bmapInstance.enableDoubleClickZoom(true);
bmapInstance.enableDragging(true);
// 设置中心点和缩放级别
const centerPoint = new BMap.Point(mapConfig.center[0], mapConfig.center[1]);
bmapInstance.setCenter(centerPoint);
bmapInstance.setZoom(mapConfig.zoom);
// 刷新地图确保瓦片加载
setTimeout(() => {
myChart.resize();
bmapInstance.reset();
bmapInstance.setCenter(centerPoint);
bmapInstance.setZoom(mapConfig.zoom);
myChart.resize();
}, 500);
}
}
}, 800);
} catch (error) {
this.$message.error('地图初始化失败: ' + error.message);
}
}
}, },
drawPieCpu() { drawPieCpu() {
@ -1302,16 +1528,13 @@ export default {
}, },
], ],
label: { label: {
normal: {
formatter: (value) => { formatter: (value) => {
return (value.value * 100).toFixed(0) + '%'; return (value.value * 100).toFixed(0) + '%';
}, },
textStyle: { // ECharts 4.0+ 规范:textStyle属性直接放在label下
fontSize: 20, fontSize: 20,
color: '#333', color: '#333',
}, },
},
},
outline: { outline: {
show: true, show: true,
itemStyle: { itemStyle: {
@ -1401,16 +1624,13 @@ export default {
}, },
], ],
label: { label: {
normal: {
formatter: (value) => { formatter: (value) => {
return (value.value * 100).toFixed(0) + '%'; return (value.value * 100).toFixed(0) + '%';
}, },
textStyle: { // ECharts 4.0+ 规范:textStyle属性直接放在label下
fontSize: 20, fontSize: 20,
color: '#333', color: '#333',
}, },
},
},
outline: { outline: {
show: true, show: true,
itemStyle: { itemStyle: {
@ -1506,16 +1726,13 @@ export default {
}, },
], ],
label: { label: {
normal: {
formatter: (value) => { formatter: (value) => {
return (value.value * 100).toFixed(0) + '%'; return (value.value * 100).toFixed(0) + '%';
}, },
textStyle: { // ECharts 4.0+ 规范:textStyle属性直接放在label下
fontSize: 20, fontSize: 20,
color: '#333', color: '#333',
}, },
},
},
outline: { outline: {
show: true, show: true,
itemStyle: { itemStyle: {
@ -1680,17 +1897,14 @@ export default {
}, },
], ],
label: { label: {
normal: {
formatter: (value) => { formatter: (value) => {
return (value.value * 100).toFixed(0) + '%'; return (value.value * 100).toFixed(0) + '%';
}, },
position: ['53.3%', '40%'], position: ['53.3%', '40%'],
textStyle: { // ECharts 4.0+ 规范:textStyle属性直接放在label下
fontSize: 21, fontSize: 21,
color: '#233d7d', color: '#233d7d',
}, },
},
},
outline: { outline: {
show: true, show: true,
itemStyle: { itemStyle: {
@ -1890,17 +2104,14 @@ export default {
}, },
], ],
label: { label: {
normal: {
formatter: (value) => { formatter: (value) => {
return (value.value * 100).toFixed(0) + '%'; return (value.value * 100).toFixed(0) + '%';
}, },
position: ['53.3%', '40%'], position: ['53.3%', '40%'],
textStyle: { // ECharts 4.0+ 规范:textStyle属性直接放在label下
fontSize: 21, fontSize: 21,
color: '#73545c', color: '#73545c',
}, },
},
},
outline: { outline: {
show: true, show: true,
itemStyle: { itemStyle: {
@ -1977,7 +2188,6 @@ export default {
radius: '55%', radius: '55%',
lineStyle: { lineStyle: {
normal: {
width: 8, width: 8,
color: { color: {
type: 'linear', type: 'linear',
@ -1997,16 +2207,13 @@ export default {
shadowBlur: 10, shadowBlur: 10,
shadowOffsetY: 20, shadowOffsetY: 20,
}, },
},
itemStyle: { itemStyle: {
normal: {
color: 'rgba(72,216,191, 0.3)', color: 'rgba(72,216,191, 0.3)',
borderWidth: 10, borderWidth: 10,
/*shadowColor: 'rgba(72,216,191, 0.3)', /*shadowColor: 'rgba(72,216,191, 0.3)',
shadowBlur: 100,*/ shadowBlur: 100,*/
borderColor: '#A9F387', borderColor: '#A9F387',
}, },
},
smooth: true, smooth: true,
}, },
], ],
@ -2524,17 +2731,32 @@ $aqi-unhealthy-color: #f44336;
.map-container { .map-container {
width: 100%; width: 100%;
height: 500px; /* 减少高度 */ height: 500px;
position: relative; position: relative;
border-radius: 15px; border-radius: 15px;
overflow: hidden; overflow: hidden; /* 改为hidden,避免内容溢出 */
z-index: 10;
.map { .map {
width: 100%; width: 100% !important;
height: 100%; height: 100% !important;
/* 可根据需要添加地图相关样式 */ min-height: 500px;
position: relative;
z-index: 10;
background: transparent;
/* 确保地图容器有明确的尺寸 */
display: block;
} }
} }
/* 确保ECharts地图容器正确显示 */
.map :deep(.ec-extension-bmap) {
position: absolute !important;
width: 100% !important;
height: 100% !important;
z-index: 10 !important;
background-color: transparent !important;
}
} }
/* 响应式设计 */ /* 响应式设计 */

View File

@ -1,98 +1,211 @@
<template> <!DOCTYPE html>
<div class="dashboard-editor-container"> <html>
<head>
<panel-group @handleSetLineChartData="handleSetLineChartData" /> <title>百度地图测试页面</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;"> <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<line-chart :chart-data="lineChartData" /> <style type="text/css">
</el-row> html, body {
width: 100%;
<el-row :gutter="32"> height: 100%;
<el-col :xs="24" :sm="24" :lg="8"> margin: 0;
<div class="chart-wrapper"> padding: 20px;
<raddar-chart /> box-sizing: border-box;
</div> background-color: #f5f5f5;
</el-col>
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<pie-chart />
</div>
</el-col>
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<bar-chart />
</div>
</el-col>
</el-row>
</div>
</template>
<script>
import PanelGroup from './dashboard/PanelGroup'
import LineChart from './dashboard/LineChart'
import RaddarChart from './dashboard/RaddarChart'
import PieChart from './dashboard/PieChart'
import BarChart from './dashboard/BarChart'
const lineChartData = {
newVisitis: {
expectedData: [100, 120, 161, 134, 105, 160, 165],
actualData: [120, 82, 91, 154, 162, 140, 145]
},
messages: {
expectedData: [200, 192, 120, 144, 160, 130, 140],
actualData: [180, 160, 151, 106, 145, 150, 130]
},
purchases: {
expectedData: [80, 100, 121, 104, 105, 90, 100],
actualData: [120, 90, 100, 138, 142, 130, 130]
},
shoppings: {
expectedData: [130, 140, 141, 142, 145, 150, 160],
actualData: [120, 82, 91, 154, 162, 140, 130]
} }
} .container {
width: 100%;
height: 600px;
background-color: #fff;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 20px;
box-sizing: border-box;
}
#map-container {
width: 100%;
height: 100%;
border: 1px solid #ddd;
border-radius: 5px;
}
.info {
margin-bottom: 20px;
padding: 15px;
background-color: #e3f2fd;
border-radius: 5px;
border-left: 4px solid #2196F3;
}
.info h3 {
margin: 0 0 10px 0;
color: #1976D2;
}
.info p {
margin: 5px 0;
color: #424242;
}
.status {
display: inline-block;
padding: 5px 10px;
border-radius: 3px;
font-size: 12px;
margin-left: 10px;
}
.status.success {
background-color: #4CAF50;
color: white;
}
.status.error {
background-color: #f44336;
color: white;
}
.status.loading {
background-color: #FF9800;
color: white;
}
</style>
</head>
<body>
<div class="info">
<h3>百度地图测试页面</h3>
<p>AK配置: <span id="ak-status" class="status loading">检查中...</span></p>
<p>BMap状态: <span id="bmap-status" class="status loading">等待加载...</span></p>
<p>地图容器: <span id="container-status" class="status loading">检查中...</span></p>
</div>
export default { <div class="container">
name: 'Index', <div id="map-container"></div>
components: { </div>
PanelGroup,
LineChart, <script type="text/javascript">
RaddarChart, // 从环境变量获取AK(这里需要手动设置,因为这是静态HTML文件)
PieChart, // 实际项目中应该从 process.env.VUE_APP_BAI_DU_AK 获取
BarChart const AK = 's9mUXN9a7bKgVu6U6Z3bXhKSxtz97YPI'; // 请替换为您的AK
},
data() { // 更新状态显示
return { function updateStatus(elementId, text, className) {
lineChartData: lineChartData.newVisitis const element = document.getElementById(elementId);
element.textContent = text;
element.className = 'status ' + className;
} }
},
methods: { // 检查AK
handleSetLineChartData(type) { if (AK && AK !== 'your_baidu_map_ak_here') {
this.lineChartData = lineChartData[type] updateStatus('ak-status', '已配置', 'success');
} else {
updateStatus('ak-status', '未配置', 'error');
document.getElementById('map-container').innerHTML = '<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#f44336;">请在代码中配置百度地图AK</div>';
} }
// 检查容器
const container = document.getElementById('map-container');
if (container) {
updateStatus('container-status', '存在 (' + container.offsetWidth + 'x' + container.offsetHeight + ')', 'success');
} else {
updateStatus('container-status', '不存在', 'error');
}
// 加载百度地图API
console.log('[测试页面] 开始加载百度地图API...');
console.log('[测试页面] AK:', AK);
// 使用callback方式加载
window.onBMapCallback = function() {
console.log('[测试页面] 百度地图API加载成功');
updateStatus('bmap-status', '已加载', 'success');
if (typeof BMap === 'undefined') {
console.error('[测试页面] BMap未定义');
updateStatus('bmap-status', 'BMap未定义', 'error');
return;
}
try {
// 创建地图实例
var map = new BMap.Map("map-container");
console.log('[测试页面] 地图实例创建成功');
// 创建中心点坐标(北京天安门)
var point = new BMap.Point(116.404, 39.915);
console.log('[测试页面] 中心点:', point);
// 初始化地图,设置中心点和缩放级别
map.centerAndZoom(point, 15);
console.log('[测试页面] 地图中心点和缩放级别已设置');
// 启用一些交互功能
map.enableScrollWheelZoom(true);
map.enableDoubleClickZoom();
console.log('[测试页面] 地图交互功能已启用');
// 添加默认缩放平移控件
map.addControl(new BMap.NavigationControl());
console.log('[测试页面] 地图控件已添加');
// 添加标记点
var marker = new BMap.Marker(point);
map.addOverlay(marker);
console.log('[测试页面] 标记点已添加');
// 添加信息窗口
var infoWindow = new BMap.InfoWindow(
'<div style="padding:10px;">' +
'<h3 style="margin:0 0 10px 0;">天安门广场</h3>' +
'<p style="margin:5px 0;">位于北京市中心,是世界上最大的城市广场之一。</p>' +
'<p style="margin:5px 0;">坐标:116.404, 39.915</p>' +
'</div>',
{ width: 250, height: 150 }
);
// 标记点点击事件
marker.addEventListener("click", function() {
map.openInfoWindow(infoWindow, point);
});
console.log('[测试页面] 地图初始化完成');
// 检查地图是否正常显示
setTimeout(function() {
const mapContainer = map.getContainer();
const tiles = mapContainer.querySelectorAll('img');
console.log('[测试页面] 地图瓦片数量:', tiles.length);
if (tiles.length > 0) {
console.log('[测试页面] ✓ 地图瓦片已加载,地图应该正常显示');
} else {
console.warn('[测试页面] ⚠ 地图瓦片未加载,可能存在问题');
}
}, 2000);
} catch (error) {
console.error('[测试页面] 地图初始化失败:', error);
updateStatus('bmap-status', '初始化失败: ' + error.message, 'error');
document.getElementById('map-container').innerHTML =
'<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#f44336;">' +
'地图初始化失败: ' + error.message +
'</div>';
}
};
// 如果AK已配置,加载百度地图API
if (AK && AK !== 'your_baidu_map_ak_here') {
const script = document.createElement('script');
script.type = 'text/javascript';
const protocol = document.location.protocol === 'https:' ? 'https:' : 'http:';
const apiUrl = protocol + '//api.map.baidu.com/api?v=2.0&ak=' + AK + '&callback=onBMapCallback';
script.src = apiUrl;
console.log('[测试页面] 正在加载:', apiUrl);
script.onerror = function(error) {
console.error('[测试页面] 脚本加载失败:', error);
updateStatus('bmap-status', '脚本加载失败', 'error');
};
script.onload = function() {
console.log('[测试页面] 脚本标签加载完成,等待回调...');
};
document.head.appendChild(script);
} }
}
</script> </script>
</body>
</html>
<style lang="scss" scoped>
.dashboard-editor-container {
padding: 32px;
background-color: rgb(240, 242, 245);
position: relative;
.chart-wrapper {
background: #fff;
padding: 16px 16px 0;
margin-bottom: 32px;
}
}
@media (max-width:1024px) {
.chart-wrapper {
padding: 8px;
}
}
</style>