|
|
@@ -1,8 +1,7 @@
|
|
|
<template>
|
|
|
<div class="container">
|
|
|
<!-- 顶部操作栏 -->
|
|
|
- <div class="toolbar">
|
|
|
- <!-- 文件上传区域 -->
|
|
|
+ <!-- <div class="toolbar">
|
|
|
<div class="upload-section">
|
|
|
<input type="file" ref="fileInput" accept=".csv" @change="handleFileUpload" style="display: none">
|
|
|
<el-button class="custom-button" @click="triggerFileUpload">
|
|
|
@@ -19,7 +18,6 @@
|
|
|
<el-icon class="upload-icon"><Document /></el-icon>
|
|
|
上传并计算
|
|
|
</el-button>
|
|
|
- <!-- 添加从数据库计算按钮 -->
|
|
|
<el-button
|
|
|
class="custom-button"
|
|
|
:loading="isCalculatingFromDB"
|
|
|
@@ -28,7 +26,7 @@
|
|
|
<el-icon class="upload-icon"><Box /></el-icon>
|
|
|
从数据库计算
|
|
|
</el-button>
|
|
|
- </div>
|
|
|
+ </div> -->
|
|
|
<!-- 操作按钮 -->
|
|
|
<!-- <div class="action-buttons">
|
|
|
<el-button class="custom-button" :disabled="!mapBlob" @click="exportMap">
|
|
|
@@ -41,23 +39,28 @@
|
|
|
<el-icon class="upload-icon"><Download /></el-icon>
|
|
|
导出数据</el-button>
|
|
|
</div> -->
|
|
|
- </div>
|
|
|
+ <!-- </div> -->
|
|
|
|
|
|
<!-- 主体内容区 -->
|
|
|
<div class="content-area">
|
|
|
<!-- 地图区域 - 单独一行 -->
|
|
|
<div class="map-section">
|
|
|
<h3>水稻Cd预测地图</h3>
|
|
|
- <div v-if="loadingMap" class="loading-container">
|
|
|
- <el-icon class="loading-icon"><Loading /></el-icon>
|
|
|
- <span>地图加载中...</span>
|
|
|
- </div>
|
|
|
- <img v-if="mapImageUrl && !loadingMap" :src="mapImageUrl" alt="作物态Cd预测地图" class="map-image">
|
|
|
- <div v-if="!mapImageUrl && !loadingMap" class="no-data">
|
|
|
- <el-icon><Picture /></el-icon>
|
|
|
- <p>暂无地图数据</p>
|
|
|
+ <!-- 颜色图例 -->
|
|
|
+ <div class="legend">
|
|
|
+ <div class="legend-item">
|
|
|
+ <div class="color-box" style="background-color: #15b392;"></div>
|
|
|
+ <span>安全区间 (0.0-0.2 mg/kg)</span>
|
|
|
+ </div>
|
|
|
+ <div class="legend-item">
|
|
|
+ <div class="color-box" style="background-color: #ffea56;"></div>
|
|
|
+ <span>预警区间 (0.2-0.3 mg/kg)</span>
|
|
|
+ </div>
|
|
|
+ <div class="legend-item">
|
|
|
+ <div class="color-box" style="background-color: #FF0000;"></div>
|
|
|
+ <span>超标区间 (≥0.3 mg/kg)</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
-
|
|
|
<div v-if="loadingTownshipMap" class="loading-container">
|
|
|
<el-icon class="loading-icon"><Loading /></el-icon>
|
|
|
<span>乡镇地图加载中...</span>
|
|
|
@@ -71,37 +74,51 @@
|
|
|
class="township-map-container"
|
|
|
></div>
|
|
|
</div>
|
|
|
-
|
|
|
- <!-- 颜色图例 -->
|
|
|
- <div class="legend">
|
|
|
- <div class="legend-item">
|
|
|
- <div class="color-box" style="background-color: #00FF00;"></div>
|
|
|
- <span>安全区间 (0.0-0.2 mg/kg)</span>
|
|
|
- </div>
|
|
|
- <div class="legend-item">
|
|
|
- <div class="color-box" style="background-color: #FFFF00;"></div>
|
|
|
- <span>预警区间 (0.2-0.3 mg/kg)</span>
|
|
|
- </div>
|
|
|
- <div class="legend-item">
|
|
|
- <div class="color-box" style="background-color: #FF0000;"></div>
|
|
|
- <span>超标区间 (≥0.3 mg/kg)</span>
|
|
|
- </div>
|
|
|
- <div class="legend-item">
|
|
|
- <div class="color-box" style="background-color: #CCCCCC;"></div>
|
|
|
- <span>无数据</span>
|
|
|
- </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>
|
|
|
<p>暂无乡镇边界数据</p>
|
|
|
</div>
|
|
|
+ <div v-if="loadingMap" class="loading-container">
|
|
|
+ <el-icon class="loading-icon"><Loading /></el-icon>
|
|
|
+ <span>地图加载中...</span>
|
|
|
+ </div>
|
|
|
+ <img v-if="mapImageUrl && !loadingMap" :src="mapImageUrl" alt="水稻态Cd预测地图" class="map-image">
|
|
|
+ <div v-if="!mapImageUrl && !loadingMap" class="no-data">
|
|
|
+ <el-icon><Picture /></el-icon>
|
|
|
+ <p>暂无地图数据</p>
|
|
|
+ </div>
|
|
|
+
|
|
|
</div>
|
|
|
|
|
|
<!-- 统计图表区域 -->
|
|
|
<div class="stats-area">
|
|
|
<h3>水稻Cd预测统计信息</h3>
|
|
|
<div class="model-info">
|
|
|
- <el-tag type="info">{{ modelInfo.modelType || 'Cd预测模型' }}</el-tag>
|
|
|
+ <el-tag type="info">{{ '水稻Cd预测模型' }}</el-tag>
|
|
|
<span class="update-time">
|
|
|
最后更新: {{ modelInfo.updateTime ? new Date(modelInfo.updateTime).toLocaleString() : '未知' }}
|
|
|
</span>
|
|
|
@@ -113,21 +130,7 @@
|
|
|
</div>
|
|
|
|
|
|
<div v-if="!loadingStats && statisticsData.length" class="stats-container">
|
|
|
- <!-- 基础统计表格 -->
|
|
|
- <h4>基础统计信息</h4>
|
|
|
- <el-table
|
|
|
- :data="statisticsData"
|
|
|
- style="width: 100%; margin-bottom: 20px;"
|
|
|
- border
|
|
|
- stripe
|
|
|
- >
|
|
|
- <el-table-column prop="name" label="统计项" min-width="180" />
|
|
|
- <el-table-column prop="value" label="值" min-width="150" />
|
|
|
- <el-table-column prop="unit" label="单位" min-width="100" />
|
|
|
- <el-table-column prop="description" label="描述" min-width="200" />
|
|
|
- </el-table>
|
|
|
-
|
|
|
- <!-- 分布统计表格 -->
|
|
|
+ <!-- 分布统计表格 -->
|
|
|
<h4>水稻镉含量分布统计</h4>
|
|
|
<el-table
|
|
|
:data="distributionData"
|
|
|
@@ -145,6 +148,20 @@
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
+
|
|
|
+ <!-- 基础统计表格 -->
|
|
|
+ <h4>基础统计信息</h4>
|
|
|
+ <el-table
|
|
|
+ :data="statisticsData"
|
|
|
+ style="width: 100%; margin-bottom: 20px;"
|
|
|
+ border
|
|
|
+ stripe
|
|
|
+ >
|
|
|
+ <el-table-column prop="name" label="统计项" min-width="180" />
|
|
|
+ <el-table-column prop="value" label="值" min-width="150" />
|
|
|
+ <el-table-column prop="unit" label="单位" min-width="100" />
|
|
|
+ <el-table-column prop="description" label="描述" min-width="200" />
|
|
|
+ </el-table>
|
|
|
</div>
|
|
|
|
|
|
<div v-if="!loadingStats && !statisticsData.length" class="no-data">
|
|
|
@@ -161,7 +178,7 @@
|
|
|
<el-icon class="loading-icon"><Loading /></el-icon>
|
|
|
<span>直方图加载中...</span>
|
|
|
</div>
|
|
|
- <img v-if="histogramImageUrl && !loadingHistogram" :src="histogramImageUrl" alt="作物态Cd预测直方图" class="histogram-image">
|
|
|
+ <img v-if="histogramImageUrl && !loadingHistogram" :src="histogramImageUrl" alt="水稻Cd预测直方图" class="histogram-image">
|
|
|
<div v-if="!histogramImageUrl && !loadingHistogram" class="no-data">
|
|
|
<el-icon><Histogram /></el-icon>
|
|
|
<p>暂无直方图数据</p>
|
|
|
@@ -192,6 +209,7 @@ export default {
|
|
|
},
|
|
|
data() {
|
|
|
return {
|
|
|
+ currentFeatureInfo: null, // 存储当前点击要素的详细信息
|
|
|
mapContainerStyle: {
|
|
|
width: '100%',
|
|
|
height: '800px'
|
|
|
@@ -226,6 +244,8 @@ export default {
|
|
|
currentTooltipData: null, // 当前乡镇的详情数据
|
|
|
isTooltipLoading: false, // tooltip 是否在加载中
|
|
|
dataMap:{},
|
|
|
+ fluxDataMap: {}, // 存储通量Cd数据
|
|
|
+ fluxDataLoaded: false, // 通量Cd数据是否已加载
|
|
|
};
|
|
|
},
|
|
|
|
|
|
@@ -233,6 +253,7 @@ export default {
|
|
|
// 组件挂载时获取最新数据
|
|
|
this.fetchLatestResults();
|
|
|
this.fetchStatistics();
|
|
|
+ this.fetchFluxData();
|
|
|
|
|
|
// 使用更智能的初始化时机
|
|
|
this.$nextTick(() => {
|
|
|
@@ -256,6 +277,7 @@ export default {
|
|
|
// 组件销毁前移除事件监听,避免内存泄漏
|
|
|
window.removeEventListener('resize', this.handleResize);
|
|
|
if (this.townshipMapInstance) {
|
|
|
+ this.townshipMapInstance.off('click', this.handleWmsFeatureClick);
|
|
|
this.townshipMapInstance.remove();
|
|
|
this.townshipMapInstance = null;
|
|
|
}
|
|
|
@@ -263,6 +285,24 @@ export default {
|
|
|
|
|
|
|
|
|
methods: {
|
|
|
+ async fetchFluxData() {
|
|
|
+ try {
|
|
|
+ this.loadingTownshipMap = true;
|
|
|
+ const response = await api8000.get('/api/cd-flux/statistics/town/all');
|
|
|
+
|
|
|
+ if (response.data && response.data.success) {
|
|
|
+ this.fluxDataMap = response.data.data.statistics || {};
|
|
|
+ this.fluxDataLoaded = true;
|
|
|
+ console.log('通量Cd数据加载成功', this.fluxDataMap);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取通量Cd数据失败:', error);
|
|
|
+ this.fluxDataMap = {};
|
|
|
+ } finally {
|
|
|
+ this.loadingTownshipMap = false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
handleResize() {
|
|
|
if (this.townshipMapInstance) {
|
|
|
this.townshipMapInstance.invalidateSize();
|
|
|
@@ -430,25 +470,6 @@ export default {
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- // 根据Cd含量获取对应的颜色
|
|
|
- getColorByCdValue(cdValue) {
|
|
|
- if (cdValue === null || cdValue === undefined) {
|
|
|
- return '#CCCCCC'; // 无数据时显示灰色
|
|
|
- }
|
|
|
- const numValue = Number(cdValue);
|
|
|
- if (isNaN(numValue)) {
|
|
|
- return '#CCCCCC';
|
|
|
- }
|
|
|
- if (numValue >= 0.0 && numValue < 0.2) {
|
|
|
- return '#00FF00'; // 安全区间 - 绿色
|
|
|
- } else if (numValue >= 0.2 && numValue < 0.3) {
|
|
|
- return '#FFFF00'; // 预警区间 - 黄色
|
|
|
- } else if (numValue >= 0.3) {
|
|
|
- return '#FF0000'; // 超标区间 - 红色
|
|
|
- }
|
|
|
- return '#CCCCCC'; // 默认灰色
|
|
|
- },
|
|
|
-
|
|
|
// 根据乡镇名请求接口
|
|
|
async fetchTownshipDetailByName(townName) {
|
|
|
if (this.dataMap[townName]) {
|
|
|
@@ -472,6 +493,9 @@ export default {
|
|
|
async initTownshipMap() {
|
|
|
try {
|
|
|
this.loadingTownshipMap = true;
|
|
|
+ if (!this.fluxDataLoaded) {
|
|
|
+ await this.fetchFluxData();
|
|
|
+ }
|
|
|
|
|
|
// 先强制设置容器尺寸
|
|
|
this.forceMapContainerSize();
|
|
|
@@ -655,32 +679,30 @@ renderTownshipMap() {
|
|
|
attributionControl: false
|
|
|
});
|
|
|
|
|
|
- // 添加缩放控件
|
|
|
+ // 添加缩放控件
|
|
|
L.control.zoom({
|
|
|
position: 'bottomright'
|
|
|
}).addTo(this.townshipMapInstance);
|
|
|
|
|
|
+ // 添加比例尺控件
|
|
|
+ L.control.scale({
|
|
|
+ position: 'bottomleft',
|
|
|
+ imperial: false // 使用公制单位
|
|
|
+ }).addTo(this.townshipMapInstance);
|
|
|
+
|
|
|
// 添加空白底图
|
|
|
L.tileLayer('').addTo(this.townshipMapInstance);
|
|
|
|
|
|
// 处理GeoJSON数据
|
|
|
const geoJsonLayer = L.geoJSON(this.townshipGeoJson, {
|
|
|
- style: (feature) => {
|
|
|
- const townName = feature.properties.name;
|
|
|
- const townData = this.dataMap[townName];
|
|
|
- let avgValue = null;
|
|
|
-
|
|
|
- if (townData?.data?.基础统计) {
|
|
|
- avgValue = Number(townData.data.基础统计.平均值);
|
|
|
- }
|
|
|
-
|
|
|
+ style: () => {
|
|
|
return {
|
|
|
- fillColor: this.getColorByCdValue(avgValue),
|
|
|
+ fillColor: 'transparent', // 固定为白色填充
|
|
|
weight: 2,
|
|
|
opacity: 1,
|
|
|
- color: '#000',
|
|
|
+ color: '#000', // 黑色边界线
|
|
|
dashArray: '',
|
|
|
- fillOpacity: 0.7
|
|
|
+ fillOpacity: 0
|
|
|
};
|
|
|
},
|
|
|
onEachFeature: (feature, layer) => {
|
|
|
@@ -688,13 +710,15 @@ renderTownshipMap() {
|
|
|
|
|
|
// 保存原始样式,用于恢复
|
|
|
const originalStyle = {
|
|
|
+ fillColor: 'transparent', // 固定为白色填充
|
|
|
weight: 2,
|
|
|
- color: '#000',
|
|
|
+ opacity: 1,
|
|
|
+ color: '#000', // 黑色边界线
|
|
|
dashArray: '',
|
|
|
- fillOpacity: 0.7,
|
|
|
- fillColor: this.getColorByCdValue(this.dataMap[townName]?.data?.基础统计?.平均值)
|
|
|
+ fillOpacity: 0
|
|
|
};
|
|
|
|
|
|
+
|
|
|
// 鼠标悬停事件 - 修复版本
|
|
|
layer.on('mouseover', (e) => {
|
|
|
// 高亮当前区域
|
|
|
@@ -719,14 +743,18 @@ renderTownshipMap() {
|
|
|
layer.closeTooltip();
|
|
|
}
|
|
|
});
|
|
|
-
|
|
|
- // 点击事件
|
|
|
- layer.on('click', () => {
|
|
|
- this.$message.info(`${townName} 的Cd含量数据已显示`);
|
|
|
- });
|
|
|
}
|
|
|
}).addTo(this.townshipMapInstance);
|
|
|
|
|
|
+ // 添加GeoServer WMS图层作为底图
|
|
|
+ const wmsLayer = L.tileLayer.wms('http://localhost:8080/geoserver/soilgd/wms', {
|
|
|
+ layers: 'soilgd:shapeMap', // 替换为你的图层名称
|
|
|
+ format: 'image/png',
|
|
|
+ transparent: true,
|
|
|
+ version: '1.1.0',
|
|
|
+ attribution: 'GeoServer WMS'
|
|
|
+ }).addTo(this.townshipMapInstance);
|
|
|
+
|
|
|
// 调整地图视野
|
|
|
if (geoJsonLayer.getBounds().isValid()) {
|
|
|
// 方式1:完全禁用 fitBounds,直接设置中心和zoom
|
|
|
@@ -744,6 +772,7 @@ renderTownshipMap() {
|
|
|
}
|
|
|
|
|
|
console.log('地图渲染完成');
|
|
|
+ this.townshipMapInstance.on('click', this.handleWmsFeatureClick);
|
|
|
|
|
|
} catch (error) {
|
|
|
console.error('渲染地图时发生错误:', error);
|
|
|
@@ -751,48 +780,131 @@ renderTownshipMap() {
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- // 新增:显示tooltip的方法
|
|
|
-// 修复显示tooltip的方法
|
|
|
-showTooltip(layer, townName, latlng) {
|
|
|
- const townData = this.dataMap[townName];
|
|
|
- let tooltipContent = `<div class="town-tooltip"><h3>${townName}</h3>`;
|
|
|
-
|
|
|
- if (!townData?.data?.基础统计) {
|
|
|
- tooltipContent += '<p>数据加载中...</p></div>';
|
|
|
- } else {
|
|
|
- const stats = townData.data.基础统计;
|
|
|
- const dist = townData.data.分布统计表格;
|
|
|
- tooltipContent += `
|
|
|
- <div style="height:1px;background:#0066CC;margin:5px 0;"></div>
|
|
|
- <p><strong>样本数量:</strong> ${stats.采样点数量 ?? '无数据'}</p>
|
|
|
- <p><strong>平均值:</strong> ${stats.平均值?.toFixed(4) ?? '无数据'} mg/kg</p>
|
|
|
- <p><strong>最小值:</strong> ${stats.最小值?.toFixed(4) ?? '无数据'} mg/kg</p>
|
|
|
- <p><strong>最大值:</strong> ${stats.最大值?.toFixed(4) ?? '无数据'} mg/kg</p>
|
|
|
- <div style="height:1px;background:#0066CC;margin:5px 0;"></div>
|
|
|
- <p><strong>安全区间占比:</strong> ${dist?.汇总?.安全区间占比 ?? '无'}%</p>
|
|
|
- <p><strong>预警区间占比:</strong> ${dist?.汇总?.预警区间占比 ?? '无'}%</p>
|
|
|
- <p><strong>超标区间占比:</strong> ${dist?.汇总?.超标区间占比 ?? '无'}%</p>
|
|
|
- </div>`;
|
|
|
- }
|
|
|
-
|
|
|
- // 先解除之前的tooltip绑定
|
|
|
- if (layer._tooltip) {
|
|
|
- layer.unbindTooltip();
|
|
|
+
|
|
|
+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)
|
|
|
+ });
|
|
|
+
|
|
|
+ // 打印完整请求 URL(用于调试)
|
|
|
+ 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}`);
|
|
|
+
|
|
|
+ // 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();
|
|
|
+
|
|
|
+ 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 // 最终镉浓度
|
|
|
+ };
|
|
|
+
|
|
|
+ // 打印验证(可选)
|
|
|
+ console.log('当前点击要素信息:', this.currentFeatureInfo);
|
|
|
+ console.log('WMS 要素点击信息(JSON):', featureData);
|
|
|
+ } catch (error) {
|
|
|
+ console.error('WMS 要素点击交互失败:', error);
|
|
|
+ this.$message.warning('获取要素信息失败:' + error.message);
|
|
|
}
|
|
|
-
|
|
|
- // 使用Leaflet的tooltip
|
|
|
- layer.bindTooltip(tooltipContent, {
|
|
|
- className: 'custom-town-tooltip',
|
|
|
- direction: 'top',
|
|
|
- permanent: false,
|
|
|
- sticky: true,
|
|
|
- offset: [0, -10],
|
|
|
- interactive: false // 确保tooltip不会拦截鼠标事件
|
|
|
- });
|
|
|
-
|
|
|
- // 打开tooltip
|
|
|
- layer.openTooltip(latlng);
|
|
|
},
|
|
|
+
|
|
|
+ // 新增:显示tooltip的方法
|
|
|
+ showTooltip(layer, townName, latlng) {
|
|
|
+ const riceData = this.dataMap[townName]; // 水稻Cd数据
|
|
|
+ const fluxData = this.fluxDataMap[townName]; // 通量Cd数据
|
|
|
+
|
|
|
+ let tooltipContent = `<div class="town-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>
|
|
|
+ `;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 通量Cd数据部分
|
|
|
+ tooltipContent += `<div style="margin-top: 10px; margin-bottom: 10px;"><strong>通量Cd</strong></div>`;
|
|
|
+ if (!fluxData) {
|
|
|
+ tooltipContent += '<p>通量Cd数据加载中...</p>';
|
|
|
+ } else {
|
|
|
+ tooltipContent += `
|
|
|
+ <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>
|
|
|
+ `;
|
|
|
+ }
|
|
|
+
|
|
|
+ tooltipContent += `</div>`;
|
|
|
+
|
|
|
+ // 先解除之前的tooltip绑定
|
|
|
+ if (layer._tooltip) {
|
|
|
+ layer.unbindTooltip();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用Leaflet的tooltip
|
|
|
+ layer.bindTooltip(tooltipContent, {
|
|
|
+ className: 'custom-town-tooltip',
|
|
|
+ direction: 'top',
|
|
|
+ permanent: false,
|
|
|
+ sticky: true,
|
|
|
+ offset: [0, -10],
|
|
|
+ interactive: false
|
|
|
+ });
|
|
|
+
|
|
|
+ // 打开tooltip
|
|
|
+ layer.openTooltip(latlng);
|
|
|
+ },
|
|
|
|
|
|
// 上传并计算
|
|
|
async calculate() {
|
|
|
@@ -812,7 +924,7 @@ showTooltip(layer, townName, latlng) {
|
|
|
formData.append('data_file', this.selectedFile);
|
|
|
formData.append('use_database', 'false');
|
|
|
|
|
|
- // 调用作物Cd地图接口
|
|
|
+ // 调用水稻Cd地图接口
|
|
|
const mapResponse = await api8000.post(
|
|
|
'/api/cd-prediction/crop-cd/generate-and-get-map',
|
|
|
formData,
|
|
|
@@ -869,7 +981,7 @@ showTooltip(layer, townName, latlng) {
|
|
|
formData.append('area', this.countyName);
|
|
|
formData.append('use_database', 'true');
|
|
|
|
|
|
- // 调用作物Cd地图接口
|
|
|
+ // 调用水稻Cd地图接口
|
|
|
const mapResponse = await api8000.post(
|
|
|
'/api/cd-prediction/crop-cd/generate-and-get-map',
|
|
|
formData,
|
|
|
@@ -923,7 +1035,7 @@ showTooltip(layer, townName, latlng) {
|
|
|
|
|
|
const link = document.createElement('a');
|
|
|
link.href = URL.createObjectURL(this.mapBlob);
|
|
|
- link.download = `${this.countyName}_作物态Cd预测地图.jpg`;
|
|
|
+ link.download = `${this.countyName}_水稻Cd预测地图.jpg`;
|
|
|
link.click();
|
|
|
URL.revokeObjectURL(link.href);
|
|
|
},
|
|
|
@@ -937,7 +1049,7 @@ showTooltip(layer, townName, latlng) {
|
|
|
|
|
|
const link = document.createElement('a');
|
|
|
link.href = URL.createObjectURL(this.histogramBlob);
|
|
|
- link.download = `${this.countyName}_作物态Cd预测直方图.jpg`;
|
|
|
+ link.download = `${this.countyName}_水稻Cd预测直方图.jpg`;
|
|
|
link.click();
|
|
|
URL.revokeObjectURL(link.href);
|
|
|
},
|
|
|
@@ -945,7 +1057,7 @@ showTooltip(layer, townName, latlng) {
|
|
|
// 导出数据
|
|
|
async exportData() {
|
|
|
try {
|
|
|
- this.$message.info('正在获取作物态Cd预测数据...');
|
|
|
+ this.$message.info('正在获取水稻态Cd预测数据...');
|
|
|
|
|
|
const response = await api8000.get(
|
|
|
`/api/cd-prediction/crop-cd/export-csv`,
|
|
|
@@ -955,7 +1067,7 @@ showTooltip(layer, townName, latlng) {
|
|
|
const blob = new Blob([response.data], { type: 'text/csv' });
|
|
|
const link = document.createElement('a');
|
|
|
link.href = URL.createObjectURL(blob);
|
|
|
- link.download = `作物态Cd预测数据.csv`;
|
|
|
+ link.download = `水稻态Cd预测数据.csv`;
|
|
|
link.click();
|
|
|
URL.revokeObjectURL(link.href);
|
|
|
|
|
|
@@ -983,7 +1095,7 @@ showTooltip(layer, townName, latlng) {
|
|
|
|
|
|
.township-map-wrapper {
|
|
|
position: relative;
|
|
|
- width: 100%;
|
|
|
+ width: 90%;
|
|
|
max-width: 1000px;
|
|
|
margin: 15px auto;
|
|
|
border: 1px solid #e0e0e0;
|
|
|
@@ -1028,28 +1140,44 @@ showTooltip(layer, townName, latlng) {
|
|
|
}
|
|
|
|
|
|
.town-tooltip {
|
|
|
- min-width: 200px;
|
|
|
- max-width: 300px;
|
|
|
+ min-width: 280px;
|
|
|
+ max-width: 350px;
|
|
|
}
|
|
|
|
|
|
.town-tooltip h3 {
|
|
|
- margin: 0 0 5px;
|
|
|
+ margin: 0 0 10px;
|
|
|
color: #0066CC;
|
|
|
text-align: center;
|
|
|
font-size: 16px;
|
|
|
+ border-bottom: 2px solid #0066CC;
|
|
|
+ padding-bottom: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.town-tooltip strong {
|
|
|
+ color: #333;
|
|
|
+ font-weight: 600;
|
|
|
}
|
|
|
|
|
|
.town-tooltip p {
|
|
|
- margin: 2px 0;
|
|
|
+ margin: 3px 0;
|
|
|
font-size: 12px;
|
|
|
line-height: 1.4;
|
|
|
}
|
|
|
|
|
|
+/* 区分不同数据部分的样式 */
|
|
|
+.town-tooltip div strong {
|
|
|
+ color: #47C3B9;
|
|
|
+ display: block;
|
|
|
+ margin: 8px 0 5px;
|
|
|
+ padding-left: 5px;
|
|
|
+ border-left: 3px solid #47C3B9;
|
|
|
+}
|
|
|
+
|
|
|
/* Leaflet地图容器样式 */
|
|
|
.township-map-container {
|
|
|
width: 100% !important;
|
|
|
max-width: 1000px;
|
|
|
- height: 800px !important;
|
|
|
+ height: 500px !important;
|
|
|
border-radius: 4px;
|
|
|
background-color: #f5f5f5;
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
@@ -1263,4 +1391,61 @@ showTooltip(layer, townName, latlng) {
|
|
|
height: 500px !important;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+/* 地图容器包裹层:确保内部元素垂直排列 */
|
|
|
+.township-map-wrapper {
|
|
|
+ position: relative;
|
|
|
+ width: 90%;
|
|
|
+ max-width: 1000px;
|
|
|
+ margin: 15px auto;
|
|
|
+ border: 1px solid #e0e0e0;
|
|
|
+ border-radius: 8px;
|
|
|
+ background: white;
|
|
|
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
|
+ overflow: hidden;
|
|
|
+ display: flex; /* 新增:包裹层使用 Flex 布局 */
|
|
|
+ flex-direction: column; /* 子元素垂直排列 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 要素信息面板:水平平铺 */
|
|
|
+.feature-info-panel {
|
|
|
+ background-color: #f8f9fa;
|
|
|
+ border: 1px solid #e9ecef;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 15px;
|
|
|
+ margin-top: 15px; /* 与地图容器间距 */
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
|
+ width: 100%; /* 占满包裹层宽度 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 信息面板标题:占满宽度 */
|
|
|
+.feature-info-panel h4 {
|
|
|
+ width: 100%;
|
|
|
+ margin-bottom: 10px; /* 标题与内容间距 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 信息列表:水平排列 */
|
|
|
+.feature-info-panel ul {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap; /* 允许换行 */
|
|
|
+ gap: 15px; /* 项之间的间距 */
|
|
|
+ list-style: none;
|
|
|
+ padding: 0;
|
|
|
+ margin: 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 信息项:水平平铺 */
|
|
|
+.feature-info-panel li {
|
|
|
+ flex: 1; /* 每项占据等宽空间 */
|
|
|
+ min-width: 150px; /* 最小宽度,防止过窄 */
|
|
|
+ margin-bottom: 0; /* 移除垂直间距 */
|
|
|
+ font-size: 14px;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+/* 强调文字样式 */
|
|
|
+.feature-info-panel li strong {
|
|
|
+ color: #0066CC;
|
|
|
+ margin-right: 5px;
|
|
|
+}
|
|
|
</style>
|