From a36ebcb8f190ec8701223440a9bf12ae2954f25c Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期三, 13 八月 2025 16:11:46 +0800
Subject: [PATCH] 设备监控页面添加
---
src/views/equipmentManagement/iotMonitor/index.vue | 317 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/router/index.js | 13 ++
2 files changed, 330 insertions(+), 0 deletions(-)
diff --git a/src/router/index.js b/src/router/index.js
index 10adf72..4b4f154 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -72,6 +72,19 @@
]
},
{
+ path: '/equipment',
+ component: Layout,
+ redirect: '/equipment/iot-monitor',
+ children: [
+ {
+ path: 'iot-monitor',
+ component: () => import('@/views/equipmentManagement/iotMonitor/index.vue'),
+ name: 'IoTMonitor',
+ meta: { title: 'IoT鐩戞帶', icon: 'monitor', noCache: true }
+ }
+ ]
+ },
+ {
path: '/main/MobileChat',
component: Layout,
redirect: '',
diff --git a/src/views/equipmentManagement/iotMonitor/index.vue b/src/views/equipmentManagement/iotMonitor/index.vue
new file mode 100644
index 0000000..84534e1
--- /dev/null
+++ b/src/views/equipmentManagement/iotMonitor/index.vue
@@ -0,0 +1,317 @@
+<template>
+ <div class="app-container iot-monitor">
+ <div class="header">
+ <div class="title">瀹炴椂宸ュ喌鐩戞帶锛圛oT锛�</div>
+ <div class="actions">
+ <el-button type="primary" @click="toggleCollecting">{{ collecting ? '鏆傚仠閲囬泦' : '鍚姩閲囬泦' }}</el-button>
+ <el-button @click="resetAll">閲嶇疆</el-button>
+ <span class="ts">涓婃鏇存柊鏃堕棿锛歿{ lastUpdatedDisplay }}</span>
+ </div>
+ </div>
+
+ <el-alert
+ title="杈圭紭棰勮瑙勫垯锛氳酱鎵跨(鎹�-鎸姩鍊煎亸绂诲熀绾柯�5%瑙﹀彂鍛婅锛涙俯搴�/鍘嬪姏瓒婄晫瑙﹀彂鎻愰啋"
+ type="info"
+ :closable="false"
+ show-icon
+ class="rule-alert"
+ />
+
+ <el-row :gutter="16">
+ <el-col v-for="dev in devices" :key="dev.id" :span="12">
+ <el-card :class="['device-card', dev.hasAlert ? 'is-alert' : '']">
+ <template #header>
+ <div class="card-header">
+ <div class="card-title">
+ <span class="device-name">{{ dev.name }}</span>
+ <el-tag :type="dev.hasAlert ? 'danger' : 'success'" size="small">{{ dev.hasAlert ? '鍛婅' : '姝e父' }}</el-tag>
+ </div>
+ <div class="meta">绫诲瀷锛歿{ dev.type }}锝滃熀绾挎尟鍔細{{ dev.baseline.vibration.toFixed(2) }} mm/s</div>
+ </div>
+ </template>
+
+ <div class="metrics">
+ <div class="metric" :class="{ 'metric-alert': dev.alerts.vibration }">
+ <div class="metric-head">
+ <span>鎸姩(mm/s)</span>
+ <el-tag :type="dev.alerts.vibration ? 'danger' : 'info'" size="small">{{ dev.alerts.vibration ? '卤5%瓒婄晫' : '鍩虹嚎卤5%' }}</el-tag>
+ </div>
+ <div class="metric-value">{{ currentValue(dev.series.vibration).toFixed(2) }}</div>
+ <Echarts
+ :xAxis="[{ type: 'category', data: xAxisLabels }]"
+ :yAxis="[{ type: 'value', name: 'mm/s' }]"
+ :series="[{ type: 'line', smooth: true, showSymbol: false, data: dev.series.vibration }]"
+ :tooltip="{ trigger: 'axis' }"
+ :grid="{ left: 40, right: 10, top: 10, bottom: 20 }"
+ :chartStyle="{ height: '160px', width: '100%' }"
+ :lineColors="['#409EFF']"
+ />
+ </div>
+
+ <div class="metric" :class="{ 'metric-alert': dev.alerts.temperature }">
+ <div class="metric-head">
+ <span>娓╁害(掳C)</span>
+ <el-tag :type="dev.alerts.temperature ? 'warning' : 'info'" size="small">{{ dev.alerts.temperature ? '瓒婄晫' : '20~80' }}</el-tag>
+ </div>
+ <div class="metric-value">{{ currentValue(dev.series.temperature).toFixed(1) }}</div>
+ <Echarts
+ :xAxis="[{ type: 'category', data: xAxisLabels }]"
+ :yAxis="[{ type: 'value', name: '掳C' }]"
+ :series="[{ type: 'line', smooth: true, showSymbol: false, data: dev.series.temperature }]"
+ :tooltip="{ trigger: 'axis' }"
+ :grid="{ left: 40, right: 10, top: 10, bottom: 20 }"
+ :chartStyle="{ height: '160px', width: '100%' }"
+ :lineColors="['#E6A23C']"
+ />
+ </div>
+
+ <div class="metric" :class="{ 'metric-alert': dev.alerts.pressure }">
+ <div class="metric-head">
+ <span>鍘嬪姏(MPa)</span>
+ <el-tag :type="dev.alerts.pressure ? 'warning' : 'info'" size="small">{{ dev.alerts.pressure ? '瓒婄晫' : '0.2~1.5' }}</el-tag>
+ </div>
+ <div class="metric-value">{{ currentValue(dev.series.pressure).toFixed(2) }}</div>
+ <Echarts
+ :xAxis="[{ type: 'category', data: xAxisLabels }]"
+ :yAxis="[{ type: 'value', name: 'MPa' }]"
+ :series="[{ type: 'line', smooth: true, showSymbol: false, data: dev.series.pressure }]"
+ :tooltip="{ trigger: 'axis' }"
+ :grid="{ left: 40, right: 10, top: 10, bottom: 20 }"
+ :chartStyle="{ height: '160px', width: '100%' }"
+ :lineColors="['#67C23A']"
+ />
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ </el-row>
+ </div>
+
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, onBeforeUnmount, computed } from 'vue'
+import { ElNotification } from 'element-plus'
+import Echarts from '@/components/Echarts/echarts.vue'
+
+defineOptions({ name: 'IoTMonitor' })
+
+const windowSize = 30
+const collecting = ref(true)
+const lastUpdated = ref(Date.now())
+const lastUpdatedDisplay = computed(() => new Date(lastUpdated.value).toLocaleTimeString())
+
+const xAxisLabels = ref(Array.from({ length: windowSize }, (_, i) => i - (windowSize - 1)).map(n => `${n}s`))
+
+function makeSeries(fill, decimals = 2) {
+ return Array.from({ length: windowSize }, () => Number(fill.toFixed(decimals)))
+}
+
+const devices = reactive([
+ {
+ id: 'water-pump',
+ name: '姘存车',
+ type: '鍥哄畾璁惧',
+ baseline: { vibration: 9 },
+ initial: { temperature: 40, pressure: 0.70 },
+ alerts: { vibration: false, temperature: false, pressure: false },
+ hasAlert: false,
+ series: {
+ vibration: makeSeries(9),
+ temperature: makeSeries(40, 1),
+ pressure: makeSeries(0.7, 2),
+ },
+ },
+ {
+ id: 'fluid-supply-truck',
+ name: '渚涙恫杞�',
+ type: '绉诲姩瑁呭',
+ baseline: { vibration: 7 },
+ initial: { temperature: 30, pressure: 0.60 },
+ alerts: { vibration: false, temperature: false, pressure: false },
+ hasAlert: false,
+ series: {
+ vibration: makeSeries(7),
+ temperature: makeSeries(30, 1),
+ pressure: makeSeries(0.6, 2),
+ },
+ },
+ {
+ id: 'fracturing-truck',
+ name: '鍘嬭杞�',
+ type: '绉诲姩瑁呭',
+ baseline: { vibration: 12 },
+ initial: { temperature: 65, pressure: 1.40 },
+ alerts: { vibration: false, temperature: false, pressure: false },
+ hasAlert: false,
+ series: {
+ vibration: makeSeries(12),
+ temperature: makeSeries(65, 1),
+ pressure: makeSeries(1.4, 2),
+ },
+ },
+ {
+ id: 'oil-tank-truck',
+ name: '娌圭綈杞�',
+ type: '绉诲姩瑁呭',
+ baseline: { vibration: 6 },
+ initial: { temperature: 28, pressure: 0.50 },
+ alerts: { vibration: false, temperature: false, pressure: false },
+ hasAlert: false,
+ series: {
+ vibration: makeSeries(6),
+ temperature: makeSeries(28, 1),
+ pressure: makeSeries(0.5, 2),
+ },
+ },
+])
+
+function currentValue(arr) {
+ return arr[arr.length - 1] ?? 0
+}
+
+function pushWindow(arr, val) {
+ if (arr.length >= windowSize) arr.shift()
+ arr.push(val)
+}
+
+function clamp(val, min, max) { return Math.max(min, Math.min(max, val)) }
+
+function tickDevice(dev) {
+ const vibBase = dev.baseline.vibration
+ // 鎸姩锛氬熀绾柯�2%闅忔満娉㈠姩锛�5%姒傜巼瑙﹀彂8%~12%灏栧嘲妯℃嫙鍛婅
+ const spike = Math.random() < 0.05
+ const vibNoise = vibBase * (spike ? (1 + (Math.random() * 0.08 + 0.04) * (Math.random() < 0.5 ? -1 : 1)) : (1 + (Math.random() - 0.5) * 0.04))
+ const vibVal = Number(vibNoise.toFixed(2))
+ pushWindow(dev.series.vibration, vibVal)
+
+ // 娓╁害锛氱紦鎱㈤殢鏈烘父璧帮紝骞舵坊鍔犲伓鍙戦珮娓╁亸绉�
+ const tPrev = currentValue(dev.series.temperature)
+ const tDrift = tPrev + (Math.random() - 0.5) * 0.8 + (Math.random() < 0.02 ? 6 : 0)
+ const tVal = Number(clamp(tDrift, 15, 95).toFixed(1))
+ pushWindow(dev.series.temperature, tVal)
+
+ // 鍘嬪姏锛氬皬骞呮尝鍔紝鍋跺彂浣庡帇/楂樺帇
+ const pPrev = currentValue(dev.series.pressure)
+ const pDrift = pPrev + (Math.random() - 0.5) * 0.05 + (Math.random() < 0.02 ? (Math.random() < 0.5 ? -0.3 : 0.3) : 0)
+ const pVal = Number(clamp(pDrift, 0.05, 2.0).toFixed(2))
+ pushWindow(dev.series.pressure, pVal)
+
+ // 杈圭紭璁$畻闃堝�煎垽鏂�
+ const vibDelta = Math.abs(vibVal - vibBase) / vibBase
+ const vibAlert = vibDelta > 0.05
+ const tAlert = tVal < 20 || tVal > 80
+ const pAlert = pVal < 0.2 || pVal > 1.5
+
+ const prevHasAlert = dev.hasAlert
+ dev.alerts.vibration = vibAlert
+ dev.alerts.temperature = tAlert
+ dev.alerts.pressure = pAlert
+ dev.hasAlert = vibAlert || tAlert || pAlert
+
+ if (dev.hasAlert && !prevHasAlert) {
+ const reasons = []
+ if (vibAlert) reasons.push(`鎸姩鍋忕卤5% (褰撳墠 ${vibVal} / 鍩虹嚎 ${vibBase})`)
+ if (tAlert) reasons.push(`娓╁害瓒婄晫 (褰撳墠 ${tVal}掳C, 鏈熸湜 20~80掳C) `)
+ if (pAlert) reasons.push(`鍘嬪姏瓒婄晫 (褰撳墠 ${pVal}MPa, 鏈熸湜 0.2~1.5MPa) `)
+ ElNotification({
+ title: `${dev.name} 鍛婅`,
+ message: reasons.join('锛�'),
+ type: vibAlert ? 'error' : 'warning',
+ duration: 5000,
+ })
+ }
+}
+
+let timer = null
+function start() {
+ if (timer) return
+ timer = setInterval(() => {
+ if (!collecting.value) return
+ devices.forEach(tickDevice)
+ lastUpdated.value = Date.now()
+ }, 10000)
+}
+
+function stop() {
+ if (timer) {
+ clearInterval(timer)
+ timer = null
+ }
+}
+
+function toggleCollecting() { collecting.value = !collecting.value }
+
+function resetAll() {
+ devices.forEach(dev => {
+ dev.series.vibration = makeSeries(dev.baseline.vibration)
+ const t0 = dev.initial?.temperature ?? 45
+ const p0 = dev.initial?.pressure ?? 0.8
+ dev.series.temperature = makeSeries(t0, 1)
+ dev.series.pressure = makeSeries(p0, 2)
+ dev.alerts.vibration = false
+ dev.alerts.temperature = false
+ dev.alerts.pressure = false
+ dev.hasAlert = false
+ })
+ lastUpdated.value = Date.now()
+}
+
+onMounted(() => {
+ start()
+})
+
+onBeforeUnmount(() => {
+ stop()
+})
+</script>
+
+<style lang="scss" scoped>
+.iot-monitor {
+ .header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 12px;
+ .title { font-size: 18px; font-weight: 600; }
+ .actions { display: flex; align-items: center; gap: 8px; }
+ .ts { color: #909399; font-size: 12px; }
+ }
+ .rule-alert { margin-bottom: 12px; }
+}
+
+.device-card {
+ margin-bottom: 16px;
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
+ &.is-alert { border-color: #F56C6C; box-shadow: 0 0 0 2px rgba(245,108,108,0.2) inset; }
+ .card-header {
+ display: flex; flex-direction: column; gap: 4px;
+ .card-title { display: flex; align-items: center; gap: 8px; font-weight: 600; }
+ .meta { color: #909399; font-size: 12px; }
+ }
+ .metrics {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 12px;
+ }
+}
+
+.metric {
+ border: 1px solid #ebeef5;
+ border-radius: 6px;
+ padding: 8px 8px 0 8px;
+ &-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; font-size: 13px; color: #606266; }
+ &-value { font-size: 20px; font-weight: 600; margin: 2px 0 6px 0; }
+}
+
+.metric-alert {
+ border-color: #F56C6C;
+ background: #FFF6F6;
+}
+
+@media (min-width: 1200px) {
+ .device-card .metrics { grid-template-columns: 1fr 1fr 1fr; }
+}
+</style>
+
+
--
Gitblit v1.9.3