浏览代码

修改样式

yangtaodemon 1 月之前
父节点
当前提交
a1968d5761
共有 1 个文件被更改,包括 217 次插入97 次删除
  1. 217 97
      src/views/User/cadmiumPrediction/FutureCadmiumPrediction.vue

+ 217 - 97
src/views/User/cadmiumPrediction/FutureCadmiumPrediction.vue

@@ -15,10 +15,10 @@
         <el-button 
           class="custom-button"
           :loading="isGenerating"
-          :disabled="!canGenerate"
+          :disabled="(!isValidYear || isGenerating)"
           @click="generateForecast"
         >
-          <el-icon class="play-icon"></el-icon>
+          <el-icon><Right /></el-icon>
           开始预测
         </el-button>
       </div>
@@ -26,31 +26,34 @@
 
     <!-- 主体内容区 -->
     <div class="content-area">
-      <!-- 预测结果展示区 -->
+      <!-- 预测结果展示区 - 垂直排列 -->
       <div class="result-display">
-        <div class="map-section">
+        <!-- 地图部分 -->
+        <div class="visualization-section">
           <h3>未来Cd浓度预测地图</h3>
           <div v-if="loadingMap" class="loading-container">
             <el-icon class="loading-icon"><Loading /></el-icon>
             <span>地图加载中...</span>
           </div>
-          <img :src=mapImageUrl class="result-img"></img>
-          <div v-if="!loadingMap && !mapImageUrl" class="no-data">
+          <img v-if="mapImageUrl" :src="mapImageUrl" class="result-img"></img>
+          <div v-else class="no-data">
             <el-icon><Picture /></el-icon>
             <p>暂无地图数据</p>
+          </div>
         </div>
-        </div>
-        <div class="histogram-section">
+
+        <!-- 直方图部分 -->
+        <div class="visualization-section">
           <h3>预测直方图</h3>
           <div v-if="loadingHistogram" class="loading-container">
             <el-icon class="loading-icon"><Loading /></el-icon>
             <span>直方图加载中...</span>
           </div>
-          <img :src=histogramImageUrl class="result-img"></img>
-          <div v-if="!loadingHistogram && !histogramImageUrl" class="no-data">
-            <el-icon><Picture /></el-icon>
+          <img v-if="histogramImageUrl" :src="histogramImageUrl" class="result-img"></img>
+          <div v-else class="no-data">
+            <el-icon><Histogram /></el-icon>
             <p>暂无直方图数据</p>
-        </div>
+          </div>
         </div>
       </div>
     </div>
@@ -58,14 +61,17 @@
 </template>
 
 <script>
-import { SaveAs } from 'file-saver';
 import { api8000 } from '@/utils/request';
