【web】 异常预警对接

dev
Wangxin 9 months ago
parent 55d93dfe66
commit 9dfe27a026
  1. 26
      ALOps_sys_fe/alops-ui/src/api/device/files.js
  2. 2
      ALOps_sys_fe/alops-ui/src/api/inMonitoring/devicesState.js
  3. 2
      ALOps_sys_fe/alops-ui/src/router/index.js
  4. 166
      ALOps_sys_fe/alops-ui/src/views/dashboard/PieChart.vue
  5. 11
      ALOps_sys_fe/alops-ui/src/views/device/deviceInformation/index.vue
  6. 163
      ALOps_sys_fe/alops-ui/src/views/device/files/index.vue
  7. 34
      ALOps_sys_fe/alops-ui/src/views/inMonitoring/devicesState/cardItem.vue
  8. 509
      ALOps_sys_fe/alops-ui/src/views/inMonitoring/devicesState/detail.vue
  9. 10
      ALOps_sys_fe/alops-ui/src/views/inMonitoring/devicesState/index.vue
  10. 2
      ALOps_sys_fe/alops-ui/src/views/inMonitoring/inMonitor/message/index.vue
  11. 96
      ALOps_sys_fe/alops-ui/src/views/inMonitoring/inMonitor/rules/index.vue
  12. 117
      ALOps_sys_fe/alops-ui/src/views/index.vue

