|
@@ -1,53 +1,544 @@
|
|
|
<template>
|
|
|
- <div class="">
|
|
|
- <!-- 添加删除和导入按钮 -->
|
|
|
- <el-button type="danger" @click="handleBatchDelete" :disabled="!selectedRows.length">删除</el-button>
|
|
|
- <el-button type="primary" @click="handleImport">导入</el-button>
|
|
|
-
|
|
|
- <!-- 添加表格 -->
|
|
|
- <el-table
|
|
|
- :data="tableData"
|
|
|
- style="width: 100%"
|
|
|
- @selection-change="handleSelectionChange"
|
|
|
- >
|
|
|
- <el-table-column type="selection" width="55" />
|
|
|
- <el-table-column prop="name" label="名称" width="180" />
|
|
|
- <el-table-column prop="date" label="日期" width="180" />
|
|
|
- <el-table-column prop="status" label="状态" width="180" />
|
|
|
- </el-table>
|
|
|
+ <div class="metal-tables-container">
|
|
|
+ <!-- 灌溉水表格 -->
|
|
|
+ <div class="table-section">
|
|
|
+ <el-row :gutter="10" align="middle">
|
|
|
+ <el-col :span="20">
|
|
|
+ <div class="title">灌溉水采样点各重金属平均值</div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4" style="text-align: right;">
|
|
|
+ <div class="sample-count" v-if="waterValidSamples > 0">
|
|
|
+ 有效样本:{{ waterValidSamples }}个
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-table
|
|
|
+ v-if="waterTableData.length && !waterLoading && !waterError"
|
|
|
+ :data="waterTableData"
|
|
|
+ border
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ <el-table-column label="指标" prop="indicator" width="80" />
|
|
|
+ <el-table-column
|
|
|
+ v-for="(metal, field) in waterMetals"
|
|
|
+ :key="field"
|
|
|
+ :label="metal.label"
|
|
|
+
|
|
|
+ >
|
|
|
+ <template #default="scope">
|
|
|
+ {{ scope.row[field] }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <div v-else class="empty-state" v-if="!waterLoading && !waterError">
|
|
|
+ <el-empty description="灌溉水数据为空,请刷新" />
|
|
|
+ </div>
|
|
|
+ <el-alert
|
|
|
+ v-if="waterError"
|
|
|
+ type="error"
|
|
|
+ :description="waterError"
|
|
|
+ show-icon
|
|
|
+ class="error-alert"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 断面数据表格 -->
|
|
|
+ <div class="table-section">
|
|
|
+ <el-row :gutter="10" align="middle">
|
|
|
+ <el-col :span="20">
|
|
|
+ <div class="title">断面采样点镉含量平均值</div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4" style="text-align: right;">
|
|
|
+ <div class="sample-count" v-if="sectionValidSamples > 0">
|
|
|
+ 有效样本:{{ sectionValidSamples }}个
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-table
|
|
|
+ v-if="sectionTableData.length && !sectionLoading && !sectionError"
|
|
|
+ :data="sectionTableData"
|
|
|
+ border
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ <el-table-column label="指标" prop="indicator" width="80" />
|
|
|
+ <el-table-column
|
|
|
+ v-for="(metal, field) in sectionMetals"
|
|
|
+ :key="field"
|
|
|
+ :label="metal.label"
|
|
|
+
|
|
|
+ >
|
|
|
+ <template #default="scope">
|
|
|
+ {{ scope.row[field] }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <div v-else class="empty-state" v-if="!sectionLoading && !sectionError">
|
|
|
+ <el-empty description="断面数据为空,请刷新" />
|
|
|
+ </div>
|
|
|
+ <el-alert
|
|
|
+ v-if="sectionError"
|
|
|
+ type="error"
|
|
|
+ :description="sectionError"
|
|
|
+ show-icon
|
|
|
+ class="error-alert"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 大气企业表格 -->
|
|
|
+ <div class="table-section">
|
|
|
+ <el-row :gutter="10" align="middle">
|
|
|
+ <el-col :span="20">
|
|
|
+ <div class="title">大气企业颗粒物排放平均值</div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4" style="text-align: right;">
|
|
|
+ <div class="sample-count" v-if="atmoCompanyValidSamples > 0">
|
|
|
+ 有效样本:{{ atmoCompanyValidSamples }}个
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-table
|
|
|
+ v-if="atmoCompanyTableData.length && !atmoCompanyLoading && !atmoCompanyError"
|
|
|
+ :data="atmoCompanyTableData"
|
|
|
+ border
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ <el-table-column label="指标" prop="indicator" width="80" />
|
|
|
+ <el-table-column
|
|
|
+ v-for="(metric, field) in atmoCompanyMetrics"
|
|
|
+ :key="field"
|
|
|
+ :label="metric.label"
|
|
|
+
|
|
|
+ >
|
|
|
+ <template #default="scope">
|
|
|
+ {{ scope.row[field]}}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <div v-else class="empty-state" v-if="!atmoCompanyLoading && !atmoCompanyError">
|
|
|
+ <el-empty description="大气企业数据为空,请刷新" />
|
|
|
+ </div>
|
|
|
+ <el-alert
|
|
|
+ v-if="atmoCompanyError"
|
|
|
+ type="error"
|
|
|
+ :description="atmoCompanyError"
|
|
|
+ show-icon
|
|
|
+ class="error-alert"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 大气样本表格 -->
|
|
|
+ <div class="table-section">
|
|
|
+ <el-row :gutter="10" align="middle">
|
|
|
+ <el-col :span="20">
|
|
|
+ <div class="title">大气样本重金属平均值</div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="4" style="text-align: right;">
|
|
|
+ <div class="sample-count" v-if="atmoSampleValidSamples > 0">
|
|
|
+ 有效样本:{{ atmoSampleValidSamples }}个
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-table
|
|
|
+ v-if="atmoSampleTableData.length && !atmoSampleLoading && !atmoSampleError"
|
|
|
+ :data="atmoSampleTableData"
|
|
|
+ border
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ <el-table-column label="指标" prop="indicator" width="80" />
|
|
|
+ <el-table-column
|
|
|
+ v-for="(metric, field) in atmoSampleMetrics"
|
|
|
+ :key="field"
|
|
|
+ :label="metric.label"
|
|
|
+ :width="getColWidth(field)"
|
|
|
+ >
|
|
|
+ <template #default="scope">
|
|
|
+ {{ scope.row[field] }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <div v-else class="empty-state" v-if="!atmoSampleLoading && !atmoSampleError">
|
|
|
+ <el-empty description="大气样本数据为空,请刷新" />
|
|
|
+ </div>
|
|
|
+ <el-alert
|
|
|
+ v-if="atmoSampleError"
|
|
|
+ type="error"
|
|
|
+ :description="atmoSampleError"
|
|
|
+ show-icon
|
|
|
+ class="error-alert"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 统一刷新按钮 -->
|
|
|
+ <div class="refresh-btn">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ @click="refreshAll"
|
|
|
+ :loading="waterLoading || sectionLoading || atmoCompanyLoading || atmoSampleLoading"
|
|
|
+ icon="el-icon-refresh"
|
|
|
+ >
|
|
|
+ 刷新所有数据
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
-<script>
|
|
|
-export default {
|
|
|
- name: 'DetectionStatistics',
|
|
|
- data() {
|
|
|
- return {
|
|
|
- tableData: [
|
|
|
- { name: '示例1', date: '2023-01-01', status: '正常' },
|
|
|
- { name: '示例2', date: '2023-01-02', status: '异常' }
|
|
|
- ],
|
|
|
- selectedRows: [] // 存储选中的行
|
|
|
- };
|
|
|
- },
|
|
|
- methods: {
|
|
|
- handleSelectionChange(selection) {
|
|
|
- this.selectedRows = selection;
|
|
|
- },
|
|
|
- handleBatchDelete() {
|
|
|
- this.tableData = this.tableData.filter(
|
|
|
- item => !this.selectedRows.includes(item)
|
|
|
- );
|
|
|
- this.selectedRows = [];
|
|
|
- },
|
|
|
- handleImport() {
|
|
|
- // 导入逻辑
|
|
|
- console.log('导入按钮点击');
|
|
|
- }
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, computed, onMounted } from 'vue';
|
|
|
+import axios from 'axios';
|
|
|
+
|
|
|
+// ====================== 通用工具类型 ======================
|
|
|
+type MetricsMap<T extends string> = { [K in T]: { label: string } };
|
|
|
+type TableRow<T extends string> = { indicator: string } & { [K in T]: number };
|
|
|
+
|
|
|
+// ====================== 灌溉水模块(严格类型) ======================
|
|
|
+const waterMetals = {
|
|
|
+ cr_concentration: { label: '铬含量(ug/L)' },
|
|
|
+ as_concentration: { label: '砷含量(ug/L)' },
|
|
|
+ cd_concentration: { label: '镉含量(ug/L)' },
|
|
|
+ hg_concentration: { label: '汞含量(ug/L)' },
|
|
|
+ pb_concentration: { label: '铅含量(ug/L)' },
|
|
|
+};
|
|
|
+type WaterMetalKey = keyof typeof waterMetals;
|
|
|
+type WaterRow = TableRow<WaterMetalKey>;
|
|
|
+
|
|
|
+const waterLoading = ref(false);
|
|
|
+const waterError = ref('');
|
|
|
+const waterData = ref<{ [K in WaterMetalKey]?: number | string }[]>([]);
|
|
|
+const waterTableData = ref<WaterRow[]>([]);
|
|
|
+const waterValidSamples = ref(0); // 有效样本数
|
|
|
+
|
|
|
+const calculateWaterAverage = () => {
|
|
|
+ const stats: Record<WaterMetalKey, { sum: number; count: number }> = {} as any;
|
|
|
+ (Object.keys(waterMetals) as WaterMetalKey[]).forEach(key => {
|
|
|
+ stats[key] = { sum: 0, count: 0 };
|
|
|
+ });
|
|
|
+
|
|
|
+ let validSamples = 0;
|
|
|
+ waterData.value.forEach(item => {
|
|
|
+ let hasValidMetric = false;
|
|
|
+ (Object.keys(waterMetals) as WaterMetalKey[]).forEach(key => {
|
|
|
+ const value = Number(item[key]);
|
|
|
+ if (!isNaN(value)) {
|
|
|
+ stats[key].sum += value;
|
|
|
+ stats[key].count += 1;
|
|
|
+ hasValidMetric = true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ if (hasValidMetric) validSamples++;
|
|
|
+ });
|
|
|
+
|
|
|
+ waterTableData.value = [(Object.keys(waterMetals) as WaterMetalKey[]).reduce((row, key) => {
|
|
|
+ row.indicator = '平均值';
|
|
|
+ row[key] = stats[key].count > 0 ? stats[key].sum / stats[key].count : 0;
|
|
|
+ return row;
|
|
|
+ }, {} as WaterRow)];
|
|
|
+
|
|
|
+ waterValidSamples.value = validSamples;
|
|
|
+};
|
|
|
+
|
|
|
+const fetchWaterData = async () => {
|
|
|
+ try {
|
|
|
+ waterLoading.value = true;
|
|
|
+ waterError.value = '';
|
|
|
+ const res = await axios.get(
|
|
|
+ 'http://localhost:8000/api/vector/export/all?table_name=water_sampling_data'
|
|
|
+ );
|
|
|
+ waterData.value = res.data.features.map((f: { properties: any }) => f.properties);
|
|
|
+ calculateWaterAverage();
|
|
|
+ } catch (err: any) {
|
|
|
+ waterError.value = err.message;
|
|
|
+ waterTableData.value = [];
|
|
|
+ } finally {
|
|
|
+ waterLoading.value = false;
|
|
|
}
|
|
|
};
|
|
|
+
|
|
|
+// ====================== 断面模块(严格类型) ======================
|
|
|
+const sectionMetals = { cd_concentration: { label: '镉含量(ug/L)' } };
|
|
|
+type SectionMetalKey = keyof typeof sectionMetals;
|
|
|
+type SectionRow = TableRow<SectionMetalKey>;
|
|
|
+
|
|
|
+const sectionLoading = ref(false);
|
|
|
+const sectionError = ref('');
|
|
|
+const sectionData = ref<{ [K in SectionMetalKey]?: number | string }[]>([]);
|
|
|
+const sectionTableData = ref<SectionRow[]>([]);
|
|
|
+const sectionValidSamples = ref(0); // 有效样本数
|
|
|
+
|
|
|
+const calculateSectionAverage = () => {
|
|
|
+ const stats: Record<SectionMetalKey, { sum: number; count: number }> = {} as any;
|
|
|
+ (Object.keys(sectionMetals) as SectionMetalKey[]).forEach(key => {
|
|
|
+ stats[key] = { sum: 0, count: 0 };
|
|
|
+ });
|
|
|
+
|
|
|
+ let validSamples = 0;
|
|
|
+ sectionData.value.forEach(item => {
|
|
|
+ let hasValidMetric = false;
|
|
|
+ (Object.keys(sectionMetals) as SectionMetalKey[]).forEach(key => {
|
|
|
+ const value = Number(item[key]);
|
|
|
+ if (!isNaN(value)) {
|
|
|
+ stats[key].sum += value;
|
|
|
+ stats[key].count += 1;
|
|
|
+ hasValidMetric = true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ if (hasValidMetric) validSamples++;
|
|
|
+ });
|
|
|
+
|
|
|
+ sectionTableData.value = [(Object.keys(sectionMetals) as SectionMetalKey[]).reduce((row, key) => {
|
|
|
+ row.indicator = '平均值';
|
|
|
+ row[key] = stats[key].count > 0 ? stats[key].sum / stats[key].count : 0;
|
|
|
+ return row;
|
|
|
+ }, {} as SectionRow)];
|
|
|
+
|
|
|
+ sectionValidSamples.value = validSamples;
|
|
|
+};
|
|
|
+
|
|
|
+const fetchSectionData = async () => {
|
|
|
+ try {
|
|
|
+ sectionLoading.value = true;
|
|
|
+ sectionError.value = '';
|
|
|
+ const res = await fetch('http://localhost:8000/api/vector/export/all?table_name=cross_section');
|
|
|
+ if (!res.ok) throw new Error(`HTTP 错误:${res.status}`);
|
|
|
+ let rawText = await res.text();
|
|
|
+ rawText = rawText.replace(/:\s*NaN/g, ': null'); // 修复 NaN
|
|
|
+ const geoJSON = JSON.parse(rawText);
|
|
|
+ sectionData.value = geoJSON.features.map((f: { properties: any }) => f.properties);
|
|
|
+ calculateSectionAverage();
|
|
|
+ } catch (err: any) {
|
|
|
+ sectionError.value = err.message;
|
|
|
+ sectionTableData.value = [];
|
|
|
+ } finally {
|
|
|
+ sectionLoading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// ====================== 大气企业模块(严格类型) ======================
|
|
|
+const atmoCompanyMetrics = {
|
|
|
+ particulate_emission: { label: '颗粒物排放量' },
|
|
|
+};
|
|
|
+type AtmoCompanyKey = keyof typeof atmoCompanyMetrics;
|
|
|
+type AtmoCompanyRow = TableRow<AtmoCompanyKey>;
|
|
|
+
|
|
|
+const atmoCompanyLoading = ref(false);
|
|
|
+const atmoCompanyError = ref('');
|
|
|
+const atmoCompanyData = ref<{ [K in AtmoCompanyKey]?: number | string }[]>([]);
|
|
|
+const atmoCompanyTableData = ref<AtmoCompanyRow[]>([]);
|
|
|
+const atmoCompanyValidSamples = ref(0); // 有效样本数
|
|
|
+
|
|
|
+const calculateAtmoCompanyAverage = () => {
|
|
|
+ const stats: Record<AtmoCompanyKey, { sum: number; count: number }> = {} as any;
|
|
|
+ (Object.keys(atmoCompanyMetrics) as AtmoCompanyKey[]).forEach(key => {
|
|
|
+ stats[key] = { sum: 0, count: 0 };
|
|
|
+ });
|
|
|
+
|
|
|
+ let validSamples = 0;
|
|
|
+ atmoCompanyData.value.forEach(item => {
|
|
|
+ let hasValidMetric = false;
|
|
|
+ (Object.keys(atmoCompanyMetrics) as AtmoCompanyKey[]).forEach(key => {
|
|
|
+ const value = Number(item[key]);
|
|
|
+ if (!isNaN(value)) {
|
|
|
+ stats[key].sum += value;
|
|
|
+ stats[key].count += 1;
|
|
|
+ hasValidMetric = true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ if (hasValidMetric) validSamples++;
|
|
|
+ });
|
|
|
+
|
|
|
+ atmoCompanyTableData.value = [(Object.keys(atmoCompanyMetrics) as AtmoCompanyKey[]).reduce((row, key) => {
|
|
|
+ row.indicator = '平均值';
|
|
|
+ row[key] = stats[key].count > 0 ? stats[key].sum / stats[key].count : 0;
|
|
|
+ return row;
|
|
|
+ }, {} as AtmoCompanyRow)];
|
|
|
+
|
|
|
+ atmoCompanyValidSamples.value = validSamples;
|
|
|
+};
|
|
|
+
|
|
|
+const fetchAtmoCompanyData = async () => {
|
|
|
+ try {
|
|
|
+ atmoCompanyLoading.value = true;
|
|
|
+ atmoCompanyError.value = '';
|
|
|
+ const res = await fetch('http://localhost:8000/api/vector/export/all?table_name=atmo_company');
|
|
|
+ if (!res.ok) throw new Error(`HTTP 错误:${res.status}`);
|
|
|
+ let rawText = await res.text();
|
|
|
+ rawText = rawText.replace(/:\s*NaN/g, ': null'); // 修复 NaN
|
|
|
+ const geoJSON = JSON.parse(rawText);
|
|
|
+ atmoCompanyData.value = geoJSON.features.map((f: { properties: any }) => f.properties);
|
|
|
+ calculateAtmoCompanyAverage();
|
|
|
+ } catch (err: any) {
|
|
|
+ atmoCompanyError.value = err.message;
|
|
|
+ atmoCompanyTableData.value = [];
|
|
|
+ } finally {
|
|
|
+ atmoCompanyLoading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// ====================== 大气样本模块(严格类型) ======================
|
|
|
+const weightColumns = [
|
|
|
+ { key: 'Cr_particulate', label: 'Cr mg/kg' },
|
|
|
+ { key: 'As_particulate', label: 'As mg/kg' },
|
|
|
+ { key: 'Cd_particulate', label: 'Cd mg/kg' },
|
|
|
+ { key: 'Hg_particulate', label: 'Hg mg/kg' },
|
|
|
+ { key: 'Pb_particulate', label: 'Pb mg/kg' },
|
|
|
+ { key: 'particle_weight', label: '颗粒物重量 mg' },
|
|
|
+];
|
|
|
+
|
|
|
+const atmoSampleMetrics = weightColumns.reduce((obj, col) => {
|
|
|
+ obj[col.key] = { label: col.label };
|
|
|
+ return obj;
|
|
|
+}, {} as Record<string, { label: string }>);
|
|
|
+type AtmoSampleKey = keyof typeof atmoSampleMetrics;
|
|
|
+type AtmoSampleRow = TableRow<AtmoSampleKey>;
|
|
|
+
|
|
|
+const atmoSampleLoading = ref(false);
|
|
|
+const atmoSampleError = ref('');
|
|
|
+const atmoSampleData = ref<{ [K in AtmoSampleKey]?: number | string }[]>([]);
|
|
|
+const atmoSampleTableData = ref<AtmoSampleRow[]>([]);
|
|
|
+const atmoSampleValidSamples = ref(0); // 有效样本数
|
|
|
+
|
|
|
+const getColWidth = computed(() => (field: AtmoSampleKey) => {
|
|
|
+ const col = weightColumns.find(c => c.key === field);
|
|
|
+ return col ;
|
|
|
+});
|
|
|
+
|
|
|
+const calculateAtmoSampleAverage = () => {
|
|
|
+ const stats: Record<AtmoSampleKey, { sum: number; count: number }> = {} as any;
|
|
|
+ (Object.keys(atmoSampleMetrics) as AtmoSampleKey[]).forEach(key => {
|
|
|
+ stats[key] = { sum: 0, count: 0 };
|
|
|
+ });
|
|
|
+
|
|
|
+ let validSamples = 0;
|
|
|
+ atmoSampleData.value.forEach(item => {
|
|
|
+ let hasValidMetric = false;
|
|
|
+ (Object.keys(atmoSampleMetrics) as AtmoSampleKey[]).forEach(key => {
|
|
|
+ const value = Number(item[key]);
|
|
|
+ if (!isNaN(value)) {
|
|
|
+ stats[key].sum += value;
|
|
|
+ stats[key].count += 1;
|
|
|
+ hasValidMetric = true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ if (hasValidMetric) validSamples++;
|
|
|
+ });
|
|
|
+
|
|
|
+ atmoSampleTableData.value = [(Object.keys(atmoSampleMetrics) as AtmoSampleKey[]).reduce((row, key) => {
|
|
|
+ row.indicator = '平均值';
|
|
|
+ row[key] = stats[key].count > 0 ? stats[key].sum / stats[key].count : 0;
|
|
|
+ return row;
|
|
|
+ }, {} as AtmoSampleRow)];
|
|
|
+
|
|
|
+ atmoSampleValidSamples.value = validSamples;
|
|
|
+};
|
|
|
+
|
|
|
+const fetchAtmoSampleData = async () => {
|
|
|
+ try {
|
|
|
+ atmoSampleLoading.value = true;
|
|
|
+ atmoSampleError.value = '';
|
|
|
+ const res = await fetch('http://localhost:8000/api/vector/export/all?table_name=Atmo_sample_data');
|
|
|
+ if (!res.ok) throw new Error(`HTTP 错误:${res.status}`);
|
|
|
+ let rawText = await res.text();
|
|
|
+ rawText = rawText.replace(/:\s*NaN/g, ': null'); // 修复 NaN
|
|
|
+ const geoJSON = JSON.parse(rawText);
|
|
|
+ atmoSampleData.value = geoJSON.features.map((f: { properties: any }) => f.properties);
|
|
|
+ calculateAtmoSampleAverage();
|
|
|
+ } catch (err: any) {
|
|
|
+ atmoSampleError.value = err.message;
|
|
|
+ atmoSampleTableData.value = [];
|
|
|
+ } finally {
|
|
|
+ atmoSampleLoading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// ====================== 统一刷新 ======================
|
|
|
+const refreshAll = () => {
|
|
|
+ fetchWaterData();
|
|
|
+ fetchSectionData();
|
|
|
+ fetchAtmoCompanyData();
|
|
|
+ fetchAtmoSampleData();
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ refreshAll();
|
|
|
+});
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-
|
|
|
+.metal-tables-container {
|
|
|
+ padding: 20px;
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 2px 12px rgba(0,0,0,0.1);
|
|
|
+}
|
|
|
+
|
|
|
+.table-section {
|
|
|
+ margin-bottom: 30px;
|
|
|
+ padding-bottom: 20px;
|
|
|
+ border-bottom: 1px solid #eee;
|
|
|
+}
|
|
|
+
|
|
|
+.table-section:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+ margin-bottom: 0;
|
|
|
+ padding-bottom: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.title {
|
|
|
+ font-size: 20px;
|
|
|
+ font-weight: 500;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.sample-count {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #666;
|
|
|
+ font-style: italic;
|
|
|
+}
|
|
|
+
|
|
|
+.error-alert {
|
|
|
+ margin: 16px 0;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-state {
|
|
|
+ padding: 40px 0;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.refresh-btn {
|
|
|
+ text-align: center;
|
|
|
+ margin-top: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 关键样式:自适应列宽 + 内容不换行 */
|
|
|
+.el-table {
|
|
|
+ table-layout: auto; /* 列宽自适应内容 */
|
|
|
+ min-width: 100%; /* 确保容器宽度不足时触发滚动 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 覆盖Element UI的单元格样式(提高优先级) */
|
|
|
+.el-table td,
|
|
|
+.el-table th {
|
|
|
+ white-space: nowrap !important; /* 强制不换行 */
|
|
|
+ overflow: hidden !important; /* 溢出隐藏 */
|
|
|
+ text-overflow: ellipsis !important; /* 溢出省略号 */
|
|
|
+ word-break: normal !important; /* 禁止自动断词 */
|
|
|
+}
|
|
|
+
|
|
|
+.el-table__cell {
|
|
|
+ white-space: nowrap !important; /* 强制不换行 */
|
|
|
+ overflow: hidden; /* 溢出隐藏 */
|
|
|
+ text-overflow: ellipsis; /* 溢出显示省略号 */
|
|
|
+}
|
|
|
+
|
|
|
+.table-section {
|
|
|
+ overflow-x: auto; /* 横向滚动 */
|
|
|
+}
|
|
|
</style>
|