|
|
@@ -1 +1,629 @@
|
|
|
-<script setup>import Page from '../wyxjc/lsjc.vue'</script><template><Page /></template>
|
|
|
+<template>
|
|
|
+ <div class="monitor-page">
|
|
|
+ <div class="page-heading">
|
|
|
+ <div class="page-title">
|
|
|
+ <span class="page-title-icon"><el-icon><Compass /></el-icon></span>
|
|
|
+ <div>
|
|
|
+ <h2>压力监测</h2>
|
|
|
+ <span class="muted">供水管压力与消火栓压力原始采集记录</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="head-actions">
|
|
|
+ <el-radio-group v-model="activeTab" @change="onTabChange">
|
|
|
+ <el-radio-button label="supply">供水管压力</el-radio-button>
|
|
|
+ <el-radio-button label="hydrant">消火栓压力</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
+ <el-button class="heading-refresh" :icon="Refresh" :loading="loading" @click="loadAll">刷新</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 供水管压力 -->
|
|
|
+ <template v-if="activeTab === 'supply'">
|
|
|
+ <div class="kpis kpis-3">
|
|
|
+ <div class="card kpi">
|
|
|
+ <div class="k-top">
|
|
|
+ <span class="k-label">7 日平均压力</span>
|
|
|
+ <span class="k-icon" style="background:#e6f7f4;color:#0fb5a6"><el-icon><DataLine /></el-icon></span>
|
|
|
+ </div>
|
|
|
+ <div class="k-val">{{ fmt(supply.avgValue) }}<em>MPa</em></div>
|
|
|
+ <div class="k-foot">最近 7 天全网压力均值</div>
|
|
|
+ </div>
|
|
|
+ <div class="card kpi">
|
|
|
+ <div class="k-top">
|
|
|
+ <span class="k-label">7 日最高压力</span>
|
|
|
+ <span class="k-icon" style="background:#fff3e6;color:#f59e0b"><el-icon><Top /></el-icon></span>
|
|
|
+ </div>
|
|
|
+ <div class="k-val">{{ fmt(supply.peakValue) }}<em>MPa</em></div>
|
|
|
+ <div class="k-foot">最近 7 天单小时最高</div>
|
|
|
+ </div>
|
|
|
+ <div class="card kpi">
|
|
|
+ <div class="k-top">
|
|
|
+ <span class="k-label">7 日最低压力</span>
|
|
|
+ <span class="k-icon" style="background:#e6f0ff;color:#2563eb"><el-icon><Bottom /></el-icon></span>
|
|
|
+ </div>
|
|
|
+ <div class="k-val">{{ fmt(supply.minValue) }}<em>MPa</em></div>
|
|
|
+ <div class="k-foot">最近 7 天单小时最低</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 消火栓压力 -->
|
|
|
+ <template v-else>
|
|
|
+ <div class="kpis kpis-3">
|
|
|
+ <div class="card kpi">
|
|
|
+ <div class="k-top">
|
|
|
+ <span class="k-label">7 日平均压力</span>
|
|
|
+ <span class="k-icon" style="background:#e6f7f4;color:#0fb5a6"><el-icon><DataLine /></el-icon></span>
|
|
|
+ </div>
|
|
|
+ <div class="k-val">{{ fmt(hydrant.avgValue) }}<em>MPa</em></div>
|
|
|
+ <div class="k-foot">最近 7 天全网栓点均值</div>
|
|
|
+ </div>
|
|
|
+ <div class="card kpi">
|
|
|
+ <div class="k-top">
|
|
|
+ <span class="k-label">7 日最高压力</span>
|
|
|
+ <span class="k-icon" style="background:#fff3e6;color:#f59e0b"><el-icon><Top /></el-icon></span>
|
|
|
+ </div>
|
|
|
+ <div class="k-val">{{ fmt(hydrant.peakValue) }}<em>MPa</em></div>
|
|
|
+ <div class="k-foot">最近 7 天单小时最高</div>
|
|
|
+ </div>
|
|
|
+ <div class="card kpi">
|
|
|
+ <div class="k-top">
|
|
|
+ <span class="k-label">7 日最低压力</span>
|
|
|
+ <span class="k-icon" style="background:#e6f0ff;color:#2563eb"><el-icon><Bottom /></el-icon></span>
|
|
|
+ </div>
|
|
|
+ <div class="k-val">{{ fmt(hydrant.minValue) }}<em>MPa</em></div>
|
|
|
+ <div class="k-foot">最近 7 天单小时最低</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <el-card shadow="never" class="filter-card">
|
|
|
+ <template #header>
|
|
|
+ <div class="card-title">
|
|
|
+ <el-icon><Search /></el-icon>
|
|
|
+ <span>查询条件({{ activeTab === 'supply' ? '供水管压力' : '消火栓压力' }})</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <el-form :inline="true" :model="query" @submit.prevent="loadData">
|
|
|
+ <el-form-item label="设备信息">
|
|
|
+ <el-input v-model="query.deviceCode" clearable placeholder="请输入设备编码/名称" style="width: 200px" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" :icon="Search" :loading="loading" @click="loadData">查询</el-button>
|
|
|
+ <el-button :icon="RefreshLeft" @click="resetQuery">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-alert
|
|
|
+ v-if="loadError"
|
|
|
+ class="page-error"
|
|
|
+ type="error"
|
|
|
+ show-icon
|
|
|
+ :closable="false"
|
|
|
+ title="数据加载失败"
|
|
|
+ description="请检查后端服务或网络连接后重试。"
|
|
|
+ >
|
|
|
+ <el-button size="small" type="primary" @click="loadData">重试</el-button>
|
|
|
+ </el-alert>
|
|
|
+
|
|
|
+ <el-card shadow="never" class="table-card">
|
|
|
+ <template #header>
|
|
|
+ <div class="table-toolbar">
|
|
|
+ <div class="card-title">
|
|
|
+ <el-icon><List /></el-icon>
|
|
|
+ <span>{{ activeTab === 'supply' ? '供水管压力' : '消火栓压力' }}数据列表</span>
|
|
|
+ </div>
|
|
|
+ <el-button type="primary" plain :icon="Download" :loading="exporting" size="small" @click="handleExport">导出 Excel</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <el-table :data="records" stripe border v-loading="loading" empty-text="暂无符合条件的采集记录">
|
|
|
+ <el-table-column label="设备编码" min-width="150" show-overflow-tooltip>
|
|
|
+ <template #default="{ row }">{{ activeTab === 'supply' ? row.deviceCode : row.telemeteringStation }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="deviceName" label="设备名称" min-width="180" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="deviceType" label="设备类型" min-width="140" show-overflow-tooltip />
|
|
|
+ <el-table-column label="最新压力(MPa)" min-width="160">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <span class="val-cell">{{ activeTab === 'supply' ? fmtNum(row.pressure, 3) : fmtNum(row.pressureValue, 3) }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="createTime" label="最新采集时间" min-width="170" />
|
|
|
+ <el-table-column label="操作" min-width="120" fixed="right">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <div style="display: flex; justify-content: center; align-items: center; gap: 8px;">
|
|
|
+ <el-tooltip content="查看历史" placement="top">
|
|
|
+ <el-button link type="primary" :icon="Document" @click="openHistory(row)" />
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip content="7日趋势" placement="top">
|
|
|
+ <el-button link type="primary" :icon="DataLine" @click="openTrend(row)" />
|
|
|
+ </el-tooltip>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <el-pagination
|
|
|
+ v-model:current-page="query.pageNum"
|
|
|
+ v-model:page-size="query.pageSize"
|
|
|
+ :total="total"
|
|
|
+ layout="total, sizes, prev, pager, next"
|
|
|
+ :page-sizes="[10, 20, 50]"
|
|
|
+ @change="loadData"
|
|
|
+ />
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <HistoryDialog
|
|
|
+ v-model="historyVisible"
|
|
|
+ :title="historyTitle"
|
|
|
+ :device-key="historyDeviceKey"
|
|
|
+ :device-key-field="historyDeviceField"
|
|
|
+ :api-fn="historyApiFn"
|
|
|
+ :columns="historyColumns"
|
|
|
+ :device-title="historyDeviceTitle"
|
|
|
+ />
|
|
|
+
|
|
|
+ <DeviceTrendDialog
|
|
|
+ v-model="trendVisible"
|
|
|
+ :title="trendTitle"
|
|
|
+ :trend="trendData"
|
|
|
+ unit="MPa"
|
|
|
+ :series="trendSeries"
|
|
|
+ :threshold-min="trendThresholdMin"
|
|
|
+ :threshold-max="trendThresholdMax"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref } from 'vue'
|
|
|
+import { Bottom, Compass, DataLine, Document, Download, List, Refresh, RefreshLeft, Search, Top, Warning } from '@element-plus/icons-vue'
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
+import {
|
|
|
+ getSupplyPressureOverview,
|
|
|
+ getHydrantPressureOverview,
|
|
|
+ listSupplyPressureRecords,
|
|
|
+ listHydrantPressureRecords
|
|
|
+} from '@/api/pipeNetwork/waterSupply'
|
|
|
+import { exportTableToXlsx } from '@/utils/exportTable'
|
|
|
+import HistoryDialog from '../components/HistoryDialog.vue'
|
|
|
+import DeviceTrendDialog from '../components/DeviceTrendDialog.vue'
|
|
|
+
|
|
|
+const activeTab = ref('supply')
|
|
|
+const records = ref([])
|
|
|
+const total = ref(0)
|
|
|
+const loading = ref(false)
|
|
|
+const exporting = ref(false)
|
|
|
+const loadError = ref(false)
|
|
|
+const query = ref(createQuery())
|
|
|
+const supply = ref(emptyOverview())
|
|
|
+const hydrant = ref(emptyOverview())
|
|
|
+
|
|
|
+function emptyOverview() {
|
|
|
+ return { avgValue: null, peakValue: null, minValue: null, anomalyCount: null, thresholdMin: null, thresholdMax: null, trend: [], lastUpdate: '' }
|
|
|
+}
|
|
|
+
|
|
|
+function createQuery() {
|
|
|
+ return { pageNum: 1, pageSize: 10, deviceCode: '' }
|
|
|
+}
|
|
|
+
|
|
|
+function fmt(value) {
|
|
|
+ return value == null ? '--' : Number(value).toFixed(3)
|
|
|
+}
|
|
|
+
|
|
|
+function buildParams() {
|
|
|
+ const base = {
|
|
|
+ pageNum: query.value.pageNum,
|
|
|
+ pageSize: query.value.pageSize
|
|
|
+ }
|
|
|
+ if (activeTab.value === 'supply') {
|
|
|
+ base.deviceCode = query.value.deviceCode || undefined
|
|
|
+ } else {
|
|
|
+ base.telemeteringStation = query.value.deviceCode || undefined
|
|
|
+ }
|
|
|
+ base.latestOnly = true
|
|
|
+ return base
|
|
|
+}
|
|
|
+
|
|
|
+async function loadData() {
|
|
|
+ loading.value = true
|
|
|
+ loadError.value = false
|
|
|
+ try {
|
|
|
+ const api = activeTab.value === 'supply' ? listSupplyPressureRecords : listHydrantPressureRecords
|
|
|
+ const res = await api(buildParams())
|
|
|
+ const page = (res && res.data) || res || {}
|
|
|
+ records.value = Array.isArray(page.records) ? page.records : []
|
|
|
+ total.value = Number(page.total) || records.value.length
|
|
|
+ } catch (e) {
|
|
|
+ loadError.value = true
|
|
|
+ records.value = []
|
|
|
+ total.value = 0
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function loadOverview() {
|
|
|
+ try {
|
|
|
+ const s = await getSupplyPressureOverview({})
|
|
|
+ supply.value = Object.assign(emptyOverview(), (s && s.data) || s || {})
|
|
|
+ const h = await getHydrantPressureOverview({})
|
|
|
+ hydrant.value = Object.assign(emptyOverview(), (h && h.data) || h || {})
|
|
|
+ } catch (e) {
|
|
|
+ console.warn('[pressure] 概览加载失败', e)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function loadAll() {
|
|
|
+ loadData()
|
|
|
+ loadOverview()
|
|
|
+}
|
|
|
+
|
|
|
+function onTabChange() {
|
|
|
+ query.value.pageNum = 1
|
|
|
+ loadData()
|
|
|
+}
|
|
|
+
|
|
|
+async function handleExport() {
|
|
|
+ exporting.value = true
|
|
|
+ try {
|
|
|
+ const isSupply = activeTab.value === 'supply'
|
|
|
+ const params = { pageNum: 1, pageSize: 10000, latestOnly: true }
|
|
|
+ if (isSupply) params.deviceCode = query.value.deviceCode || undefined
|
|
|
+ else params.telemeteringStation = query.value.deviceCode || undefined
|
|
|
+ const api = isSupply ? listSupplyPressureRecords : listHydrantPressureRecords
|
|
|
+ const res = await api(params)
|
|
|
+ const list = (res && res.data && res.data.records) || []
|
|
|
+ if (!list.length) {
|
|
|
+ ElMessage.warning('当前条件下没有可导出的记录')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const columns = [
|
|
|
+ { prop: isSupply ? 'deviceCode' : 'telemeteringStation', label: '设备编码' },
|
|
|
+ { prop: 'deviceName', label: '设备名称' },
|
|
|
+ { prop: 'deviceType', label: '设备类型' },
|
|
|
+ { prop: isSupply ? 'pressure' : 'pressureValue', label: '最新压力(MPa)', format: (v) => fmtNum(v, 3) },
|
|
|
+ { prop: 'createTime', label: '最新采集时间' }
|
|
|
+ ]
|
|
|
+ const filename = isSupply ? '供水管压力记录.xlsx' : '消火栓压力记录.xlsx'
|
|
|
+ exportTableToXlsx({ rows: list, columns, filename, sheetName: isSupply ? '供水管压力' : '消火栓压力' })
|
|
|
+ ElMessage.success(`已导出 ${list.length} 条记录`)
|
|
|
+ } catch (e) {
|
|
|
+ console.warn('[pressure] 导出失败', e)
|
|
|
+ ElMessage.error('导出失败,请稍后重试')
|
|
|
+ } finally {
|
|
|
+ exporting.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const supplyTrendSeries = [
|
|
|
+ { name: '日平均压力', key: 'avg', color: '#1b6ef3', area: true },
|
|
|
+ { name: '日内最高', key: 'peak', color: '#a9c6fb', dashed: true },
|
|
|
+ { name: '日内最低', key: 'min', color: '#f59e0b', dashed: true }
|
|
|
+]
|
|
|
+const hydrantTrendSeries = [
|
|
|
+ { name: '日平均压力', key: 'avg', color: '#e5484d', area: true },
|
|
|
+ { name: '日内最高', key: 'peak', color: '#f5a3a3', dashed: true },
|
|
|
+ { name: '日内最低', key: 'min', color: '#f59e0b', dashed: true }
|
|
|
+]
|
|
|
+
|
|
|
+const historyVisible = ref(false)
|
|
|
+const historyDeviceKey = ref('')
|
|
|
+const historyDeviceField = ref('deviceCode')
|
|
|
+const historyApiFn = ref(listSupplyPressureRecords)
|
|
|
+const historyTitle = ref('供水管压力历史采集记录')
|
|
|
+const historyDeviceTitle = ref('')
|
|
|
+const supplyHistoryColumns = [
|
|
|
+ { prop: 'pressure', label: '压力(MPa)', width: 160, format: (v) => fmtNum(v, 3) },
|
|
|
+ { prop: 'createTime', label: '采集时间', width: 170 }
|
|
|
+]
|
|
|
+const hydrantHistoryColumns = [
|
|
|
+ { prop: 'pressureValue', label: '压力(MPa)', width: 160, format: (v) => fmtNum(v, 3) },
|
|
|
+ { prop: 'createTime', label: '采集时间', width: 170 }
|
|
|
+]
|
|
|
+const historyColumns = ref(supplyHistoryColumns)
|
|
|
+
|
|
|
+function openHistory(row) {
|
|
|
+ const isSupply = activeTab.value === 'supply'
|
|
|
+ const key = isSupply ? row.deviceCode : row.telemeteringStation
|
|
|
+ historyDeviceKey.value = key
|
|
|
+ historyDeviceField.value = isSupply ? 'deviceCode' : 'telemeteringStation'
|
|
|
+ historyApiFn.value = isSupply ? listSupplyPressureRecords : listHydrantPressureRecords
|
|
|
+ historyTitle.value = isSupply ? '供水管压力历史采集记录' : '消火栓压力历史采集记录'
|
|
|
+ historyDeviceTitle.value = `${key}.${row.deviceName || key}`
|
|
|
+ historyColumns.value = isSupply ? supplyHistoryColumns : hydrantHistoryColumns
|
|
|
+ historyVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const trendVisible = ref(false)
|
|
|
+const trendTitle = ref('')
|
|
|
+const trendData = ref([])
|
|
|
+const trendThresholdMin = ref(null)
|
|
|
+const trendThresholdMax = ref(null)
|
|
|
+const trendSeries = ref(supplyTrendSeries)
|
|
|
+
|
|
|
+async function openTrend(row) {
|
|
|
+ const isSupply = activeTab.value === 'supply'
|
|
|
+ const key = isSupply ? row.deviceCode : row.telemeteringStation
|
|
|
+ const field = isSupply ? 'deviceCode' : 'telemeteringStation'
|
|
|
+ const api = isSupply ? getSupplyPressureOverview : getHydrantPressureOverview
|
|
|
+ try {
|
|
|
+ const res = await api({ [field]: key, endTime: row.createTime })
|
|
|
+ const data = (res && res.data) || res || {}
|
|
|
+ trendData.value = data.trend || []
|
|
|
+ trendThresholdMin.value = data.thresholdMin ?? null
|
|
|
+ trendThresholdMax.value = data.thresholdMax ?? null
|
|
|
+ trendSeries.value = isSupply ? supplyTrendSeries : hydrantTrendSeries
|
|
|
+ trendTitle.value = `${row.deviceName || key} · 近 7 日趋势(截至 ${row.createTime})`
|
|
|
+ trendVisible.value = true
|
|
|
+ } catch (e) {
|
|
|
+ console.warn('[pressure] 趋势加载失败', e)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function resetQuery() {
|
|
|
+ query.value = createQuery()
|
|
|
+ loadData()
|
|
|
+}
|
|
|
+
|
|
|
+function fmtNum(value, digits = 1) {
|
|
|
+ if (value === null || value === undefined || String(value).trim() === '') return '-'
|
|
|
+ const n = Number(value)
|
|
|
+ return Number.isFinite(n) ? n.toFixed(digits) : String(value)
|
|
|
+}
|
|
|
+
|
|
|
+loadAll()
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.monitor-page {
|
|
|
+ padding: 20px;
|
|
|
+ background: linear-gradient(135deg, #eff6ff 0%, #f8fafc 45%, #eef2f7 100%);
|
|
|
+ min-height: calc(100vh - 84px);
|
|
|
+}
|
|
|
+
|
|
|
+.page-heading {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ gap: 16px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.page-title {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+ min-width: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.page-title-icon {
|
|
|
+ display: grid;
|
|
|
+ place-items: center;
|
|
|
+ width: 42px;
|
|
|
+ height: 42px;
|
|
|
+ border-radius: 8px;
|
|
|
+ color: #fff;
|
|
|
+ background: linear-gradient(135deg, #2563eb 0%, #06b6d4 100%);
|
|
|
+ box-shadow: 0 8px 16px rgba(37, 99, 235, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+.page-heading h2 {
|
|
|
+ margin: 0 0 4px;
|
|
|
+ font-size: 20px;
|
|
|
+ color: #1e293b;
|
|
|
+}
|
|
|
+
|
|
|
+.muted {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #64748b;
|
|
|
+}
|
|
|
+
|
|
|
+.head-actions {
|
|
|
+ display: flex;
|
|
|
+ gap: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.heading-refresh {
|
|
|
+ border-color: #bfdbfe;
|
|
|
+ color: #2563eb;
|
|
|
+ background: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-head {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-head-tools {
|
|
|
+ flex: 0 0 auto;
|
|
|
+}
|
|
|
+
|
|
|
+.kpis {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
+ gap: 16px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.kpis-3 {
|
|
|
+ grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
+}
|
|
|
+
|
|
|
+.kpi {
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 16px 18px;
|
|
|
+ box-shadow: 0 6px 14px rgba(15, 23, 42, 0.04);
|
|
|
+}
|
|
|
+
|
|
|
+.k-top {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.k-label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #475569;
|
|
|
+}
|
|
|
+
|
|
|
+.k-icon {
|
|
|
+ display: grid;
|
|
|
+ place-items: center;
|
|
|
+ width: 32px;
|
|
|
+ height: 32px;
|
|
|
+ border-radius: 8px;
|
|
|
+ font-size: 18px;
|
|
|
+}
|
|
|
+
|
|
|
+.k-val {
|
|
|
+ margin-top: 10px;
|
|
|
+ font-size: 26px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #1e293b;
|
|
|
+}
|
|
|
+
|
|
|
+.k-val em {
|
|
|
+ margin-left: 6px;
|
|
|
+ font-size: 13px;
|
|
|
+ font-weight: 500;
|
|
|
+ font-style: normal;
|
|
|
+ color: #94a3b8;
|
|
|
+}
|
|
|
+
|
|
|
+.k-foot {
|
|
|
+ margin-top: 6px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #94a3b8;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-card {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 6px 14px rgba(15, 23, 42, 0.04);
|
|
|
+}
|
|
|
+
|
|
|
+.filter-card {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 6px 14px rgba(15, 23, 42, 0.04);
|
|
|
+}
|
|
|
+
|
|
|
+.filter-card :deep(.el-form-item) {
|
|
|
+ margin-top: 8px;
|
|
|
+ margin-right: 16px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.filter-card :deep(.el-form-item__label) {
|
|
|
+ color: #475569;
|
|
|
+}
|
|
|
+
|
|
|
+.page-error {
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.table-card {
|
|
|
+ min-height: 420px;
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 6px 14px rgba(15, 23, 42, 0.04);
|
|
|
+}
|
|
|
+
|
|
|
+.card-title {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ color: #1e293b;
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+
|
|
|
+.card-title .el-icon {
|
|
|
+ color: #2563eb;
|
|
|
+ font-size: 18px;
|
|
|
+}
|
|
|
+
|
|
|
+.table-toolbar {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.total-tip {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #64748b;
|
|
|
+}
|
|
|
+
|
|
|
+.table-card :deep(.el-table) {
|
|
|
+ --el-table-border-color: #e2e8f0;
|
|
|
+ --el-table-header-bg-color: #f8fafc;
|
|
|
+ --el-table-header-text-color: #475569;
|
|
|
+ --el-table-row-hover-bg-color: #eff6ff;
|
|
|
+}
|
|
|
+
|
|
|
+.val-cell {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: baseline;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.val-cell .unit {
|
|
|
+ font-size: 12px;
|
|
|
+ font-style: normal;
|
|
|
+ color: #94a3b8;
|
|
|
+}
|
|
|
+
|
|
|
+.table-card :deep(.el-pagination) {
|
|
|
+ justify-content: flex-end;
|
|
|
+ margin-top: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.latest-card {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 6px 14px rgba(15, 23, 42, 0.04);
|
|
|
+}
|
|
|
+
|
|
|
+.latest-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 28px;
|
|
|
+}
|
|
|
+
|
|
|
+.latest-item {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.latest-label {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #64748b;
|
|
|
+}
|
|
|
+
|
|
|
+.latest-val {
|
|
|
+ font-size: 20px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #1e293b;
|
|
|
+}
|
|
|
+
|
|
|
+.latest-val em {
|
|
|
+ margin-left: 6px;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 500;
|
|
|
+ font-style: normal;
|
|
|
+ color: #94a3b8;
|
|
|
+}
|
|
|
+
|
|
|
+.latest-chart-btn {
|
|
|
+ margin-left: auto;
|
|
|
+}
|
|
|
+</style>
|