Kaynağa Gözat

Merge branch 'lili' of qw/12 into master

Ding 2 ay önce
ebeveyn
işleme
7a762251a2

+ 239 - 1
src/views/User/cadmiumPrediction/CropCadmiumPrediction.vue

@@ -57,6 +57,21 @@
           <el-icon><Picture /></el-icon>
           <p>暂无地图数据</p>
         </div>
+
+        <div v-if="loadingTownshipMap" class="loading-container">
+          <el-icon class="loading-icon"><Loading /></el-icon>
+          <span>乡镇地图加载中...</span>
+        </div>
+        <!-- ECharts地图容器 -->
+        <div 
+          v-show="!loadingTownshipMap" 
+          ref="townshipMapRef" 
+          class="township-map-container"
+        ></div>
+        <div v-if="!loadingTownshipMap && !townshipMapInstance" class="no-data">
+          <el-icon><Location /></el-icon>
+          <p>暂无乡镇边界数据</p>
+        </div>
       </div>
 
        <!-- 统计图表区域 -->
@@ -114,10 +129,11 @@
 
 <script>
 import * as XLSX from 'xlsx';
+import * as echarts from 'echarts';
 import { saveAs } from 'file-saver';
 import { api8000 } from '@/utils/request';
 import { 
-  Loading, Upload, Picture, Histogram, Download, Document, Box, DataAnalysis
+   Location,Loading, Upload, Picture, Histogram, Download, Document, Box, DataAnalysis
 } from '@element-plus/icons-vue';
 
 export default {
@@ -144,7 +160,16 @@ export default {
       mapBlob: null,
       histogramBlob: null,
       selectedFile: null,
+      // 新增:乡镇地图相关数据
+      loadingTownshipMap: false, // 乡镇地图加载状态
+      townshipMapInstance: null, // ECharts实例
+      townshipGeoJson: null,     // 本地边界数据(TopoJSON/GeoJSON)
+      townshipData: [],          // 接口获取的乡镇Cd数据
       countyName: '乐昌市', // 默认县市名称
+      townshipMapRef :null,
+      currentTooltipTown: '', // 当前悬浮的乡镇
+      currentTooltipData: null, // 当前乡镇的详情数据
+      isTooltipLoading: false, // tooltip 是否在加载中
     };
   },
 
@@ -152,11 +177,17 @@ export default {
     // 组件挂载时获取最新数据
     this.fetchLatestResults();
     this.fetchStatistics();
+    this.initTownshipMap();
   },
 
   beforeDestroy() {
     if (this.mapImageUrl) URL.revokeObjectURL(this.mapImageUrl);
     if (this.histogramImageUrl) URL.revokeObjectURL(this.histogramImageUrl);
+    // 新增:销毁ECharts实例,避免内存泄漏
+    if (this.townshipMapInstance) {
+      this.townshipMapInstance.dispose();
+      this.townshipMapInstance = null;
+    }
   },
   methods: {
     // 触发文件选择
@@ -313,6 +344,189 @@ export default {
         this.loadingStats = false;
       }
     },