@ -19,6 +19,32 @@ export function list(query) {
})
}
// 查询设备类型列表
export function deviceTypeList(query) {
return request({
url: 'device/deviceType/query',
method: 'get',
params: query
})
}
// 查询设备厂商列表
export function deviceManuList(query) {
return request({
url: 'device/deviceManu/query',
method: 'get',
params: query
})
}
// 查询设备供应商列表
export function supplierList(query) {
return request({
url: 'device/supplier/query',
method: 'get',
params: query
})
}
// 查询详细
export function getInfo(id) {
return request({

@ -5,7 +5,7 @@ const api = '/device/files/'
// 查询多台设备列表
export function list(query) {
return request({
url: api + 'list',
url: api + 'monitorById',
method: 'get',
params: query
})

@ -99,7 +99,7 @@ export const dynamicRoutes = [
permissions: ['inMonitoring:devicesState:edit'],
children: [
{
path: 'detail/:userId',
path: 'detail/:id',
component: () => import('@/views/inMonitoring/devicesState/detail'),
name: 'DevicesStateDetail',
meta: { title: '设备详情', activeMenu: '/inMonitoring/devicesState-detail' }

File diff suppressed because one or more lines are too long

@ -17,7 +17,7 @@
</el-form-item>
</el-row>
<el-row v-show="queryParams.ruleType == 0">
<el-row v-if="queryParams.ruleType == 0">
<el-col :span="24">
<el-form-item label="采集频率" prop="frequency">
<el-input-number v-model="queryParams.frequency" controls-position="right" :min="1"></el-input-number>
@ -39,7 +39,7 @@
</el-form-item>
</el-col>
</el-row>
<el-row v-show="queryParams.ruleType == 1">
<el-row v-if="queryParams.ruleType == 1">
<el-col :span="24">
<el-form-item label="采集时间限制">
<el-date-picker
@ -112,12 +112,8 @@ export default {
//
rules: {
userName: [
{ required: true, message: "用户名称不能为空", trigger: "blur" },
{ min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur' }
],
dateRange: [
{ required: true, message: "用户昵称不能为空", trigger: "blur" }
{ required: true, message: "采集时间不能为空", trigger: "blur" }
],
}
}
@ -154,7 +150,6 @@ export default {
})
},
handleDateRangeChange(value) {
console.log("%c 🐁: handleDateRangeChange -> value ", "font-size:16px;background-color:#dbe986;color:black;", value)
this.queryParams.startTime = value[0]
this.queryParams.endTime = value[1]
},

@ -45,7 +45,7 @@
width="60"
type="index"
></el-table-column>
<el-table-column label="设备ID" align="center" key="equNumber" prop="equNumber" />
<el-table-column label="设备编号" align="center" key="equNumber" prop="equNumber" />
<el-table-column label="设备名称" align="center" key="name" prop="name" :show-overflow-tooltip="true" />
<el-table-column label="设备版本" align="center" key="version" prop="version" :show-overflow-tooltip="true" />
<el-table-column label="设备类型" align="center" key="equType" prop="equType" :show-overflow-tooltip="true" />
@ -56,7 +56,7 @@
</template>
</el-table-column>
<el-table-column label="带外管理IP" align="center" key="ip" prop="ip" width="120" />
<el-table-column label="安装位置" align="center" key="location" prop="Location" width="120" />
<el-table-column label="安装位置" align="center" key="location" prop="location" width="120" />
<el-table-column label="安装时间" align="center" prop="installDate" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.installDate) }}</span>
@ -67,11 +67,10 @@
<el-table-column label="设备供应商" align="center" key="supplierName" prop="supplierName" width="120" />
<el-table-column label="设备管理员" align="center" key="inCharge" prop="inCharge" width="120" />
<el-table-column label="预计年限" align="center" key="lifeCycle" prop="lifeCycle" width="120" />
<el-table-column label="服役年限" align="center" key="serviceTime" prop="serviceTime" width="120" />
<el-table-column label="质保时间" align="center" key="warranty" prop="warranty" width="120" />
<el-table-column label="设备图片" align="center" key="image" prop="image" :show-overflow-tooltip="true" >
<el-table-column label="设备图片" align="center" key="equImage" prop="equImage" :show-overflow-tooltip="true" >
<template slot-scope="scope">
<ImagePreview :src="scope.row.image"/>
<ImagePreview :src="scope.row.equImage"/>
</template>
</el-table-column>
<el-table-column label="设备添加人" align="center" key="createBy" prop="createBy" width="120" />
@ -113,15 +112,28 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设备类型" prop="equType">
<el-input v-model="form.equType" placeholder="请输入设备类型" maxlength="30" />
<el-form-item label="设备类型" prop="equTypeId">
<el-select
v-model="form.equTypeId"
filterable
clearable
remote
reserve-keyword
placeholder="请输入"
:remote-method="remoteMethod1"
:loading="loading1">
<el-option
v-for="item in options1"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设备级别" prop="deviceLevel">
<el-select v-model="form.deviceLevel" placeholder="设备级别" clearable style="width: 240px">
<el-option v-for="dict in dict.type.warning_level" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
<el-input v-model="form.deviceLevel" placeholder="请输入设备级别" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
@ -130,8 +142,43 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设备厂商" prop="manufacture">
<el-input v-model="form.manufacture" placeholder="请输入设备厂商" maxlength="30" />
<el-form-item label="设备厂商" prop="equManuId">
<el-select
v-model="form.equManuId"
filterable
clearable
remote
reserve-keyword
placeholder="请输入"
:remote-method="remoteMethod2"
:loading="loading2">
<el-option
v-for="item in options2"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设备供应商" prop="supplierId">
<el-select
v-model="form.supplierId"
filterable
clearable
remote
reserve-keyword
placeholder="请输入"
:remote-method="remoteMethod3"
:loading="loading3">
<el-option
v-for="item in options3"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
@ -149,11 +196,6 @@
<el-input v-model="form.lifeCycle" placeholder="请输入预计年限" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="服役年限" prop="serviceTime">
<el-input v-model="form.serviceTime" placeholder="请输入服役年限" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="质保年限" prop="warranty">
<el-input v-model="form.warranty" placeholder="请输入质保年限" maxlength="30" />
@ -161,8 +203,8 @@
</el-col>
<el-col :span="24">
<el-form-item label="设备图片" prop="image">
<ImageUpload @input="upImage" :value="form.image"/>
<el-form-item label="设备图片" prop="equImage">
<ImageUpload @input="upImage" :value="form.equImage"/>
</el-form-item>
</el-col>
</el-row>
@ -198,7 +240,7 @@
</template>
<script>
import { getDict, list, getInfo, del, add, update } from "@/api/device/files"
import { list, del, add, update, deviceTypeList, deviceManuList, supplierList } from "@/api/device/files"
import { getToken } from "@/utils/auth"
import Treeselect from "@riophae/vue-treeselect"
import "@riophae/vue-treeselect/dist/vue-treeselect.css"
@ -213,6 +255,13 @@ export default {
return {
//
loading: true,
loading1: true,
loading2: true,
loading3: true,
options1: [],
options2: [],
options3: [],
//
ids: [],
//
@ -282,7 +331,7 @@ export default {
version: [
{ required: true, message: "设备版本不能为空", trigger: "blur" },
],
equType: [
equTypeId: [
{ required: true, message: "设备类型不能为空", trigger: "blur" }
],
deviceLevel: [
@ -291,9 +340,12 @@ export default {
ip: [
{ required: true, message: "带外管理IP不能为空", trigger: "blur" }
],
manufacture: [
equManuId: [
{ required: true, message: "设备厂商不能为空", trigger: "blur" },
],
supplierId: [
{ required: true, message: "设备供应商不能为空", trigger: "blur" },
],
location: [
{ required: true, message: "安装位置不能为空", trigger: "blur" }
],
@ -303,13 +355,10 @@ export default {
lifeCycle: [
{ required: true, message: "预计年限不能为空", trigger: "blur" }
],
serviceTime: [
{ required: true, message: "服役年限不能为空", trigger: "blur" },
],
warranty: [
{ required: true, message: "质保年限不能为空", trigger: "blur" }
],
image: [
equImage: [
{ required: true, message: "设备图片不能为空", trigger: "blur" }
],
}
@ -323,6 +372,10 @@ export default {
},
created() {
this.getList()
this.getDeviceTypeList()
this.getDeviceManuList()
this.getSupplierList()
},
methods: {
/** 查询设备列表 */
@ -335,9 +388,63 @@ export default {
}
)
},
getDeviceTypeList(query) {
deviceTypeList({name:query}).then(response => {
this.options1 = response.rows
this.loading1 = false
}
)
},
remoteMethod1(query) {
if (query !== '') {
this.loading1 = true;
setTimeout(() => {
this.loading = false;
this.getDeviceTypeList(query)
}, 200);
} else {
this.options1 = [];
}
},
getDeviceManuList(query) {
deviceManuList({name:query}).then(response => {
this.options2 = response.rows
this.loading2 = false
}
)
},
remoteMethod2(query) {
if (query !== '') {
this.loading2 = true;
setTimeout(() => {
this.loading = false;
this.getDeviceManuList(query)
}, 200);
} else {
this.options2 = [];
}
},
getSupplierList(query) {
supplierList({name:query}).then(response => {
this.options3 = response.rows
this.loading3 = false
}
)
},
remoteMethod3(query) {
if (query !== '') {
this.loading3 = true;
setTimeout(() => {
this.loading = false;
this.getSupplierList(query)
}, 200);
} else {
this.options3 = [];
}
},
upImage(fileList){
console.log(fileList)
this.form.image = fileList
this.form.equImage = fileList
},
//
cancel() {
@ -415,7 +522,7 @@ export default {
/** 删除按钮操作 */
handleDelete(row) {
const Ids = row.id || this.ids
this.$modal.confirm('是否确认删除设备ID为"' + Ids + '"的数据项?').then(function() {
this.$modal.confirm('是否确认删除设备编号为"' + Ids + '"的数据项?').then(function() {
return del(Ids)
}).then(() => {
this.getList()

@ -10,39 +10,41 @@
<i class="fas fa-network-wired"></i>
{{cardData.name}}
</h2>
<span class="status-badge status-online">
<i class="fas fa-circle"></i> 在线
<span class="status-badge " :class="cardData.status ? 'status-online' : 'status-danger'">
<i class="fas fa-circle"></i>
{{ cardData.status ? '正常' : '停用' }}
</span>
</div>
<div class="device-box">
<div class="device-image">
<i class="fas fa-server"></i>
<img v-if="cardData.image" :src="cardData.image" alt="" class="device-icon">
<i v-else class="fas fa-server"></i>
</div>
<div class="device-info">
<div class="info-item">
<span class="info-label">
<i class="fas fa-layer-group"></i> 设备级别
</span>
<span class="info-value">A</span>
<span class="info-value">{{cardData.deviceLevel || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-map-marker-alt"></i> 安装位置
</span>
<span class="info-value">一层</span>
<span class="info-value">{{cardData.location || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-tag"></i> 品牌型号
</span>
<span class="info-value">Cisco</span>
<span class="info-value">{{cardData.manufacture || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-heartbeat"></i> 运行状态
<i class="fas fa-heartbeat"></i> 带外IP
</span>
<span class="info-value">运行正常</span>
<span class="info-value">{{cardData.ip || '暂无'}}</span>
</div>
</div>
</div>
@ -50,28 +52,28 @@
<div class="metrics-grid">
<div class="metric-card">
<div class="metric-value cpu-usage">66%</div>
<div class="metric-value cpu-usage">{{cardData.cpuUtilization||0}}%</div>
<div class="metric-label">CPU占用</div>
<div class="progress-bar">
<div class="progress-fill" style="width: 66%"></div>
<div class="progress-fill" :style="{width: cardData.cpuUtilization||0+'%'}"></div>
</div>
</div>
<div class="metric-card">
<div class="metric-value cpu-usage">66%</div>
<div class="metric-value cpu-usage">{{cardData.contWorkPeriod||0}}%</div>
<div class="metric-label">内存占用</div>
<div class="progress-bar">
<div class="progress-fill" style="width: 66%"></div>
<div class="progress-fill" :style="{width: cardData.contWorkPeriod||0+'%'}"></div>
</div>
</div>
<div class="metric-card">
<div class="metric-value alert-pending">待开发</div>
<div class="metric-value alert-pending">{{cardData.unhandledAlertCount||0}}</div>
<div class="metric-label">待处理预警数</div>
</div>
<div class="metric-card">
<div class="metric-value alert-total">待开发</div>
<div class="metric-value alert-total">{{cardData.handledAlertCount||0}}</div>
<div class="metric-label">预警总数</div>
</div>
</div>
@ -353,6 +355,10 @@ export default {
overflow: hidden;
border: 1px solid rgba(226, 232, 240, 0.8);
}
.device-icon{
max-width: 100%;
height: auto;
}
.device-image i {
font-size: 80px;

@ -7,130 +7,141 @@
<div class="card-header">
<h2 class="card-title">
<i class="fas fa-network-wired"></i>
设备概览
{{details.name}}
</h2>
<span class="status-badge status-online">
<i class="fas fa-circle"></i> 在线
<span class="status-badge " :class="details.status ? 'status-online' : 'status-danger'">
<i class="fas fa-circle"></i>
{{ details.status ? '正常' : '停用' }}
</span>
</div>
<div class="top-section-con">
<div class="device-image">
<i class="fas fa-server"></i>
<img v-if="details.image" :src="details.image" alt="" class="device-icon">
<i v-else class="fas fa-server"></i>
</div>
<!-- 设备基本信息 -->
<div class="device-info">
<div class="info-title">设备基本信息</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-layer-group"></i> 设备级别
<i class="fas fa-heartbeat"></i> 设备编号
</span>
<span class="info-value">A级</span>
<span class="info-value">{{details.id || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-map-marker-alt"></i> 安装位置
<i class="fas fa-layer-group"></i> 固件版本
</span>
<span class="info-value">数据中心-主柜</span>
<span class="info-value">{{details.version || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-tag"></i> 品牌型号
<i class="fas fa-layer-group"></i> 设备级别
</span>
<span class="info-value">{{details.deviceLevel || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-map-marker-alt"></i> 安装位置
</span>
<span class="info-value">Cisco</span>
<span class="info-value">{{details.location || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-heartbeat"></i> 运行状态
<i class="fas fa-tag"></i> 品牌型号
</span>
<span class="info-value">运行正常</span>
<span class="info-value">{{details.manufacture || '暂无'}}</span>
</div>
</div>
<!-- 设备基本信息 -->
<!-- 设备出厂信息 -->
<div class="device-info">
<div class="info-title">设备基本信息</div>
<div class="info-title">设备出厂信息</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-layer-group"></i> 设备级别
<i class="fas fa-layer-group"></i> 生产厂商名称
</span>
<span class="info-value">A级</span>
<span class="info-value">{{details.manufacture || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-map-marker-alt"></i> 安装位置
<i class="fas fa-map-marker-alt"></i> 供货商名称
</span>
<span class="info-value">数据中心-主柜</span>
<span class="info-value">{{details.supplierName || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-tag"></i> 品牌型号
<i class="fas fa-tag"></i> 供货商联系人
</span>
<span class="info-value">Cisco</span>
<span class="info-value">{{ '无字段'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-heartbeat"></i> 运行状态
<i class="fas fa-heartbeat"></i> 供货商联系电话
</span>
<span class="info-value">运行正常</span>
<span class="info-value">{{ '无字段'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-heartbeat"></i> 质保时间
</span>
<span class="info-value">{{details.warranty || '暂无'}}</span>
</div>
</div>
<!-- 设备基本信息 -->
<!-- 设备管理信息 -->
<div class="device-info">
<div class="info-title">设备基本信息</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-layer-group"></i> 设备级别
<i class="fas fa-layer-group"></i> 带外管理IP
</span>
<span class="info-value">A级</span>
<span class="info-value">{{details.ip || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-map-marker-alt"></i> 安装位置
<i class="fas fa-layer-group"></i> 安装位置
</span>
<span class="info-value">数据中心-主柜</span>
<span class="info-value">{{details.location || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-tag"></i> 品牌型号
<i class="fas fa-layer-group"></i> 安装日期
</span>
<span class="info-value">Cisco</span>
<span class="info-value">{{parseTime(details.installDate) || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-heartbeat"></i> 运行状态
<i class="fas fa-layer-group"></i> 管理员
</span>
<span class="info-value">运行正常</span>
<span class="info-value">{{details.inCharge || '暂无'}}</span>
</div>
</div>
<!-- 设备基本信息 -->
<!-- 设备状态信息 -->
<div class="device-info">
<div class="info-title">设备基本信息</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-layer-group"></i> 设备级别
</span>
<span class="info-value">A级</span>
</div>
<div class="info-title">设备状态信息</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-map-marker-alt"></i> 安装位置
<i class="fas fa-layer-group"></i> 设备持续运行时长
</span>
<span class="info-value">数据中心-主柜</span>
<span class="info-value">{{'无字段'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-tag"></i> 品牌型号
<i class="fas fa-layer-group"></i> 待处理预警数
</span>
<span class="info-value">Cisco</span>
<span class="info-value">{{details.unhandledAlertCount || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-heartbeat"></i> 运行状态
<i class="fas fa-layer-group"></i> 预警总数
</span>
<span class="info-value">运行正常</span>
<span class="info-value">{{details.handledAlertCount || '暂无'}}</span>
</div>
</div>
</div>
@ -182,6 +193,7 @@
<script>
import { getInfo, historyQuery } from "@/api/inMonitoring/devicesState"
import * as echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import History from './history.vue'
@ -199,197 +211,230 @@ export default {
{ name: '路由表信息' },
],
tabIndex: 0,
details: {},
historyQueryList: {
minTime: '',
maxTime: '',
pageNum: 1,
pageSize: 1000,
},
}
},
mounted() {
this.initCharts();
},
mounted() {
let routeList = this.$route.params;
this.initCharts();
this.getInfoFun(routeList.id)
this.gethistoryQuery(routeList.id)
},
methods: {
initCharts() {
//
const performanceChart = echarts.init(document.getElementById('performanceChart'));
performanceChart.setOption({
tooltip: {
trigger: 'axis'
},
legend: {
data: ['CPU使用率', '内存使用率'],
textStyle: {
color: '#718096'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '24:00'],
axisLine: {
lineStyle: {
color: '#cbd5e0'
}
},
axisLabel: {
color: '#718096'
}
},
yAxis: {
type: 'value',
axisLine: {
lineStyle: {
color: '#cbd5e0'
}
},
axisLabel: {
color: '#718096',
formatter: '{value}%'
},
splitLine: {
lineStyle: {
color: 'rgba(203, 213, 224, 0.6)'
}
}
},
series: [
{
name: 'CPU使用率',
type: 'line',
smooth: true,
data: [45, 52, 38, 66, 58, 62, 48],
lineStyle: {
color: '#ed8936'
},
itemStyle: {
color: '#ed8936'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0, color: 'rgba(237, 137, 54, 0.3)'
}, {
offset: 1, color: 'rgba(237, 137, 54, 0.1)'
}]
}
}
},
{
name: '内存使用率',
type: 'line',
smooth: true,
data: [30, 45, 35, 50, 42, 38, 32],
lineStyle: {
color: '#4299e1'
},
itemStyle: {
color: '#4299e1'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0, color: 'rgba(66, 153, 225, 0.3)'
}, {
offset: 1, color: 'rgba(66, 153, 225, 0.1)'
}]
}
}
}
]
});
//
const trafficChart = echarts.init(document.getElementById('trafficChart'));
trafficChart.setOption({
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
data: ['入站流量', '出站流量'],
textStyle: {
color: '#718096'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
axisLine: {
lineStyle: {
color: '#cbd5e0'
}
},
axisLabel: {
color: '#718096'
}
},
yAxis: {
type: 'value',
name: 'MB/s',
axisLine: {
lineStyle: {
color: '#cbd5e0'
}
},
axisLabel: {
color: '#718096'
},
splitLine: {
lineStyle: {
color: 'rgba(203, 213, 224, 0.6)'
}
}
},
series: [
{
name: '入站流量',
type: 'bar',
data: [320, 332, 301, 334, 390, 330, 320],
itemStyle: {
color: '#4299e1'
}
},
{
name: '出站流量',
type: 'bar',
data: [220, 182, 191, 234, 290, 330, 310],
itemStyle: {
color: '#ed8936'
}
}
]
});
//
window.addEventListener('resize', function() {
performanceChart.resize();
trafficChart.resize();
});
initCharts() {
//
const performanceChart = echarts.init(document.getElementById('performanceChart'));
performanceChart.setOption({
tooltip: {
trigger: 'axis'
},
legend: {
data: ['CPU使用率', '内存使用率'],
textStyle: {
color: '#718096'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '24:00'],
axisLine: {
lineStyle: {
color: '#cbd5e0'
}
},
axisLabel: {
color: '#718096'
}
},
yAxis: {
type: 'value',
axisLine: {
lineStyle: {
color: '#cbd5e0'
}
},
axisLabel: {
color: '#718096',
formatter: '{value}%'
},
splitLine: {
lineStyle: {
color: 'rgba(203, 213, 224, 0.6)'
}
}
},
series: [
{
name: 'CPU使用率',
type: 'line',
smooth: true,
data: [45, 52, 38, 66, 58, 62, 48],
lineStyle: {
color: '#ed8936'
},
itemStyle: {
color: '#ed8936'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0, color: 'rgba(237, 137, 54, 0.3)'
}, {
offset: 1, color: 'rgba(237, 137, 54, 0.1)'
}]
}
}
},
{
name: '内存使用率',
type: 'line',
smooth: true,
data: [30, 45, 35, 50, 42, 38, 32],
lineStyle: {
color: '#4299e1'
},
itemStyle: {
color: '#4299e1'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0, color: 'rgba(66, 153, 225, 0.3)'
}, {
offset: 1, color: 'rgba(66, 153, 225, 0.1)'
}]
}
}
}
]
});
//
const trafficChart = echarts.init(document.getElementById('trafficChart'));
trafficChart.setOption({
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
data: ['入站流量', '出站流量'],
textStyle: {
color: '#718096'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
axisLine: {
lineStyle: {
color: '#cbd5e0'
}
},
axisLabel: {
color: '#718096'
}
},
yAxis: {
type: 'value',
name: 'MB/s',
axisLine: {
lineStyle: {
color: '#cbd5e0'
}
},
axisLabel: {
color: '#718096'
},
splitLine: {
lineStyle: {
color: 'rgba(203, 213, 224, 0.6)'
}
}
},
series: [
{
name: '入站流量',
type: 'bar',
data: [320, 332, 301, 334, 390, 330, 320],
itemStyle: {
color: '#4299e1'
}
},
{
name: '出站流量',
type: 'bar',
data: [220, 182, 191, 234, 290, 330, 310],
itemStyle: {
color: '#ed8936'
}
}
]
});
//
window.addEventListener('resize', function() {
performanceChart.resize();
trafficChart.resize();
});
},
/** 查询设备列表 */
getInfoFun(id) {
this.loading = true
getInfo(id).then(response => {
this.details = response.data
this.loading = false
}
)
},
/** 设备管理基本信息(获取设备指定时间段内的历史监控信息) */
gethistoryQuery(id) {
this.loading = true
let queryForm = {
id: id,
...this.historyQueryList
}
historyQuery(queryForm).then(response => {
this.details = response.data
this.loading = false
}
)
},
}
}
@ -542,8 +587,8 @@ export default {
}
.device-info {
display: grid;
grid-template-columns: 1fr;
display: flex;
flex-direction:column;
gap: 15px;
}
@ -557,7 +602,7 @@ export default {
.info-item {
display: flex;
flex-direction: column;
padding: 12px;
padding: 8px 12px;
background: rgba(255, 255, 255, 0.7);
border-radius: 12px;
border: 1px solid rgba(226, 232, 240, 0.8);

@ -5,7 +5,7 @@
<!--设备状态监控=-->
<pane size="84">
<el-col>
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<!-- <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="设备类型" prop="status">
<el-select v-model="queryParams.status" placeholder="启用状态" clearable style="width: 240px">
<el-option v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
@ -34,7 +34,7 @@
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-form> -->
<div class="bodyBox" v-loading="loading">
<el-row :gutter="10">
@ -360,12 +360,6 @@ export default {
this.$refs.tree.setCurrentKey(null)
this.handleQuery()
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.userId)
this.single = selection.length != 1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset()

@ -283,7 +283,7 @@ export default {
/** 删除按钮操作 */
handleDelete(row) {
const Ids = row.id || this.ids
this.$modal.confirm('是否确认删除设备ID为"' + Ids + '"的数据项?').then(function() {
this.$modal.confirm('是否确认删除设备编号为"' + Ids + '"的数据项?').then(function() {
return del(Ids)
}).then(() => {
this.getList()

@ -69,7 +69,7 @@
<el-col :span="24">
<el-form-item label="触发条件" prop="compareValue1">
<el-col :span="6">
<el-select v-model="form.paramName" placeholder="请选择" style="width: 100%">
<el-select v-model="form.paramName" placeholder="请选择" style="width: 100%" @change="byTableDataChange">
<el-option v-for="dict in byTableData" :key="dict.columnName" :label="dict.paramName" :value="dict.columnName"></el-option>
</el-select>
</el-col>
@ -128,24 +128,6 @@
</el-dialog>
<!-- 设备导入对话框 -->
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
<el-upload ref="upload" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" :action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<div class="el-upload__tip text-center" slot="tip">
<div class="el-upload__tip" slot="tip">
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的设备档案
</div>
<span>仅允许导入xlsxlsx格式文件</span>
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link>
</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="upload.open = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
@ -212,6 +194,8 @@ export default {
},
//
byTableData: [],
byTableItem: {},
alertWayOptions: [
{
value: 'noemail',
@ -317,7 +301,6 @@ export default {
getByTable() {
this.loading = true
byTable().then(response => {
console.log("%c 🇻🇳: getList -> response ", "font-size:16px;background-color:#c9f364;color:black;", response)
this.byTableData = response.data
this.loading = false
}
@ -344,6 +327,14 @@ export default {
this.queryParams.pageNum = 1
this.getList()
},
byTableDataChange(val) {
this.byTableItem = this.byTableData.find(item => item.columnName == val)
this.form.alertRule.tableName = findData.tableName
},
// byTableDataChange(val) {
// let findData = this.waming_rules.find(item => item.columnName == val)
// this.form.tableName = findData.tableName
// },
/** 重置按钮操作 */
resetQuery() {
this.dateRange = []
@ -364,38 +355,43 @@ export default {
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
this.form = {...row}
this.open = true
this.title = "修改"
this.title = "修改设备"
this.reset()
let compareValueData = this.parseCompareValue(row.compareValue)
this.form = {...row,operator:this.unescapeHtml(row.operator),compareValue1:compareValueData[0],compareValue2:compareValueData[1]}
},
/** 提交按钮 */
submitForm: function() {
this.$refs["form"].validate(valid => {
if (valid) {
let form = {...this.form}
this.form.alertRule = {
let formData = {}
formData.alertRule = {
id: form.id,
ruleName: form.ruleName,
description: form.description,
alertStation: form.alertStation ? form.alertStation : "",
alertWay: form.alertWay,
severity: form.severity,
tableName: this.byTableItem.tableName,
enable:0
}
this.form.alertRuleConditionList = [{
formData.alertRuleConditionList = [{
paramName: form.paramName,
compareValue: form.compareValue2?form.compareValue1+','+form.compareValue2:form.compareValue1,
operator: form.operator
operator: form.operator,
paramType: this.byTableItem.paramType
}]
if (this.form.id != undefined) {
update(this.form).then(response => {
update(formData).then(response => {
this.$modal.msgSuccess("修改成功")
this.open = false
this.getList()
})
} else {
add(this.form).then(response => {
add(formData).then(response => {
this.$modal.msgSuccess("新增成功")
this.open = false
this.getList()
@ -407,7 +403,7 @@ export default {
/** 删除按钮操作 */
handleDelete(row) {
const Ids = row.id || this.ids
this.$modal.confirm('是否确认删除设备ID为"' + Ids + '"的数据项?').then(function() {
this.$modal.confirm('是否确认删除设备编号为"' + Ids + '"的数据项?').then(function() {
return del(Ids)
}).then(() => {
this.getList()
@ -439,7 +435,47 @@ export default {
//
submitFileForm() {
this.$refs.upload.submit()
},
parseCompareValue(value) {
if (!value) return [0, 0];
//
const parts = value.split(/[,;\s]+/);
//
const validNumbers = parts
.map(part => parseInt(part))
.filter(num => !isNaN(num));
if (validNumbers.length === 0) {
return [0, 0];
} else if (validNumbers.length === 1) {
return [validNumbers[0], 0];
} else {
return [validNumbers[0], validNumbers[1]];
}
},
/**
* 将HTML实体转义为普通字符
*
* @param {string} str - 需要转换的字符串
* @returns {string} 转换后的字符串
*/
unescapeHtml(str){
if (typeof str !== 'string') return str;
const htmlEntities = {
'&amp;': '&',
'&lt;': '<',
'&gt;': '>',
'&quot;': '"',
'&#39;': "'",
'&#x2F;': '/',
'&#x60;': '`',
'&#x3D;': '='
};
return str.replace(/&(amp|lt|gt|quot|#39|#x2F|#x60|#x3D);/g, (match, entity) => {
return htmlEntities[match] || match;
});
}
}
}
</script>

@ -19,12 +19,12 @@
<div class="stat-value">{{ alertMessageData.monthCount }}</div>
</div>
<!-- 等级预警 -->
<div class="stat-card" :class="i.count == 0?'normal':i.count>10?'warning':''" v-for="(i,k) in alertMessageData.levelCount" :key="k">
<div class="stat-card" :class="i.count == 0?'normal':i.count>10?'warning':''" v-for="i in alertMessageData.levelCount" :key="i.id">
<div class="stat-label">{{ `等级${i.level}预警` }}</div>
<div class="stat-value">{{ i.count }}</div>
</div>
<!-- 设备预警 -->
<div class="stat-card" :class="i.count == 0?'normal':i.count>10?'warning':''" v-for="(i,k) in alertMessageData.typeCount" :key="k">
<div class="stat-card" :class="i.count == 0?'normal':i.count>10?'warning':''" v-for="i in alertMessageData.typeCount" :key="i.id">
<div class="stat-label">{{ `${i.name}预警` }}</div>
<div class="stat-value">{{ i.count }}</div>
</div>
@ -39,18 +39,7 @@
<h2 class="card-title"><i class="fas fa-microchip"></i> 检测设备信息</h2>
</div>
<ul class="device-list">
<li class="device-item" v-for="device in devices" :key="device.id" :class="device.status">
<div class="device-icon">
<i :class="device.icon"></i>
</div>
<div class="device-info">
<div class="device-name">{{ device.name }}</div>
<div class="device-details">{{ device.description }}</div>
</div>
<span :class="device.status === 'UNHANDLED' ? 'alert-badge' : 'normal-badge'">
{{ device.status === 'UNHANDLED' ? '预警中' : '正常' }}
</span>
</li>
<PieChart height="250px" :chartData="byEquTypeData"/>
</ul>
</div>
</div>
@ -61,6 +50,7 @@
<div class="card-header">
<h2 class="card-title"><i class="fas fa-bell"></i> 设备预警信息</h2>
</div>
<el-empty description="暂无预警" v-if="alerts.length == 0"></el-empty>
<ul class="device-list" v-infinite-scroll="loadMore" :infinite-scroll-disabled="isLoading || noMore" infinite-scroll-distance="50" infinite-scroll-delay="300">
<li class="device-item" v-for="alert in alerts" :key="alert.id" :class="alert.status === 'UNHANDLED' ? 'warning' : 'normal'" >
<div class="device-icon">
@ -93,7 +83,9 @@
<div class="last-update">最后更新: {{ lastUpdate }}</div>
</div>
<div class="table-container">
<table>
<el-empty description="暂无预警" v-if="tableHeaders.length == 0"></el-empty>
<table v-else>
<thead>
<tr>
<th v-for="header in tableHeaders" :key="header">{{ header }}</th>
@ -104,22 +96,22 @@
<td>{{ device.name }}</td>
<td>{{ device.location }}</td>
<td>
<span :class="getCpuStatus(device.cpuUsage)"></span>
{{ device.cpuUsage }}%
<span :class="getCpuStatus(device.cpuUtilization || 0)"></span>
{{ device.cpuUtilization || 0 }}%
</td>
<td>
<span :class="getMemoryStatus(device.memoryUsage)"></span>
{{ device.memoryUsage }}%
<span :class="getMemoryStatus(device.memoryUtilization || 0)"></span>
{{ device.memoryUtilization || 0 }}%
</td>
<td>{{ device.temperature }}°C</td>
<td>{{ device.temperature || 0 }}°C</td>
<td>
<div class="health-bar">
<div class="health-fill" :class="getHealthStatus(device.health)"></div>
<div class="health-fill" :class="getHealthStatus(device.health || 0)"></div>
</div>
{{ device.health }}%
{{ device.health || 0 }}%
</td>
<td>{{ device.uptime }}</td>
<td>{{ device.collectionTime }}</td>
<td>{{ formatDuration(device.contWorkPeriod) }}</td>
<td>{{ parseTime(device.gatherTime) || '--' }}</td>
</tr>
</tbody>
</table>
@ -173,56 +165,21 @@ export default {
alerts: [],
//
alertMessageData: [],
byEquTypeData: [],
tableHeaders: ['设备名称', '设备位置', 'CPU使用率', '内存使用率', '温度', '健康度', '设备运行时长', '采集时间'],
deviceStatus: [
{
id: 1,
name: 'PLC控制系统',
location: '主控室A区',
cpuUsage: 45,
memoryUsage: 62,
temperature: 42,
health: 85,
uptime: '125天8小时',
collectionTime: '2021-10-16 15:20:00'
},
{
id: 2,
name: '数据采集器',
location: '污水处理区',
cpuUsage: 78,
memoryUsage: 85,
temperature: 58,
health: 65,
uptime: '89天12小时',
collectionTime: '2021-10-16 15:22:00'
},
{
id: 3,
name: '监控服务器',
location: '数据中心',
cpuUsage: 32,
memoryUsage: 45,
temperature: 38,
health: 92,
uptime: '210天3小时',
collectionTime: '2021-10-16 15:25:00'
},
{
id: 4,
name: '网络交换机',
location: '通信机房',
cpuUsage: 15,
memoryUsage: 28,
temperature: 35,
health: 95,
uptime: '156天7小时',
collectionTime: '2021-10-16 15:18:00'
}
]
deviceStatus: []
}
},
methods: {
formatDuration(input) {//
if (typeof input !== 'string') return "0天0小时";
const match = input.match(/(\d+)\s*days?,\s*(\d+):/i);
return match ? `${match[1]}${match[2]}小时` : "0天0小时";
},
getCpuStatus(usage) {
if (usage < 50) return 'status-indicator status-good';
if (usage < 80) return 'status-indicator status-warning';
@ -238,25 +195,19 @@ export default {
if (health >= 60) return 'health-warning';
return 'health-critical';
},
getstatistic() {
this.loading = true
statistic().then(response => {
this.alerts = response.rows
this.loading = false
}
)
},
getbyEquType() {
this.loading = true
byEquType().then(response => {
this.loading = false
this.byEquTypeData = response.data
}
)
},
getlistMonitor() {
this.loading = true
listMonitor().then(response => {
listMonitor({pageSize: 100}).then(response => {
this.loading = false
this.deviceStatus = response.rows
}
)
},
@ -269,12 +220,10 @@ export default {
)
},
async loadMore() {
console.log("%c 🎧: loadMore -> this.isLoading || this.noMore ", "font-size:16px;background-color:#64b738;color:white;", this.isLoading , this.noMore)
if (this.isLoading || this.noMore) return;
this.isLoading = true;
try {
const res = await statistic({page:this.page}); // API
const res = await statistic({page:this.page,status:'UNHANDLED'}); // API
if (res.rows.length < 10) { // 10
this.noMore = true;
} else {
@ -286,10 +235,9 @@ export default {
} finally {
this.isLoading = false;
}
},
}
},
created() {
// this.getstatistic()
this.getbyEquType()
this.getlistMonitor()
this.getalertMessageTimes()
@ -430,6 +378,7 @@ export default {
overflow-y: auto;
overflow-x: hidden;
max-height: 255px;
padding-left: 0;
}
.device-item {

Loading…
Cancel
Save