123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- <template>
- <div class="map-wrapper">
- <div ref="mapContainer" class="map-container"></div>
- </div>
- </template>
- <script setup>
- import { ref, onMounted } from 'vue';
- import L from 'leaflet';
- import 'leaflet/dist/leaflet.css';
- const mapContainer = ref(null);
- // 定义蓝色三角形标记(保持不变)
- const blueTriangle = L.divIcon({
- className: 'custom-div-icon',
- html: `<svg width="24" height="24" viewBox="0 0 24 24">
- <path d="M12 2L2 22h20L12 2z" fill="#0066CC" stroke="#003366" stroke-width="2"/>
- </svg>`,
- iconSize: [24, 24],
- iconAnchor: [12, 24]
- });
- onMounted(() => {
- // 初始化地图(保持不变)
- if (!mapContainer.value) {
- console.error('❌ 地图容器未找到!');
- return;
- }
- const map = L.map(mapContainer.value, {
- center: [24.9, 114], // 韶关大致中心 前大往下,后大往左
- zoom: 8.5,
- minZoom: 8.3,
- });
- // 区县颜色映射 + 增强匹配(保持不变)
- const districtColorMap = {
- "武江区": "#FF6B6B",
- "浈江区": "#4ECDC4",
- "曲江区": "#FFD166",
- "始兴县": "#A0DAA9",
- "仁化县": "#6A0572",
- "翁源县": "#1A535C",
- "乳源瑶族自治县": "#FF9F1C",
- "新丰县": "#87CEEB",
- "乐昌市": "#118AB2",
- "南雄市": "#06D6A0",
- "韶关市": "#cccccc",
- };
- function getDistrictColor(name) {
- if (districtColorMap[name]) return districtColorMap[name];
- const normalizedName = name.replace(/市|县|区|自治县/g, '');
- for (const key in districtColorMap) {
- if (key.includes(normalizedName) || normalizedName.includes(key.replace(/市|县|区|自治县/g, ''))) {
- return districtColorMap[key];
- }
- }
- return '#cccccc';
- }
- // 加载区县边界(保持不变)
- fetch('/data/韶关市各区县边界图.geojson')
- .then(res => {
- if (!res.ok) throw new Error(`区县边界加载失败:${res.status}`);
- return res.json();
- })
- .then(geojson => {
- L.geoJSON(geojson, {
- style: (feature) => {
- const districtName = feature.properties.name || '';
- const color = getDistrictColor(districtName);
- return {
- fillColor: color,
- fillOpacity: 0.7,
- color: '#333333',
- weight: 2,
- };
- },
- }).addTo(map);
- // 加载水系图 + 新增接口数据加载(核心修改)
- fetch('/data/韶关市河流水系图.geojson')
- .then(res => {
- if (!res.ok) throw new Error(`水系图加载失败:${res.status}`);
- return res.json();
- })
- .then(waterGeojson => {
- L.geoJSON(waterGeojson, {
- style: {
- color: '#0066CC',
- weight: 2,
- opacity: 0.8,
- },
- }).addTo(map);
- // ========================
- // 从接口加载数据(替换本地rawData)
- // ========================
- fetch('http://localhost:8000/api/vector/export/all?table_name=cross_section')
- .then(res => {
- if (!res.ok) throw new Error(`数据加载失败:HTTP ${res.status}`);
- return res.json();
- })
- .then(geoJSONData => {
- // 提取GeoJSON的features.properties作为数据项
- const dataItems = geoJSONData.features.map(feature => feature.properties);
- console.log('✅ 接口数据加载完成,要素数:', dataItems.length);
- let markerCount = 0;
- dataItems.forEach((item, idx) => {
- try {
- // 字段映射(接口字段 → 原逻辑字段)
- const mappedItem = {
- "断面编号": item.id,
- "所属河流": item.river_name,
- "断面位置": item.position,
- "所属区县": item.county,
- "经度": item.longitude,
- "纬度": item.latitude,
- "Cd(ug/L)": item.cd_concentration
- };
- // 经纬度校验(保持原有逻辑)
- const lng = parseFloat(mappedItem.经度);
- const lat = parseFloat(mappedItem.纬度);
- if (isNaN(lat) || isNaN(lng) || lat < 22.7 || lat > 25.5 || lng < 112.7 || lng > 115.3) {
- console.warn(`❌ 坐标越界(第${idx}条):`, lat, lng, mappedItem);
- return;
- }
- // 创建标记(保持原有样式)
- const marker = L.marker([lat, lng], {
- icon: blueTriangle,
- zIndexOffset: 1000,
- }).addTo(map);
- // 镉含量格式化(保持原有逻辑)
- const cdValue = parseFloat(mappedItem["Cd(ug/L)"]);
- const formattedCd = isNaN(cdValue) ? '未知' : cdValue.toFixed(2) + ' μg/L';
- // 弹窗内容(保持原有结构)
- marker.bindPopup(`
- <div class="popup-container">
- <h3 class="popup-title">所属河流: ${mappedItem.所属河流}</h3>
- <div class="popup-divider"></div>
- <p><strong>断面编号:</strong> ${mappedItem.断面编号}</p>
- <p><strong>断面位置:</strong> ${mappedItem.断面位置}</p>
- <p><strong>所属区县:</strong> ${mappedItem.所属区县}</p>
- <p><strong>镉(Cd)含量:</strong> ${formattedCd}</p>
- </div>
- `);
- // 鼠标交互(保持原有逻辑)
- marker.on('mouseover', () => {
- marker.getElement().querySelector('svg').style.transform = 'scale(1.2)';
- }).on('mouseout', () => {
- marker.getElement().querySelector('svg').style.transform = 'scale(1)';
- });
- markerCount++;
- } catch (err) {
- console.error(`❌ 处理第${idx}条数据失败:`, err);
- }
- });
- console.log(`✅ 成功创建 ${markerCount} 个标记点`);
- })
- .catch(err => {
- console.error('❌ 采样点数据加载失败:', err);
- alert('采样点数据加载错误:' + err.message);
- });
- // ========================
- })
- .catch(err => {
- console.error('❌ 水系图加载失败:', err);
- alert('水系图加载错误:' + err.message);
- });
- })
- .catch(err => {
- console.error('❌ 区县边界加载失败:', err);
- alert('区县边界加载错误:' + err.message);
- });
- });
- </script>
- <style scoped>
- /* 原有样式保持不变 */
- .map-wrapper {
- width: 100%;
- height: 80%;
- position: relative;
- }
- .map-container {
- width: 100% !important;
- height: 100% !important;
- }
- ::v-deep .popup-title {
- text-align: center;
- font-size: 18px;
- font-weight: 700;
- color: #0066CC;
- margin: 0 0 6px;
- border-bottom: none;
- padding-bottom: 8px;
- }
- ::v-deep .popup-divider {
- height: 1px;
- background: #0066CC;
- margin: 8px 0;
- }
- ::v-deep .popup-container {
- min-width: 240px;
- max-width: 300px;
- padding: 16px;
- font-family: "Microsoft YaHei", sans-serif;
- }
- ::v-deep .popup-container p {
- margin: 6px 0;
- font-size: 15px;
- color: #666;
- line-height: 1.6;
- }
- ::v-deep .popup-container strong {
- color: #0066CC;
- font-weight: 600;
- }
- ::v-deep .exceeding {
- color: #FF3333;
- font-weight: bold;
- }
- ::v-deep .leaflet-popup-content-wrapper {
- padding: 0 !important;
- border-radius: 12px !important;
- box-shadow: 0 6px 16px rgba(0,0,0,0.2) !important;
- }
- ::v-deep .leaflet-popup-content {
- margin: 0 !important;
- width: auto !important;
- }
- ::v-deep .leaflet-popup-tip {
- display: none;
- }
- ::v-deep .info {
- padding: 6px 8px;
- background: white;
- background: rgba(255,255,255,0.9);
- box-shadow: 0 0 15px rgba(0,0,0,0.2);
- border-radius: 5px;
- }
- ::v-deep .custom-div-icon svg {
- transition: transform 0.2s;
- display: block;
- }
- </style>
|