|
@@ -28,7 +28,7 @@
|
|
<el-button class="custom-button" :disabled="!histogramBlob" @click="exportHistogram">
|
|
<el-button class="custom-button" :disabled="!histogramBlob" @click="exportHistogram">
|
|
<el-icon class="upload-icon"><Download /></el-icon>
|
|
<el-icon class="upload-icon"><Download /></el-icon>
|
|
导出直方图</el-button>
|
|
导出直方图</el-button>
|
|
- <el-button class="custom-button" :disabled="!tableData.length" @click="exportData">
|
|
|
|
|
|
+ <el-button class="custom-button" :disabled="!statisticsData.length" @click="exportData">
|
|
<el-icon class="upload-icon"><Download /></el-icon>
|
|
<el-icon class="upload-icon"><Download /></el-icon>
|
|
导出数据</el-button>
|
|
导出数据</el-button>
|
|
</div>
|
|
</div>
|
|
@@ -67,15 +67,50 @@
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
- <!-- 表格区域 -->
|
|
|
|
- <div class="table-area">
|
|
|
|
- <h3>表格数据</h3>
|
|
|
|
- <el-table :data="tableData" style="width: 100%;">
|
|
|
|
- <el-table-column prop="name" label="名称" width="180" />
|
|
|
|
- <el-table-column prop="value" label="值" width="100" />
|
|
|
|
- <el-table-column prop="unit" label="单位" width="100" />
|
|
|
|
- <el-table-column prop="description" label="描述" />
|
|
|
|
- </el-table>
|
|
|
|
|
|
+ <!-- 统计图表区域 -->
|
|
|
|
+ <div class="stats-area">
|
|
|
|
+ <h3>{{countyName}} - 作物Cd预测统计信息</h3>
|
|
|
|
+ <div class="model-info">
|
|
|
|
+ <el-tag type="info">{{currentStats?.['模型类型'] || '作物Cd模型'}}</el-tag>
|
|
|
|
+ <span class="update-time">
|
|
|
|
+ 最后更新: {{currentStats?.['数据更新时间'] ? new Date(currentStats['数据更新时间']).toLocaleString() : '未知'}}
|
|
|
|
+ </span>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div v-if="loadingStats" class="loading-container">
|
|
|
|
+ <el-icon class="loading-icon"><Loading /></el-icon>
|
|
|
|
+ <span>统计数据加载中...</span>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div v-if="!loadingStats && statisticsData.length" class="stats-container">
|
|
|
|
+ <!-- 统计表格 -->
|
|
|
|
+ <el-table
|
|
|
|
+ :data="statisticsData"
|
|
|
|
+ style="width: 100%; margin-bottom: 20px;"
|
|
|
|
+ border
|
|
|
|
+ stripe
|
|
|
|
+ >
|
|
|
|
+ <el-table-column prop="name" label="统计项" min-width="180" />
|
|
|
|
+ <el-table-column prop="value" label="值" min-width="150" />
|
|
|
|
+ <el-table-column prop="unit" label="单位" min-width="100" />
|
|
|
|
+ <el-table-column prop="description" label="描述" min-width="200" />
|
|
|
|
+ </el-table>
|
|
|
|
+
|
|
|
|
+ <!-- 统计图表 -->
|
|
|
|
+ <div class="charts-container">
|
|
|
|
+ <div class="chart-item">
|
|
|
|
+ <div ref="distributionChart" style="width: 100%; height: 400px;"></div>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="chart-item">
|
|
|
|
+ <div ref="exceedanceChart" style="width: 100%; height: 400px;"></div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div v-if="!loadingStats && !statisticsData.length" class="no-data">
|
|
|
|
+ <el-icon><DataAnalysis /></el-icon>
|
|
|
|
+ <p>暂无统计数据</p>
|
|
|
|
+ </div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@@ -85,28 +120,45 @@
|
|
import * as XLSX from 'xlsx';
|
|
import * as XLSX from 'xlsx';
|
|
import { saveAs } from 'file-saver';
|
|
import { saveAs } from 'file-saver';
|
|
import axios from 'axios';
|
|
import axios from 'axios';
|
|
-import { Loading, Upload, Picture, Histogram, Download, Document } from '@element-plus/icons-vue';
|
|
|
|
|
|
+import * as echarts from 'echarts';
|
|
|
|
+import {
|
|
|
|
+ Loading, Upload, Picture, Histogram, Download, Document, DataAnalysis
|
|
|
|
+} from '@element-plus/icons-vue';
|
|
|
|
|
|
export default {
|
|
export default {
|
|
name: 'CropCadmiumPrediction',
|
|
name: 'CropCadmiumPrediction',
|
|
- components: { Loading, Upload, Picture, Histogram, Download, Document },
|
|
|
|
|
|
+ components: {
|
|
|
|
+ Loading, Upload, Picture, Histogram, Download, Document, DataAnalysis
|
|
|
|
+ },
|
|
data() {
|
|
data() {
|
|
return {
|
|
return {
|
|
isCalculating: false,
|
|
isCalculating: false,
|
|
loadingMap: false,
|
|
loadingMap: false,
|
|
loadingHistogram: false,
|
|
loadingHistogram: false,
|
|
- tableData: [],
|
|
|
|
|
|
+ loadingStats: false,
|
|
|
|
+ statisticsData: [],
|
|
mapImageUrl: null,
|
|
mapImageUrl: null,
|
|
histogramImageUrl: null,
|
|
histogramImageUrl: null,
|
|
mapBlob: null,
|
|
mapBlob: null,
|
|
histogramBlob: null,
|
|
histogramBlob: null,
|
|
selectedFile: null,
|
|
selectedFile: null,
|
|
- countyName: '乐昌市' // 默认县市名称
|
|
|
|
|
|
+ countyName: '乐昌市', // 默认县市名称
|
|
|
|
+ distributionChart: null,
|
|
|
|
+ exceedanceChart: null
|
|
};
|
|
};
|
|
},
|
|
},
|
|
|
|
+
|
|
mounted() {
|
|
mounted() {
|
|
// 组件挂载时获取最新数据
|
|
// 组件挂载时获取最新数据
|
|
this.fetchLatestResults();
|
|
this.fetchLatestResults();
|
|
|
|
+ this.fetchStatistics();
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ beforeDestroy() {
|
|
|
|
+ if (this.mapImageUrl) URL.revokeObjectURL(this.mapImageUrl);
|
|
|
|
+ if (this.histogramImageUrl) URL.revokeObjectURL(this.histogramImageUrl);
|
|
|
|
+ if (this.distributionChart) this.distributionChart.dispose();
|
|
|
|
+ if (this.exceedanceChart) this.exceedanceChart.dispose();
|
|
},
|
|
},
|
|
methods: {
|
|
methods: {
|
|
// 触发文件选择
|
|
// 触发文件选择
|
|
@@ -177,6 +229,197 @@ export default {
|
|
}
|
|
}
|
|
},
|
|
},
|
|
|
|
|
|
|
|
+ // 格式化统计数据
|
|
|
|
+ formatStatisticsData(stats) {
|
|
|
|
+ return [
|
|
|
|
+ { name: '数据点总数', value: stats['基础统计']['数据点总数'], unit: '个', description: '总样本数量' },
|
|
|
|
+ { name: '平均值', value: stats['基础统计']['均值'].toFixed(4), unit: '(mg/kg)', description: '所有样本的平均Cd含量' },
|
|
|
|
+ { name: '中位数', value: stats['基础统计']['中位数'].toFixed(4), unit: '(mg/kg)', description: '样本的中位Cd含量' },
|
|
|
|
+ { name: '标准差', value: stats['基础统计']['标准差'].toFixed(4), unit: '(mg/kg)', description: 'Cd含量的标准差' },
|
|
|
|
+ { name: '最小值', value: stats['基础统计']['最小值'].toFixed(4), unit: '(mg/kg)', description: '样本中的最小Cd含量' },
|
|
|
|
+ { name: '最大值', value: stats['基础统计']['最大值'].toFixed(4), unit: '(mg/kg)', description: '样本中的最大Cd含量' },
|
|
|
|
+ { name: '偏度', value: stats['基础统计']['偏度'].toFixed(4), unit: '', description: '数据分布偏斜程度' },
|
|
|
|
+ { name: '峰度', value: stats['基础统计']['峰度'].toFixed(4), unit: '', description: '数据分布峰态' },
|
|
|
|
+ {
|
|
|
|
+ name: '经度范围',
|
|
|
|
+ value: `${stats['空间统计']['经度范围']['最小值'].toFixed(6)} - ${stats['空间统计']['经度范围']['最大值'].toFixed(6)}`,
|
|
|
|
+ unit: '度',
|
|
|
|
+ description: `跨度: ${stats['空间统计']['经度范围']['跨度'].toFixed(6)}度`
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ name: '纬度范围',
|
|
|
|
+ value: `${stats['空间统计']['纬度范围']['最小值'].toFixed(6)} - ${stats['空间统计']['纬度范围']['最大值'].toFixed(6)}`,
|
|
|
|
+ unit: '度',
|
|
|
|
+ description: `跨度: ${stats['空间统计']['纬度范围']['跨度'].toFixed(6)}度`
|
|
|
|
+ }
|
|
|
|
+ ];
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 初始化图表 - 根据实际数据更新
|
|
|
|
+ initCharts() {
|
|
|
|
+ if (!this.statisticsData.length || !this.currentStats) return;
|
|
|
|
+
|
|
|
|
+ // 销毁旧图表
|
|
|
|
+ if (this.distributionChart) this.distributionChart.dispose();
|
|
|
|
+ if (this.exceedanceChart) this.exceedanceChart.dispose();
|
|
|
|
+
|
|
|
|
+ const histData = this.currentStats['分布直方图'];
|
|
|
|
+
|
|
|
|
+ // 1. 分布直方图
|
|
|
|
+ this.distributionChart = echarts.init(this.$refs.distributionChart);
|
|
|
|
+ this.distributionChart.setOption({
|
|
|
|
+ title: {
|
|
|
|
+ text: 'Cd含量分布直方图',
|
|
|
|
+ left: 'center'
|
|
|
|
+ },
|
|
|
|
+ tooltip: {
|
|
|
|
+ trigger: 'item',
|
|
|
|
+ formatter: params => {
|
|
|
|
+ const index = params.dataIndex;
|
|
|
|
+ const lowerBound = histData['区间边界'][index].toFixed(4);
|
|
|
|
+ const upperBound = histData['区间边界'][index + 1].toFixed(4);
|
|
|
|
+ return `区间: ${lowerBound} ~ ${upperBound}<br/>频次: ${params.value}`;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ xAxis: {
|
|
|
|
+ type: 'category',
|
|
|
|
+ data: histData['区间中心'].map(v => v.toFixed(4)),
|
|
|
|
+ name: 'Cd含量',
|
|
|
|
+ axisLabel: {
|
|
|
|
+ rotate: 45
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ yAxis: {
|
|
|
|
+ type: 'value',
|
|
|
|
+ name: '频次'
|
|
|
|
+ },
|
|
|
|
+ series: [{
|
|
|
|
+ name: '样本分布',
|
|
|
|
+ type: 'bar',
|
|
|
|
+ data: histData['频次'],
|
|
|
|
+ itemStyle: {
|
|
|
|
+ color: '#47C3B9'
|
|
|
|
+ },
|
|
|
|
+ barWidth: '80%'
|
|
|
|
+ }],
|
|
|
|
+ grid: {
|
|
|
|
+ bottom: '20%'
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 2. 箱线图/统计图表
|
|
|
|
+ this.exceedanceChart = echarts.init(this.$refs.exceedanceChart);
|
|
|
|
+
|
|
|
|
+ // 准备箱线图数据
|
|
|
|
+ const boxData = [
|
|
|
|
+ [
|
|
|
|
+ this.currentStats['基础统计']['最小值'],
|
|
|
|
+ this.currentStats['基础统计']['25%分位数'],
|
|
|
|
+ this.currentStats['基础统计']['中位数'],
|
|
|
|
+ this.currentStats['基础统计']['75%分位数'],
|
|
|
|
+ this.currentStats['基础统计']['最大值'],
|
|
|
|
+ // 还可以添加离群点数据(如果有)
|
|
|
|
+ ]
|
|
|
|
+ ];
|
|
|
|
+
|
|
|
|
+ this.exceedanceChart.setOption({
|
|
|
|
+ title: {
|
|
|
|
+ text: 'Cd含量统计指标',
|
|
|
|
+ left: 'center'
|
|
|
|
+ },
|
|
|
|
+ tooltip: {
|
|
|
|
+ trigger: 'item',
|
|
|
|
+ axisPointer: {
|
|
|
|
+ type: 'shadow'
|
|
|
|
+ },
|
|
|
|
+ formatter: params => {
|
|
|
|
+ const data = boxData[0];
|
|
|
|
+ return [
|
|
|
|
+ '最大值: ' + data[4].toFixed(4),
|
|
|
|
+ '75%分位数: ' + data[3].toFixed(4),
|
|
|
|
+ '中位数: ' + data[2].toFixed(4),
|
|
|
|
+ '25%分位数: ' + data[1].toFixed(4),
|
|
|
|
+ '最小值: ' + data[0].toFixed(4)
|
|
|
|
+ ].join('<br/>');
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ xAxis: {
|
|
|
|
+ type: 'category',
|
|
|
|
+ data: ['Cd含量统计'],
|
|
|
|
+ axisLabel: {
|
|
|
|
+ rotate: 45
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ yAxis: {
|
|
|
|
+ type: 'value',
|
|
|
|
+ name: '(Cd含量)'
|
|
|
|
+ },
|
|
|
|
+ series: [{
|
|
|
|
+ name: '统计值',
|
|
|
|
+ type: 'boxplot',
|
|
|
|
+ data: boxData,
|
|
|
|
+ itemStyle: {
|
|
|
|
+ color: '#47C3B9',
|
|
|
|
+ borderColor: '#2F4554'
|
|
|
|
+ },
|
|
|
|
+ emphasis: {
|
|
|
|
+ itemStyle: {
|
|
|
|
+ color: '#FF6B6B',
|
|
|
|
+ borderColor: '#C23531'
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ tooltip: {
|
|
|
|
+ formatter: param => {
|
|
|
|
+ const data = boxData[0];
|
|
|
|
+ return [
|
|
|
|
+ '最大值: ' + data[4].toFixed(4),
|
|
|
|
+ '75%分位数: ' + data[3].toFixed(4),
|
|
|
|
+ '中位数: ' + data[2].toFixed(4),
|
|
|
|
+ '25%分位数: ' + data[1].toFixed(4),
|
|
|
|
+ '最小值: ' + data[0].toFixed(4)
|
|
|
|
+ ].join('<br/>');
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }],
|
|
|
|
+ grid: {
|
|
|
|
+ bottom: '15%'
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 响应式调整
|
|
|
|
+ window.addEventListener('resize', this.handleResize);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 修改fetchStatistics方法
|
|
|
|
+ async fetchStatistics() {
|
|
|
|
+ try {
|
|
|
|
+ this.loadingStats = true;
|
|
|
|
+
|
|
|
|
+ const response = await axios.get(
|
|
|
|
+ `https://soilgd.com:8000/api/cd-prediction/crop-cd/statistics/${this.countyName}`
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ if (response.data.success && response.data.data) {
|
|
|
|
+ this.currentStats = response.data.data; // 保存原始统计数据
|
|
|
|
+ this.statisticsData = this.formatStatisticsData(response.data.data);
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
+ this.initCharts();
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('获取统计信息失败:', error);
|
|
|
|
+ this.$message.warning('获取统计信息失败');
|
|
|
|
+ } finally {
|
|
|
|
+ this.loadingStats = false;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 处理窗口大小变化
|
|
|
|
+ handleResize() {
|
|
|
|
+ if (this.distributionChart) this.distributionChart.resize();
|
|
|
|
+ if (this.exceedanceChart) this.exceedanceChart.resize();
|
|
|
|
+ },
|
|
|
|
+
|
|
// 上传并计算
|
|
// 上传并计算
|
|
async calculate() {
|
|
async calculate() {
|
|
if (!this.selectedFile) {
|
|
if (!this.selectedFile) {
|
|
@@ -188,13 +431,14 @@ export default {
|
|
this.isCalculating = true;
|
|
this.isCalculating = true;
|
|
this.loadingMap = true;
|
|
this.loadingMap = true;
|
|
this.loadingHistogram = true;
|
|
this.loadingHistogram = true;
|
|
|
|
+ this.loadingStats = true;
|
|
|
|
|
|
// 创建FormData
|
|
// 创建FormData
|
|
const formData = new FormData();
|
|
const formData = new FormData();
|
|
formData.append('county_name', this.countyName);
|
|
formData.append('county_name', this.countyName);
|
|
formData.append('data_file', this.selectedFile);
|
|
formData.append('data_file', this.selectedFile);
|
|
|
|
|
|
- // 调用有效态Cd地图接口
|
|
|
|
|
|
+ // 调用作物Cd地图接口
|
|
const mapResponse = await axios.post(
|
|
const mapResponse = await axios.post(
|
|
'https://soilgd.com:8000/api/cd-prediction/crop-cd/generate-and-get-map',
|
|
'https://soilgd.com:8000/api/cd-prediction/crop-cd/generate-and-get-map',
|
|
formData,
|
|
formData,
|
|
@@ -210,14 +454,9 @@ export default {
|
|
this.mapBlob = mapResponse.data;
|
|
this.mapBlob = mapResponse.data;
|
|
this.mapImageUrl = URL.createObjectURL(this.mapBlob);
|
|
this.mapImageUrl = URL.createObjectURL(this.mapBlob);
|
|
|
|
|
|
- // 更新后重新获取直方图(因为生成新数据后直方图也会更新)
|
|
|
|
|
|
+ // 更新后重新获取直方图和统计数据
|
|
await this.fetchLatestHistogram();
|
|
await this.fetchLatestHistogram();
|
|
-
|
|
|
|
- // 更新表格数据(示例)
|
|
|
|
- this.tableData = [
|
|
|
|
- { name: '样本1', value: 10, unit: 'mg/L', description: '描述1' },
|
|
|
|
- { name: '样本2', value: 20, unit: 'mg/L', description: '描述2' }
|
|
|
|
- ];
|
|
|
|
|
|
+ await this.fetchStatistics();
|
|
|
|
|
|
this.$message.success('计算完成!');
|
|
this.$message.success('计算完成!');
|
|
|
|
|
|
@@ -226,7 +465,6 @@ export default {
|
|
let errorMessage = '计算失败,请重试';
|
|
let errorMessage = '计算失败,请重试';
|
|
|
|
|
|
if (error.response) {
|
|
if (error.response) {
|
|
- // 处理不同错误状态码
|
|
|
|
if (error.response.status === 400) {
|
|
if (error.response.status === 400) {
|
|
errorMessage = '文件格式错误:' + (error.response.data.detail || '请上传正确的CSV文件');
|
|
errorMessage = '文件格式错误:' + (error.response.data.detail || '请上传正确的CSV文件');
|
|
} else if (error.response.status === 404) {
|
|
} else if (error.response.status === 404) {
|
|
@@ -241,6 +479,7 @@ export default {
|
|
this.isCalculating = false;
|
|
this.isCalculating = false;
|
|
this.loadingMap = false;
|
|
this.loadingMap = false;
|
|
this.loadingHistogram = false;
|
|
this.loadingHistogram = false;
|
|
|
|
+ this.loadingStats = false;
|
|
}
|
|
}
|
|
},
|
|
},
|
|
|
|
|
|
@@ -272,29 +511,41 @@ export default {
|
|
URL.revokeObjectURL(link.href);
|
|
URL.revokeObjectURL(link.href);
|
|
},
|
|
},
|
|
|
|
|
|
- // 导出数据
|
|
|
|
- exportData() {
|
|
|
|
- if (!this.tableData.length) {
|
|
|
|
- this.$message.warning('暂无数据可导出');
|
|
|
|
- return;
|
|
|
|
|
|
+ // 导出数据 - 修改为获取作物Cd的CSV文件
|
|
|
|
+ async exportData() {
|
|
|
|
+ try {
|
|
|
|
+ this.$message.info('正在获取作物Cd预测数据...');
|
|
|
|
+
|
|
|
|
+ const response = await axios.get(
|
|
|
|
+ `https://soilgd.com:8000/api/cd-prediction/download-final-crop-cd-csv`,
|
|
|
|
+ { responseType: 'blob' }
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ const blob = new Blob([response.data], { type: 'text/csv' });
|
|
|
|
+ const link = document.createElement('a');
|
|
|
|
+ link.href = URL.createObjectURL(blob);
|
|
|
|
+ link.download = `${this.countyName}_作物Cd预测数据.csv`;
|
|
|
|
+ link.click();
|
|
|
|
+ URL.revokeObjectURL(link.href);
|
|
|
|
+
|
|
|
|
+ this.$message.success('数据导出成功');
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('导出数据失败:', error);
|
|
|
|
+ this.$message.error('导出数据失败: ' + (error.response?.data?.detail || '请稍后重试'));
|
|
}
|
|
}
|
|
-
|
|
|
|
- const workbook = XLSX.utils.book_new();
|
|
|
|
- const worksheet = XLSX.utils.json_to_sheet(this.tableData);
|
|
|
|
- XLSX.utils.book_append_sheet(workbook, worksheet, '作物态Cd数据');
|
|
|
|
- const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
|
|
|
|
- const excelData = new Blob([excelBuffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
|
|
|
|
- saveAs(excelData, `${this.countyName}_作物态Cd数据.xlsx`);
|
|
|
|
}
|
|
}
|
|
- },
|
|
|
|
- beforeDestroy() {
|
|
|
|
- if (this.mapImageUrl) URL.revokeObjectURL(this.mapImageUrl);
|
|
|
|
- if (this.histogramImageUrl) URL.revokeObjectURL(this.histogramImageUrl);
|
|
|
|
}
|
|
}
|
|
};
|
|
};
|
|
</script>
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
<style scoped>
|
|
|
|
+::v-deep .el-table th.el-table__cell {
|
|
|
|
+ text-align: center;
|
|
|
|
+ background-color: #f5f7fa !important;
|
|
|
|
+}
|
|
|
|
+::v-deep .el-table td.el-table__cell {
|
|
|
|
+ text-align: center;
|
|
|
|
+}
|
|
.container {
|
|
.container {
|
|
padding: 20px;
|
|
padding: 20px;
|
|
background-color: #f5f7fa;
|
|
background-color: #f5f7fa;
|
|
@@ -367,7 +618,7 @@ export default {
|
|
|
|
|
|
.map-section, .histogram-section {
|
|
.map-section, .histogram-section {
|
|
flex: 1;
|
|
flex: 1;
|
|
- min-width: 300px; /* 最小宽度,确保在小屏幕上也能正常显示 */
|
|
|
|
|
|
+ min-width: 300px;
|
|
background-color: white;
|
|
background-color: white;
|
|
border-radius: 8px;
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
padding: 15px;
|
|
@@ -384,15 +635,34 @@ export default {
|
|
border-radius: 4px;
|
|
border-radius: 4px;
|
|
}
|
|
}
|
|
|
|
|
|
-.table-area {
|
|
|
|
|
|
+.stats-area {
|
|
width: 100%;
|
|
width: 100%;
|
|
background-color: white;
|
|
background-color: white;
|
|
border-radius: 8px;
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
padding: 15px;
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.stats-container {
|
|
margin-top: 20px;
|
|
margin-top: 20px;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+.charts-container {
|
|
|
|
+ display: flex;
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
+ gap: 20px;
|
|
|
|
+ margin-top: 30px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.chart-item {
|
|
|
|
+ flex: 1;
|
|
|
|
+ min-width: 300px;
|
|
|
|
+ background: #f9f9f9;
|
|
|
|
+ padding: 15px;
|
|
|
|
+ border-radius: 8px;
|
|
|
|
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
|
|
|
+}
|
|
|
|
+
|
|
.loading-container {
|
|
.loading-container {
|
|
display: flex;
|
|
display: flex;
|
|
flex-direction: column;
|
|
flex-direction: column;
|
|
@@ -443,4 +713,29 @@ export default {
|
|
flex: none;
|
|
flex: none;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+.model-info {
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ gap: 15px;
|
|
|
|
+ margin: 10px 0;
|
|
|
|
+ color: #666;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.update-time {
|
|
|
|
+ font-size: 14px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.chart-item {
|
|
|
|
+ flex: 1;
|
|
|
|
+ min-width: 400px;
|
|
|
|
+ background: white;
|
|
|
|
+ padding: 15px;
|
|
|
|
+ border-radius: 8px;
|
|
|
|
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.el-table {
|
|
|
|
+ margin-top: 20px;
|
|
|
|
+}
|
|
</style>
|
|
</style>
|