From fb551b446015880d1e6e2070cd1fcb8765b4057d Mon Sep 17 00:00:00 2001 From: pyt <626651354@qq.com> Date: 星期一, 24 三月 2025 15:27:48 +0800 Subject: [PATCH] Merge branch 'master' of http://120.76.84.145:10101/gitblit/r/H5/chongzhou-screen --- src/components/datascreen/map.vue | 679 +++++++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 501 insertions(+), 178 deletions(-) diff --git a/src/components/datascreen/map.vue b/src/components/datascreen/map.vue index dd38884..e25ed20 100644 --- a/src/components/datascreen/map.vue +++ b/src/components/datascreen/map.vue @@ -1,181 +1,303 @@ <template> - <div class="map-container"> + <!-- 地图容器 --> + <div class="map-container" :class="{ loading: isLoading }"> + <!-- echarts地图渲染区域 --> <div class="chart" ref="myMap" /> + <!-- 新增加载提示 --> + <div v-if="isLoading" class="loading-tip">地图加载中...</div> </div> </template> <script> -import mapBg from "@/assets/map/mapBg.png"; -import tooltipBg from "@/assets/map/tooltipBg.png"; -import tooltipBlue from "@/assets/map/tooltipBlue.png"; -import tooltipOrange from "@/assets/map/tooltipOrange.png"; +// 导入防抖函数 +import _ from 'lodash'; + +// 导入地图相关的背景和图标资源 +import mapBg from "@/assets/map/mapBg.png"; // 地图背景图片 +import tooltipBg from "@/assets/map/tooltipBg.png"; // 提示框背景图片 +import tooltipBlue from "@/assets/map/tooltipBlue.png"; // 蓝色标记图标 +import tooltipOrange from "@/assets/map/tooltipOrange.png"; // 橙色标记图标 + +// 定义地图常量配置 +const MAP_CONSTANTS = { + // 阴影相关配置 + SHADOW: { + COLOR: "#00eaff", + BLUR: 25, + OFFSET: 0 + }, + // 标记点相关配置 + MARKER: { + SIZE: [20, 30], + OFFSET: [0, -15], + Z_INDEX: 20, + COLORS: { + BLUE: '#00eaff', + ORANGE: '#ff8e3a' + } + }, + // 提示框相关配置 + TOOLTIP: { + OFFSET: [-100, -160], + WIDTH: '211px', + FONT_FAMILY: 'pangmenzhengdao' + }, + // 地图图层配置 + LAYERS: { + 1: { // 最底层 + top: "15%", + bottom: "10%", + borderColor: "#00A0F5", + borderWidth: 1 + }, + 2: { // 中间层 + top: "16%", + bottom: "9%", + borderColor: "#0C93C5", + borderWidth: 1 + }, + 3: { // 最顶层 + top: "17%", + bottom: "8%", + borderColor: "#0A5C83", + borderWidth: 2 + } + } +}; export default { name: 'Map', + // 接收父组件传递的数据 props: { data: { - type: Array, - default: () => [] + type: Object, + default: () => ({}), + // 新增:数据格式验证 + validator(value) { + if (!value.mapResponses) return true; + return Array.isArray(value.mapResponses) && + value.mapResponses.every(item => + item.street && + typeof item.householdCount === 'number' && + typeof item.personCount === 'number' + ); + } } }, data() { return { - mapBg: null, // 地图背景图片 - // 地图标记点数据 - mapPoints: [ - { name: "点位1", value: [103.6672, 30.6333], type: "blue", symbolSize: 32 }, - { name: "点位2", value: [103.6772, 30.6533], type: "orange", symbolSize: 32 }, - { name: "点位3", value: [103.6572, 30.6433], type: "blue", symbolSize: 32 }, - { name: "点位4", value: [103.6872, 30.6633], type: "orange", symbolSize: 32 }, - { name: "点位5", value: [103.6472, 30.6233], type: "blue", symbolSize: 32 } - ] + mapBg: null, // 地图背景图片实例 + mapPoints: [], // 存储处理后的地图标记点数据 + chart: null, // echarts实例 + isLoading: false, // 新增:加载状态标志 + errorMessage: '' // 新增:错误信息 } }, - created() { - if (Object.keys(this.data).length > 0) { - console.log(this.data); - // this.mapPoints = this.data.mapResponses; + + // 新增:计算属性 + computed: { + // 检查数据是否有效 + hasValidData() { + return this.data && this.data.mapResponses && this.data.mapResponses.length > 0; } }, + mounted() { - this.initMap(); + // 组件挂载后初始化地图 + this.initEventListeners(); // 新增:初始化事件监听 + this.$nextTick(() => { + if (this.hasValidData) { + this.initMap(); + } + }); }, methods: { - initMap() { - let mapJson = require("@/utils/map/chongzhou.json"); - let myChart = this.$echarts.init(this.$refs.myMap); - myChart.showLoading(); - myChart.hideLoading(); - this.$echarts.registerMap("myMap", mapJson); - - let option = { - tooltip: { - show: false - }, - geo: [ - this.getGeoLayers(1), - this.getGeoLayers(2), - this.getGeoLayers(3) - ], - series: [ - { - type: "map", - name: "地图", - selectedMode: false, - aspectScale: 1, - zoom: 1, - roam: false, - regionHeight: 2, - map: "myMap", - z: 10, - top: "14%", - bottom: "11%", - viewControl: { - distance: 115, - alpha: 40, - rotateSensitivity: [0, 0], - beta: 0 - }, - label: { - show: false, - textStyle: { - color: "#fff", - fontSize: 10, - fontWeight: "normal", - opacity: 1 - } - }, - itemStyle: { - areaColor: { - type: 'pattern', - image: mapBg, - repeat: 'no-repeat', - imageSize: '100%', - patternSize: [815, 534], - patternPosition: [0, 0] - }, - borderColor: "#324D6B", - borderWidth: 2, - }, - emphasis: { - disabled: false, - label: { - show: false, - textStyle: { - color: "#fff", - fontSize: 10, - fontWeight: "normal", - opacity: 1 - } - }, - itemStyle: { - areaColor: { - type: 'pattern', - image: this.mapBg, - repeat: 'no-repeat', - imageSize: '100%', - patternSize: [815, 534], - patternPosition: [0, 0] - }, - borderColor: "#00eaff", - borderWidth: 2, - shadowColor: "#00eaff", - shadowBlur: 25, - shadowOffsetX: 0, - shadowOffsetY: 0, - opacity: 1 - } - }, - select: { - disabled: true - }, - light: { - main: { - color: "#00eaff", - intensity: 1.2, - shadow: true, - shadowQuality: 'ultra', - alpha: 40, - beta: 0 - }, - ambient: { - color: "#00eaff", - intensity: 0.3 - } - }, - tooltip: { - show: false - }, - silent: false // 禁用鼠标事件 - }, - this.getMarkerSeries("blue"), - this.getMarkerSeries("orange") - ] - }; - myChart.setOption(option); + // 新增:初始化事件监听器 + initEventListeners() { + window.addEventListener('resize', this.handleResize); + // 新增:错误监听 + window.addEventListener('error', this.handleGlobalError, true); }, - // 生成标记点配置的方法 + + // 新增:全局错误处理 + handleGlobalError(event) { + if (event.target.tagName === 'SCRIPT') { + this.handleError(new Error('地图资源加载失败'), '资源加载'); + } + }, + + // 统一的错误处理方法 + handleError(error, context) { + console.error(`地图组件错误 - ${context}:`, error); + this.errorMessage = `加载失败: ${error.message}`; + this.isLoading = false; + // 可以添加错误上报逻辑 + }, + + // 处理窗口大小变化,使用防抖优化 + handleResize: _.debounce(function () { + if (this.chart) { + this.chart.resize(); + } + }, 300), + + // 处理地图数据,将原始数据转换为echarts可用的格式 + processMapData() { + if (!this.data.mapResponses) return; + + // 获取地图JSON数据 + const mapJson = require("@/utils/map/chongzhou.json"); + + // 遍历处理每个地点数据 + this.mapPoints = this.data.mapResponses.map(point => { + // 在地图JSON中查找对应的地理信息 + const feature = mapJson.features.find(f => f.properties.name === point.street); + + if (!feature) { + console.warn(`未找到街道: ${point.street} 的坐标`); + return null; + } + + // 判断是街道还是乡镇,用于显示不同的图标 + const isStreet = point.street.includes('街道'); + + // 获取中心点坐标 + let center; + if (feature.properties.center) { + // 优先使用预设的中心点坐标 + center = feature.properties.center; + } else if (feature.geometry && feature.geometry.coordinates) { + // 否则根据地理数据计算中心点 + if (feature.geometry.type === 'Polygon') { + // 处理单个多边形 + const coordinates = feature.geometry.coordinates[0]; + center = this.calculateCenter(coordinates); + } else if (feature.geometry.type === 'MultiPolygon') { + // 处理多个多边形 + const coordinates = feature.geometry.coordinates[0][0]; + center = this.calculateCenter(coordinates); + } + } + + if (!center) { + console.warn(`无法获取 ${point.street} 的中心点坐标`); + return null; + } + + // 返回处理后的标记点数据 + return { + name: point.street, + value: center, + type: isStreet ? 'orange' : 'blue', // 街道使用橙色,乡镇使用蓝色 + symbolSize: 32, + householdCount: point.householdCount, // 安置户数 + personCount: point.personCount // 安置人数 + }; + }).filter(Boolean); // 过滤掉无效的数据 + }, + + // 计算多边形的中心点坐标 + calculateCenter(coordinates) { + if (!coordinates || coordinates.length === 0) return null; + + // 计算所有顶点的平均值作为中心点 + let sumX = 0; + let sumY = 0; + let count = coordinates.length; + + coordinates.forEach(coord => { + sumX += coord[0]; + sumY += coord[1]; + }); + + return [sumX / count, sumY / count]; + }, + + // 新增:检查地图实例状态 + checkChartStatus() { + if (!this.chart) { + throw new Error('地图实例未初始化'); + } + return true; + }, + + // 初始化地图方法优化 + initMap: _.debounce(async function () { + try { + if (!this.$refs.myMap) { + throw new Error('地图DOM元素未准备好'); + } + + this.isLoading = true; + this.errorMessage = ''; + + // 新增:等待资源加载 + await this.loadMapResources(); + + this.processMapData(); + + if (this.chart) { + this.chart.dispose(); + } + + this.chart = this.$echarts.init(this.$refs.myMap); + this.chart.showLoading(); + + const mapJson = require("@/utils/map/chongzhou.json"); + this.$echarts.registerMap("myMap", mapJson); + + // 设置配置项 + this.chart.setOption(this.getMapOption()); + this.chart.hideLoading(); + + this.isLoading = false; + } catch (error) { + this.handleError(error, '初始化地图'); + } + }, 300), + + // 新增:加载地图资源 + async loadMapResources() { + try { + const mapImage = new Image(); + mapImage.src = mapBg; + await new Promise((resolve, reject) => { + mapImage.onload = resolve; + mapImage.onerror = reject; + }); + this.mapBg = mapImage; + } catch (error) { + throw new Error('地图背景图片加载失败'); + } + }, + + // 生成标记点系列的配置 getMarkerSeries(type) { return { - name: type === "blue" ? "蓝色标记" : "橙色标记", - type: "scatter", - coordinateSystem: "geo", - geoIndex: 0, - data: this.mapPoints.filter(point => point.type === type), - symbol: `image://${type === "blue" ? tooltipBlue : tooltipOrange}`, - symbolSize: [20, 30], - symbolOffset: [0, -15], - z: 20, + name: type === "blue" ? "蓝色标记" : "橙色标记", // 系列名称 + type: "scatter", // 散点图类型 + coordinateSystem: "geo", // 使用地理坐标系 + geoIndex: 0, // 关联第一个geo组件 + data: this.mapPoints.filter(point => point.type === type), // 筛选对应类型的数据 + symbol: `image://${type === "blue" ? tooltipBlue : tooltipOrange}`, // 标记图标 + symbolSize: [20, 30], // 标记大小 + symbolOffset: [0, -15], // 标记偏移量 + z: 20, // 层级 + + // 提示框配置 tooltip: { - show: true, - trigger: 'item', - backgroundColor: 'transparent', - borderWidth: 0, - padding: [0, 0, 0, 0], + show: true, // 显示提示框 + trigger: 'item', // 触发类型:数据项 + backgroundColor: 'transparent', // 背景透明 + borderWidth: 0, // 无边框 + padding: [0, 0, 0, 0], // 内边距 + // 提示框位置 position: function (point) { return [point[0] - 100, point[1] - 160]; }, + // 提示框内容格式化 formatter: function (params) { + // 返回自定义HTML结构的提示框内容 return `<div style=" background: url(${tooltipBg}) no-repeat center center; background-size: 100% 100%; @@ -189,83 +311,284 @@ <div style="color: #fff; font-size: 16px; margin: 21px 0;font-weight: bold;">${params.name}</div> <div style="display: flex; justify-content: space-around; width: 100%;"> <div style="text-align: center;"> - <div style="color: rgba(102, 255, 255, 1); font-size: 20px;margin-bottom: 8px;font-family: 'pangmenzhengdao';">99999</div> + <div style="color: rgba(102, 255, 255, 1); font-size: 20px;margin-bottom: 8px;font-family: 'pangmenzhengdao';">${params.data.householdCount}</div> <div style="color: #fff; font-size: 12px;">安置户数(户)</div> </div> <div style="text-align: center;"> - <div style="color: rgba(102, 255, 255, 1); font-size: 20px;margin-bottom: 8px;font-family: 'pangmenzhengdao';">99999</div> + <div style="color: rgba(102, 255, 255, 1); font-size: 20px;margin-bottom: 8px;font-family: 'pangmenzhengdao';">${params.data.personCount}</div> <div style="color: #fff; font-size: 12px;">安置人数(人)</div> </div> </div> </div>`; } }, + + // 标签配置 label: { - show: false + show: false // 不显示标签 }, + + // 高亮效果配置 emphasis: { - scale: true, - scaleSize: 1.2, + scale: true, // 启用缩放效果 + scaleSize: 1.2, // 放大比例 itemStyle: { - shadowBlur: 10, - shadowColor: type === "blue" ? '#00eaff' : '#ff8e3a' + shadowBlur: 10, // 阴影模糊大小 + shadowColor: type === "blue" ? '#00eaff' : '#ff8e3a' // 根据类型设置阴影颜色 } } }; }, - // 生成geo层级配置的方法 + + // 生成地图图层的配置 + // 参数 index: 图层索引(1-3),用于创建三层叠加的立体效果 getGeoLayers(index) { + // 定义每层的具体配置 const layerConfig = { - 1: { top: "15%", bottom: "10%", borderColor: "#00A0F5", borderWidth: 1 }, - 2: { top: "16%", bottom: "9%", borderColor: "#0C93C5", borderWidth: 1 }, - 3: { top: "17%", bottom: "8%", borderColor: "#0A5C83", borderWidth: 2 } + 1: { // 最底层 + top: "15%", // 距顶部距离 + bottom: "10%", // 距底部距离 + borderColor: "#00A0F5", // 边框颜色 + borderWidth: 1 // 边框宽度 + }, + 2: { // 中间层 + top: "16%", + bottom: "9%", + borderColor: "#0C93C5", + borderWidth: 1 + }, + 3: { // 最顶层 + top: "17%", + bottom: "8%", + borderColor: "#0A5C83", + borderWidth: 2 + } }; const config = layerConfig[index]; return { - map: "myMap", - aspectScale: 1, - zoom: 1, - roam: false, - top: config.top, - bottom: config.bottom, - z: index, + map: "myMap", // 使用注册的地图名称 + aspectScale: 1, // 地图长宽比 + zoom: 1, // 缩放级别 + roam: false, // 是否开启鼠标缩放和平移漫游 + top: config.top, // 距容器顶部距离 + bottom: config.bottom, // 距容器底部距离 + z: index, // 图层层级,值越大越靠前 + + // 地图区域的样式设置 itemStyle: { - areaColor: "transparent", - borderColor: config.borderColor, - borderWidth: config.borderWidth, + areaColor: "transparent", // 区域填充色为透明 + borderColor: config.borderColor, // 边框颜色 + borderWidth: config.borderWidth, // 边框宽度 + // 仅在最顶层添加阴影效果 ...(index === 3 ? { - shadowColor: "#00eaff", - shadowBlur: 25, - shadowOffsetX: 0, - shadowOffsetY: 0, - opacity: 1 + shadowColor: "#00eaff", // 阴影颜色 + shadowBlur: 25, // 阴影模糊大小 + shadowOffsetX: 0, // 阴影水平偏移 + shadowOffsetY: 0, // 阴影垂直偏移 + opacity: 1 // 不透明度 } : {}) }, + + // 高亮状态配置 emphasis: { - disabled: true + disabled: true // 禁用高亮效果 }, + + // 选中状态配置 select: { - disabled: true + disabled: true // 禁用选中状态 }, + + // 提示框配置 + tooltip: { + show: false // 禁用提示框 + }, + + silent: true // 禁用鼠标事件响应 + }; + }, + + // 新增:获取地图完整配置项 + getMapOption() { + return { + // 关闭默认的tooltip tooltip: { show: false }, - silent: true + // geo组件配置,用于地图的三层叠加效果 + geo: [ + this.getGeoLayers(1), // 最底层 + this.getGeoLayers(2), // 中间层 + this.getGeoLayers(3) // 最顶层 + ], + // 系列列表 + series: [ + { + type: "map", // 图表类型为地图 + name: "地图", // 系列名称 + selectedMode: false, // 禁用选中模式 + aspectScale: 1, // 地图长宽比 + zoom: 1, // 地图缩放比例 + roam: false, // 禁用地图平移缩放 + regionHeight: 2, // 三维地图的高度 + map: "myMap", // 使用注册的地图名称 + z: 10, // 图层的层级 + top: "14%", // 距离容器顶部的距离 + bottom: "11%", // 距离容器底部的距离 + + // 地图区域的样式 + itemStyle: { + areaColor: { // 区域填充样式 + type: 'pattern', // 使用图案填充 + image: mapBg, // 背景图片 + repeat: 'no-repeat', // 图片不重复 + imageSize: '100%', // 图片大小 + patternSize: [815, 534], // 图案大小 + patternPosition: [0, 0] // 图案位置 + }, + borderColor: "#324D6B", // 边框颜色 + borderWidth: 2, // 边框宽度 + }, + + // 高亮状态的配置 + emphasis: { + disabled: false, // 启用高亮效果 + label: { + show: true, // 显示标签 + textStyle: { // 标签文字样式 + color: "#fff", + fontSize: 10, + fontWeight: "normal", + opacity: 1 + } + }, + itemStyle: { // 高亮时的样式 + areaColor: { // 区域填充,保持与正常状态相同的背景 + type: 'pattern', + image: mapBg, + repeat: 'no-repeat', + imageSize: '100%', + patternSize: [815, 534], + patternPosition: [0, 0] + }, + borderColor: MAP_CONSTANTS.SHADOW.COLOR, // 高亮时的边框颜色 + borderWidth: 2, // 高亮时的边框宽度 + shadowColor: 'rgba(0, 234, 255, 0.8)', // 阴影颜色,添加透明度 + shadowBlur: 40, // 增大阴影模糊半径 + shadowOffsetX: 0, // 保持阴影水平偏移为0 + shadowOffsetY: 0, // 保持阴影垂直偏移为0 + glow: { + width: 20, // 发光宽度 + color: 'rgba(0, 234, 255, 0.2)' // 发光颜色 + }, + opacity: 0.9 // 略微降低不透明度,增强光晕效果 + } + }, + + // 选中状态配置 + select: { + disabled: true // 禁用选中状态 + }, + + // 标签配置 + label: { + show: true, // 显示标签 + textStyle: { // 标签文字样式 + color: "#fff", + fontSize: 10, + fontWeight: "normal", + opacity: 1 + } + }, + + tooltip: { + show: false // 禁用该系列的tooltip + }, + silent: false // 允许鼠标事件 + }, + // 添加蓝色和橙色标记点系列 + this.getMarkerSeries("blue"), // 蓝色标记点系列 + this.getMarkerSeries("orange") // 橙色标记点系列 + ] }; }, + }, + watch: { + // 监听数据变化,更新地图 + data: { + handler(newVal) { + if (this.hasValidData) { + this.$nextTick(() => { + this.initMap(); + }); + } + }, + immediate: false // 不立即执行,等待mounted + } + }, + beforeDestroy() { + // 组件销毁前清理echarts实例 + window.removeEventListener('resize', this.handleResize); + window.removeEventListener('error', this.handleGlobalError, true); + if (this.chart) { + this.chart.dispose(); + this.chart = null; + } } } </script> -<style scoped> +<style lang="less" scoped> +/* 地图容器样式 */ .map-container { width: 815px; height: 534px; + position: relative; + + // 加载状态样式 + &.loading { + &::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 51, 0.6); + display: flex; + justify-content: center; + align-items: center; + } + } + + // 新增:加载提示样式 + .loading-tip { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: #fff; + font-size: 14px; + z-index: 10; + } + + // 新增:错误提示样式 + .error-message { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: #ff4d4f; + font-size: 14px; + z-index: 10; + } } +/* 图表容器样式 */ .chart { width: 100%; height: 100%; + transition: all 0.3s ease; } </style> \ No newline at end of file -- Gitblit v1.7.1