cynthia-qin 2 tygodni temu
rodzic
commit
39cce4491f

+ 18 - 0
src/api/statistics/report.js

@@ -55,3 +55,21 @@ export async function getzgbytztqkInfo(data) {
     data: data
   })
 }
+
+// 生成病菌预测世界地图
+export function generatesjbytfzzbqkInfo(data) {
+  return request({
+    url: '/data/report/sjbytfzzbqk',
+    method: 'post',
+    data: data
+  })
+}
+
+// 生成病菌预测中国地图
+export function generatezgbytfzzbqkInfo(data) {
+  return request({
+    url: '/data/report/zgbytfzzbqk',
+    method: 'post',
+    data: data
+  })
+}

+ 80 - 122
src/views/statistics/Spatiotemporal-distribution/index.vue

@@ -178,6 +178,8 @@ export default {
       barChart: null,
       pieChart: null,
       mapChart: null,
+      chinaPieData: [],
+      worldPieData: [],
       form:{
   batch: "",
   bytName: "",
@@ -194,6 +196,19 @@ export default {
     // this.initRegionOptions();
     this.fetchAllData()
     this.initCharts();
+      // 地图点击切换
+  this.$nextTick(() => {
+    this.mapChart.on('click', params => {
+      console.log('地图点击:', params);
+      if (params && params.name === 'China' && !this.isChinaMap) {
+        this.isChinaMap = true;
+        this.fetchAllData();
+      } else if (params && params.name !== 'China' && this.isChinaMap) {
+        this.isChinaMap = false;
+        this.fetchAllData();
+      }
+    });
+  });
   },
   methods: {
     async searchDisease(query) {
@@ -247,47 +262,14 @@ export default {
         const response4 = await getzgbytztqkInfo(this.form);
         this.growthData = response.data;
         this.barData = response2.data;
+        this.worldPieData = response3.data;
+        this.chinaPieData = response4.data;
+        this.mapData = this.isChinaMap ? this.chinaPieData : this.worldPieData;
       } catch (error) {
         console.error('获取数据失败:', error);
         this.$message.error('获取数据失败,请稍后重试');
       }
 
-      // // 联动下拉框
-      // if (this.filters.disease) {
-      //   // 假数据
-      //   this.subtypeOptions = ['亚型 1', '亚型 2', '亚型 3'];
-      // }
-      // if (this.filters.subtype) {
-      //   // 假数据
-      //   this.branchOptions = ['分支 A', '分支 B', '分支 C'];
-      // }
-      // // 获取假数据
-      // this.growthData = [
-      //   { date: '2025-06-01', value: 10, ratio: 10, count: 5 },
-      //   { date: '2025-06-02', value: 20, ratio: 20, count: 8 }
-      // ];
-      // this.barData = [
-      //   { region: '中国', count: 100, percent: 60 },
-      //   { region: '美国', count: 80, percent: 40 }
-      // ];
-      // this.mapData = this.isChinaMap
-      //   ? Object.keys(chinaPieData).map(name => {
-      //       const geoCoord = chinaGeoCoordMap[name] || [0, 0]; // 若经纬度不存在,使用默认值 [0, 0]
-      //       return {
-      //         name,
-      //         value: [...geoCoord, chinaPieData[name].reduce((sum, item) => sum + item.value, 0)],
-      //         data: chinaPieData[name]
-      //       };
-      //     })
-      //   : Object.keys(worldPieData).map(name => {
-      //       const geoCoord = worldGeoCoordMap[name] || [0, 0]; // 若经纬度不存在,使用默认值 [0, 0]
-      //       return {
-      //         name,
-      //         value: [...geoCoord, worldPieData[name].reduce((sum, item) => sum + item.value, 0)],
-      //         data: worldPieData[name]
-      //       };
-      //     });
-      // this.description = '这是一段假的数据分析文本。';
       this.renderCharts();
     },
     initCharts() {
@@ -366,93 +348,69 @@ export default {
         // 使用动态生成的系列数据
         series: seriesData
       });
-      // 子图三:分支饼图
-      // this.pieChart.setOption({
-      //   tooltip: {
-      //     trigger: 'item',
-      //     formatter: '{b}: {c} ({d}%)'
-      //   },
-      //   series: [{
-      //     type: 'pie',
-      //     radius: '60%',
-      //     data: this.pieData
-      //   }]
-      // })
       // // 地图
-      // this.mapChart.setOption({
-      //   tooltip: {
-      //     trigger: 'item',
-      //     formatter: p => {
-      //       if (p.seriesType === 'scatter') {
-      //         const total = p.data.value[2];
-      //         const pieData = p.data.data;
-      //         let tooltipStr = `${p.name}<br/>总分支数: ${total}<br/>`;
-      //         if (pieData) {
-      //           pieData.forEach(item => {
-      //             const percent = ((item.value / total) * 100).toFixed(2);
-      //             tooltipStr += `${item.name}: ${item.value} (${percent}%)<br/>`;
-      //           });
-      //         } else {
-      //           tooltipStr += '暂无分支数据<br/>';
-      //         }
-      //         return tooltipStr;
-      //       }
-      //       return p.name;
-      //     }
-      //   },
-      //   // 添加 geo 组件,用于显示地图背景
-      //   geo: {
-      //     map: this.isChinaMap ? 'china' : 'world',
-      //     roam: true,
-      //     // 设置中国地图和世界地图的中心位置和缩放级别
-      //     center: this.isChinaMap ? [104.1954, 35.8617] : [10, 10],
-      //     zoom: this.isChinaMap ? 2 : 1.2,
-      //     label: {
-      //       emphasis: {
-      //         show: false
-      //       }
-      //     },
-      //     itemStyle: {
-      //       normal: {
-      //         // 修改为浅色背景,这里使用浅灰色作为示例
-      //         areaColor: '#f0f0f0',
-      //         borderColor: '#999'
-      //       },
-      //       emphasis: {
-      //         // 修改为稍深一点的浅色,用于鼠标悬停状态
-      //         areaColor: '#e0e0e0'
-      //       }
-      //     }
-      //   },
-      //   series: [
-      //     {
-      //       type: 'scatter',
-      //       coordinateSystem: 'geo',
-      //       data: this.mapData,
-      //       symbolSize: val => Math.sqrt(val[2]) * 2,
-      //       label: {
-      //         show: true,
-      //         formatter: '{b}'
-      //       },
-      //       itemStyle: {
-      //         color: 'rgba(255, 0, 0, 0.8)'
-      //       },
-      //       emphasis: {
-      //         itemStyle: {
-      //           color: 'red'
-      //         }
-      //       }
-      //     }
-      //   ]
-      // });
-      // // 地图点击切换
-      // this.mapChart.off('click');
-      // this.mapChart.on('click', params => {
-      //   if (!this.isChinaMap && params.name === '中国') {
-      //     this.isChinaMap = true;
-      //     this.fetchAllData();
-      //   }
-      // });
+      // 处理 mapData 数据,转换为符合 ECharts 要求的格式
+      const processedMapData = this.mapData.map(item => {
+        const [lat, lng] = item.latlng.split(',').map(Number);
+        return {
+          name: item.area,
+          value: [lng, lat, item.num],
+          num: item.num
+        };
+      });
+   this.mapChart.setOption({
+  title: { text: '病毒预测分布', left: 'center' },
+  tooltip: {
+    trigger: 'item',
+    formatter: p => `${p.name}<br/>数量: ${p.data ? p.data.num : '-'}`
+  },
+  visualMap: {
+    min: 0,
+    max: 150,
+    left: 'left',
+    top: 'bottom',
+    text: ['高', '低'],
+    calculable: true,
+    inRange: {
+      color: ['#e0f3f8', '#abd9e9', '#74add1', '#4575b4', '#313695'] // 颜色深浅
+    }
+  },
+  geo: {
+    map: this.isChinaMap ? 'china' : 'world',
+    roam: true,
+    label: { show: false },
+    itemStyle: {
+      areaColor: '#f0f0f0',
+      borderColor: '#999'
+    },
+    emphasis: {
+      itemStyle: {
+        areaColor: '#e0e0e0'
+      }
+    }
+  },
+  series: [
+    {
+      type: 'scatter',
+      coordinateSystem: 'geo',
+      data: processedMapData,
+      symbolSize: val => Math.max(8, Math.sqrt(val[2]) * 2), // 数量越大点越大
+      encode: { value: 2 },
+      label: {
+        show: false
+      },
+      itemStyle: {
+        color: '#4575b4'
+      },
+      emphasis: {
+        itemStyle: {
+          color: '#d73027'
+        }
+      }
+    }
+  ]
+});
+
     }
   }
 };

