Jelajahi Sumber

Merge branch 'ding' of qw/12 into master

Ding 1 bulan lalu
induk
melakukan
3a77b244f4
1 mengubah file dengan 406 tambahan dan 141 penghapusan
  1. 406 141
      src/views/User/cadmiumPrediction/CropCadmiumPrediction.vue

+ 406 - 141
src/views/User/cadmiumPrediction/CropCadmiumPrediction.vue

@@ -73,30 +73,6 @@
           ref="townshipMapRef" 
           class="township-map-container"
         ></div>
-        </div>
-         <!-- 新增:要素详细信息栏 -->
-        <div v-if="currentFeatureInfo" class="feature-info-panel">
-          <h4 style="margin-top: 0; color: #0066CC; border-bottom: 1px solid #eee; padding-bottom: 10px;">
-            地块详细信息
-          </h4>
-          <ul style="list-style: none; padding: 0; margin: 0;">
-            <li style="margin-bottom: 8px; font-size: 14px;">
-              <strong>水稻镉含量:</strong>{{ currentFeatureInfo.avgmean.toFixed(4) }} mg/kg
-            </li>
-            <li style="margin-bottom: 8px; font-size: 14px;">
-              <strong>镉输入通量:</strong>{{ currentFeatureInfo.lncd.toFixed(4) }} g/ha/a
-              <!-- 若有单位可补充:{{ currentFeatureInfo.lncdUnit }} -->
-            </li>
-            <li style="margin-bottom: 8px; font-size: 14px;">
-              <strong>镉输出通量:</strong>{{ currentFeatureInfo.outcd.toFixed(4) }} g/ha/a
-            </li>
-            <li style="margin-bottom: 8px; font-size: 14px;">
-              <strong>镉净通量:</strong>{{ currentFeatureInfo.netcd.toFixed(4) }} g/ha/a
-            </li>
-            <li style="margin-bottom: 8px; font-size: 14px;">
-              <strong>当年镉浓度:</strong>{{ currentFeatureInfo.endcd.toFixed(4) }} g/ha/a
-            </li>
-          </ul>
         </div>
         <div v-if="!loadingTownshipMap && !townshipMapInstance" class="no-data">
           <el-icon><Location /></el-icon>
