| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629 |
- <!--管线流量监测-->
- <template>
- <div class="flow-monitor">
- <!-- 页面头部 -->
- <div class="page-header">
- <div class="header-left">
- <el-icon><TrendCharts /></el-icon>
- <span class="title">管线流量监测</span>
- <div class="stats-badge">
- <span class="stat-item normal"><i class="dot"></i>正常 {{ normalCount }}</span>
- <span class="stat-item warning"><i class="dot"></i>预警 {{ warningCount }}</span>
- <span class="stat-item danger"><i class="dot"></i>告警 {{ dangerCount }}</span>
- <span class="stat-item offline"><i class="dot"></i>离线 {{ offlineCount }}</span>
- </div>
- </div>
- <div class="header-right">
- <el-input v-model="searchKeyword" placeholder="搜索测点名称/管线" clearable :prefix-icon="Search" style="width:200px" />
- <el-button type="primary" plain :icon="Refresh" @click="refreshData">刷新</el-button>
- </div>
- </div>
- <!-- 主内容区 -->
- <div class="main-layout">
- <!-- 左侧:测点列表 -->
- <div class="point-list-panel">
- <div class="list-header">
- <span>流量监测点</span>
- <span class="total-num">共 {{ filteredPoints.length }} 个测点</span>
- </div>
- <div class="filter-row">
- <el-select v-model="pipeType" placeholder="管线类型" clearable size="small" style="width:100%">
- <el-option label="高压管线" value="high" />
- <el-option label="中压管线" value="medium" />
- <el-option label="低压管线" value="low" />
- </el-select>
- </div>
- <div class="filter-row">
- <el-select v-model="statusFilter" placeholder="运行状态" clearable size="small" style="width:100%">
- <el-option label="正常" value="normal" />
- <el-option label="预警" value="warning" />
- <el-option label="告警" value="danger" />
- <el-option label="离线" value="offline" />
- </el-select>
- </div>
- <div class="point-list">
- <div
- v-for="point in filteredPoints"
- :key="point.id"
- class="point-item"
- :class="{ active: selectedPoint?.id === point.id, [point.status]: true }"
- @click="selectPoint(point)"
- >
- <div class="point-header">
- <span class="point-name">{{ point.name }}</span>
- <el-tag :type="statusTagType(point.status)" size="small">{{ statusLabel(point.status) }}</el-tag>
- </div>
- <div class="point-pipeline">{{ point.pipelineName }}</div>
- <div class="point-value">
- <span class="val">{{ point.latestFlow }}</span>
- <span class="unit">Nm³/h</span>
- <span class="threshold">量程: 0~{{ point.maxFlow }} Nm³/h</span>
- </div>
- <div class="point-sub" v-if="point.status !== 'offline'">
- <span>今日累计 <b>{{ point.todayTotal }}</b> Nm³</span>
- </div>
- <div class="point-addr">{{ point.address }}</div>
- <div class="point-time">{{ point.latestTime }}</div>
- </div>
- <el-empty v-if="filteredPoints.length === 0" description="暂无匹配测点" :image-size="50" />
- </div>
- </div>
- <!-- 右侧:详情面板 -->
- <div class="detail-panel">
- <template v-if="selectedPoint">
- <div class="detail-header">
- <div class="detail-title">
- <span class="point-name">{{ selectedPoint.name }}</span>
- <el-tag :type="statusTagType(selectedPoint.status)" size="small">{{ statusLabel(selectedPoint.status) }}</el-tag>
- </div>
- <div class="detail-meta">
- <span>所属管线:{{ selectedPoint.pipelineName }}</span>
- <span>管线类型:{{ pipeTypeLabel(selectedPoint.pipeType) }}</span>
- <span>计量表型号:TBQZ-{{ selectedPoint.meterModel }}</span>
- <span>安装位置:{{ selectedPoint.address }}</span>
- <span>监测单位:沅陵县燃气安全监测中心</span>
- </div>
- </div>
- <!-- 读数卡片 -->
- <div class="gauge-cards">
- <div class="gauge-card" :class="selectedPoint.status">
- <div class="gauge-label">瞬时流量</div>
- <div class="gauge-value">{{ selectedPoint.latestFlow }} <span class="unit">Nm³/h</span></div>
- <div class="gauge-sub">采集时间 {{ selectedPoint.latestTime }}</div>
- </div>
- <div class="gauge-card">
- <div class="gauge-label">今日累计</div>
- <div class="gauge-value high">{{ selectedPoint.todayTotal }} <span class="unit">Nm³</span></div>
- <div class="gauge-sub">当日0时起累计</div>
- </div>
- <div class="gauge-card">
- <div class="gauge-label">昨日累计</div>
- <div class="gauge-value low">{{ selectedPoint.yesterdayTotal }} <span class="unit">Nm³</span></div>
- <div class="gauge-sub">较昨日 {{ selectedPoint.dodChange }}</div>
- </div>
- <div class="gauge-card">
- <div class="gauge-label">运行状态</div>
- <div class="gauge-status" :class="selectedPoint.status">{{ statusLabel(selectedPoint.status) }}</div>
- <div class="gauge-sub">量程 0~{{ selectedPoint.maxFlow }} Nm³/h</div>
- </div>
- </div>
- <!-- 流量趋势图 -->
- <div class="chart-card">
- <div class="chart-header">
- <span>流量趋势(近10天)</span>
- <div class="chart-legend">
- <span class="leg-item"><i class="leg-dot flow"></i> 瞬时流量</span>
- <span class="leg-item"><i class="leg-line max"></i> 上限</span>
- </div>
- </div>
- <div ref="chartRef" class="chart-container"></div>
- </div>
- <!-- 历史数据表格 -->
- <div class="table-card">
- <div class="table-header">
- <span>历史监测记录</span>
- </div>
- <el-table :data="pagedHistory" size="small">
- <el-table-column prop="time" label="采集时间" width="160" />
- <el-table-column prop="flow" label="瞬时流量(Nm³/h)" width="140">
- <template #default="{ row }">
- <span :style="{ color: row.flow > selectedPoint.maxFlow ? '#f56c6c' : '#303133', fontWeight: row.flow > selectedPoint.maxFlow ? '600' : '400' }">
- {{ row.flow }}
- </span>
- </template>
- </el-table-column>
- <el-table-column prop="dailyTotal" label="日累计(Nm³)" width="130" />
- <el-table-column prop="temperature" label="温度(°C)" width="100" />
- <el-table-column prop="pressure" label="压力(kPa)" width="110" />
- <el-table-column prop="status" label="状态" width="100">
- <template #default="{ row }">
- <el-tag :type="row.flow > selectedPoint.maxFlow ? 'danger' : 'success'" size="small">
- {{ row.flow > selectedPoint.maxFlow ? '超限' : '正常' }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="collector" label="采集设备" min-width="140" />
- <el-table-column prop="remark" label="备注" min-width="120" show-overflow-tooltip />
- </el-table>
- <div class="history-pagination">
- <pagination
- v-model:page="historyPage"
- v-model:limit="historyPageSize"
- :total="historyData.length"
- :auto-scroll="false"
- layout="total, prev, pager, next"
- />
- </div>
- </div>
- </template>
- <div v-else class="empty-detail">
- <el-empty description="请从左侧选择流量监测点查看详情" :image-size="100" />
- </div>
- </div>
- </div>
- </div>
- </template>
- <script setup>
- import { ref, computed, onMounted, onBeforeUnmount, nextTick } from 'vue'
- import { TrendCharts, Search, Refresh } from '@element-plus/icons-vue'
- import * as echarts from 'echarts'
- const searchKeyword = ref('')
- const pipeType = ref('')
- const statusFilter = ref('')
- const selectedPoint = ref(null)
- const historyPage = ref(1)
- const historyPageSize = ref(10)
- const chartRef = ref(null)
- let chartInstance = null
- // ==================== 沅陵县管线流量测点数据 ====================
- const mockPoints = ref([
- {
- id: 1, name: '辰州北路流量计1#', pipelineName: '辰州北路高压段', pipeType: 'high',
- address: '沅陵县太常片区辰州北路门站出口计量间', lng: 110.3938, lat: 28.4695,
- meterModel: 'IIIC-150', latestFlow: 1850, maxFlow: 2500, status: 'normal', latestTime: '2026-06-13 15:32:18',
- todayTotal: '36,420', yesterdayTotal: '38,150', dodChange: '-4.5%'
- },
- {
- id: 2, name: '辰州中路流量计2#', pipelineName: '辰州中路高压段', pipeType: 'high',
- address: '沅陵县太常片区辰州中路调压站进口侧', lng: 110.3935, lat: 28.4665,
- meterModel: 'IIIC-150', latestFlow: 2620, maxFlow: 2500, status: 'danger', latestTime: '2026-06-13 15:31:45',
- todayTotal: '48,920', yesterdayTotal: '42,300', dodChange: '+15.6%'
- },
- {
- id: 3, name: '辰州南路流量计3#', pipelineName: '辰州南路高压段', pipeType: 'high',
- address: '沅陵县太常片区辰州南路滨江路口计量柜', lng: 110.3934, lat: 28.4575,
- meterModel: 'IIIC-100', latestFlow: 1120, maxFlow: 2000, status: 'normal', latestTime: '2026-06-13 15:32:05',
- todayTotal: '22,180', yesterdayTotal: '23,050', dodChange: '-3.8%'
- },
- {
- id: 4, name: '古城北路流量计1#', pipelineName: '古城北路中压段', pipeType: 'medium',
- address: '沅陵县太常片区古城北路与天宁路交叉口', lng: 110.3977, lat: 28.4741,
- meterModel: 'IIIC-80', latestFlow: 580, maxFlow: 800, status: 'normal', latestTime: '2026-06-13 15:30:22',
- todayTotal: '11,350', yesterdayTotal: '12,100', dodChange: '-6.2%'
- },
- {
- id: 5, name: '古城中路流量计2#', pipelineName: '古城中路中压段', pipeType: 'medium',
- address: '沅陵县太常片区古城中路太常安置区段', lng: 110.3970, lat: 28.4669,
- meterModel: 'IIIC-80', latestFlow: 820, maxFlow: 800, status: 'danger', latestTime: '2026-06-13 15:29:58',
- todayTotal: '16,840', yesterdayTotal: '13,500', dodChange: '+24.7%'
- },
- {
- id: 6, name: '古城南路流量计3#', pipelineName: '古城南路中压段', pipeType: 'medium',
- address: '沅陵县太常片区古城南路近建设路口', lng: 110.3963, lat: 28.4598,
- meterModel: 'IIIC-80', latestFlow: 420, maxFlow: 800, status: 'normal', latestTime: '2026-06-13 15:31:30',
- todayTotal: '8,920', yesterdayTotal: '9,450', dodChange: '-5.6%'
- },
- {
- id: 7, name: '迎宾西路流量计1#', pipelineName: '迎宾西路中压段', pipeType: 'medium',
- address: '沅陵县太常片区迎宾西路县政府对面', lng: 110.3870, lat: 28.4642,
- meterModel: 'IIIC-65', latestFlow: 310, maxFlow: 500, status: 'normal', latestTime: '2026-06-13 15:28:15',
- todayTotal: '6,180', yesterdayTotal: '6,540', dodChange: '-5.5%'
- },
- {
- id: 8, name: '迎宾东路流量计2#', pipelineName: '迎宾东路中压段', pipeType: 'medium',
- address: '沅陵县太常片区迎宾东路商贸城北侧', lng: 110.4054, lat: 28.4630,
- meterModel: 'IIIC-65', latestFlow: 280, maxFlow: 500, status: 'normal', latestTime: '2026-06-13 15:30:50',
- todayTotal: '5,650', yesterdayTotal: '5,980', dodChange: '-5.5%'
- },
- {
- id: 9, name: '建设路流量计1#', pipelineName: '建设路中压段', pipeType: 'medium',
- address: '沅陵县太常片区建设路县政府南侧计量站', lng: 110.3937, lat: 28.4582,
- meterModel: 'IIIC-80', latestFlow: 0, maxFlow: 800, status: 'offline', latestTime: '2026-06-13 08:15:00',
- todayTotal: '--', yesterdayTotal: '14,200', dodChange: '--'
- },
- {
- id: 10, name: '建设西路流量计2#', pipelineName: '建设西路中压段', pipeType: 'medium',
- address: '沅陵县太常片区建设西路工业园计量站', lng: 110.3840, lat: 28.4588,
- meterModel: 'IIIC-100', latestFlow: 680, maxFlow: 1000, status: 'normal', latestTime: '2026-06-13 15:29:30',
- todayTotal: '13,520', yesterdayTotal: '14,080', dodChange: '-4.0%'
- },
- {
- id: 11, name: '滨江路流量计1#', pipelineName: '滨江路段低压段', pipeType: 'low',
- address: '沅陵县太常片区滨江西路住宅区调压柜后', lng: 110.3820, lat: 28.4568,
- meterModel: 'IIIC-50', latestFlow: 95, maxFlow: 200, status: 'normal', latestTime: '2026-06-13 15:31:10',
- todayTotal: '1,850', yesterdayTotal: '1,920', dodChange: '-3.6%'
- },
- {
- id: 12, name: '滨江东路流量计2#', pipelineName: '滨江东路低压段', pipeType: 'low',
- address: '沅陵县太常片区滨江东路商业街调压柜后', lng: 110.4066, lat: 28.4557,
- meterModel: 'IIIC-50', latestFlow: 210, maxFlow: 200, status: 'danger', latestTime: '2026-06-13 15:30:40',
- todayTotal: '4,320', yesterdayTotal: '3,650', dodChange: '+18.4%'
- },
- {
- id: 13, name: '天宁西路流量计1#', pipelineName: '天宁西路低压段', pipeType: 'low',
- address: '沅陵县太常片区天宁西路太常卫生院旁', lng: 110.3850, lat: 28.4703,
- meterModel: 'IIIC-50', latestFlow: 75, maxFlow: 200, status: 'normal', latestTime: '2026-06-13 15:32:00',
- todayTotal: '1,520', yesterdayTotal: '1,650', dodChange: '-7.9%'
- },
- {
- id: 14, name: '天宁东路流量计2#', pipelineName: '天宁东路低压段', pipeType: 'low',
- address: '沅陵县太常片区天宁东路末端近滨江路', lng: 110.4088, lat: 28.4690,
- meterModel: 'IIIC-50', latestFlow: 88, maxFlow: 200, status: 'normal', latestTime: '2026-06-13 15:31:55',
- todayTotal: '1,760', yesterdayTotal: '1,840', dodChange: '-4.3%'
- },
- {
- id: 15, name: '建设东路流量计3#', pipelineName: '建设东路高压段', pipeType: 'high',
- address: '沅陵县太常片区建设东路工业园入口处', lng: 110.4050, lat: 28.4577,
- meterModel: 'IIIC-150', latestFlow: 2380, maxFlow: 2500, status: 'warning', latestTime: '2026-06-13 15:31:25',
- todayTotal: '42,150', yesterdayTotal: '38,600', dodChange: '+9.2%'
- }
- ])
- const normalCount = computed(() => mockPoints.value.filter(p => p.status === 'normal').length)
- const warningCount = computed(() => mockPoints.value.filter(p => p.status === 'warning').length)
- const dangerCount = computed(() => mockPoints.value.filter(p => p.status === 'danger').length)
- const offlineCount = computed(() => mockPoints.value.filter(p => p.status === 'offline').length)
- const filteredPoints = computed(() => {
- let list = mockPoints.value
- if (searchKeyword.value) {
- const kw = searchKeyword.value.toLowerCase()
- list = list.filter(p => p.name.toLowerCase().includes(kw) || p.pipelineName.toLowerCase().includes(kw))
- }
- if (pipeType.value) list = list.filter(p => p.pipeType === pipeType.value)
- if (statusFilter.value) list = list.filter(p => p.status === statusFilter.value)
- return list
- })
- function statusTagType(status) {
- const map = { normal: 'success', warning: 'warning', danger: 'danger', offline: 'info' }
- return map[status] || 'info'
- }
- function statusLabel(status) {
- const map = { normal: '正常', warning: '预警', danger: '告警', offline: '离线' }
- return map[status] || '未知'
- }
- function pipeTypeLabel(type) {
- const map = { high: '高压', medium: '中压', low: '低压' }
- return map[type] || type
- }
- // ==================== 近10天静态流量数据(每天4时段: 6/12/18/23时, 模拟用气高峰) ====================
- function generateHistory(point) {
- const baseFlow = point.latestFlow > 0 ? point.latestFlow : Math.round(point.maxFlow * 0.5)
- // 用气高峰因子: 早6点、午12点、晚18点为高峰,23点为低谷
- const hourFactors = { 6: 1.25, 12: 1.45, 18: 1.35, 23: 0.65 }
- // 每点预设的日波动序列(10天)
- const daySeeds = [
- { 6: 0, 12: 5, 18: 3, 23: -2 }, { 6: -2, 12: 2, 18: 0, 23: -4 },
- { 6: 3, 12: 8, 18: 5, 23: 0 }, { 6: -1, 12: 4, 18: 2, 23: -3 },
- { 6: 2, 12: 6, 18: 4, 23: 1 }, { 6: -3, 12: 1, 18: -1, 23: -5 },
- { 6: 1, 12: 7, 18: 6, 23: -1 }, { 6: 0, 12: 3, 18: 1, 23: -2 },
- { 6: -2, 12: 5, 18: 3, 23: -3 }, { 6: 4, 12: 9, 18: 6, 23: 0 }
- ]
- const data = []
- const now = new Date()
- let cumulative = 0
- const hours = [6, 12, 18, 23]
- for (let d = 9; d >= 0; d--) {
- const dayIdx = 9 - d
- for (const h of hours) {
- const date = new Date(now)
- date.setDate(date.getDate() - d)
- date.setHours(h, 0, 0)
- const factor = hourFactors[h] || 1
- const seed = (daySeeds[dayIdx] && daySeeds[dayIdx][h]) || 0
- const variation = baseFlow * (factor - 1) + seed * (point.maxFlow / 50)
- const flow = Math.round((baseFlow + variation) * 10) / 10
- const dailyCumulative = Math.round(Math.abs(flow) * 6)
- cumulative += dailyCumulative
- const temp = (24 + (h < 8 ? 1 : h < 14 ? 3 : h < 20 ? 4 : 0)).toFixed(1)
- const pressure = point.pipeType === 'high' ? (380 + h * 0.5).toFixed(1) :
- point.pipeType === 'medium' ? (190 + h * 0.3).toFixed(1) :
- (110 + h * 0.2).toFixed(1)
- data.push({
- time: date.toISOString().replace('T', ' ').substring(0, 19),
- flow,
- dailyTotal: Math.round(cumulative).toLocaleString(),
- temperature: temp,
- pressure,
- collector: `YLLL-${String(point.id).padStart(3, '0')}`,
- remark: flow > point.maxFlow ? '流量超量程上限' : ''
- })
- }
- }
- return data
- }
- const historyData = ref([])
- const pagedHistory = computed(() => {
- const start = (historyPage.value - 1) * historyPageSize.value
- return historyData.value.slice(start, start + historyPageSize.value)
- })
- // ==================== 选择测点 ====================
- function selectPoint(point) {
- selectedPoint.value = point
- historyPage.value = 1
- historyData.value = generateHistory(point)
- nextTick(() => renderChart())
- }
- // ==================== ECharts趋势图 ====================
- function renderChart() {
- if (!chartRef.value || !selectedPoint.value) return
- if (!chartInstance) {
- chartInstance = echarts.init(chartRef.value)
- }
- const point = selectedPoint.value
- const times = historyData.value.map(d => d.time.substring(5, 16).replace(' ', '\n'))
- const flows = historyData.value.map(d => d.flow)
- chartInstance.setOption({
- tooltip: {
- trigger: 'axis',
- formatter: params => {
- const p = params[0]
- const val = p.value
- const status = val > point.maxFlow ? '⚠ 超量程上限' : '✓ 正常'
- return `${p.axisValue.replace('\n', ' ')}<br/>瞬时流量: <b>${val} Nm³/h</b> ${status}`
- }
- },
- grid: { left: '3%', right: '5%', top: 20, bottom: 30, containLabel: true },
- xAxis: {
- type: 'category', data: times, boundaryGap: false,
- axisLine: { lineStyle: { color: '#ccc' } },
- axisTick: { show: false },
- axisLabel: { fontSize: 10, interval: 3 }
- },
- yAxis: {
- type: 'value', name: 'Nm³/h',
- axisLine: { show: false }, axisTick: { show: false },
- splitLine: { lineStyle: { color: '#f0f0f0' } },
- min: 0,
- max: Math.max(point.maxFlow + 300, ...flows) + 100
- },
- series: [
- {
- name: '瞬时流量', type: 'line', data: flows,
- smooth: true, symbol: 'circle', symbolSize: 4,
- lineStyle: { width: 2, color: '#52c41a' },
- itemStyle: { color: '#52c41a' },
- areaStyle: {
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: 'rgba(82,196,26,0.25)' },
- { offset: 1, color: 'rgba(82,196,26,0.02)' }
- ])
- },
- markLine: {
- silent: true,
- symbol: 'none',
- label: { fontSize: 11, formatter: '{b} {c} Nm³/h' },
- data: [
- { name: '量程上限', yAxis: point.maxFlow, lineStyle: { color: '#f56c6c', type: 'dashed', width: 1.5 } }
- ]
- }
- }
- ]
- })
- }
- function refreshData() {
- historyData.value = selectedPoint.value ? generateHistory(selectedPoint.value) : []
- nextTick(() => renderChart())
- }
- onMounted(() => {
- window.addEventListener('resize', () => { try { chartInstance?.resize() } catch {} })
- // 默认选中第一条数据
- if (mockPoints.value.length > 0) {
- selectPoint(mockPoints.value[0])
- }
- })
- onBeforeUnmount(() => {
- try { chartInstance?.dispose() } catch {}
- chartInstance = null
- })
- </script>
- <style scoped>
- .flow-monitor {
- background: #f0f2f6;
- height: 100vh;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- }
- .page-header {
- background: #fff;
- padding: 12px 24px;
- display: flex;
- justify-content: space-between;
- align-items: center;
- border-bottom: 1px solid #e9ecef;
- flex-shrink: 0;
- }
- .header-left { display: flex; align-items: center; gap: 20px; }
- .header-left .title { font-size: 1.25rem; font-weight: 600; color: #1f2d3d; }
- .stats-badge { display: flex; gap: 20px; }
- .stat-item { font-size: 13px; display: flex; align-items: center; gap: 6px; }
- .stat-item .dot { width: 8px; height: 8px; border-radius: 50%; }
- .stat-item.normal { color: #67c23a; } .stat-item.normal .dot { background: #67c23a; }
- .stat-item.warning { color: #e6a23c; } .stat-item.warning .dot { background: #e6a23c; }
- .stat-item.danger { color: #f56c6c; } .stat-item.danger .dot { background: #f56c6c; }
- .stat-item.offline { color: #909399; } .stat-item.offline .dot { background: #909399; }
- .header-right { display: flex; gap: 12px; }
- .main-layout {
- display: flex;
- flex: 1;
- overflow: hidden;
- padding: 12px;
- gap: 12px;
- }
- /* 左侧测点列表 */
- .point-list-panel {
- width: 370px;
- background: #fff;
- border-radius: 12px;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- flex-shrink: 0;
- }
- .list-header {
- padding: 14px 16px;
- border-bottom: 1px solid #e9ecef;
- display: flex;
- justify-content: space-between;
- align-items: center;
- font-weight: 600;
- }
- .total-num { font-size: 12px; color: #909399; font-weight: 400; }
- .filter-row { padding: 6px 16px; }
- .point-list { flex: 1; overflow-y: auto; padding: 4px 8px 8px; }
- .point-item {
- padding: 12px;
- margin-bottom: 6px;
- border-radius: 12px;
- background: #fafbfc;
- cursor: pointer;
- transition: all 0.2s;
- border: 1px solid #e9ecef;
- }
- .point-item:hover { background: #f0f5ff; }
- .point-item.active { background: #ecf5ff; border-color: #409eff; }
- .point-item.danger { border-left: 3px solid #f56c6c; }
- .point-item.warning { border-left: 3px solid #e6a23c; }
- .point-item.offline { opacity: 0.65; }
- .point-header { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
- .point-name { font-weight: 600; font-size: 14px; flex: 1; }
- .point-pipeline { font-size: 12px; color: #909399; margin-bottom: 6px; }
- .point-value { display: flex; align-items: baseline; gap: 6px; margin-bottom: 4px; }
- .point-value .val { font-size: 24px; font-weight: 700; color: #1f2d3d; }
- .point-value .unit { font-size: 12px; color: #909399; }
- .point-value .threshold { font-size: 11px; color: #c0c4cc; margin-left: auto; }
- .point-sub { font-size: 12px; color: #606266; margin-bottom: 2px; }
- .point-sub b { color: #1f2d3d; }
- .point-addr { font-size: 11px; color: #909399; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
- .point-time { font-size: 11px; color: #c0c4cc; margin-top: 2px; }
- /* 右侧详情面板 */
- .detail-panel {
- flex: 1;
- background: #fff;
- border-radius: 12px;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- }
- .empty-detail {
- flex: 1;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .detail-header {
- padding: 16px 20px;
- border-bottom: 1px solid #e9ecef;
- }
- .detail-title { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
- .detail-title .point-name { font-size: 18px; font-weight: 600; }
- .detail-meta { display: flex; flex-wrap: wrap; gap: 0 24px; font-size: 13px; color: #909399; }
- /* 读数卡片 */
- .gauge-cards {
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- gap: 16px;
- padding: 16px 20px;
- }
- .gauge-card {
- background: #f8f9fc;
- border-radius: 12px;
- padding: 16px;
- text-align: center;
- }
- .gauge-card.danger { background: #fef0f0; }
- .gauge-card.warning { background: #fdf6ec; }
- .gauge-label { font-size: 12px; color: #909399; margin-bottom: 8px; }
- .gauge-value { font-size: 28px; font-weight: 700; color: #1f2d3d; }
- .gauge-value .unit { font-size: 13px; font-weight: 400; color: #909399; }
- .gauge-value.high { color: #1890ff; }
- .gauge-value.low { color: #52c41a; }
- .gauge-status { font-size: 20px; font-weight: 700; }
- .gauge-status.normal { color: #67c23a; }
- .gauge-status.warning { color: #e6a23c; }
- .gauge-status.danger { color: #f56c6c; }
- .gauge-status.offline { color: #909399; }
- .gauge-sub { font-size: 11px; color: #c0c4cc; margin-top: 6px; }
- /* 趋势图 */
- .chart-card {
- margin: 0 20px 16px;
- padding: 16px;
- background: #fafbfc;
- border-radius: 12px;
- }
- .chart-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 12px;
- font-weight: 600;
- }
- .chart-legend { display: flex; gap: 16px; font-size: 12px; font-weight: 400; }
- .leg-item { display: flex; align-items: center; gap: 4px; }
- .leg-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }
- .leg-dot.flow { background: #52c41a; }
- .leg-line { width: 20px; height: 2px; display: inline-block; }
- .leg-line.max { background: repeating-linear-gradient(90deg, #f56c6c 0, #f56c6c 4px, transparent 4px, transparent 7px); }
- .chart-container { width: 100%; height: 300px; }
- /* 历史表格 */
- .table-card {
- margin: 0 20px 16px;
- padding: 16px;
- background: #fafbfc;
- border-radius: 12px;
- flex: 1;
- overflow: auto;
- }
- .table-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 12px;
- font-weight: 600;
- }
- /* 历史记录分页:沿用若依分页组件,去掉其自带的底色与上下留白 */
- .history-pagination :deep(.pagination-container) {
- padding: 12px 0 0;
- background: transparent;
- }
- </style>
|