Files
klp-oa/klp-ui/src/views/ems/overview/index.vue
2025-10-15 16:29:00 +08:00

340 lines
7.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="container">
<div class="setting">
<el-checkbox-group v-model="setting">
<el-checkbox-button label="left">实时监控</el-checkbox-button>
<el-checkbox-button label="right">统计信息</el-checkbox-button>
<el-checkbox-button label="bottom">告警记录</el-checkbox-button>
</el-checkbox-group>
</div>
<div class="left" v-if="setting.includes('left')">
<Live />
</div>
<div class="right" v-if="setting.includes('right')">
<Statistic />
</div>
<div class="bottom" v-if="setting.includes('bottom')">
<Record />
</div>
<div class="points">
<!-- 标点渲染 -->
<div v-for="(point, index) in points" :key="index" class="point-marker"
:style="{ left: point.x + '%', top: point.y + '%' }"
:class="{ 'normal-status': point.status === 0, 'abnormal-status': point.status === 1 }"
@mouseenter="showTooltip(index)" @mouseleave="hideTooltip(index)">
<div
style="background-color: #00000060; display: flex; align-items: center; justify-content: center; gap: 4px; padding: 4px; color: #fff;">
<!-- 标点图标 -->
<div class="marker-icon">
<i class="fa" :class="point.status === 0 ? 'fa-check-circle' : 'fa-exclamation-circle'"></i>
</div>
<div class="point-name">{{ point.name }}</div>
</div>
<!-- 悬停提示框 -->
<div class="point-tooltip" :class="{ 'visible': activeTooltip === index }">
<div class="tooltip-header">
<h4>{{ point.name }}</h4>
<span class="status-badge" :class="point.status === 0 ? 'normal' : 'abnormal'">
{{ point.status === 0 ? '正常' : '异常' }}
</span>
</div>
<div class="tooltip-body" v-if="point.children && point.children.length > 0">
<table class="device-table">
<thead>
<tr>
<th>设备名称</th>
<th>设备IP</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr v-for="(device, devIdx) in point.children" :key="devIdx">
<td>{{ device.deviceName }}</td>
<td>{{ device.ipAddress || '无数据' }}</td>
<td>
<!-- <span class="device-status" :class="device.status === 0 ? 'normal' : 'abnormal'">
{{ device.status === 0 ? '正常' : '异常' }}
</span> -->
<dict-tag :options="dict.type.alarm_device_status" :value="device.status"/>
</td>
</tr>
</tbody>
</table>
</div>
<div class="tooltip-body" v-else>
<div class="no-device">暂无设备</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Live from './components/Live.vue'
import Record from './components/Record.vue'
import Statistic from './components/Statistic.vue'
import { getLeftWithDevices } from '@/api/ems/overview'
export default {
components: {
Live,
Record,
Statistic
},
dicts: ['alarm_device_status'],
data() {
return {
// 标点数据补充
setting: ['left', 'right'],
points: [],
activeTooltip: -1 // 控制提示框显示
}
},
created() {
this.getLeftWithDevices()
},
methods: {
showTooltip(index) {
this.activeTooltip = index
},
hideTooltip() {
this.activeTooltip = -1
},
getLeftWithDevices() {
getLeftWithDevices().then(res => {
console.log(res.data);
// 1. 树结构转数组只保留叶子节点children为空数组的节点
const leafNodes = [];
// 递归收集叶子节点的函数
const collectLeafs = (nodes) => {
if (!nodes || !Array.isArray(nodes)) return;
nodes.forEach(node => {
// 检查是否为叶子节点children存在且为空数组
if (node.children && Array.isArray(node.children) && node.children.length === 0) {
leafNodes.push(node);
} else {
// 递归处理子节点
collectLeafs(node.children);
}
});
};
// 开始收集叶子节点
collectLeafs(res.data);
// 2. 转换数组结构
const transformedData = leafNodes.map(node => ({
name: node.path, // 将path转换为name
status: 0, // 状态设为0
children: node.devices, // children使用原来的devices字段
x: node.x, // 保留x字段
y: node.y // 保留y字段
// 如果有其他需要保留的字段,可以在这里继续添加
}));
// 这里可以使用转换后的数据,例如赋值给组件的状态
this.points = transformedData;
console.log('转换后的叶子节点数据:', transformedData);
}).catch(error => {
console.error('获取数据失败:', error);
});
}
}
}
</script>
<style>
.container {
width: 100%;
height: calc(100vh - 84px);
position: relative;
background: url('../../../assets/images/bird.png') no-repeat center center;
background-size: cover;
overflow: hidden;
}
.setting {
position: absolute;
top: 10px;
left: 10px;
z-index: 100;
}
.left {
position: absolute;
top: 60px;
left: 10px;
width: 220px;
z-index: 10;
}
.right {
position: absolute;
top: 10px;
right: 10px;
width: 220px;
z-index: 10;
}
.bottom {
position: absolute;
bottom: 10px;
left: 10px;
z-index: 10;
}
.points {
width: 100%;
height: 100%;
position: relative;
z-index: 5;
}
/* 标点样式 */
.point-marker {
position: absolute;
transform: translate(-50%, -50%);
cursor: pointer;
transition: all 0.3s ease;
}
.marker-icon {
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.normal-status .marker-icon {
background-color: #43a047;
/* 正常-绿色 */
}
.abnormal-status .marker-icon {
background-color: #e53935;
/* 异常-红色 */
animation: pulse 1.5s infinite;
}
/* 脉冲动画 */
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(229, 57, 53, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(229, 57, 53, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(229, 57, 53, 0);
}
}
.tooltip-body {
max-height: 200px;
overflow-y: auto;
}
/* 提示框样式 */
.point-tooltip {
position: absolute;
top: 30px;
left: 50%;
transform: translateX(-50%);
width: 320px;
background-color: rgba(255, 255, 255, 0.95);
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
padding: 12px;
display: none;
z-index: 100;
}
.point-tooltip.visible {
display: block;
}
.tooltip-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid #eee;
}
.tooltip-header h4 {
margin: 0;
font-size: 16px;
color: #333;
}
.status-badge {
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
color: white;
}
.status-badge.normal {
background-color: #43a047;
}
.status-badge.abnormal {
background-color: #e53935;
}
.device-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.device-table th {
padding: 6px 4px;
text-align: left;
background-color: #f5f5f5;
font-weight: 500;
color: #666;
}
.device-table td {
padding: 8px 4px;
border-bottom: 1px solid #f0f0f0;
}
.device-table tr:last-child td {
border-bottom: none;
}
.device-status {
padding: 1px 6px;
border-radius: 4px;
font-size: 12px;
}
.device-status.normal {
background-color: #e8f5e9;
color: #2e7d32;
}
.device-status.abnormal {
background-color: #ffebee;
color: #c62828;
}
</style>