+ 149 - 45
src/views/statistics/popularity-prediction/index.vue

@@ -3,11 +3,11 @@
     <div class="tree-map-container">
       <div class="phylo-tree">
         <!-- <h3>病毒进化树</h3> -->
-        <div id="phyloTree" ref="treeContainer" style="width: 50vw; height: 500px;"></div>
+        <div id="phyloTree" ref="treeContainer" style="width: 100%; height: 500px;"></div>
       </div>
       <div class="world-map">
         <!-- <h3>世界地图病毒预测图</h3> -->
-        <div id="virusMap" style="width: 50vw; height: 500px;"></div>
+        <div id="virusMap" style="width: 100%; height: 500px;"></div>
       </div>
     </div>
   </div>
@@ -17,64 +17,162 @@
 import * as echarts from 'echarts';
 // 引入世界地图数据
 import worldJson from 'echarts/map/json/world.json';
+// 引入中国地图数据
+import chinaJson from 'echarts/map/json/china.json';
+import { generatesjbytfzzbqkInfo, generatezgbytfzzbqkInfo } from '@/api/statistics/report';
 
 // 注册世界地图
 echarts.registerMap('world', worldJson);
+// 注册中国地图
+echarts.registerMap('china', chinaJson);
 
 export default {
   name: 'PopularityPrediction',
+  data() {
+    return {
+      form: {
+        batch: '',
+        bytName: '',
+        country: '',
+        endDate: '',
+        model: '',
+        province: '',
+        startDate: '',
+      },
+      mapData: [],
+      isChina: false, // 是否显示中国地图
+      worldData: [],
+      chinaData: [],
+    };
+  },
   methods: {
     renderVirusMap() {
-      // 示例病毒预测数据
-      const mapData = [
-        { name: 'China', value: 120 },
-        { name: 'United States', value: 80 },
-        { name: 'Brazil', value: 60 },
-        { name: 'India', value: 90 }
-      ];
       const chart = echarts.init(document.getElementById('virusMap'));
+
+      // 定义病原体颜色映射
+      const pathogenColorMap = {};
+      const colorList = ['#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF', '#800080', '#FFA500', '#808080', '#008000'];
+      let colorIndex = 0;
+
+      // 处理数据,生成散点数据
+      const scatterData = [];
+      const currentData = this.isChina ? this.chinaData : this.worldData;
+      currentData.forEach(item => {
+        const [lat, lng] = item.latlng.split(',').map(str => {
+          const num = parseFloat(str.trim());
+          return isNaN(num) ? 0 : num;
+        });
+        const pathogenData = [];
+        Object.keys(item).filter(key => key!== 'area' && key!== 'latlng').forEach(pathogen => {
+          if (!pathogenColorMap[pathogen]) {
+            pathogenColorMap[pathogen] = colorList[colorIndex % colorList.length];
+            colorIndex++;
+          }
+          pathogenData.push({
+            name: pathogen,
+            value: item[pathogen],
+            color: pathogenColorMap[pathogen]
+          });
+        });
+
+        scatterData.push({
+          name: item.area,
+          value: [lng, lat, pathogenData.reduce((sum, data) => sum + data.value, 0)],
+          pathogenData: pathogenData
+        });
+      });
+
       chart.setOption({
         title: { text: '病毒预测分布', left: 'center' },
-        tooltip: { trigger: 'item' },
-        visualMap: {
-          min: 0,
-          max: 150,
-          left: 'left',
-          top: 'bottom',
-          text: ['高', '低'],
-          calculable: true
+        tooltip: {
+          trigger: 'item',
+          formatter: (params) => {
+            let tooltipStr = `<div>${params.data.name}</div>`;
+            params.data.pathogenData.forEach(data => {
+              tooltipStr += `<div style="color: ${data.color}">● ${data.name}: ${data.value}</div>`;
+            });
+            return tooltipStr;
+          }
+        },
+        legend: {
+          data: Object.keys(pathogenColorMap).map(pathogen => ({
+            name: pathogen,
+            icon: 'circle',
+            textStyle: {
+              color: pathogenColorMap[pathogen]
+            }
+          })),
+          type: 'scroll',
+          orient: 'vertical',
+          right: 10,
+          top: 20,
+          bottom: 20
+        },
+        geo: {
+          map: this.isChina ? 'china' : 'world',
+          roam: true,
+          label: {
+            emphasis: {
+              show: false
+            }
+          },
+          itemStyle: {
+            normal: {
+              areaColor: '#f0f0f0',
+              borderColor: '#999'
+            },
+            emphasis: {
+              areaColor: '#e0e0e0'
+            }
+          },
+          // 设置初始中心和缩放级别
+          center: this.isChina ? [104.1954, 35.8617] : [10, 10],
+          zoom: this.isChina ? 2 : 1.2
         },
         series: [
           {
-            name: '病毒预测',
-            type: 'map',
-            // 确保地图名称正确
-            map: 'world',
-            roam: true,
-            // 处理空数据情况
-            silent: false,
-            // 确保数据正确绑定
-            data: mapData.map(item => ({
-              name: this.convertCountryName(item.name),
-              value: item.value
-            })),
-            // 显示地图标签
+            type: 'scatter',
+            coordinateSystem: 'geo',
+            data: scatterData,
+            // 根据地图类型调整散点大小
+            symbolSize: val => {
+              const baseSize = Math.sqrt(val[2]) * 2;
+              return this.isChina ? baseSize : baseSize * 0.6; // 世界地图散点缩小为原来的 60%
+            },
             label: {
-              show: false,
+              show: false
+            },
+            itemStyle: {
+              color: (params) => {
+                // 随机选择一个病原体颜色作为散点颜色
+                const randomIndex = Math.floor(Math.random() * params.data.pathogenData.length);
+                return params.data.pathogenData[randomIndex].color;
+              }
+            },
+            emphasis: {
+              itemStyle: {
+                borderWidth: 2,
+                borderColor: '#fff'
+              }
             }
           }
         ]
       });
-    },
-    // 转换国家名称为 ECharts 世界地图支持的名称
-    convertCountryName(name) {
-      const nameMap = {
-        'China': 'China',
-        'United States': 'United States of America',
-        'Brazil': 'Brazil',
-        'India': 'India'
-      };
-      return nameMap[name] || name;
+
+      // 添加地图点击事件监听
+      chart.on('click', params => {
+        if (params.componentType === 'geo') {
+          if (params.name === 'China' && !this.isChina) {
+            this.isChina = true;
+            this.mapData = this.chinaData;
+            this.renderVirusMap();
+          } else if (params.name!== 'China' && this.isChina) {
+            this.isChina = false;
+            this.mapData = this.worldData;
+            this.renderVirusMap();
+          }
+        }
+      });
     },
     renderPhyloTree() {
       const strainColors = {
@@ -266,8 +364,18 @@ export default {
           }
         ]
       });
+    },
+    async initData() {
+      const res = await  generatesjbytfzzbqkInfo(this.form)
+      const res2 = await generatezgbytfzzbqkInfo(this.form)
+      this.worldData = res.data
+      this.chinaData = res2.data
+      this.mapData = this.isChina ? this.chinaData : this.worldData
     }
   },
+  created() {
+    this.initData(); // 初始化数据
+  },
   mounted() {
     // 延迟 500 毫秒渲染,确保容器尺寸正确
     setTimeout(() => {
@@ -279,10 +387,6 @@ export default {
 </script>
 
 <style scoped>
-.tree-map-container {
-  display: flex;
-  justify-content: space-between;
-}
 .phylo-tree, .world-map {
   background: #fff;
   padding: 16px;