| 
									
										
										
										
											2025-03-19 09:17:05 +08:00
										 |  |  | <template> | 
					
						
							|  |  |  |   <div ref="mapContainer" class="map-container"></div> | 
					
						
							|  |  |  | </template> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <script setup> | 
					
						
							| 
									
										
										
										
											2025-03-20 18:31:12 +08:00
										 |  |  | import { ref, onMounted, watch,defineEmits } from 'vue' | 
					
						
							| 
									
										
										
										
											2025-03-19 09:17:05 +08:00
										 |  |  | import * as d3 from 'd3' | 
					
						
							|  |  |  | import chinaData from './newJson.json' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const props = defineProps({ | 
					
						
							|  |  |  |   coordinates: { | 
					
						
							|  |  |  |     type: Array, | 
					
						
							|  |  |  |     default: () => [ | 
					
						
							|  |  |  |       [116.405285, 39.904989], // 北京
 | 
					
						
							|  |  |  |       [121.472644, 31.231706]  // 上海
 | 
					
						
							|  |  |  |     ] | 
					
						
							| 
									
										
										
										
											2025-03-20 18:31:12 +08:00
										 |  |  |   }, | 
					
						
							|  |  |  |   qvehuan: { | 
					
						
							|  |  |  |     type: Boolean, | 
					
						
							|  |  |  |     default: () => false | 
					
						
							| 
									
										
										
										
											2025-03-19 09:17:05 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | }) | 
					
						
							| 
									
										
										
										
											2025-03-20 18:31:12 +08:00
										 |  |  | const emit = defineEmits(['qvehuan1']); | 
					
						
							| 
									
										
										
										
											2025-03-19 09:17:05 +08:00
										 |  |  | const mapContainer = ref(null) | 
					
						
							|  |  |  | let svg, mapGroup, markersGroup, zoom | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const initMap = () => { | 
					
						
							|  |  |  |   const width = 1000 | 
					
						
							|  |  |  |   const height = 800 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const projection = d3.geoMercator() | 
					
						
							|  |  |  |     .center([104, 37]) | 
					
						
							|  |  |  |     .scale(600) | 
					
						
							|  |  |  |     .translate([width / 2, height / 2]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const path = d3.geoPath().projection(projection) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (svg) svg.selectAll("*").remove() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   svg = d3.select(mapContainer.value) | 
					
						
							|  |  |  |     .append('svg') | 
					
						
							|  |  |  |     .attr('width', width) | 
					
						
							|  |  |  |     .attr('height', height) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // 修改分组结构
 | 
					
						
							|  |  |  |   const mainGroup = svg.append('g') | 
					
						
							|  |  |  |   mapGroup = mainGroup.append('g') | 
					
						
							|  |  |  |   markersGroup = mainGroup.append('g') // 标记层放入主缩放组
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   mapGroup.append('g') | 
					
						
							|  |  |  |     .selectAll('path') | 
					
						
							|  |  |  |     .data(chinaData.features) | 
					
						
							|  |  |  |     .enter() | 
					
						
							|  |  |  |     .append('path') | 
					
						
							|  |  |  |     .attr('d', path) | 
					
						
							|  |  |  |     .attr('fill', '#e7e7e7') | 
					
						
							|  |  |  |     .attr('stroke', '#fff') | 
					
						
							| 
									
										
										
										
											2025-03-20 18:31:12 +08:00
										 |  |  |   mapGroup.append('g') | 
					
						
							|  |  |  |     .selectAll('text') | 
					
						
							|  |  |  |     .data(chinaData.features) | 
					
						
							|  |  |  |     .enter() | 
					
						
							|  |  |  |     .append('text') | 
					
						
							|  |  |  |     .attr('x', d => { | 
					
						
							|  |  |  |       const centroid = path.centroid(d) | 
					
						
							|  |  |  |       return centroid[0] | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |     .attr('y', d => { | 
					
						
							|  |  |  |       const centroid = path.centroid(d) | 
					
						
							|  |  |  |       return centroid[1] | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |     .text(d => d.properties.name || '') // 确保JSON数据包含properties.name
 | 
					
						
							|  |  |  |     .attr('text-anchor', 'middle') | 
					
						
							|  |  |  |     .attr('dominant-baseline', 'central') | 
					
						
							|  |  |  |     .attr('fill', '#333') | 
					
						
							|  |  |  |     .style('font-size', '12px') | 
					
						
							|  |  |  |     .style('font-family', 'Arial') | 
					
						
							|  |  |  |     .style('pointer-events', 'none') // 防止干扰交互
 | 
					
						
							| 
									
										
										
										
											2025-03-19 09:17:05 +08:00
										 |  |  |   zoom = d3.zoom() | 
					
						
							|  |  |  |     .scaleExtent([0.2, 8]) | 
					
						
							|  |  |  |     .on('zoom', (event) => { | 
					
						
							|  |  |  |       mainGroup.attr('transform', event.transform) | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |   svg.call(zoom) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const updateRoute = () => { | 
					
						
							|  |  |  |   const projection = d3.geoMercator() | 
					
						
							|  |  |  |     .center([104, 37]) | 
					
						
							|  |  |  |     .scale(600) | 
					
						
							|  |  |  |     .translate([1000 / 2, 800 / 2]) | 
					
						
							| 
									
										
										
										
											2025-03-20 18:31:12 +08:00
										 |  |  |   if (initqie.value) { | 
					
						
							|  |  |  |     // debugger
 | 
					
						
							|  |  |  |     mapGroup.selectAll('.route-path').remove() | 
					
						
							|  |  |  |     markersGroup.selectAll('*').remove() | 
					
						
							|  |  |  |     initqie.value = false | 
					
						
							|  |  |  |     emit('qvehuan1', initqie.value); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-03-19 09:17:05 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const routeData = { | 
					
						
							|  |  |  |     type: "LineString", | 
					
						
							|  |  |  |     coordinates: props.coordinates | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   mapGroup.append('path') | 
					
						
							|  |  |  |     .datum(routeData) | 
					
						
							|  |  |  |     .attr('class', 'route-path') | 
					
						
							|  |  |  |     .attr('d', d3.geoPath().projection(projection)) | 
					
						
							|  |  |  |     .attr('fill', 'none') | 
					
						
							|  |  |  |     .attr('stroke', '#f00') | 
					
						
							| 
									
										
										
										
											2025-03-20 18:31:12 +08:00
										 |  |  |     .attr('stroke-width', 1) | 
					
						
							|  |  |  |     .attr('stroke-dasharray', function () { | 
					
						
							| 
									
										
										
										
											2025-03-19 09:17:05 +08:00
										 |  |  |       return this.getTotalLength() | 
					
						
							|  |  |  |     }) | 
					
						
							| 
									
										
										
										
											2025-03-20 18:31:12 +08:00
										 |  |  |     .attr('stroke-dashoffset', function () { | 
					
						
							| 
									
										
										
										
											2025-03-19 09:17:05 +08:00
										 |  |  |       return this.getTotalLength() | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |     .transition() | 
					
						
							|  |  |  |     .duration(2000) | 
					
						
							|  |  |  |     .attr('stroke-dashoffset', 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // 修改标记点绘制逻辑
 | 
					
						
							|  |  |  |   props.coordinates.forEach((coord, i) => { | 
					
						
							|  |  |  |     if (!Array.isArray(coord) || coord.length !== 2) return | 
					
						
							|  |  |  |     const lon = Number(coord[0]) | 
					
						
							|  |  |  |     const lat = Number(coord[1]) | 
					
						
							| 
									
										
										
										
											2025-03-20 18:31:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-19 09:17:05 +08:00
										 |  |  |     if (isNaN(lon) || isNaN(lat)) return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const [x, y] = projection([lon, lat]) | 
					
						
							| 
									
										
										
										
											2025-03-20 18:31:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // 添加缩放补偿逻
 | 
					
						
							| 
									
										
										
										
											2025-03-19 09:17:05 +08:00
										 |  |  |     markersGroup.append('circle') | 
					
						
							|  |  |  |       .attr('cx', x) | 
					
						
							|  |  |  |       .attr('cy', y) | 
					
						
							| 
									
										
										
										
											2025-03-20 18:31:12 +08:00
										 |  |  |       .attr('r', 1) // 根据缩放级别调整半径
 | 
					
						
							| 
									
										
										
										
											2025-03-19 09:17:05 +08:00
										 |  |  |       .attr('fill', '#ff4757') | 
					
						
							|  |  |  |       .attr('stroke', 'white') | 
					
						
							| 
									
										
										
										
											2025-03-20 18:31:12 +08:00
										 |  |  |       .attr('stroke-width', 0.1) // 同步调整描边宽度
 | 
					
						
							| 
									
										
										
										
											2025-03-19 09:17:05 +08:00
										 |  |  |   }) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-03-20 18:31:12 +08:00
										 |  |  | const initqie = ref(false) | 
					
						
							| 
									
										
										
										
											2025-03-19 09:17:05 +08:00
										 |  |  | onMounted(() => { | 
					
						
							| 
									
										
										
										
											2025-03-20 18:31:12 +08:00
										 |  |  |   if (props.coordinates) { | 
					
						
							| 
									
										
										
										
											2025-03-19 09:17:05 +08:00
										 |  |  |     initMap() | 
					
						
							|  |  |  |     updateRoute() | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-20 18:31:12 +08:00
										 |  |  | }) | 
					
						
							|  |  |  | watch(() => props.qvehuan, (newVal) => { | 
					
						
							|  |  |  |   initqie.value = newVal | 
					
						
							|  |  |  |   updateRoute() | 
					
						
							|  |  |  | }, { deep: true }) | 
					
						
							|  |  |  | watch(() => props.coordinates, (newVal) => { | 
					
						
							| 
									
										
										
										
											2025-03-19 09:17:05 +08:00
										 |  |  |   updateRoute() | 
					
						
							|  |  |  | }, { deep: true }) | 
					
						
							|  |  |  | </script> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <style> | 
					
						
							|  |  |  | /* 保持原有样式不变 */ | 
					
						
							|  |  |  | .map-container { | 
					
						
							|  |  |  |   background-color: #f0f8ff; | 
					
						
							|  |  |  |   margin: 20px; | 
					
						
							|  |  |  |   overflow: hidden; | 
					
						
							|  |  |  |   touch-action: none; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | </style> |