|
|
@@ -1,226 +1,678 @@
|
|
|
<template>
|
|
|
- <div class="app-container power-index-content">
|
|
|
- <el-row type="flex" :gutter="20">
|
|
|
- <el-col :span="24">
|
|
|
- <div class="gl-filters">
|
|
|
- <SwitchTag
|
|
|
- :ds="areaTag"
|
|
|
- :def-tag="defArea"
|
|
|
- :tagClick="onSwitchTagClick"
|
|
|
+ <div class="app-container device-alarm-content">
|
|
|
+ <el-row :gutter="10">
|
|
|
+ <!-- 左侧区域树 -->
|
|
|
+ <el-col :span="4" :xs="24">
|
|
|
+ <div class="head-container">
|
|
|
+ <el-input
|
|
|
+ v-model="areaName"
|
|
|
+ placeholder="请输入服务区名称"
|
|
|
+ clearable
|
|
|
+ size="small"
|
|
|
+ prefix-icon="el-icon-search"
|
|
|
+ style="margin-bottom: 20px"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="head-container">
|
|
|
+ <el-tree
|
|
|
+ ref="tree"
|
|
|
+ :data="areaOptions"
|
|
|
+ :default-expand-all="true"
|
|
|
+ :expand-on-click-node="false"
|
|
|
+ :filter-node-method="filterNode"
|
|
|
+ node-key="id"
|
|
|
+ highlight-current
|
|
|
+ @node-click="handleNodeClick"
|
|
|
/>
|
|
|
</div>
|
|
|
</el-col>
|
|
|
- </el-row>
|
|
|
- <el-row type="flex" :gutter="20" style="margin-top: 20px">
|
|
|
- <el-col :span="12">
|
|
|
- <BarChartBlock
|
|
|
- title="设备数量统计"
|
|
|
- :opt-cfg="devcNum"
|
|
|
- />
|
|
|
- </el-col>
|
|
|
- <el-col :span="12">
|
|
|
- <PieChartBlock title="设备状态统计" :opt-cfg="devcStat"></PieChartBlock>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- <el-row type="flex" :gutter="20" style="margin-top: 20px">
|
|
|
- <el-col :span="12">
|
|
|
- <PieChartBlock title="供应商占比" :opt-cfg="supplyerIndex"></PieChartBlock>
|
|
|
- </el-col>
|
|
|
- <el-col :span="12">
|
|
|
- <LineChartBlock title="故障发生情况" :opt-cfg="devcFault" />
|
|
|
+
|
|
|
+ <!-- 右侧内容区 -->
|
|
|
+ <el-col :span="20" :xs="24">
|
|
|
+ <!-- KPI卡片区 -->
|
|
|
+ <el-row :gutter="20" class="kpi-cards">
|
|
|
+ <el-col :span="6" v-for="(item, index) in kpiData" :key="index">
|
|
|
+ <div class="kpi-card" :class="'kpi-' + item.type">
|
|
|
+ <div class="kpi-icon">
|
|
|
+ <i :class="item.icon"></i>
|
|
|
+ </div>
|
|
|
+ <div class="kpi-content">
|
|
|
+ <div class="kpi-value">{{ item.value }}</div>
|
|
|
+ <div class="kpi-label">{{ item.label }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 第一行图表 -->
|
|
|
+ <el-row :gutter="20" style="margin-top: 20px">
|
|
|
+ <el-col :span="12">
|
|
|
+ <PieChartBlock title="设备状态分布" :opt-cfg="deviceStatusChart" />
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <LineChartBlock title="告警趋势分析" :opt-cfg="alarmTrendChart">
|
|
|
+ <template v-slot:filters>
|
|
|
+ <SwitchTag
|
|
|
+ :ds="dateTypeOptions"
|
|
|
+ :defTag="dateType"
|
|
|
+ :tagClick="onDateTypeSwitch"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </LineChartBlock>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 第二行 -->
|
|
|
+ <el-row :gutter="20" style="margin-top: 20px">
|
|
|
+ <el-col :span="14">
|
|
|
+ <BlockTable title="实时告警列表" :table-data="realTimeAlarmData">
|
|
|
+ <template v-slot:columns>
|
|
|
+ <el-table-column type="index" label="序号" align="center" width="60" />
|
|
|
+ <el-table-column prop="subSystemName" label="子系统" align="center" width="100" />
|
|
|
+ <el-table-column prop="objName" label="设备名称" align="center" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="alarmMsg" label="告警描述" align="center" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="alarmTime" label="告警时间" align="center" width="160" />
|
|
|
+ <el-table-column label="告警类型" align="center" width="100">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag :type="getAlarmTypeTag(scope.row.alarmType)" size="small">
|
|
|
+ {{ getAlarmTypeName(scope.row.alarmType) }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="状态" align="center" width="100">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-dropdown @command="(cmd) => handleAlarmStateChange(cmd, scope.row)" v-if="scope.row.alarmState !== 2 && scope.row.alarmState !== 3">
|
|
|
+ <span class="el-dropdown-link">
|
|
|
+ <el-tag :type="getAlarmStateTag(scope.row.alarmState)" size="small">
|
|
|
+ {{ getAlarmStateName(scope.row.alarmState) }}
|
|
|
+ </el-tag>
|
|
|
+ <i class="el-icon-arrow-down el-icon--right"></i>
|
|
|
+ </span>
|
|
|
+ <el-dropdown-menu slot="dropdown">
|
|
|
+ <el-dropdown-item :command="1" v-if="scope.row.alarmState === 0">
|
|
|
+ 开始处理
|
|
|
+ </el-dropdown-item>
|
|
|
+ <el-dropdown-item :command="2">已处置</el-dropdown-item>
|
|
|
+ <el-dropdown-item :command="3">已消散</el-dropdown-item>
|
|
|
+ </el-dropdown-menu>
|
|
|
+ </el-dropdown>
|
|
|
+ <el-tag v-else :type="getAlarmStateTag(scope.row.alarmState)" size="small">
|
|
|
+ {{ getAlarmStateName(scope.row.alarmState) }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </template>
|
|
|
+ </BlockTable>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="10">
|
|
|
+ <PieChartBlock title="告警类型分布" :opt-cfg="alarmTypeChart" />
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-
|
|
|
-import { ApiCode } from '@/api/apiEmums';
|
|
|
-import { get } from '@/api/commonApi';
|
|
|
-import BarChartBlock from '@/components/Block/charts/BarChartBlock.vue';
|
|
|
+import { areaTreeSelect } from '@/api/basecfg/area';
|
|
|
+import {
|
|
|
+ listAlarmInfo,
|
|
|
+ updateAlarmInfo,
|
|
|
+ fetchAlarmIndexDay,
|
|
|
+ fetchAlarmIndexMonth,
|
|
|
+ fetchAlarmIndexYear,
|
|
|
+ fetchAlarmIndex,
|
|
|
+} from '@/api/alarm/alarm-info';
|
|
|
+import { listDeviceStatus } from '@/api/device/device';
|
|
|
+import BlockTable from '@/components/Block/BlockTable/index.vue';
|
|
|
+import LineChartBlock from '@/components/Block/charts/LineChartBlock.vue';
|
|
|
import PieChartBlock from '@/components/Block/charts/PieChartBlock.vue';
|
|
|
+import SwitchTag from '@/components/SwitchTag/index.vue';
|
|
|
import { DateTool } from '@/utils/DateTool';
|
|
|
-import * as areaApi from '../../../api/basecfg/area';
|
|
|
-import LineChartBlock from '../../../components/Block/charts/LineChartBlock.vue';
|
|
|
-import SwitchTag from '../../../components/SwitchTag/index.vue';
|
|
|
+import dayjs from 'dayjs';
|
|
|
|
|
|
export default {
|
|
|
+ name: 'DeviceAlarm',
|
|
|
components: {
|
|
|
- BarChartBlock,
|
|
|
- PieChartBlock,
|
|
|
+ BlockTable,
|
|
|
LineChartBlock,
|
|
|
+ PieChartBlock,
|
|
|
SwitchTag,
|
|
|
},
|
|
|
data() {
|
|
|
return {
|
|
|
- areaTag: [],
|
|
|
- defArea: {},
|
|
|
- devcStat: {
|
|
|
+ // 区域筛选
|
|
|
+ areaName: '',
|
|
|
+ areaOptions: [],
|
|
|
+ areaCode: '', // 默认为空字符串,表示全部
|
|
|
+
|
|
|
+ // 时间维度
|
|
|
+ dateType: { val: 'year', text: '按年' },
|
|
|
+ dateTypeOptions: [
|
|
|
+ { val: 'day', text: '按日' },
|
|
|
+ { val: 'month', text: '按月' },
|
|
|
+ { val: 'year', text: '按年' },
|
|
|
+ ],
|
|
|
+
|
|
|
+ // KPI数据
|
|
|
+ kpiData: [
|
|
|
+ {
|
|
|
+ label: '设备总数',
|
|
|
+ value: 0,
|
|
|
+ icon: 'el-icon-monitor',
|
|
|
+ type: 'primary',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '在线设备',
|
|
|
+ value: 0,
|
|
|
+ icon: 'el-icon-circle-check',
|
|
|
+ type: 'success',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '离线设备',
|
|
|
+ value: 0,
|
|
|
+ icon: 'el-icon-warning-outline',
|
|
|
+ type: 'danger',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '待处理告警',
|
|
|
+ value: 0,
|
|
|
+ icon: 'el-icon-bell',
|
|
|
+ type: 'warning',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+
|
|
|
+ // 设备状态分布
|
|
|
+ deviceStatusChart: {
|
|
|
series: [
|
|
|
{
|
|
|
type: 'pie',
|
|
|
radius: ['50%', '70%'],
|
|
|
- data: [
|
|
|
- {
|
|
|
- value: 8,
|
|
|
- name: '故障',
|
|
|
- },
|
|
|
- {
|
|
|
- value: 75,
|
|
|
- name: '运行',
|
|
|
- },
|
|
|
- {
|
|
|
- value: 2,
|
|
|
- name: '未运行',
|
|
|
- },
|
|
|
- ],
|
|
|
+ data: [],
|
|
|
},
|
|
|
],
|
|
|
},
|
|
|
- supplyerIndex: {
|
|
|
- series: [
|
|
|
- {
|
|
|
- type: 'pie',
|
|
|
- radius: ['0%', '70%'],
|
|
|
- data: [
|
|
|
- {
|
|
|
- value: 7,
|
|
|
- name: '西门子',
|
|
|
- },
|
|
|
- {
|
|
|
- value: 6,
|
|
|
- name: '施耐德',
|
|
|
- },
|
|
|
- {
|
|
|
- value: 9,
|
|
|
- name: '通用电器',
|
|
|
- },
|
|
|
- {
|
|
|
- value: 14,
|
|
|
- name: '飞利浦',
|
|
|
- },
|
|
|
- {
|
|
|
- value: 18,
|
|
|
- name: '霍尼韦尔',
|
|
|
- },
|
|
|
- ],
|
|
|
- },
|
|
|
- ],
|
|
|
- },
|
|
|
- devcNum: {
|
|
|
- categories: ['空调', '灯具', '杀菌设备', '出纳机', '感应器'],
|
|
|
- series: [
|
|
|
- {
|
|
|
- name: '故障',
|
|
|
- data: [1, 0, 0, 1, 1],
|
|
|
- },
|
|
|
- {
|
|
|
- name: '运行',
|
|
|
- data: [20, 109, 20, 20, 10],
|
|
|
- },
|
|
|
- {
|
|
|
- name: '未运行',
|
|
|
- data: [1, 2, 4, 0, 1],
|
|
|
- },
|
|
|
- ],
|
|
|
- },
|
|
|
- devcFault: {
|
|
|
- unit:" ",
|
|
|
+
|
|
|
+ // 告警趋势
|
|
|
+ alarmTrendChart: {
|
|
|
+ unit: '',
|
|
|
xAxis: {
|
|
|
type: 'category',
|
|
|
- data: [
|
|
|
- '1月',
|
|
|
- '2月',
|
|
|
- '03月',
|
|
|
- '04月',
|
|
|
- '05月',
|
|
|
- '06月',
|
|
|
- '07月',
|
|
|
- '08月',
|
|
|
- ],
|
|
|
+ data: [],
|
|
|
},
|
|
|
+ series: [],
|
|
|
+ },
|
|
|
+
|
|
|
+ // 告警类型分布
|
|
|
+ alarmTypeChart: {
|
|
|
series: [
|
|
|
{
|
|
|
- data: [
|
|
|
- 73, 57, 37, 40, 37, 40, 73, 57,
|
|
|
- ],
|
|
|
- type: 'line',
|
|
|
- areaStyle: {
|
|
|
- color: '#d7e4fc',
|
|
|
- emphasis: {
|
|
|
- color: '#6093f5',
|
|
|
- },
|
|
|
- },
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['0%', '70%'],
|
|
|
+ data: [],
|
|
|
},
|
|
|
],
|
|
|
},
|
|
|
+
|
|
|
+ // 实时告警数据
|
|
|
+ realTimeAlarmData: [],
|
|
|
};
|
|
|
},
|
|
|
- mounted() {
|
|
|
- this.initData();
|
|
|
+ watch: {
|
|
|
+ areaName(val) {
|
|
|
+ this.$refs.tree.filter(val);
|
|
|
+ },
|
|
|
},
|
|
|
- created() {
|
|
|
+ mounted() {
|
|
|
+ this.getAreaTree();
|
|
|
},
|
|
|
methods: {
|
|
|
- onSwitchTagClick(item) {
|
|
|
- console.log(item);
|
|
|
- },
|
|
|
- async initData() {
|
|
|
- const {
|
|
|
- rows,
|
|
|
- total,
|
|
|
- } = await areaApi.listArea({
|
|
|
- pageNum: 1,
|
|
|
- pageSize: 10,
|
|
|
- });
|
|
|
- if (rows.length > 0) {
|
|
|
- rows.forEach(item => {
|
|
|
- this.areaTag.push({
|
|
|
- val: item.areaCode,
|
|
|
- text: item.areaName,
|
|
|
- });
|
|
|
+ // 获取区域树
|
|
|
+ async getAreaTree() {
|
|
|
+ try {
|
|
|
+ const response = await areaTreeSelect('0', 1);
|
|
|
+ this.areaOptions = [
|
|
|
+ {
|
|
|
+ id: '',
|
|
|
+ label: '全部',
|
|
|
+ children: [],
|
|
|
+ },
|
|
|
+ ].concat(response.data || []);
|
|
|
+
|
|
|
+ // 区域树加载完成后再加载数据
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.loadAllData();
|
|
|
});
|
|
|
- this.defArea = this.areaTag[0];
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取区域树失败:', error);
|
|
|
+ this.$message.error('获取区域树失败');
|
|
|
}
|
|
|
- this.queryCharts();
|
|
|
},
|
|
|
- queryCharts() {
|
|
|
- this.getPvList();
|
|
|
+
|
|
|
+ // 筛选节点
|
|
|
+ filterNode(value, data) {
|
|
|
+ if (!value) return true;
|
|
|
+ return data.label.indexOf(value) !== -1;
|
|
|
},
|
|
|
- async getPvList() {
|
|
|
- const {
|
|
|
- data,
|
|
|
- code,
|
|
|
- } = await get('/prod/list/prod/this/day/index', {
|
|
|
- areaCode: this.defArea.val,
|
|
|
- });
|
|
|
- const result = {};
|
|
|
- if (ApiCode.SUCCESS !== code || !data || data.length < 1) {
|
|
|
- return null;
|
|
|
+
|
|
|
+ // 区域切换
|
|
|
+ handleNodeClick(data) {
|
|
|
+ this.areaCode = data.id;
|
|
|
+ this.loadAllData();
|
|
|
+ },
|
|
|
+
|
|
|
+ // 时间维度切换
|
|
|
+ async onDateTypeSwitch(item) {
|
|
|
+ this.dateType = item;
|
|
|
+ await this.loadAlarmTrend();
|
|
|
+ },
|
|
|
+
|
|
|
+ // 构建查询参数
|
|
|
+ buildQueryParams(extraParams = {}) {
|
|
|
+ const params = {
|
|
|
+ areaCode: this.areaCode || '', // 总是传递 areaCode,空字符串表示全部
|
|
|
+ ...extraParams
|
|
|
+ };
|
|
|
+ return params;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加载所有数据
|
|
|
+ async loadAllData() {
|
|
|
+ try {
|
|
|
+ await Promise.all([
|
|
|
+ this.loadKpiData(),
|
|
|
+ this.loadDeviceStatus(),
|
|
|
+ this.loadAlarmTrend(),
|
|
|
+ this.loadRealTimeAlarm(),
|
|
|
+ this.loadAlarmTypeDistribution(),
|
|
|
+ ]);
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载数据失败:', error);
|
|
|
}
|
|
|
- const xAxis = DateTool.getTime(60);
|
|
|
- data.forEach(item => {
|
|
|
- const {
|
|
|
- elecQuantity,
|
|
|
- timeIndex,
|
|
|
- } = item;
|
|
|
- result[timeIndex] = {
|
|
|
- elecQuantity,
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加载KPI数据
|
|
|
+ async loadKpiData() {
|
|
|
+ try {
|
|
|
+ // 获取设备统计
|
|
|
+ const deviceRes = await listDeviceStatus(this.buildQueryParams());
|
|
|
+ const deviceData = deviceRes.data || {};
|
|
|
+
|
|
|
+ // 获取待处理告警数
|
|
|
+ const alarmRes = await listAlarmInfo(
|
|
|
+ this.buildQueryParams({
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 1,
|
|
|
+ alarmStateList: [0, 1],
|
|
|
+ })
|
|
|
+ );
|
|
|
+
|
|
|
+ this.kpiData = [
|
|
|
+ {
|
|
|
+ label: '设备总数',
|
|
|
+ value: deviceData.total || 0,
|
|
|
+ icon: 'el-icon-monitor',
|
|
|
+ type: 'primary',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '在线设备',
|
|
|
+ value: deviceData.onlineCount || 0,
|
|
|
+ icon: 'el-icon-circle-check',
|
|
|
+ type: 'success',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '离线设备',
|
|
|
+ value: deviceData.offlineCount || 0,
|
|
|
+ icon: 'el-icon-warning-outline',
|
|
|
+ type: 'danger',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '待处理告警',
|
|
|
+ value: alarmRes.total || 0,
|
|
|
+ icon: 'el-icon-bell',
|
|
|
+ type: 'warning',
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载KPI数据失败:', error);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加载设备状态分布
|
|
|
+ async loadDeviceStatus() {
|
|
|
+ try {
|
|
|
+ const { data } = await listDeviceStatus(this.buildQueryParams());
|
|
|
+
|
|
|
+ this.deviceStatusChart = {
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['50%', '70%'],
|
|
|
+ data: [
|
|
|
+ { value: data.onlineCount || 0, name: '在线' },
|
|
|
+ { value: data.offlineCount || 0, name: '离线' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
};
|
|
|
- });
|
|
|
- const chartData = [];
|
|
|
- xAxis.forEach((item, index) => {
|
|
|
- const timeIndex = index + 1;
|
|
|
- if (result[timeIndex]) {
|
|
|
- chartData.push(result[timeIndex].elecQuantity);
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载设备状态失败:', error);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加载告警趋势
|
|
|
+ async loadAlarmTrend() {
|
|
|
+ try {
|
|
|
+ let xAxis = [];
|
|
|
+ let data = [];
|
|
|
+
|
|
|
+ const params = this.buildQueryParams();
|
|
|
+
|
|
|
+ if (this.dateType.val === 'day') {
|
|
|
+ const res = await fetchAlarmIndexDay(params);
|
|
|
+ data = res.data || [];
|
|
|
+ xAxis = DateTool.getTime(24);
|
|
|
+ } else if (this.dateType.val === 'month') {
|
|
|
+ const res = await fetchAlarmIndexMonth(params);
|
|
|
+ data = res.data || [];
|
|
|
+ xAxis = DateTool.getDayOfRange(
|
|
|
+ dayjs().subtract(1, 'month'),
|
|
|
+ dayjs(),
|
|
|
+ DateTool.DateFormat.YYYY_MM_DD
|
|
|
+ );
|
|
|
} else {
|
|
|
- chartData.push(0);
|
|
|
+ const res = await fetchAlarmIndexYear(params);
|
|
|
+ data = res.data || [];
|
|
|
+ xAxis = DateTool.getMonthsOfYearAgo();
|
|
|
}
|
|
|
+
|
|
|
+ const series = this.transformAlarmTrendData(data, xAxis);
|
|
|
+
|
|
|
+ this.alarmTrendChart = {
|
|
|
+ unit: '',
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: xAxis,
|
|
|
+ },
|
|
|
+ series,
|
|
|
+ };
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载告警趋势失败:', error);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 转换告警趋势数据
|
|
|
+ transformAlarmTrendData(data, xAxis) {
|
|
|
+ const dayGroup = _.groupBy(data, 'alarmType');
|
|
|
+ const series = [];
|
|
|
+
|
|
|
+ const alarmTypeMap = {
|
|
|
+ 1: '一般告警',
|
|
|
+ 2: '重要告警',
|
|
|
+ 3: '紧急告警',
|
|
|
+ 4: '恢复告警',
|
|
|
+ 5: '诊断告警',
|
|
|
+ 6: '其他告警',
|
|
|
+ };
|
|
|
+
|
|
|
+ Object.keys(dayGroup).forEach((alarmType) => {
|
|
|
+ let ds = {};
|
|
|
+ dayGroup[alarmType].forEach((item) => {
|
|
|
+ ds[item.dateIndex] = item.cnt;
|
|
|
+ });
|
|
|
+
|
|
|
+ let seriesData = [];
|
|
|
+ xAxis.forEach((item) => seriesData.push(ds[item] || 0));
|
|
|
+
|
|
|
+ series.push({
|
|
|
+ name: alarmTypeMap[alarmType] || '未知',
|
|
|
+ type: 'line',
|
|
|
+ smooth: true,
|
|
|
+ data: seriesData,
|
|
|
+ });
|
|
|
});
|
|
|
- this.pvData.xAxis.data = xAxis;
|
|
|
- this.pvData.series[0].data = chartData;
|
|
|
+
|
|
|
+ return series;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加载实时告警
|
|
|
+ async loadRealTimeAlarm() {
|
|
|
+ try {
|
|
|
+ const { rows } = await listAlarmInfo(
|
|
|
+ this.buildQueryParams({
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ alarmStateList: [0, 1],
|
|
|
+ })
|
|
|
+ );
|
|
|
+
|
|
|
+ this.realTimeAlarmData = rows || [];
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载实时告警失败:', error);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加载告警类型分布
|
|
|
+ async loadAlarmTypeDistribution() {
|
|
|
+ try {
|
|
|
+ const endTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
|
|
+ const startTime = dayjs().startOf('year').format('YYYY-MM-DD HH:mm:ss'); // 当年1月1日 00:00:00
|
|
|
+
|
|
|
+ const params = this.buildQueryParams({
|
|
|
+ startRecTime: startTime,
|
|
|
+ endRecTime: endTime,
|
|
|
+ });
|
|
|
+
|
|
|
+ const { data } = await fetchAlarmIndex(params);
|
|
|
+
|
|
|
+ const alarmTypeMap = {
|
|
|
+ 1: '一般告警',
|
|
|
+ 2: '重要告警',
|
|
|
+ 3: '紧急告警',
|
|
|
+ 4: '恢复告警',
|
|
|
+ 5: '诊断告警',
|
|
|
+ 6: '其他告警',
|
|
|
+ };
|
|
|
+
|
|
|
+ const chartData = (data || []).map((item) => ({
|
|
|
+ value: item.cnt,
|
|
|
+ name: alarmTypeMap[item.alarmType] || '未知',
|
|
|
+ }));
|
|
|
+
|
|
|
+ this.alarmTypeChart = {
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['0%', '70%'],
|
|
|
+ data: chartData,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ };
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载告警类型分布失败:', error);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 告警状态变更
|
|
|
+ async handleAlarmStateChange(command, row) {
|
|
|
+ try {
|
|
|
+ await updateAlarmInfo({
|
|
|
+ id: row.id,
|
|
|
+ alarmState: command,
|
|
|
+ });
|
|
|
+
|
|
|
+ this.$message.success('告警状态更新成功');
|
|
|
+ await this.loadRealTimeAlarm();
|
|
|
+ await this.loadKpiData();
|
|
|
+ } catch (error) {
|
|
|
+ console.error('更新告警状态失败:', error);
|
|
|
+ this.$message.error('告警状态更新失败');
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取告警类型名称
|
|
|
+ getAlarmTypeName(type) {
|
|
|
+ const typeMap = {
|
|
|
+ 1: '一般',
|
|
|
+ 2: '重要',
|
|
|
+ 3: '紧急',
|
|
|
+ 4: '恢复',
|
|
|
+ 5: '诊断',
|
|
|
+ 6: '其他',
|
|
|
+ };
|
|
|
+ return typeMap[type] || '未知';
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取告警类型标签
|
|
|
+ getAlarmTypeTag(type) {
|
|
|
+ const tagMap = {
|
|
|
+ 1: 'info',
|
|
|
+ 2: 'warning',
|
|
|
+ 3: 'danger',
|
|
|
+ 4: 'success',
|
|
|
+ 5: '',
|
|
|
+ 6: 'info',
|
|
|
+ };
|
|
|
+ return tagMap[type] || 'info';
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取告警状态名称
|
|
|
+ getAlarmStateName(state) {
|
|
|
+ const stateMap = {
|
|
|
+ 0: '新增',
|
|
|
+ 1: '处理中',
|
|
|
+ 2: '已处置',
|
|
|
+ 3: '已消散',
|
|
|
+ };
|
|
|
+ return stateMap[state] || '未知';
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取告警状态标签
|
|
|
+ getAlarmStateTag(state) {
|
|
|
+ const tagMap = {
|
|
|
+ 0: 'danger',
|
|
|
+ 1: 'warning',
|
|
|
+ 2: 'success',
|
|
|
+ 3: 'info',
|
|
|
+ };
|
|
|
+ return tagMap[state] || 'info';
|
|
|
},
|
|
|
},
|
|
|
};
|
|
|
</script>
|
|
|
-<style src="./index.scss" lang="scss" />
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+@import '@/assets/styles/variables.scss';
|
|
|
+
|
|
|
+.device-alarm-content {
|
|
|
+ background: #fff;
|
|
|
+ padding: 20px;
|
|
|
+
|
|
|
+ // KPI卡片样式
|
|
|
+ .kpi-cards {
|
|
|
+ .kpi-card {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 20px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
|
+ transition: all 0.3s;
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ transform: translateY(-4px);
|
|
|
+ box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.15);
|
|
|
+ }
|
|
|
+
|
|
|
+ .kpi-icon {
|
|
|
+ width: 60px;
|
|
|
+ height: 60px;
|
|
|
+ border-radius: 50%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 28px;
|
|
|
+ margin-right: 15px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .kpi-content {
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+ .kpi-value {
|
|
|
+ font-size: 28px;
|
|
|
+ font-weight: bold;
|
|
|
+ line-height: 1.2;
|
|
|
+ margin-bottom: 5px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .kpi-label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #666;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &.kpi-primary {
|
|
|
+ .kpi-icon {
|
|
|
+ background: rgba(64, 158, 255, 0.1);
|
|
|
+ color: #409eff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .kpi-value {
|
|
|
+ color: #409eff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &.kpi-success {
|
|
|
+ .kpi-icon {
|
|
|
+ background: rgba(103, 194, 58, 0.1);
|
|
|
+ color: #67c23a;
|
|
|
+ }
|
|
|
+
|
|
|
+ .kpi-value {
|
|
|
+ color: #67c23a;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &.kpi-danger {
|
|
|
+ .kpi-icon {
|
|
|
+ background: rgba(245, 108, 108, 0.1);
|
|
|
+ color: #f56c6c;
|
|
|
+ }
|
|
|
+
|
|
|
+ .kpi-value {
|
|
|
+ color: #f56c6c;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &.kpi-warning {
|
|
|
+ .kpi-icon {
|
|
|
+ background: rgba(230, 162, 60, 0.1);
|
|
|
+ color: #e6a23c;
|
|
|
+ }
|
|
|
+
|
|
|
+ .kpi-value {
|
|
|
+ color: #e6a23c;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 区域树样式
|
|
|
+ .head-container {
|
|
|
+ background: #f5f7fa;
|
|
|
+ padding: 10px;
|
|
|
+ border-radius: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 下拉链接样式
|
|
|
+ .el-dropdown-link {
|
|
|
+ cursor: pointer;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|