@@ -209,6 +185,7 @@ export default {
   },
   data() {
     return {
+      currentScale: 0, // 当前地图比例尺
       currentFeatureInfo: null, // 存储当前点击要素的详细信息
       mapContainerStyle: {
       width: '100%',
@@ -246,6 +223,16 @@ export default {
       dataMap:{},
       fluxDataMap: {}, // 存储通量Cd数据
       fluxDataLoaded: false, // 通量Cd数据是否已加载
+      geoserverConfig: {
+          baseUrl: 'http://localhost:8080/geoserver',
+          workspace: 'soilgd',
+          wmsService: 'WMS',
+          version: '1.1.0'
+        },
+      currentLayerInfo: null, // 当前图层信息
+      layerLoading: false, // 图层加载状态
+      layerInfoFetched: false, // 新增:标记是否已获取图层信息
+      fetchingLayer: false,    // 新增:标记是否正在获取中
     };
   },
 
@@ -285,6 +272,67 @@ export default {
 
 
   methods: {
+    // 新增:获取最新GeoServer图层信息
+    async fetchLatestLayerInfo() {
+      // 如果正在获取或已获取,直接返回
+      if (this.fetchingLayer || this.layerInfoFetched) {
+        return this.currentLayerInfo || this.getDefaultLayerInfo();
+      }
+      
+      try {
+        this.fetchingLayer = true;
+        const response = await api8000.get('/api/geoserver/latest-layer');
+        
+        if (response.data && response.data.geoserver && response.data.geoserver.exists) {
+          this.currentLayerInfo = response.data;
+          this.layerInfoFetched = true; // 标记为已获取
+          console.log('获取到最新图层信息:', this.currentLayerInfo);
+          return this.currentLayerInfo;
+        } else {
+          console.warn('未找到最新图层信息,使用默认配置');
+          return this.getDefaultLayerInfo();
+        }
+      } catch (error) {
+        console.error('获取最新图层信息失败:', error);
+        return this.getDefaultLayerInfo();
+      } finally {
+        this.fetchingLayer = false;
+      }
+    },
+
+    // 新增:获取默认图层配置
+    getDefaultLayerInfo() {
+      return {
+        geoserver: {
+          exists: true,
+          layer_name: 'shapeMap',
+          full_name: `${this.geoserverConfig.workspace}:shapeMap`,
+          workspace: this.geoserverConfig.workspace
+        },
+        wms: {
+          wms_url: `${this.geoserverConfig.baseUrl}/${this.geoserverConfig.workspace}/wms`,
+          layer_name: `${this.geoserverConfig.workspace}:shapeMap`
+        }
+      };
+    },
+
+    // 新增:构建WMS服务URL
+    getWmsUrl() {
+      if (this.currentLayerInfo && this.currentLayerInfo.wms) {
+        return this.currentLayerInfo.wms.wms_url;
+      }
+      return `${this.geoserverConfig.baseUrl}/${this.geoserverConfig.workspace}/wms`;
+    },
+
+    // 新增:获取图层名称
+    getLayerName() {
+      if (this.currentLayerInfo && this.currentLayerInfo.geoserver) {
+        return this.currentLayerInfo.geoserver.full_name || 
+              `${this.geoserverConfig.workspace}:${this.currentLayerInfo.geoserver.layer_name}`;
+      }
+      return `${this.geoserverConfig.workspace}:shapeMap`;
+    },
+    
     async fetchFluxData() {
       try {
         this.loadingTownshipMap = true;
@@ -491,8 +539,19 @@ export default {
 
     // 初始化乡镇边界地图
 async initTownshipMap() {
+   // 如果正在加载,直接返回
+    if (this.loadingTownshipMap) {
+      return;
+    }
+    
     try {
       this.loadingTownshipMap = true;
+      
+      // 只有在未获取过图层信息时才获取
+      if (!this.layerInfoFetched) {
+        await this.fetchLatestLayerInfo();
+      }
+    
       if (!this.fluxDataLoaded) {
         await this.fetchFluxData();
       }
@@ -641,8 +700,7 @@ async initTownshipMap() {
       }
     },
 
-    // 使用Leaflet渲染乡镇地图并填充颜色
-    // 修改渲染地图方法,增加错误处理
+
 // 使用Leaflet渲染乡镇地图并填充颜色
 renderTownshipMap() {
   try {
@@ -679,6 +737,15 @@ renderTownshipMap() {
       attributionControl: false
     });
 
+    this.townshipMapInstance.on('zoomend', () => {
+      this.handleZoomChange();
+    });
+
+    // 添加移动事件监听,确保实时更新
+    this.townshipMapInstance.on('moveend', () => {
+      this.handleZoomChange();
+    });
+
    // 添加缩放控件
     L.control.zoom({
       position: 'bottomright'
@@ -705,50 +772,51 @@ renderTownshipMap() {
           fillOpacity: 0
         };
       },
-      onEachFeature: (feature, layer) => {
-        const townName = feature.properties.name;
+    onEachFeature: (feature, layer) => {
+      const townName = feature.properties.name;
+      
+      const originalStyle = {
+        fillColor: 'transparent',
+        weight: 2,
+        opacity: 1,
+        color: '#000',
+        dashArray: '',
+        fillOpacity: 0
+      };
+      
+      // 鼠标悬停事件 - 只显示镇级tooltip
+      layer.on('mouseover', (e) => {
+        // 实时检查比例尺
+        const currentScale = this.getCurrentMapScale();
         
-        // 保存原始样式,用于恢复
-        const originalStyle = {
-          fillColor: 'transparent', // 固定为白色填充
-          weight: 2,
-          opacity: 1,
-          color: '#000', // 黑色边界线
+        // 高亮当前区域(无论比例尺如何都高亮)
+        layer.setStyle({
+          weight: 6,
+          color: '#0066CC',
           dashArray: '',
-          fillOpacity: 0
-        };
-        
-        
-        // 鼠标悬停事件 - 修复版本
-        layer.on('mouseover', (e) => {
-          // 高亮当前区域
-          layer.setStyle({
-            weight: 6,
-            color: '#0066CC',
-            dashArray: '',
-            fillOpacity: 0.95
-          });
-          
-          // 显示tooltip
-          this.showTooltip(layer, townName, e.latlng);
+          fillOpacity: 0.95
         });
         
-        // 鼠标离开事件 - 修复版本
-        layer.on('mouseout', (e) => {
-          // 恢复原始样式 - 使用保存的样式而不是resetStyle
-          layer.setStyle(originalStyle);
-          
-          // 正确关闭tooltip
-          if (layer._tooltip) {
-            layer.closeTooltip();
-          }
-        });
-      }
+        // 只有在比例尺 >= 17.28 时才显示镇级tooltip
+        if (currentScale >= 17.28) {
+          this.showTownshipTooltip(layer, townName, e.latlng);
+        }
+      });
+      
+      // 鼠标离开事件
+      layer.on('mouseout', (e) => {
+        layer.setStyle(originalStyle);
+        
+        if (layer._tooltip) {
+          layer.closeTooltip();
+        }
+      });
+    }
     }).addTo(this.townshipMapInstance);
 
     // 添加GeoServer WMS图层作为底图
-    const wmsLayer = L.tileLayer.wms('http://localhost:8080/geoserver/soilgd/wms', {
-      layers: 'soilgd:shapeMap', // 替换为你的图层名称
+    const wmsLayer = L.tileLayer.wms(this.getWmsUrl(), {
+      layers: this.getLayerName(), // 替换为你的图层名称
       format: 'image/png',
       transparent: true,
       version: '1.1.0',
@@ -781,99 +849,162 @@ renderTownshipMap() {
 },
 
 
-async handleWmsFeatureClick(e) {
-  try {
-    const { lat, lng } = e.latlng;
-    const mapSize = this.townshipMapInstance.getSize();
-
-    // 1. 转换坐标:地理坐标 → 地图像素坐标(Leaflet 默认 EPSG:4326)
-    const containerPoint = this.townshipMapInstance.latLngToContainerPoint([lat, lng]);
-    const x = Math.round(containerPoint.x);
-    const y = Math.round(containerPoint.y);
-
-    // 2. 构造 GetFeatureInfo 请求 URL(强制 JSON + 修正参数)
-    const wmsBaseUrl = 'http://localhost:8080/geoserver/soilgd/wms'; // 确保工作区和服务名正确
-    const params = new URLSearchParams({
-      SERVICE: 'WMS',
-      VERSION: '1.1.1', // 建议用 1.1.1(GeoServer 更兼容)
-      REQUEST: 'GetFeatureInfo',
-      LAYERS: 'soilgd:shapeMap',
-      QUERY_LAYERS: 'soilgd:shapeMap',
-      FORMAT: 'image/png',
-      TRANSPARENT: 'true',
-      INFO_FORMAT: 'application/json', // 强制返回 JSON
-      bbox: this.townshipMapInstance.getBounds().toBBoxString(),
-      X: x,
-      Y: y,
-      WIDTH: mapSize.x,
-      HEIGHT: mapSize.y,
-      SRS: 'EPSG:4326', // 与地图 CRS 一致
-      FEATURE_COUNT: 1 // 限制返回 1 个要素(关键:确保日志中 FeatureCount=1)
-    });
+    async handleWmsFeatureClick(e) {
+      try {
+        const { lat, lng } = e.latlng;
+        const mapSize = this.townshipMapInstance.getSize();
+
+        // 1. 转换坐标:地理坐标 → 地图像素坐标
+        const containerPoint = this.townshipMapInstance.latLngToContainerPoint([lat, lng]);
+        const x = Math.round(containerPoint.x);
+        const y = Math.round(containerPoint.y);
+
+        // 2. 构造 GetFeatureInfo 请求 URL
+        const wmsBaseUrl = this.getWmsUrl();
+        const params = new URLSearchParams({
+          SERVICE: 'WMS',
+          VERSION: '1.1.1',
+          REQUEST: 'GetFeatureInfo',
+          LAYERS: this.getLayerName(),
+          QUERY_LAYERS: this.getLayerName(),
+          FORMAT: 'image/png',
+          TRANSPARENT: 'true',
+          INFO_FORMAT: 'application/json',
+          bbox: this.townshipMapInstance.getBounds().toBBoxString(),
+          X: x,
+          Y: y,
+          WIDTH: mapSize.x,
+          HEIGHT: mapSize.y,
+          SRS: 'EPSG:4326',
+          FEATURE_COUNT: 1
+        });
 
-    // 打印完整请求 URL(用于调试)
-    const requestUrl = `${wmsBaseUrl}?${params.toString()}`;
-    console.log('GetFeatureInfo 完整请求 URL:', requestUrl);
+        const requestUrl = `${wmsBaseUrl}?${params.toString()}`;
+        console.log('GetFeatureInfo 请求 URL:', requestUrl);
 
-    // 3. 发送请求并解析 JSON
-    const response = await fetch(requestUrl);
-    if (!response.ok) throw new Error(`请求失败:${response.status} ${response.statusText}`);
+        // 3. 发送请求并解析 JSON
+        const response = await fetch(requestUrl);
+        if (!response.ok) throw new Error(`请求失败:${response.status} ${response.statusText}`);
 
-    // 4. 验证响应类型(必须为 JSON)
-    const contentType = response.headers.get('Content-Type');
-    if (!contentType.includes('application/json')) {
-      throw new Error(`非 JSON 响应:${contentType},请检查 GeoServer 配置`);
-    }
-    const featureData = await response.json();
+        const contentType = response.headers.get('Content-Type');
+        if (!contentType.includes('application/json')) {
+          throw new Error(`非 JSON 响应:${contentType}`);
+        }
+        
+        const featureData = await response.json();
 
-    if (!featureData?.features || !Array.isArray(featureData.features) || featureData.features.length === 0) {
-      this.$message.warning('该区域无要素数据');
-      return; // 无要素时直接返回,不继续处理
-    }
+        if (!featureData?.features || !Array.isArray(featureData.features) || featureData.features.length === 0) {
+          this.$message.warning('该区域无要素数据');
+          return;
+        }
 
-    const properties = featureData.features[0].properties || {};
-      this.currentFeatureInfo = {
-        avgmean: properties._avgmean,       // 平均镉含量
-        lncd: properties.Ln_Cd,             // 输入镉通量
-        outcd: properties.Out_Cd,           // 输出镉通量
-        netcd: properties.Net_Cd,           // 净镉通量
-        endcd: properties.End_Cd            // 最终镉浓度
-      };
+        const properties = featureData.features[0].properties || {};
+        const parcelInfo = {
+          name: properties.ZLDWMC,              //村名
+          avgmean: properties.CropCd_mean,       // 平均镉含量
+          lncd: properties.In_Cd_mean,          // 输入镉通量
+          outcd: properties.Out_Cd_mean,         // 输出镉通量
+          netcd: properties.Net_Cd_mean,         // 净镉通量
+          endcd: properties.End_Cd_mean          // 最终镉浓度
+        };
+        
+        console.log('获取到地块信息:', parcelInfo);
+        
+        // 显示地块级tooltip
+        this.showParcelTooltip(e.latlng, parcelInfo);
+        
+      } catch (error) {
+        console.error('WMS 要素点击交互失败:', error);
+        this.$message.warning('获取要素信息失败:' + error.message);
+      }
+    },
+
+    // 修改 getCurrentMapScale 方法,确保准确计算
+    getCurrentMapScale() {
+      if (!this.townshipMapInstance) return 0;
       
-      // 打印验证(可选)
-      console.log('当前点击要素信息:', this.currentFeatureInfo);
-    console.log('WMS 要素点击信息(JSON):', featureData);
-  } catch (error) {
-    console.error('WMS 要素点击交互失败:', error);
-    this.$message.warning('获取要素信息失败:' + error.message);
-  }
-},
+      try {
+        // 获取地图中心点纬度
+        const center = this.townshipMapInstance.getCenter();
+        const lat = center.lat;
+        
+        // 获取当前缩放级别
+        const zoom = this.townshipMapInstance.getZoom();
+        
+        // 更准确的比例尺计算公式
+        const metersPerPixel = (156543.03392 * Math.cos(lat * Math.PI / 180)) / Math.pow(2, zoom);
+        
+        // 更新当前比例尺状态
+        this.currentScale = metersPerPixel;
+        
+        console.log(`当前比例尺: ${metersPerPixel.toFixed(2)} 米/像素, 缩放级别: ${zoom}`);
+        return metersPerPixel;
+      } catch (error) {
+        console.error('计算比例尺失败:', error);
+        return 0;
+      }
+    },
+
+    // 新增:处理缩放变化
+    handleZoomChange() {
+      const currentScale = this.getCurrentMapScale();
+      console.log(`缩放变化,当前比例尺: ${currentScale.toFixed(2)} 米/像素`);
+      
+      // 如果比例尺大于等于17.28米/像素,允许显示镇级tooltip
+      if (currentScale >= 17.28) {
+        console.log('比例尺合适,允许显示镇级tooltip');
+      } else {
+        // 如果比例尺小于17.28米/像素,关闭所有打开的tooltip,准备显示地块级tooltip
+        console.log('比例尺过小,准备显示地块级tooltip');
+        this.closeAllTooltips();
+      }
+    },
+
+    // 新增:关闭所有tooltip
+    closeAllTooltips() {
+      if (this.townshipMapInstance) {
+        // 遍历所有图层并关闭tooltip
+        this.townshipMapInstance.eachLayer((layer) => {
+          if (layer._tooltip) {
+            layer.closeTooltip();
+          }
+        });
+      }
+    },
 
-  // 新增:显示tooltip的方法
-    showTooltip(layer, townName, latlng) {
-      const riceData = this.dataMap[townName]; // 水稻Cd数据
-      const fluxData = this.fluxDataMap[townName]; // 通量Cd数据
+    showTownshipTooltip(layer, townName, latlng) {
+      // 实时计算当前比例尺
+      const currentScale = this.getCurrentMapScale();
+      console.log(`尝试显示镇级tooltip,比例尺: ${currentScale.toFixed(2)} 米/像素`);
+      
+      // 只有在比例尺大于等于17.28米/像素时才显示镇级tooltip
+      if (currentScale < 17.28) {
+        console.log('比例尺过小,不显示镇级tooltip');
+        return;
+      }
       
-      let tooltipContent = `<div class="town-tooltip"><h3>${townName}</h3>`;
+      const riceData = this.dataMap[townName];
+      const fluxData = this.fluxDataMap[townName];
+      
+      let tooltipContent = `<div class="township-tooltip"><h3>${townName}</h3>`;
       
       // 水稻Cd数据部分
-      tooltipContent += `<div style="margin-bottom: 10px;"><strong>水稻Cd</strong></div>`;
       if (!riceData?.data?.基础统计) {
         tooltipContent += '<p>水稻Cd数据加载中...</p>';
       } else {
         const stats = riceData.data.基础统计;
         const dist = riceData.data.分布统计表格;
         tooltipContent += `
-          <p><strong>样本数量:</strong> ${stats.采样点数量 ?? '无数据'}</p>
-          <p><strong>平均值:</strong> ${stats.平均值?.toFixed(4) ?? '无数据'} mg/kg</p>
-          <p><strong>安全区间占比:</strong> ${dist?.汇总?.安全区间占比 ?? '无'}</p>
-          <p><strong>预警区间占比:</strong> ${dist?.汇总?.预警区间占比 ?? '无'}</p>
-          <p><strong>超标区间占比:</strong> ${dist?.汇总?.超标区间占比 ?? '无'}</p>
+          <div class="tooltip-section">
+            <p><strong>水稻Cd含量:</strong> ${stats.平均值?.toFixed(4) ?? '无数据'} mg/kg</p>
+            <p><strong>安全区间占比:</strong> ${dist?.汇总?.安全区间占比 ?? '无'}%</p>
+            <p><strong>预警区间占比:</strong> ${dist?.汇总?.预警区间占比 ?? '无'}%</p>
+            <p><strong>超标区间占比:</strong> ${dist?.汇总?.超标区间占比 ?? '无'}%</p>
+          </div>
         `;
       }
       
       // 通量Cd数据部分
-      tooltipContent += `<div style="margin-top: 10px; margin-bottom: 10px;"><strong>通量Cd</strong></div>`;
       if (!fluxData) {
         tooltipContent += '<p>通量Cd数据加载中...</p>';
       } else {
@@ -881,11 +1012,11 @@ async handleWmsFeatureClick(e) {
           <p><strong>输入通量:</strong> ${fluxData.in_cd?.mean?.toFixed(4) ?? '无数据'} ${fluxData.in_cd?.unit ?? ''}</p>
           <p><strong>输出通量:</strong> ${fluxData.out_cd?.mean?.toFixed(4) ?? '无数据'} ${fluxData.out_cd?.unit ?? ''}</p>
           <p><strong>净通量:</strong> ${fluxData.net_cd?.mean?.toFixed(4) ?? '无数据'} ${fluxData.net_cd?.unit ?? ''}</p>
-          <p><strong>土壤Cd浓度:</strong> ${fluxData.end_cd?.mean?.toFixed(4) ?? '无数据'} ${fluxData.end_cd?.unit ?? ''}</p>
+          <p><strong>当年浓度:</strong> ${fluxData.end_cd?.mean?.toFixed(4) ?? '无数据'} ${fluxData.end_cd?.unit ?? ''}</p>
         `;
       }
       
-      tooltipContent += `</div>`;
+      tooltipContent += `</div></div>`;
       
       // 先解除之前的tooltip绑定
       if (layer._tooltip) {
@@ -894,18 +1025,59 @@ async handleWmsFeatureClick(e) {
       
       // 使用Leaflet的tooltip
       layer.bindTooltip(tooltipContent, {
-        className: 'custom-town-tooltip',
+        className: 'custom-township-tooltip',
         direction: 'top',
         permanent: false,
         sticky: true,
         offset: [0, -10],
-        interactive: false
+        interactive: true
       });
       
       // 打开tooltip
       layer.openTooltip(latlng);
     },
-    
+
+    // 新增:显示地块级tooltip
+    showParcelTooltip(latlng, featureInfo) {
+      // 实时计算当前比例尺
+      const currentScale = this.getCurrentMapScale();
+      console.log(`尝试显示地块级tooltip,比例尺: ${currentScale.toFixed(2)} 米/像素`);
+      
+      // 只有在比例尺小于17.28米/像素时才显示地块级tooltip
+      if (currentScale >= 17.28) {
+        console.log('比例尺过大,不显示地块级tooltip');
+        return;
+      }
+      
+      if (!featureInfo) {
+        console.log('无地块信息,不显示tooltip');
+        return;
+      }
+      
+      const tooltipContent = `
+        <div class="parcel-tooltip">
+          <h3>地块详细信息</h3>
+          <div class="tooltip-section">
+            <p><strong>村名:</strong>${featureInfo.name || '无数据'}</p>
+            <p><strong>水稻镉含量:</strong>${featureInfo.avgmean?.toFixed(4) || '无数据'} mg/kg</p>
+            <p><strong>镉输入通量:</strong>${featureInfo.lncd?.toFixed(4) || '无数据'} g/ha/a</p>
+            <p><strong>镉输出通量:</strong>${featureInfo.outcd?.toFixed(4) || '无数据'} g/ha/a</p>
+            <p><strong>镉净通量:</strong>${featureInfo.netcd?.toFixed(4) || '无数据'} g/ha/a</p>
+            <p><strong>当年镉浓度:</strong>${featureInfo.endcd?.toFixed(4) || '无数据'} g/ha/a</p>
+          </div>
+        </div>
+      `;
+      
+      // 关闭所有现有的tooltip
+      this.closeAllTooltips();
+      
+      // 创建并显示popup
+      L.popup()
+        .setLatLng(latlng)
+        .setContent(tooltipContent)
+        .openOn(this.townshipMapInstance);
+    },
+            
     // 上传并计算
     async calculate() {
       if (!this.selectedFile) {
@@ -1448,4 +1620,97 @@ async handleWmsFeatureClick(e) {
   color: #0066CC;
   margin-right: 5px;
 }
+
+/* 移除Leaflet点击要素时的黑色边框 */
+.township-map-container :deep(.leaflet-interactive:focus) {
+  outline: none !important;
+}
+
+.township-map-container :deep(.leaflet-interactive) {
+  outline: none !important;
+}
+
+/* 移除所有Leaflet要素的焦点轮廓 */
+:deep(.leaflet-container) {
+  outline: none !important;
+}
+
+:deep(.leaflet-clickable) {
+  outline: none !important;
+}
+
+/* 确保GeoJSON要素没有焦点样式 */
+:deep(.leaflet-geojson-layer) *:focus {
+  outline: none !important;
+}
+/* 镇级tooltip样式 */
+::v-deep .custom-township-tooltip {
+  background-color: rgba(255, 255, 255, 0.98) !important;
+  border: 2px solid #47C3B9 !important;
+  border-radius: 8px !important;
+  padding: 15px !important;
+  box-shadow: 0 4px 12px rgba(0,0,0,0.3) !important;
+  max-width: 350px !important;
+  font-size: 14px !important;
+}
+
+::v-deep .custom-township-tooltip h3 {
+  color: #0066CC;
+  margin: 0 0 10px 0;
+  padding-bottom: 8px;
+  border-bottom: 2px solid #47C3B9;
+  text-align: center;
+}
+
+::v-deep .custom-township-tooltip .tooltip-section {
+  margin: 10px 0;
+  padding: 8px 0;
+  border-top: 1px solid #eee;
+}
+
+::v-deep .custom-township-tooltip .tooltip-section h4 {
+  color: #47C3B9;
+  margin: 0 0 5px 0;
+  font-size: 13px;
+}
+
+::v-deep .custom-township-tooltip p {
+  margin: 3px 0;
+  line-height: 1.4;
+}
+
+/* 地块级popup样式 */
+::v-deep .leaflet-popup-content-wrapper {
+  background-color: rgba(255, 255, 255, 0.98) !important;
+  border-radius: 8px !important;
+  box-shadow: 0 4px 12px rgba(0,0,0,0.3) !important;
+}
+
+::v-deep .leaflet-popup-content {
+  margin: 15px !important;
+  font-size: 14px !important;
+  line-height: 1.4 !important;
+}
+
+.parcel-tooltip h3 {
+  color: #0066CC;
+  margin: 0 0 10px 0;
+  padding-bottom: 8px;
+  border-bottom: 2px solid #47C3B9;
+  text-align: center;
+}
+
+.tooltip-section {
+  margin: 10px 0;
+}
+
+.tooltip-section p {
+  margin: 5px 0;
+}
+
+.tooltip-section strong {
+  color: #333;
+  min-width: 100px;
+  display: inline-block;
+}
 </style>