+
+    // 新增方法:根据乡镇名请求接口
+    async fetchTownshipDetailByName(townName) {
+      try {
+        // 调用对应乡镇的接口,假设接口地址为 /api/township-details/{townName}
+        const encodedTownName = encodeURIComponent(townName);
+        const response = await api8000.get(`/api/cd-prediction/crop-cd/statistics/town/${encodedTownName}`);
+        if (response.data && response.data.success) {
+          return response.data.data;
+        }
+       return null;
+      } catch (error) {
+        console.error(`获取${townName}详情失败:`, error);
+        return null;
+      }
+    },
+
+    // 新增1:初始化乡镇边界地图(核心逻辑,仅加载边界)
+    async initTownshipMap() {
+      try {
+        this.loadingTownshipMap = true;
+        // 步骤1:加载本地 GeoJSON 边界文件
+        await this.loadLocalGeoJson();
+        // 步骤2:渲染边界地图(不关联接口数据)
+        this.renderTownshipMap();
+      } catch (error) {
+        console.error('乡镇边界加载失败:', error);
+        this.townshipMapInstance = null; // 标记加载失败
+      } finally {
+        this.loadingTownshipMap = false;
+      }
+    },
+
+    // 新增2:加载本地 GeoJSON 文件(关键:路径必须正确)
+    async loadLocalGeoJson() {
+      try {
+        // 1. 确认文件路径:public/data/韶关市乡镇划分图5.geojson
+        const geoJsonPath = '/data/韶关市乡镇划分图5.geojson';
+        
+        // 2. 发送请求加载 GeoJSON
+        const response = await fetch(geoJsonPath);
+        // 3. 检查请求是否成功(状态码 200-299 为成功)
+        if (!response.ok) {
+          throw new Error(`文件加载失败:状态码 ${response.status}(路径:${geoJsonPath})`);
+        }
+        
+        // 4. 解析 GeoJSON 数据
+        let originalGeoJson = await response.json();
+
+        // 关键修改:过滤只保留 FXZQMC 为"乐昌市"的特征数据
+        this.townshipGeoJson = {
+          ...originalGeoJson,  // 保留原有属性(如 type、crs 等)
+          features: originalGeoJson.features// 过滤出乐昌市的乡镇
+        .filter(feature => feature.properties?.FXZQMC === '乐昌市')
+        // 为每个乡镇添加name字段,值为TXZQMC(ECharts默认读取name字段)
+        .map(feature => ({
+          ...feature,
+          properties: {
+            ...feature.properties,
+            name: feature.properties?.TXZQMC || '未知乡镇' // 核心:映射TXZQMC到name
+          }
+        }))
+        };
+        
+        // 5. 验证 GeoJSON 格式(必须包含 features 数组,否则是无效格式)
+        if (!this.townshipGeoJson.features || !Array.isArray(this.townshipGeoJson.features)) {
+          throw new Error('GeoJSON 格式错误:缺少 features 数组');
+        }
+
+        console.log('GeoJSON 加载成功,包含乡镇数量:', this.townshipGeoJson.features.length);
+      } catch (error) {
+        console.error('GeoJSON 加载异常:', error);
+        throw error; // 抛出错误,让 initTownshipMap 捕获
+      }
+    },
+
+    // 新增3:渲染乡镇边界(仅显示边界和乡镇名,不关联数据)
+    renderTownshipMap() {
+      // 1. 获取地图容器 DOM(必须存在)
+      const mapContainer = this.$refs.townshipMapRef;
+      if (!mapContainer) {
+        throw new Error('ECharts 容器不存在:请检查 ref="townshipMapRef" 是否正确');
+      }
+
+      // 2. 初始化 ECharts 实例
+      this.townshipMapInstance = echarts.init(mapContainer);
+
+      // 3. 注册地图:将 GeoJSON 数据注册到 ECharts(名称用 countyName:乐昌市)
+      echarts.registerMap(this.countyName, this.townshipGeoJson);
+
+      // 4. ECharts 配置项(仅显示边界和乡镇名,无数据关联)
+      const option = {
+        // 标题(可选,显示在地图上方)
+        title: {
+          text: '乐昌市乡镇边界',
+          left: 'center',
+          textStyle: { fontSize: 16, fontWeight: 'bold' }
+        },
+        // 提示框(鼠标悬浮时显示乡镇名)
+        tooltip: {
+          trigger: 'item', // 按乡镇区域触发
+          formatter: () => {
+    if (this.isTooltipLoading) {
+      return '<div style="padding: 5px;">加载中...</div>';
+    } else if (this.currentTooltipData) {
+      const detail = this.currentTooltipData;
+      let content = `
+        <div class="town-tooltip">
+          <h3 style="margin: 0 0 5px; color: #0066CC; text-align : center;">${this.currentTooltipTown}</h3>
+          <div style="height: 1px; background-color: #0066CC; margin-bottom: 8px;"></div>
+          <p>采样点数量: ${detail.基础统计.采样点数量}</p>
+          <p>平均值: ${detail.基础统计.平均值.toFixed(4)} mg/kg</p>
+          <p>最小值: ${detail.基础统计.最小值.toFixed(4)} mg/kg</p>
+          <p>最大值: ${detail.基础统计.最大值.toFixed(4)} mg/kg</p>
+          <p>数据更新时间: ${new Date(detail.数据更新时间).toLocaleString()}</p>
+          <div style="height: 1px; background-color: #0066CC; margin-bottom: 8px;"></div>
+          <p style="color:black; font-size:16px;">分布统计:</p>
+          <p>安全区间占比: ${detail.分布统计表格.汇总.安全区间占比}</p>
+          <p>预警区间占比: ${detail.分布统计表格.汇总.预警区间占比}</p>
+          <p>超标区间占比: ${detail.分布统计表格.汇总.超标区间占比}</p>
+        </div>
+      `;
+      return content;
+    } 
+  },
+
+        },
+        // 地图系列(核心:渲染边界)
+        series: [
+          {
+            type: 'map',
+            map: this.countyName, // 对应注册的地图名称(乐昌市)
+            roam: true, // 允许鼠标缩放、平移地图(方便查看)
+            label: {
+              show: true, // 显示乡镇名称标签
+              fontSize: 10, // 标签字体大小(避免重叠)
+              color: '#333' // 标签颜色
+            },
+            itemStyle: {
+              color: 'transparent', // 乡镇区域填充色(透明,仅显示边界)
+              borderColor: '#000000', // 边界颜色(青色,醒目)
+              borderWidth: 1.5 // 边界宽度
+            },
+            emphasis: {
+              // 鼠标悬浮时的样式(高亮边界和标签)
+              label: { color: '#fff', fontSize: 11 },
+              itemStyle: { color: 'rgba(71, 195, 185, 0.3)' } // 悬浮区域填充色
+            }
+          }
+        ]
+      };
+
+      this.townshipMapInstance.setOption(option);
+  // 监听鼠标悬浮事件
+  this.townshipMapInstance.on('mouseover', async (params) => {
+    if (params.componentType === 'series' && params.seriesType === 'map') {
+      const townName = params.name;
+      this.currentTooltipTown = townName;
+      this.isTooltipLoading = true;
+      this.currentTooltipData = null;
+      // 手动触发 tooltip 更新
+      this.townshipMapInstance.setOption({ tooltip: {} });
+      const detail = await this.fetchTownshipDetailByName(townName);
+      this.isTooltipLoading = false;
+      this.currentTooltipData = detail;
+      // 再次手动触发 tooltip 更新,显示获取到的数据
+      this.townshipMapInstance.setOption({ tooltip: {} });
+    }
+  });
+  // 监听鼠标移出事件,重置状态
+  this.townshipMapInstance.on('mouseout', () => {
+    this.currentTooltipTown = '';
+    this.currentTooltipData = null;
+    this.isTooltipLoading = false;
+  });
+      // 5. 渲染地图
+      this.townshipMapInstance.setOption(option);
+
+      // 6. 监听窗口 resize(地图自适应)
+      window.addEventListener('resize', () => {
+        this.townshipMapInstance && this.townshipMapInstance.resize();
+      });
+    },
     
     // 上传并计算
     async calculate() {
@@ -353,6 +567,7 @@ export default {
         await this.fetchStatistics();
         
         this.$message.success('计算完成!');
+         await this.initTownshipMap();
         
       } catch (error) {
         console.error('计算失败:', error);
@@ -409,6 +624,7 @@ export default {
         await this.fetchStatistics();
         
         this.$message.success('数据库计算完成!');
+        await this.initTownshipMap();
         
       } catch (error) {
         console.error('从数据库计算失败:', error);
@@ -504,6 +720,28 @@ export default {
   box-sizing: border-box;
 }
 
+/* 新增:乡镇地图样式 */
+.township-map-section {
+  background-color: rgba(255, 255, 255, 0.8);
+  border-radius: 8px;
+  padding: 15px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
+  position: relative;
+  min-height: 500px; /* 与原有地图高度一致 */
+  backdrop-filter: blur(5px);
+  margin-bottom: 20px; /* 与下方地图间距 */
+}
+
+.township-map-container {
+  width: 1000px;
+  height: 500px; /* 减去标题高度 */
+  min-height: 470px;
+  border-radius: 4px;
+  background-color: #fff;
+}
+
+
+
 .toolbar {
   display: flex;
   flex-direction: column;