-import { 
-  Loading, Picture, Histogram
-} from '@element-plus/icons-vue';
+import { Loading, Picture, Histogram, Right } from '@element-plus/icons-vue';
 
 export default {
   name: 'FutureCdPrediction',
+  components: {
+    Loading,
+    Picture,
+    Histogram,
+    Right
+  },
   props: {
     countyName: {
       type: String,
@@ -84,7 +90,7 @@ export default {
       histogramBlob: null,
       mapImageUrl: '',
       histogramImageUrl: ''
-    }
+    };
   },
   computed: {
     isValidYear() {
@@ -96,169 +102,283 @@ export default {
   },
   watch: {
     forecastYear(newVal) {
-      newVal = parseInt(newVal)
-      if (isNaN(newVal)) this.forecastYear = null
-      if (newVal < 1) this.forecastYear = 1
-      if (newVal > 100) this.forecastYear = 100
+      newVal = parseInt(newVal);
+      if (isNaN(newVal)) this.forecastYear = null;
+      if (newVal < 1) this.forecastYear = 1;
+      if (newVal > 100) this.forecastYear = 100;
     }
   },
   methods: {
     // 预测控制逻辑
-   async generateForecast() {
-    if (this.isGenerating) return
-    
-    this.isGenerating = true
-    this.mapBlob = null
-    this.histogramBlob = null
-    this.mapImageUrl = ''
-    this.histogramImageUrl = ''
-
-    try {
-      // 1. 构建正确URL(使用GET方法+查询参数)
-      let url = `/api/cd-flux/predict-future-cd?years=${encodeURIComponent(this.forecastYear)}`
-      if (this.countyName) url += `&area=${encodeURIComponent(this.countyName)}`
-      if (this.generateVisualization) url += '&generate_visualization=true'
-
-      // 2. 发送GET请求(移除FormData)
-      const predictResponse = await api8000.get(url, { responseType: 'json' })
-
-      if (!predictResponse.data.success) {
-        throw new Error(predictResponse.data.message)
+    async generateForecast() {
+      if (this.isGenerating) return;
+
+      this.isGenerating = true;
+      this.mapBlob = null;
+      this.histogramBlob = null;
+      this.mapImageUrl = '';
+      this.histogramImageUrl = '';
+
+      try {
+        // 1. 尝试加载现有预测数据
+        await this.checkExistingData();
+
+        // 2. 如果存在历史数据,直接显示
+        this.$message.success(`成功加载${this.forecastYear}年预测数据`);
+        this.isGenerating = false;
+        return;
+      } catch (loadError) {
+        console.log('未找到历史数据,开始生成预测...', loadError);
       }
 
-      // 3. 加载结果(保持原有逻辑)
+      try {
+        // 3. 生成新预测
+        await this.generateNewPrediction();
+        
+        // 4. 加载新生成的数据
+        await Promise.all([
+          this.loadVisualization('map', this.forecastYear),
+          this.loadVisualization('histogram', this.forecastYear)
+        ]);
+
+        this.$message.success(`预测生成成功(年份:${this.forecastYear})`);
+      } catch (generateError) {
+        this.$message.error(`预测失败:${generateError.message}`);
+      } finally {
+        this.isGenerating = false;
+      }
+    },
+
+    // 检查历史数据是否存在
+    async checkExistingData() {
       await Promise.all([
         this.loadVisualization('map', this.forecastYear),
         this.loadVisualization('histogram', this.forecastYear)
-      ])
+      ]);
+    },
 
-      this.$message.success(`预测生成成功(年份:${this.forecastYear})`)
-    } catch (error) {
-      this.$message.error(`预测失败:${error.message}`)
-    } finally {
-      this.isGenerating = false
-    }
-   },
-    
-    // 结果加载逻辑
+    // 生成新预测
+    async generateNewPrediction() {
+      let url = `/api/cd-flux/predict-future-cd?years=${encodeURIComponent(
+        this.forecastYear
+      )}`;
+      if (this.countyName) {
+        url += `&area=${encodeURIComponent(this.countyName)}`;
+      }
+      if (this.generateVisualization) {
+        url += '&generate_visualization=true';
+      }
+
+      const response = await api8000.get(url, { responseType: 'json' });
+      if (!response.data.success) {
+        throw new Error(response.data.message || '生成预测失败');
+      }
+    },
+
+    // 加载可视化数据
     async loadVisualization(type, year) {
       try {
         const response = await api8000.get(
           `/api/cd-flux/predict-future-cd/${type}/${year}`,
           { responseType: 'blob' }
-        )
+        );
 
         if (type === 'map') {
-          this.mapBlob = response.data
-          this.mapImageUrl = URL.createObjectURL(this.mapBlob)
+          this.mapBlob = response.data;
+          this.mapImageUrl = URL.createObjectURL(this.mapBlob);
         } else {
-          this.histogramBlob = response.data
-          this.histogramImageUrl = URL.createObjectURL(this.histogramBlob)
+          this.histogramBlob = response.data;
+          this.histogramImageUrl = URL.createObjectURL(this.histogramBlob);
         }
       } catch (error) {
-        this.$message.error(`加载${type}失败:${error.message}`)
+        // 显式抛出错误供外部捕获
+        throw new Error(`加载${type}失败: ${error.message}`);
       }
     },
-    
-    // 结果导出逻辑
-    exportAllResults() {
-      if (this.mapBlob) SaveAs(this.mapBlob, `future_cd_map_${this.forecastYear}.jpg`)
-      if (this.histogramBlob) SaveAs(this.histogramBlob, `future_cd_histogram_${this.forecastYear}.jpg`)
-    }
   }
-}
+};
 </script>
 
-
 <style scoped>
+/* 全局颜色变量 */
+:root {
+  --primary-color: #47C3B9;
+  --secondary-color: #36A897;
+  --text-color: #333;
+  --bg-color: #F5F7FA;
+  --border-color: #E0E0E0;
+  --loading-color: #47C3B9;
+  --error-color: #FF4D4F;
+}
+
+/* 容器样式 */
 .container {
   max-width: 1400px;
   margin: 20px auto;
   padding: 0 20px;
+  background: var(--bg-color);
+  border-radius: 8px;
+  box-shadow: 0 2px 12px rgba(0,0,0,0.1);
+  overflow: hidden;
 }
 
