xiaohuo 8 months ago
commit 839f9af17f
  1. 9
      ALOps_sys_fe/alops-ui/src/api/index/index.js
  2. 603
      ALOps_sys_fe/alops-ui/src/views/index.vue

@ -19,6 +19,15 @@ export function statistic(query) {
}) })
} }
// 楼层统计数据
export function floorStatistic(query) {
return request({
url: '/device/files/floorStatistic',
method: 'get',
params: query
})
}
// 统计预警信息次数 // 统计预警信息次数
export function alertMessageTimes(query) { export function alertMessageTimes(query) {
return request({ return request({

@ -1,235 +1,237 @@
<template> <template class="">
<div id="app" class="dashboard" style="overflow-x: hidden;"> <div class="index-box">
<div id="app" class="dashboard" style="overflow-x: hidden;">
<div class="header"> <div class="header">
<h1>唐山市设备监控中心大屏 - 智能管理版</h1> <h1>唐山市设备监控中心大屏 - 智能管理版</h1>
<div class="time-display">{{ currentTime }}</div> <div class="time-display">{{ currentTime }}</div>
</div> </div>
<div class="main-content"> <div class="main-content">
<!-- 左侧面板 --> <!-- 左侧面板 -->
<div class="left-panel"> <div class="left-panel">
<div class="panel"> <div class="panel">
<div class="panel-title"> <div class="panel-title">
设备状态监控 设备状态监控
<div class="panel-actions"> <div class="panel-actions">
<div class="panel-btn" @click="refreshDeviceStatus">刷新</div> <div class="panel-btn" @click="refreshDeviceStatus">刷新</div>
</div> </div>
</div>
<div class="device-list" style="height: calc(100% - 50px); overflow-y: auto;">
<div v-for="device in deviceStatus || []"
:key="device.id || Math.random()"
class="device-item"
@click="showDeviceDetail(device)">
<div :class="['device-status', `status-${device.status || 'normal'}`]"></div>
<div class="device-name">{{ device.equManuName || device.name || '未知设备' }}</div>
<div class="device-value">{{ device.cpuUtilization || 0 }}%</div>
</div> </div>
</div> <div class="device-list" style="height: calc(100% - 50px); overflow-y: auto;">
</div> <div v-for="device in deviceStatus || []"
<div class="panel"> :key="device.id || Math.random()"
<div class="panel-title"> class="device-item"
楼层设备分布情况 @click="showDeviceDetail(device)">
</div> <div :class="['device-status', `status-${device.status || 'normal'}`]"></div>
<div class="floor-device-container"> <div class="device-name">{{ device.equManuName || device.name || '未知设备' }}</div>
<div class="floor-device-grid"> <div class="device-value">{{ device.cpuUtilization || 0 }}%</div>
<div v-for="floor in floorDeviceDistribution"
:key="floor.level"
class="floor-device-card">
<div class="floor-level">{{ floor.level }}</div>
<div class="floor-count">{{ floor.count }}</div>
</div> </div>
</div> </div>
</div> </div>
</div> <div class="panel">
<div class="panel-title">
</div> 楼层设备分布情况
<!-- 右侧面板 -->
<div class="right-panel">
<div class="panel">
<div class="panel-title">设备类型分布</div>
<div class="device-stats-container">
<div class="device-stat-item">
<div class="device-stat-value">{{ totalDevices }}</div>
<div class="device-stat-label">设备总数</div>
</div> </div>
<div class="device-stat-item"> <div class="floor-device-container">
<div class="device-stat-value">{{ onlineDevices }}</div> <div class="floor-device-grid">
<div class="device-stat-label">在线设备数</div> <div v-for="floor in floorDeviceDistribution"
:key="floor.level"
class="floor-device-card">
<div class="floor-level">{{ floor.floor }}</div>
<div class="floor-count">{{ floor.count }}</div>
</div>
</div>
</div> </div>
</div> </div>
<div class="chart-container" ref="pieChart"></div>
</div>
<div class="panel"> </div>
<div class="panel-title"> <!-- 右侧面板 -->
预警信息统计 <div class="right-panel">
</div>
<div class="warning-stats"> <div class="panel">
<div class="warning-row"> <div class="panel-title">设备信息统计</div>
<div class="stat-item time-item" @click="filterByWarningLevel('annual')"> <div class="device-stats-container">
<div class="stat-value stat-annual">{{ warningStats.annual }}</div> <div class="device-stat-item">
<div class="stat-label">年度预警</div> <div class="device-stat-value">{{ totalDevices }}</div>
<div class="device-stat-label">设备总数</div>
</div> </div>
<div class="stat-item time-item" @click="filterByWarningLevel('monthly')"> <div class="device-stat-item">
<div class="stat-value stat-monthly">{{ warningStats.monthly }}</div> <div class="device-stat-value">{{ onlineDevices }}</div>
<div class="stat-label">月度预警</div> <div class="device-stat-label">在线设备数</div>
</div> </div>
</div> </div>
<div class="warning-row"> <div class="chart-container" ref="pieChart"></div>
<div class="stat-item level-item" @click="filterByWarningLevel('level1')"> </div>
<div class="stat-value stat-level1">{{ warningStats.level1 }}</div>
<div class="stat-label">1级预警</div> <div class="panel">
</div> <div class="panel-title">
<div class="stat-item level-item" @click="filterByWarningLevel('level2')"> 预警信息统计
<div class="stat-value stat-level2">{{ warningStats.level2 }}</div> </div>
<div class="stat-label">2级预警</div> <div class="warning-stats">
<div class="warning-row">
<div class="stat-item time-item" @click="filterByWarningLevel('annual')">
<div class="stat-value stat-annual">{{ warningStats.annual }}</div>
<div class="stat-label">年度预警</div>
</div>
<div class="stat-item time-item" @click="filterByWarningLevel('monthly')">
<div class="stat-value stat-monthly">{{ warningStats.monthly }}</div>
<div class="stat-label">月度预警</div>
</div>
</div> </div>
<div class="stat-item level-item" @click="filterByWarningLevel('level3')"> <div class="warning-row">
<div class="stat-value stat-level3">{{ warningStats.level3 }}</div> <div class="stat-item level-item" @click="filterByWarningLevel('level1')">
<div class="stat-label">3级预警</div> <div class="stat-value stat-level1">{{ warningStats.level1 }}</div>
<div class="stat-label">1级预警</div>
</div>
<div class="stat-item level-item" @click="filterByWarningLevel('level2')">
<div class="stat-value stat-level2">{{ warningStats.level2 }}</div>
<div class="stat-label">2级预警</div>
</div>
<div class="stat-item level-item" @click="filterByWarningLevel('level3')">
<div class="stat-value stat-level3">{{ warningStats.level3 }}</div>
<div class="stat-label">3级预警</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
<!-- 中间面板 - 楼层房间布局展示 --> <!-- 中间面板 - 楼层房间布局展示 -->
<div class="center-panel"> <div class="center-panel">
<div class="panel"> <div class="panel">
<div class="panel-title"> <div class="panel-title">
近期预警信息 近期预警信息
</div>
<el-scrollbar class="warning-scrollbar">
<div v-for="warning in filteredWarnings"
:key="warning.id"
class="warning-item"
@click="showDeviceDetail(warning)">
<div class="warning-header">
<span :class="['warning-type', `level-${warning.level}`]">{{ warning.name }}</span>
</div>
<div class="warning-time">预警时间: {{ warning.time }}</div>
<div class="warning-position">设备位置: {{ warning.location }}</div>
<div class="warning-detail">设备编号 {{ warning.deviceId }} 设备名称{{ warning.deviceName }}{{ warning.description }}</div>
</div> </div>
</el-scrollbar> <el-scrollbar class="warning-scrollbar">
<div v-for="warning in filteredWarnings"
:key="warning.id"
class="warning-item"
@click="showDeviceDetail(warning)">
<div class="warning-header">
<span :class="['warning-type', `level-${warning.displayLevel}`]">{{ warning.name }}</span>
</div>
<div class="warning-time">预警时间: {{ warning.time }}</div>
<div class="warning-position">设备位置: {{ warning.location }}</div>
<div class="warning-detail">预警信息: {{ warning.ruleName }}</div>
</div>
</el-scrollbar>
</div>
</div> </div>
</div>
</div> </div>
<div class="footer"> <div class="footer">
<div>唐山市监控系统 v4.0 - 智能管理版</div> <div>唐山市监控系统 v4.0 - 智能管理版</div>
<div>最后更新: <span class="update-time">{{ updateTime }}</span></div> <div>最后更新: <span class="update-time">{{ updateTime }}</span></div>
<div>监控设备总数: {{ totalDevices }} | 在线: {{ onlineDevices }} </div> <div>监控设备总数: {{ totalDevices }} | 在线: {{ onlineDevices }} </div>
</div> </div>
<!-- 设备详情模态框 --> <!-- 设备详情模态框 -->
<div class="modal-overlay" v-if="showDeviceModal"> <div class="modal-overlay" v-if="showDeviceModal">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h2 class="modal-title">设备详情 - {{ selectedDevice.name }}</h2> <h2 class="modal-title">设备详情 - {{ selectedDevice.name }}</h2>
<button class="modal-close" @click="closeDeviceModal">&times;</button> <button class="modal-close" @click="closeDeviceModal">&times;</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="device-detail-grid"> <div class="device-detail-grid">
<div class="detail-section"> <div class="detail-section">
<h3>基本信息</h3> <h3>基本信息</h3>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">设备名称:</span> <span class="detail-label">设备名称:</span>
<span class="detail-value">{{ selectedDevice.name }}</span> <span class="detail-value">{{ selectedDevice.name }}</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">设备ID:</span> <span class="detail-label">设备ID:</span>
<span class="detail-value">{{ selectedDevice.id }}</span> <span class="detail-value">{{ selectedDevice.id }}</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">所在位置:</span> <span class="detail-label">所在位置:</span>
<span class="detail-value">{{ selectedDevice.location || '未知' }}</span> <span class="detail-value">{{ selectedDevice.location || '未知' }}</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">设备类型:</span> <span class="detail-label">设备类型:</span>
<span class="detail-value">{{ selectedDevice.type || '服务器' }}</span> <span class="detail-value">{{ selectedDevice.type || '服务器' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">设备状态:</span>
<span class="detail-value" :style="{color: selectedDevice.status === '1' ? 'green' : 'red'}">
{{ getStatusText(selectedDevice.status) }}
</span>
</div>
<!-- <div class="detail-item">
<span class="detail-label">当前值:</span>
<span class="detail-value">{{ selectedDevice.value || 'N/A' }}</span>
</div> -->
</div> </div>
<div class="detail-item">
<span class="detail-label">设备状态:</span> <div class="detail-section">
<span class="detail-value" :style="{color: selectedDevice.status === '1' ? 'green' : 'red'}"> <h3>运行状态</h3>
{{ getStatusText(selectedDevice.status) }} <div class="detail-item">
</span> <span class="detail-label">运行时间:</span>
<span class="detail-value">{{ formatDuration(selectedDevice.contWorkPeriod) || '0天' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">CPU使用率:</span>
<span class="detail-value">{{ selectedDevice.cpuUtilization || '0' }}%</span>
</div>
<div class="detail-item">
<span class="detail-label">内存使用率:</span>
<span class="detail-value">{{ selectedDevice.memoryUtilization || '0' }}%</span>
</div>
<div class="detail-item">
<span class="detail-label">风扇转速:</span>
<span class="detail-value">{{ selectedDevice.fanSpeed || '0' }}'RPM/s'</span>
</div>
<div class="detail-item">
<span class="detail-label">温度:</span>
<span class="detail-value">{{ selectedDevice.temperature || '0' }}°C</span>
</div>
<!-- <div class="detail-item">
<span class="detail-label">最后维护:</span>
<span class="detail-value">{{ selectedDevice.lastMaintenance || '2024-09-15' }}</span>
</div> -->
<div class="control-buttons">
<button class="control-btn btn-restart" @click="controlDevice('restart')">重启设备</button>
<button class="control-btn btn-shutdown" @click="controlDevice('shutdown')">关机</button>
<button class="control-btn btn-reset" @click="controlDevice('reset')">重置配置</button>
</div>
</div> </div>
<!-- <div class="detail-item">
<span class="detail-label">当前值:</span>
<span class="detail-value">{{ selectedDevice.value || 'N/A' }}</span>
</div> -->
</div> </div>
<div class="detail-section"> <div class="detail-section">
<h3>运行状态</h3> <h3>历史数据
<div class="detail-item"> <div class="search-controls">
<span class="detail-label">运行时间:</span> <input type="date" class="date-input" v-model="historyStartDate">
<span class="detail-value">{{ formatDuration(selectedDevice.contWorkPeriod) || '0天' }}</span> <span></span>
</div> <input type="date" class="date-input" v-model="historyEndDate">
<div class="detail-item"> <button class="panel-btn" @click="loadHistoryData">查询</button>
<span class="detail-label">CPU使用率:</span> </div>
<span class="detail-value">{{ selectedDevice.cpuUtilization || '0' }}%</span> </h3>
</div> <div class="history-chart" ref="historyChart"></div>
<div class="detail-item">
<span class="detail-label">内存使用率:</span>
<span class="detail-value">{{ selectedDevice.memoryUtilization || '0' }}%</span>
</div>
<div class="detail-item">
<span class="detail-label">风扇转速:</span>
<span class="detail-value">{{ selectedDevice.fanSpeed || '0' }}'RPM/s'</span>
</div>
<div class="detail-item">
<span class="detail-label">温度:</span>
<span class="detail-value">{{ selectedDevice.temperature || '0' }}°C</span>
</div>
<!-- <div class="detail-item">
<span class="detail-label">最后维护:</span>
<span class="detail-value">{{ selectedDevice.lastMaintenance || '2024-09-15' }}</span>
</div> -->
<div class="control-buttons">
<button class="control-btn btn-restart" @click="controlDevice('restart')">重启设备</button>
<button class="control-btn btn-shutdown" @click="controlDevice('shutdown')">关机</button>
<button class="control-btn btn-reset" @click="controlDevice('reset')">重置配置</button>
</div>
</div> </div>
</div> </div>
<div class="detail-section">
<h3>历史数据
<div class="search-controls">
<input type="date" class="date-input" v-model="historyStartDate">
<span></span>
<input type="date" class="date-input" v-model="historyEndDate">
<button class="panel-btn" @click="loadHistoryData">查询</button>
</div>
</h3>
<div class="history-chart" ref="historyChart"></div>
</div>
</div> </div>
</div> </div>
</div>
<!-- 房间设备列表模态框 --> <!-- 房间设备列表模态框 -->
<div class="modal-overlay" v-if="showRoomModal"> <div class="modal-overlay" v-if="showRoomModal">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h2 class="modal-title">房间设备 - {{ selectedRoom.name }}</h2> <h2 class="modal-title">房间设备 - {{ selectedRoom.name }}</h2>
<button class="modal-close" @click="closeRoomModal">&times;</button> <button class="modal-close" @click="closeRoomModal">&times;</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="device-list"> <div class="device-list">
<div v-for="device in getRoomDevices()" <div v-for="device in getRoomDevices()"
:key="device.id" :key="device.id"
class="device-item" class="device-item"
@click="showDeviceDetail(device)"> @click="showDeviceDetail(device)">
<div :class="['device-status', `status-${device.status}`]"></div> <div :class="['device-status', `status-${device.status}`]"></div>
<div class="device-name">{{ device.name }}</div> <div class="device-name">{{ device.name }}</div>
<div class="device-value">{{ device.value || 'N/A' }}</div> <div class="device-value">{{ device.value || 'N/A' }}</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -240,6 +242,7 @@
<script> <script>
import { getInfo, historyQuery } from "@/api/inMonitoring/devicesState" import { getInfo, historyQuery } from "@/api/inMonitoring/devicesState"
import { byEquType, statistic, alertMessageTimes, floorStatistic } from "@/api/index/index"
import "@riophae/vue-treeselect/dist/vue-treeselect.css" import "@riophae/vue-treeselect/dist/vue-treeselect.css"
import "splitpanes/dist/splitpanes.css" import "splitpanes/dist/splitpanes.css"
import * as echarts from 'echarts' import * as echarts from 'echarts'
@ -283,18 +286,7 @@ export default {
], ],
currentFloor: 1, currentFloor: 1,
floorDeviceDistribution: [ floorDeviceDistribution: [
{ level: 1, count: 15 }, { floor: "1层", count: 6 },
{ level: 2, count: 18 },
{ level: 3, count: 22 },
{ level: 4, count: 14 },
{ level: 5, count: 16 },
{ level: 6, count: 13 },
{ level: 7, count: 17 },
{ level: 8, count: 11 },
{ level: 9, count: 12 },
{ level: 10, count: 9 },
{ level: 11, count: 14 },
{ level: 12, count: 16 }
], ],
floors: [ floors: [
{ {
@ -401,7 +393,13 @@ export default {
barChart: null, barChart: null,
pieChart: null, pieChart: null,
historyChart: null, historyChart: null,
highlightedDevice: null highlightedDevice: null,
totalDevices: 0,
onlineDevices: 0,
deviceTypeData: [],
alertStatistics: {},
alertTimes: {},
floorDeviceDistribution: []
}}, }},
computed: { computed: {
// 使deviceStatus // 使deviceStatus
@ -410,10 +408,32 @@ export default {
if (this.warningLevelFilter === 'all') { if (this.warningLevelFilter === 'all') {
return this.deviceWarnings; return this.deviceWarnings;
} }
return this.deviceWarnings.filter(warning =>
(this.warningLevelFilter === 'error' && warning.level === 'error') || //
(this.warningLevelFilter === 'warning' && warning.level === 'warning') switch (this.warningLevelFilter) {
); case 'level1':
// 1level1
return this.deviceWarnings.filter(warning => warning.level === 1 || warning.level === '1');
case 'level2':
// 2level2
return this.deviceWarnings.filter(warning => warning.level === 2 || warning.level === '2');
case 'level3':
// 3level3
return this.deviceWarnings.filter(warning => warning.level === 3 || warning.level === '3' || warning.level === 'error');
case 'annual':
case 'monthly':
//
//
return this.deviceWarnings;
case 'error':
//
return this.deviceWarnings.filter(warning => warning.level > 2 || warning.level === 'error');
case 'warning':
//
return this.deviceWarnings.filter(warning => warning.level <= 2 || warning.level === 'warning');
default:
return this.deviceWarnings;
}
} }
}, },
mounted() { mounted() {
@ -440,6 +460,15 @@ export default {
// //
this.refreshDeviceStatus(); this.refreshDeviceStatus();
//
this.loadDeviceTypeData();
//
this.loadAlertStatistics();
//
this.loadAlertTimes();
//
this.loadFloorDeviceDistribution();
// //
setInterval(() => { setInterval(() => {
this.refreshDeviceStatus(); this.refreshDeviceStatus();
@ -480,7 +509,140 @@ export default {
} }
}, },
loadDeviceTypeData() {
byEquType().then(response => {
const data = response.data || {};
// 线
this.totalDevices = data.totalCount || 0;
this.onlineDevices = data.onlineCount || 0;
// list
const colors = ['#4dabff', '#62f7ff', '#ffa726', '#4CAF50', '#a0aec0', '#ff6b6b', '#95e1d3'];
this.deviceTypeData = (data.list || []).map((item, index) => ({
name: item.equTypeName || '未知类型',
value: item.count || 0,
itemStyle: { color: colors[index % colors.length] }
}));
//
this.initPieChart();
}).catch(error => {
console.error('获取设备类型数据失败:', error);
});
},
//
loadAlertStatistics() {
statistic().then(response => {
this.alertStatistics = response.data || {};
// rowsdeviceWarnings
if (response.rows && Array.isArray(response.rows)) {
this.deviceWarnings = response.rows.map((row, index) => ({
id: row.id || index + 1,
name: row.name || '未命名设备',
level: row.level, // level
displayLevel: row.level > 2 ? 'error' : 'warning', //
time: row.occurTime ? this.formatOccurTime(row.occurTime) : '',
location: row.location || '未知位置',
ruleName: row.ruleName || '-',
message: row.message || '-'
}));
}
}).catch(error => {
console.error('获取预警统计数据失败:', error);
});
},
// occurTime
formatOccurTime(timeString) {
if (!timeString) return '';
const date = new Date(timeString);
if (isNaN(date.getTime())) return timeString;
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
});
},
//
loadAlertTimes() {
alertMessageTimes().then(response => {
this.alertTimes = response.data || {};
// warningStats
if (response.code === 200 && response.data) {
const data = response.data;
//
this.warningStats.annual = data.yearCount || 0;
this.warningStats.monthly = data.monthCount || 0;
// levelCount1-3
if (data.levelCount && Array.isArray(data.levelCount)) {
// 1-30
this.warningStats.level1 = 0;
this.warningStats.level2 = 0;
this.warningStats.level3 = 0;
// level
data.levelCount.forEach(item => {
if (item.level === '1') {
this.warningStats.level1 = item.count || 0;
} else if (item.level === '2') {
this.warningStats.level2 = item.count || 0;
} else if (item.level === '3') {
this.warningStats.level3 = item.count || 0;
}
});
}
}
}).catch(error => {
console.error('获取预警次数数据失败:', error);
});
},
//
loadFloorDeviceDistribution() {
floorStatistic().then(response => {
// 1-12count0使floor
const floorData = [];
for (let i = 1; i <= 12; i++) {
floorData.push({ floor: i + '层', count: 0 });
}
// APIcount
if (response && response.code === 200 && response.data && Array.isArray(response.data)) {
response.data.forEach(item => {
//
const index = floorData.findIndex(f => f.floor === item.floor);
if (index !== -1) {
floorData[index].count = item.count || 0;
}
});
}
this.floorDeviceDistribution = floorData;
}).catch(error => {
console.error('获取楼层设备分布失败:', error);
// 1-12count0
const floorData = [];
for (let i = 1; i <= 12; i++) {
floorData.push({ floor: i + '层', count: 0 });
}
this.floorDeviceDistribution = floorData;
});
},
initPieChart() { initPieChart() {
if (!this.$refs.pieChart) return;
this.pieChart = echarts.init(this.$refs.pieChart); this.pieChart = echarts.init(this.$refs.pieChart);
const option = { const option = {
@ -523,12 +685,8 @@ export default {
labelLine: { labelLine: {
show: false show: false
}, },
data: [ data: this.deviceTypeData.length > 0 ? this.deviceTypeData : [
{ value: 35, name: '服务器', itemStyle: { color: '#4dabff' } }, { value: 0, name: '暂无数据', itemStyle: { color: '#a0aec0' } }
{ value: 25, name: '网络设备', itemStyle: { color: '#62f7ff' } },
{ value: 18, name: '存储设备', itemStyle: { color: '#ffa726' } },
{ value: 15, name: '安全设备', itemStyle: { color: '#4CAF50' } },
{ value: 7, name: '其他设备', itemStyle: { color: '#a0aec0' } }
] ]
} }
] ]
@ -903,10 +1061,11 @@ export default {
font-family: "Microsoft YaHei", sans-serif; font-family: "Microsoft YaHei", sans-serif;
} }
.dashboard { .index-box {
background-color: #0a1633; background-color: #0a1633;
color: #e6e6e6; color: #e6e6e6;
overflow: hidden; overflow: hidden;
padding: 1.5vh 1.5vw;
} }
.dashboard { .dashboard {

Loading…
Cancel
Save