| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- <template>
- <div class="container">
- <!-- 顶部操作栏 -->
- <div class="forecast-controls">
- <div class="year-selector">
- <el-input
- v-model.number="forecastYear"
- type="number"
- placeholder="输入预测年份(1-100)"
- :min="1"
- :max="100"
- @keypress.enter="generateForecast"
- :class="{ 'is-invalid': !isValidYear }"
- ></el-input>
- <el-button
- class="custom-button"
- :loading="isGenerating"
- :disabled="!canGenerate"
- @click="generateForecast"
- >
- <el-icon class="play-icon"></el-icon>
- 开始预测
- </el-button>
- </div>
- </div>
- <!-- 主体内容区 -->
- <div class="content-area">
- <!-- 预测结果展示区 -->
- <div class="result-display">
- <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 :src=mapImageUrl class="result-img"></img>
- <div v-if="!loadingMap && !mapImageUrl" class="no-data">
- <el-icon><Picture /></el-icon>
- <p>暂无地图数据</p>
- </div>
- </div>
- <div class="histogram-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>
- <p>暂无直方图数据</p>
- </div>
- </div>
- </div>
- </div>
- </div>
- </template>
- <script>
- import { SaveAs } from 'file-saver';
- import { api8000 } from '@/utils/request';
- import {
- Loading, Picture, Histogram
- } from '@element-plus/icons-vue';
- export default {
- name: 'FutureCdPrediction',
- props: {
- countyName: {
- type: String,
- required: true,
- default: '乐昌市'
- }
- },
- data() {
- return {
- forecastYear: null,
- isGenerating: false,
- generateVisualization: true,
- loadingMap: false,
- loadingHistogram: false,
- mapBlob: null,
- histogramBlob: null,
- mapImageUrl: '',
- histogramImageUrl: ''
- }
- },
- computed: {
- isValidYear() {
- return this.forecastYear >= 1 && this.forecastYear <= 100;
- },
- canGenerate() {
- return this.isValidYear && !this.isGenerating;
- }
- },
- watch: {
- forecastYear(newVal) {
- 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)
- }
- // 3. 加载结果(保持原有逻辑)
- 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 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)
- } else {
- this.histogramBlob = response.data
- this.histogramImageUrl = URL.createObjectURL(this.histogramBlob)
- }
- } catch (error) {
- this.$message.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>
- .container {
- max-width: 1400px;
- margin: 20px auto;
- padding: 0 20px;
- }
- .toolbar {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 15px;
- background-color: #f5f7fa;
- 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;
- }
- .action-buttons {
- display: flex;
- gap: 10px;
- }
- .result-display {
- display: grid;
- grid-template-columns: 1fr 1fr;
- gap: 20px;
- }
- .map-section, .histogram-section {
- background-color: #fff;
- padding: 20px;
- border-radius: 8px;
- box-shadow: 0 2px 4px rgba(0,0,0,0.05);
- }
- h3 {
- margin-top: 0;
- margin-bottom: 15px;
- font-size: 16px;
- color: #333;
- }
- .loading-container {
- display: flex;
- align-items: center;
- gap: 10px;
- color: #666;
- }
- .no-data {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 10px;
- color: #999;
- }
- .result-img {
- width: 100%;
- height: auto;
- object-fit: contain;
- }
- </style>
|