-.toolbar {
+/* 顶部工具栏 */
+.forecast-controls {
   display: flex;
   justify-content: space-between;
   align-items: center;
-  padding: 15px;
-  background-color: #f5f7fa;
+  padding: 15px 20px;
+  background-color: rgba(230, 247, 255, 0.7);
   border-radius: 8px;
   margin-bottom: 20px;
 }
 
-.upload-section {
-  display: flex;
-  align-items: center;
-  gap: 15px;
-}
-
-.forecast-controls {
-  display: flex;
-  gap: 15px;
-  align-items: center;
-}
-
+/* 预测控制区 */
 .year-selector {
   display: flex;
   gap: 10px;
   align-items: center;
 }
 
-.file-name {
-  font-size: 14px;
-  color: #666;
-}
-
+/* 自定义按钮 */
 .custom-button {
   padding: 8px 16px;
   border-radius: 4px;
   font-size: 14px;
+  background-color: var(--primary-color) !important;
+  color: white !important;
+  border: none;
+  cursor: pointer;
+  transition: all 0.3s;
+  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+  display: flex;
+  align-items: center;
+  gap: 5px;
 }
 
-.action-buttons {
-  display: flex;
-  gap: 10px;
+.custom-button:hover {
+  background-color: #36a897;
 }
 
+.custom-button:active {
+  transform: translateY(0);
+}
+
+.custom-button:disabled {
+  background-color: #cccccc !important;
+  color: rgba(255,255,255,0.5) !important;
+  cursor: not-allowed;
+}
+
+/* 结果展示区 - 垂直布局 */
 .result-display {
-  display: grid;
-  grid-template-columns: 1fr 1fr;
+  display: flex;
+  flex-direction: column;
   gap: 20px;
+  margin-top: 10px;
 }
 
-.map-section, .histogram-section {
-  background-color: #fff;
+/* 可视化部分通用样式 */
+.visualization-section {
+  background-color: white;
   padding: 20px;
   border-radius: 8px;
   box-shadow: 0 2px 4px rgba(0,0,0,0.05);
+  min-height: 400px;
+  display: flex;
+  flex-direction: column;
 }
 
-h3 {
+.visualization-section h3 {
   margin-top: 0;
   margin-bottom: 15px;
   font-size: 16px;
-  color: #333;
+  color: var(--text-color);
+  border-bottom: 2px solid var(--secondary-color);
+  padding-bottom: 5px;
+  flex-shrink: 0;
 }
 
+/* 加载状态 */
 .loading-container {
   display: flex;
   align-items: center;
   gap: 10px;
-  color: #666;
+  color: var(--loading-color);
+  min-height: 300px;
+  justify-content: center;
+  flex-grow: 1;
 }
 
+/* 无数据提示 */
 .no-data {
   display: flex;
   flex-direction: column;
   align-items: center;
   gap: 10px;
   color: #999;
+  min-height: 300px;
+  justify-content: center;
+  flex-grow: 1;
 }
 
+/* 结果图片 */
 .result-img {
   width: 100%;
-  height: auto;
+  height: 350px;
   object-fit: contain;
+  border-radius: 4px;
+  margin-top: 10px;
+  flex-grow: 1;
+  background-color: #f9f9f9;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.result-img:empty {
+  display: none;
+}
+
+/* 响应式设计 */
+@media (max-width: 992px) {
+  .forecast-controls {
+    flex-direction: column;
+    gap: 15px;
+    align-items: stretch;
+  }
+  
+  .year-selector {
+    flex-direction: column;
+    align-items: stretch;
+  }
+  
+  .custom-button {
+    width: 100%;
+  }
+}
+
+/* 图标样式 */
+.el-icon {
+  color: var(--primary-color);
+  font-size: 18px;
+}
+
+/* 输入框样式 */
+.el-input {
+  border: 1px solid var(--border-color);
+  border-radius: 4px;
+  transition: border-color 0.3s;
+}
+
+.el-input:focus {
+  border-color: var(--primary-color);
+  box-shadow: 0 0 5px var(--primary-color);
+}
+
+.el-input__inner {
+  padding: 8px 12px;
+  font-size: 14px;
+  color: var(--text-color);
 }
 </style>