crossSetionData1.vue 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <template>
  2. <!--柱状图容器-->
  3. <div class="chart-page">
  4. <div ref="chartContainer" class="chart-container"></div>
  5. </div>
  6. </template>
  7. <script setup>
  8. import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'
  9. import { wgs84togcj02 } from 'coordtransform';
  10. import * as echarts from 'echarts'
  11. // 状态管理
  12. const error = ref(null)
  13. const chartContainer=ref(null)
  14. let chart = null
  15. const state = reactive({
  16. excelData: [], // 存储解析后的断面数据
  17. riverAvgData:[],//存储按河流分组后的平均数据
  18. })
  19. // 初始化断面数据(直接嵌入你的Excel数据)
  20. const initData = () => {
  21. const rawData = [
  22. { "断面编号": 0, "所属河流": "浈江", "断面位置": "小古录", "所属区县": "始兴县", "经度": 114.208543, "纬度": 25.059851, "Cd(ug/L)": 0.11 },
  23. { "断面编号": 1, "所属河流": "浈江", "断面位置": "长坝", "所属区县": "仁化县", "经度": 113.692874, "纬度": 24.874845, "Cd(ug/L)": 1.116 },
  24. { "断面编号": 2, "所属河流": "浈江", "断面位置": "东河桥", "所属区县": "浈江区", "经度": 113.601631, "纬度": 24.80784, "Cd(ug/L)": 3.46 },
  25. { "断面编号": 3, "所属河流": "武江", "断面位置": "坪石", "所属区县": "乐昌市", "经度": 113.066281, "纬度": 25.274421, "Cd(ug/L)": 0.98 },
  26. { "断面编号": 4, "所属河流": "武江", "断面位置": "乐昌", "所属区县": "乐昌市", "经度": 113.338782, "纬度": 25.129212, "Cd(ug/L)": 0.11 },
  27. { "断面编号": 5, "所属河流": "武江", "断面位置": "武江桥", "所属区县": "乐昌市", "经度": 113.349815, "纬度": 25.120278, "Cd(ug/L)": 0.15 },
  28. { "断面编号": 6, "所属河流": "北江", "断面位置": "九公里", "所属区县": "浈江区", "经度": 113.580758, "纬度": 24.761299, "Cd(ug/L)": 7.83 },
  29. { "断面编号": 7, "所属河流": "北江", "断面位置": "白土", "所属区县": "曲江区", "经度": 113.531284, "纬度": 24.679958, "Cd(ug/L)": 5.94 },
  30. { "断面编号": 8, "所属河流": "浈江", "断面位置": "昆仑水站", "所属区县": "南雄市", "经度": 114.3629285, "纬度": 25.10053746, "Cd(ug/L)": 0.517 },
  31. { "断面编号": 9, "所属河流": "北江", "断面位置": "白沙", "所属区县": "曲江", "经度": 113.5707136, "纬度": 24.58139261, "Cd(ug/L)": 1.54 },
  32. { "断面编号": 10, "所属河流": "浈江", "断面位置": "周田水站", "所属区县": "仁化县", "经度": 113.8293461, "纬度": 24.97851516, "Cd(ug/L)": 0.182 },
  33. { "断面编号": 11, "所属河流": "武江", "断面位置": "坪石水站", "所属区县": "乐昌市", "经度": 113.0467854, "纬度": 25.28883459, "Cd(ug/L)": 1.071 }
  34. ];
  35. // 处理坐标(WGS84转GCJ02,腾讯地图用GCJ02)
  36. state.excelData = rawData.map(item => {
  37. const lng = Number(item.经度);
  38. const lat = Number(item.纬度);
  39. if (isNaN(lat) || isNaN(lng)) {
  40. console.error('无效经纬度:', item);
  41. return null;
  42. }
  43. const [gcjLng, gcjLat] = wgs84togcj02(lng, lat); // 坐标转换
  44. return {
  45. id: item.断面编号,
  46. river: item.所属河流,
  47. location: item.断面位置,
  48. district: item.所属区县,
  49. cdValue: item["Cd(ug/L)"],
  50. latitude: gcjLat,
  51. longitude: gcjLng,
  52. };
  53. }).filter(item => item !== null);
  54. calculateRiverAvg();
  55. }
  56. const calculateRiverAvg = () => {
  57. // 按河流分组
  58. const riverGroups = {};
  59. // 分组并累加浓度值
  60. state.excelData.forEach(item => {
  61. if (!riverGroups[item.river]) {
  62. riverGroups[item.river] = {
  63. total: 0,
  64. count: 0,
  65. avg: 0
  66. };
  67. }
  68. riverGroups[item.river].total += item.cdValue;
  69. riverGroups[item.river].count += 1;
  70. });
  71. // 计算每组平均值
  72. const riverAvg = [];
  73. let totalAll = 0;
  74. let countAll = 0;
  75. for (const river in riverGroups) {
  76. const avg = riverGroups[river].total / riverGroups[river].count;
  77. riverAvg.push({
  78. river,
  79. avg: parseFloat(avg.toFixed(3)) // 保留3位小数
  80. });
  81. totalAll += riverGroups[river].total;
  82. countAll += riverGroups[river].count;
  83. }
  84. // 计算总平均值并添加到数组
  85. const totalAvg = {
  86. river: '总河流平均',
  87. avg: parseFloat((totalAll / countAll).toFixed(3))
  88. };
  89. riverAvg.push(totalAvg);
  90. state.riverAvgData = riverAvg;
  91. // 更新图表
  92. updateChart();
  93. }
  94. // 新增:初始化图表
  95. const initChart = () => {
  96. if (chartContainer.value) {
  97. chart = echarts.init(chartContainer.value);
  98. updateChart();
  99. }
  100. }
  101. // 新增:更新图表数据
  102. const updateChart = () => {
  103. if (!chart || state.riverAvgData.length === 0) return;
  104. // 准备图表数据
  105. const rivers = state.riverAvgData.map(item => item.river);
  106. const avgs = state.riverAvgData.map(item => item.avg);
  107. // 配置图表选项
  108. const option = {
  109. tooltip: {
  110. trigger: 'axis',
  111. axisPointer: {
  112. type: 'shadow'
  113. },
  114. formatter: '{a} <br/>{b}: {c} ug/L'
  115. },
  116. grid: {
  117. //left: '3%',
  118. right: '4%',
  119. bottom: '3%',
  120. containLabel: true
  121. },
  122. xAxis: {
  123. type: 'category',
  124. data: rivers,
  125. axisLabel: {
  126. interval: 0,
  127. fontSize:18
  128. }
  129. },
  130. yAxis: {
  131. type: 'value',
  132. name: 'Cd浓度 (ug/L)',
  133. min: 0,
  134. nameTextStyle:{
  135. fontSize:18,
  136. },
  137. axisLabel: {
  138. formatter: '{value}',
  139. fontSize:18
  140. }
  141. },
  142. series: [
  143. {
  144. name: '平均镉浓度',
  145. type: 'bar',
  146. data: avgs,
  147. itemStyle: {
  148. // 为总平均值设置不同颜色
  149. color: function(params) {
  150. return params.dataIndex === rivers.length - 1 ? '#FF4500' : '#1E88E5';
  151. }
  152. },
  153. label: {
  154. show: true,
  155. position: 'top',
  156. formatter: '{c}',
  157. fontSize:18
  158. },
  159. emphasis: {
  160. focus: 'series'
  161. }
  162. }
  163. ]
  164. };
  165. // 设置图表选项
  166. chart.setOption(option);
  167. }
  168. // 生命周期
  169. onMounted(async () => {
  170. try {
  171. initData();
  172. initChart();
  173. //监听窗口大小变化,调整图表
  174. window.addEventListener('resize',()=>{
  175. if(chart){
  176. chart.resize();
  177. }
  178. })
  179. } catch (err) {
  180. error.value = err.message;
  181. }
  182. })
  183. onBeforeUnmount(() => {
  184. if(chart){
  185. chart.dispose();
  186. }
  187. })
  188. </script>
  189. <style>
  190. /* 图表容器样式 */
  191. .chart-container {
  192. width: 100%; /* 占满图表容器宽度 */
  193. height: 400px;
  194. margin: 0 auto;
  195. border-radius: 12px;
  196. }
  197. </style>