refluxcedataStatictics.vue 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. <template>
  2. <div class="chart-container">
  3. <h3 class="title">{{ $t('AcidificationDataStatistics.refluxTitle') }}</h3>
  4. <div class="echarts-box" ref="chartRef"></div>
  5. <!-- 加载状态提示 -->
  6. <div v-if="isLoading" class="loading-tip">
  7. {{ $t('DetectionStatistics.dataLoading') }}
  8. </div>
  9. <!-- 错误提示 -->
  10. <div v-if="errorMessage && !isLoading" class="error-tip">
  11. {{ errorMessage }}
  12. <el-button type="primary" size="small" @click="fetchData">
  13. {{ $t('SoilCdStatistics.retry') }}
  14. </el-button>
  15. </div>
  16. </div>
  17. </template>
  18. <script setup>
  19. import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue';
  20. import * as echarts from 'echarts';
  21. import { api5000 } from '@/utils/request';
  22. import { useI18n } from 'vue-i18n';
  23. const { t } = useI18n();
  24. // 图表相关
  25. const chartRef = ref(null);
  26. let myChart = null;
  27. // 数据状态
  28. const chartData = ref([]);
  29. const isLoading = ref(false);
  30. const errorMessage = ref('');
  31. // 接口地址
  32. const API_URL = '/api/table-data?table_name=dataset_81';
  33. // 获取数据函数
  34. const fetchData = async () => {
  35. errorMessage.value = '';
  36. isLoading.value = true;
  37. try {
  38. const response = await api5000.get(API_URL);
  39. if (response.data && response.data.success) {
  40. if (Array.isArray(response.data.data) && response.data.data.length > 0) {
  41. // 检查数据结构,确保有 id 和 Delta_pH_105day 字段
  42. const validData = response.data.data.filter(item =>
  43. item.id !== undefined && item.Delta_pH_105day !== undefined
  44. );
  45. if (validData.length > 0) {
  46. chartData.value = validData;
  47. } else {
  48. errorMessage.value = t('DetectionStatistics.noValidData');
  49. }
  50. } else {
  51. errorMessage.value = t('DetectionStatistics.noValidData');
  52. }
  53. } else {
  54. errorMessage.value = t('AcidificationDataStatistics.apiError');
  55. }
  56. } catch (error) {
  57. console.error('数据获取失败:', error);
  58. if (error.response) {
  59. errorMessage.value = `${t('AcidificationDataStatistics.requestError')} (${error.response.status})`;
  60. } else if (error.request) {
  61. errorMessage.value = t('AcidificationDataStatistics.networkError');
  62. } else {
  63. errorMessage.value = t('AcidificationDataStatistics.requestError');
  64. }
  65. } finally {
  66. isLoading.value = false;
  67. }
  68. };
  69. // 初始化图表
  70. const initChart = () => {
  71. if (!chartRef.value) {
  72. console.error('图表容器未找到');
  73. return;
  74. }
  75. if (chartData.value.length === 0) {
  76. console.error('没有数据可用于渲染图表');
  77. return;
  78. }
  79. if (myChart) {
  80. myChart.dispose();
  81. }
  82. myChart = echarts.init(chartRef.value);
  83. const xAxisData = chartData.value.map(item => item.id);
  84. const seriesData = chartData.value.map(item => ({
  85. value: item.Delta_pH_105day,
  86. itemStyle: {
  87. color: item.Delta_pH_105day >= 0 ? '#0F52BA' : '#F44336'
  88. }
  89. }));
  90. const option = {
  91. tooltip: {
  92. trigger: 'axis',
  93. axisPointer: {
  94. type: 'shadow'
  95. },
  96. formatter: function(params) {
  97. // 安全地获取数据,避免 undefined 错误
  98. const itemName = params[0].name;
  99. const item = chartData.value.find(d => d.id === itemName);
  100. if (item) {
  101. return `ID: ${item.id}<br/>Delta_pH_105day: ${item.Delta_pH_105day.toFixed(4)}`;
  102. } else {
  103. return `ID: ${itemName}<br/>Delta_pH_105day: ${params[0].value.toFixed(4)}`;
  104. }
  105. }
  106. },
  107. grid: {
  108. left: '5%',
  109. right: '1%',
  110. bottom: '5%',
  111. containLabel: true
  112. },
  113. xAxis: {
  114. type: 'category',
  115. data: xAxisData,
  116. name: 'ID',
  117. nameLocation: 'middle',
  118. nameGap: 30,
  119. },
  120. yAxis: {
  121. type: 'value',
  122. name: 'Delta_pH_105day',
  123. nameLocation: 'middle',
  124. nameGap: 50,
  125. axisLabel: {
  126. formatter: '{value}'
  127. }
  128. },
  129. series: [
  130. {
  131. name: 'Delta_pH_105day',
  132. type: 'bar',
  133. data: seriesData,
  134. barWidth: '60%',
  135. label: {
  136. show: false,
  137. position: 'top',
  138. formatter: function(params) {
  139. return params.value.toFixed(4);
  140. }
  141. }
  142. }
  143. ]
  144. };
  145. myChart.setOption(option);
  146. };
  147. // 监听数据变化
  148. watch(chartData, () => {
  149. nextTick(() => {
  150. if (myChart) {
  151. myChart.clear(); // 清除现有内容
  152. myChart.setOption({}); // 重置选项
  153. }
  154. initChart(); // 重新初始化
  155. })
  156. });
  157. const handleResize = () => {
  158. if (myChart) {
  159. setTimeout(() => {
  160. myChart.resize();
  161. }, 100);
  162. }
  163. };
  164. onMounted(async() => {
  165. await fetchData();
  166. initChart();
  167. window.addEventListener('resize', handleResize);
  168. });
  169. onUnmounted(() => {
  170. if (myChart) {
  171. myChart.dispose();
  172. }
  173. window.removeEventListener('resize', handleResize);
  174. });
  175. </script>
  176. <style scoped>
  177. .chart-container {
  178. width: 100%;
  179. height: 470px;
  180. padding: 20px;
  181. box-sizing: border-box;
  182. position: relative;
  183. }
  184. .echarts-box {
  185. width: 100%;
  186. height: 100%;
  187. border: 1px solid #e0e0e0;
  188. border-radius: 6px;
  189. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  190. }
  191. .title {
  192. text-align: center;
  193. margin-bottom: 15px;
  194. font-size: 16px;
  195. font-weight: bold;
  196. color: #333;
  197. }
  198. .loading-tip {
  199. position: absolute;
  200. top: 50%;
  201. left: 50%;
  202. transform: translate(-50%, -50%);
  203. font-size: 14px;
  204. color: #666;
  205. }
  206. .error-tip {
  207. position: absolute;
  208. top: 50%;
  209. left: 50%;
  210. transform: translate(-50%, -50%);
  211. text-align: center;
  212. color: #f44336;
  213. font-size: 14px;
  214. }
  215. .error-tip .el-button {
  216. margin-left: 10px;
  217. }
  218. </style>