Files
yudao-dev/src/views/equipment/equipmentOverview/index.vue
‘937886381’ b7ba173ca3 修改
2025-12-09 16:57:38 +08:00

1309 lines
39 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="app-container">
<!-- 1. 顶部导航栏固定高度 -->
<!-- <div class="top-nav">
</div> -->
<!-- 2. 搜索栏固定高度占自身空间 -->
<div class="search-container energyOverlimitLog">
<span class="blue-block"></span>
<span class="tip">设备总览</span>
<search-bar removeBlue :formConfigs="formConfig" ref="searchBarForm" @headBtnClick="buttonClick" />
</div>
<div class="num-container">
<div class="equipmentNum">
<div class="equipment-info">
<div class="info">
<div class="num equipment">
{{ globalCount.total ? globalCount.total : 0 }}
</div>
<div class="title">
设备数量
</div>
</div>
<img class="numImg" style="width: 86px;height: 86px;" src="../../../assets/images/equipmentNumImg.png" alt="">
</div>
</div>
<div class="runNum">
<div class="equipment-info">
<div class="info">
<div class="num run">
{{ globalCount.run ? globalCount.run : 0 }}
</div>
<div class="title">
运行数量
</div>
</div>
<img class="numImg" style="width: 86px;height: 86px;" src="../../../assets/images/runNumImg.png" alt="">
</div>
</div>
<div class="stopNum">
<div class="equipment-info">
<div class="info">
<div class="num stop">
{{ globalCount.stop ? globalCount.stop : 0 }}
</div>
<div class="title">
停机数量
</div>
</div>
<img class="numImg" style="width: 86px;height: 86px;" src="../../../assets/images/stopNumImg.png" alt="">
</div>
</div>
</div>
<div class="content-container energyOverlimitLog">
<span class="blue-block"></span>
<span class="tip">设备运行情况</span>
<div class="content">
<el-tabs class="custom-tabs" v-model="activeLabel" :stretch="true" @tab-click="handleTabClick">
<el-tab-pane :label="'全部'" name="all">
</el-tab-pane>
<el-tab-pane :label="'\u3000运行中\u3000'" name="running">
</el-tab-pane>
<el-tab-pane :label="'\u3000停机\u3000'" name="stop">
</el-tab-pane>
</el-tabs>
<div class="legend">
<div class="legend-item">
<div class="circle run"></div>
<div class="title">运行中</div>
</div>
<div class="legend-item">
<div class="circle stop"></div>
<div class="title">停机</div>
</div>
</div>
<div class="eqRun">
<!-- 循环渲染设备列表key绑定唯一equipmentId -->
<div v-for="item in filteredList" :key="item.equipmentId" class="eqItem" :class="{
'eq-item-disabled-false': !disabled,
'eq-item-disabled-true': disabled
}">
<div class="title">
<div class="eqName">
<!-- 设备状态图标status=0运行中其他停机根据实际业务调整 -->
<div class="circle" :class="{ run: item.status === 0, stop: item.status !== 0 }"
:style="!disabled ? { background: '#B4B4B4', boxShadow: '0px 0px 6px 0px #B4B4B4' } : {}"></div>
<div class="name" :style="!disabled ? { color: 'rgba(180, 180, 180, 1)' } : {}">
<!-- 显示产线+设备名称根据list数据调整字段 -->
产线 {{ item.lineId }} · {{ item.equipmentName }}
</div>
</div>
<!-- 警告数量这里假设用模拟数据实际需从接口获取 -->
<el-tooltip v-if="disabled && item.equipmentAlarms" placement="bottom" effect="light"
popper-class="no-border-tooltip" :visible-arrow="false">
<div slot="content">
<div class="tooltips-item" v-for="(alarm, index) in item.equipmentAlarms" :key="index">
<div class="line" />
<div class="text">
<div class="time">
{{ formatTime(alarm.time) }}
</div>
<div class="alarm">
{{ alarm.content }}
</div>
</div>
</div>
</div>
<div class="alarmNum">
{{ item.equipmentCount }} 条警告
</div>
</el-tooltip>
</div>
<div class="warningHours">
<div class="hours" style="font-size: 14px;" :style="!disabled ? { color: 'rgba(180, 180, 180, 1)' } : {}">
24 小时运行状态
</div>
<!-- 参数异常提示模拟数据 -->
<el-tooltip v-if="disabled && item.paramAlarms" placement="bottom" effect="light"
popper-class="no-border-tooltip" :visible-arrow="false">
<div slot="content">
<div class="tooltips-item" v-for="(alarm, index) in item.paramAlarms" :key="index">
<div class="line" />
<div class="text">
<div class="time">
{{ formatTime(alarm.time) }}
</div>
<div class="alarm">
参数名称 {{ alarm.paramName }} 报警值 {{ alarm.paramValue }}
<!-- 超出上限 -->
<span v-if="alarm.overMax">
超出上限值
<span style="color: rgba(255, 189, 2, 1);">
{{
((Number(alarm.paramValue) || 0) - (Number(alarm.maxValue) || 0))
.toFixed(2)
.replace(/\.?0*$/, '')
}}
</span>
</span>
<!-- 超出下限 -->
<span v-else-if="alarm.overMin">
超出下限值
<span style="color: rgba(255, 189, 2, 1);">
{{
((Number(alarm.minValue) || 0) - (Number(alarm.paramValue) || 0))
.toFixed(2)
.replace(/\.?0*$/, '')
}}
</span>
</span>
<!-- <span v-else>无超出阈值</span> -->
</div>
</div>
</div>
</div>
<div class="warning">
{{ item.paramCount }} 条参数异常
</div>
</el-tooltip>
</div>
<div class="progress-container">
<!-- 运行进度条绑定item.run的百分比 -->
<div class="progress-running" :style="!disabled ? { width: '0%' } : { width: `${item.run}%` }"></div>
</div>
<div class="progress-text">
<!-- 运行百分比显示item.run的值增加值存在性判断 -->
<div class="run" :style="!disabled ? { color: 'rgba(180, 180, 180, 1)' } : {}">
运行 {{
!disabled
? '—'
: (item.run !== undefined && item.run !== null && !isNaN(item.run))
? `${item.run.toFixed(1) }%`
: '—'
}}
</div>
<!-- 停机百分比显示item.stop的值增加值存在性判断 -->
<div class="stop" :style="!disabled ? { color: 'rgba(180, 180, 180, 1)' } : {}">
停机 {{
!disabled
? '—'
: (item.stop !== undefined && item.stop !== null && !isNaN(item.stop))
? `${item.stop.toFixed(1) }%`
: '—'
}}
</div>
</div>
<div v-if="disabled" class="bottom">
<div @click="handleGetData(item)" class="runBottom">数据监控</div>
<div class="runBottom" @click="handleAlarm(item)">报警统计</div>
</div>
</div>
<!-- 无数据提示 -->
<!-- <div v-if="filteredList.length === 0" class="no-data">
暂无设备运行数据
</div> -->
</div>
</div>
</div>
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList" />
<alarm-or-update v-if="alarmOrUpdateVisible" ref="alarmOrUpdate" @refreshDataList="getDataList" />
</div>
</template>
<script>
// import { parseTime } from '@/filter/code-filter';
import {
getPdList,
} from '@/api/core/monitoring/auto';
// import { getFactoryPage } from '@/api/core/base/factory';
import { getTree } from '@/api/base/equipment';
import { getEquipmentTypePage } from '@/api/base/equipmentType';
import { getEquipmentOverall } from '@/api/base/equipment';
// import * as XLSX from 'xlsx';
// import FileSaver from 'file-saver';
import ButtonNav from '@/components/ButtonNav';
// import { formatTime } from '@/utils';
import { getAccessToken } from '@/utils/auth';
import AddOrUpdate from './add-or-updata';
import alarmOrUpdate from './alarm-or-updata';
import store from "@/store";
export default {
components: { ButtonNav, AddOrUpdate, alarmOrUpdate },
data() {
return {
urlOptions: { getDataListURL: getEquipmentOverall },
disabled: false,
listQuery: {
equipmentIds: undefined,
lineId: undefined,
equipmentTypeId: undefined,
status: undefined,
},
status: undefined,
countEq: undefined,
countRun: undefined,
countStop: undefined,
addOrUpdateVisible: false,
activeLabel: 'all',
list: [], // 折线图数据
formConfig: [
{
type: 'select',
label: '产线',
selectOptions: [],
param: 'lineId',
onchange: true
},
{
type: 'select',
label: '设备类型',
selectOptions: [],
param: 'equipmentTypeId',
// labelField: 'label',
// valueField: 'label',
},
{
type: 'cascader',
label: '设备名称',
selectOptions: [],
param: 'equipmentIds',
showAllLevels: false,
clearable: false,
cascaderProps: {
multiple: true,
filterable: true,
emitPath: false,
label: 'name', // 提前配置,后续可覆盖
value: 'id'
},
collapseTags: true,
width: 250,
// type: 'select',
// label: '设备选择',
// selectOptions: [],
// param: 'equipmentId',
labelField: 'name',
valueField: 'id',
},
{ type: 'button', btnName: '查询', name: 'search', color: 'primary' },
{ type: 'separate' },
{
type: 'button', btnName: '连接', name: 'link', plain: true,
color: 'primary',
},
// {
// type: 'button',
// btnName: '重置',
// name: 'reset',
// },
],
searchBarData:{},
alarmOrUpdateVisible:false,
sseReader: null, // 保存流读取器
abortController: null, // 用于中止 fetch 请求
retryCount: 0, // 当前重试次数
isDestroyed: false, // 标记组件是否已销毁
isSSEConnected: false, // 标记是否已建立 SSE 连接(避免重复连接)
currentSSEReaders: [], // 保存所有设备的 SSE 读取器(用于批量关闭)
};
},
mounted() {
this.getLineData()
this.getDataList()
},
computed: {
// 筛选后的列表(按 status 变化,不影响统计)
filteredList() {
if (this.status === undefined) {
return this.list; // 全部数据
}
// 只筛选列表,不改变统计
return this.list.filter(item => item.status === this.status);
},
// 全局统计(基于原始 list不随 status 变化)
globalCount() {
// 优先使用接口返回的统计数据(如果接口提供)
if (this.countEq > 0 || this.countRun > 0 || this.countStop > 0) {
return {
total: this.countEq,
run: this.countRun,
stop: this.countStop
};
}
// 接口未提供时,基于原始 list 计算(兜底方案)
return {
total: this.list.length,
run: this.list.filter(item => item.status === 0).length,
stop: this.list.filter(item => item.status === 1).length
};
}
},
methods: {
formatTime(time) {
// 处理时间戳10位秒级转13位毫秒级
let timestamp = typeof time === 'number' ? time : Date.parse(time);
if (timestamp.toString().length === 10) {
timestamp *= 1000;
}
const date = new Date(timestamp);
// 补零函数:确保数字为两位数
const padZero = (num) => num.toString().padStart(2, '0');
// 提取时间分量
const year = padZero(date.getFullYear().toString().slice(-2)); // 取年份后两位(如 2025 → 25
const month = padZero(date.getMonth() + 1); // 月份从0开始+1后补零
const day = padZero(date.getDate());
const hour = padZero(date.getHours());
const minute = padZero(date.getMinutes());
const second = padZero(date.getSeconds());
// 拼接为目标格式:时分秒/年.月.日
return `${hour}:${minute}:${second}/${year}.${month}.${day}`;
},
/**
* 接收数组格式的树形数据,去掉最后一级的 children 属性
* @param {Array} treeArray - 树形结构数组(即原 response.data
* @returns {Array} 处理后的树形数组(结构不变,仅删最后一级 children
*/
transformAndRemoveLastChildren(treeArray) {
// 深拷贝:避免修改原数组数据
const deepClone = (obj) => {
if (obj === null || typeof obj !== 'object') return obj;
if (Array.isArray(obj)) {
const arrClone = [];
for (let i = 0; i < obj.length; i++) {
arrClone[i] = deepClone(obj[i]);
}
return arrClone;
}
const objClone = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
objClone[key] = deepClone(obj[key]);
}
}
return objClone;
};
// 深拷贝传入的数组
const clonedArray = deepClone(treeArray) || [];
// 递归处理节点:只删最后一级 children
const processNode = (node) => {
if (!node || typeof node !== 'object') return node;
// 数组直接递归处理每个子项
if (Array.isArray(node)) {
return node.map(item => processNode(item));
}
const processedNode = { ...node };
// 判断是否为最后一级
let isLastLevel = true;
if (Array.isArray(processedNode.children) && processedNode.children.length > 0) {
isLastLevel = !processedNode.children.some(child =>
Array.isArray(child.children) && child.children.length > 0
);
}
// 最后一级删除 children
if (isLastLevel) {
delete processedNode.children;
} else {
processedNode.children = processNode(processedNode.children);
}
return processedNode;
};
// 处理数组中的所有节点
return processNode(clonedArray);
},
async getLineData() {
getPdList().then(res => {
this.formConfig[0].selectOptions = res.data || [];
})
getEquipmentTypePage({
pageSize: 100,
pageNo: 1
}).then(res => {
this.formConfig[1].selectOptions = res.data.list || [];
})
// getEquipmentPage({
// pageSize: 100,
// pageNo: 1
// }).then(res => {
// this.formConfig[2].selectOptions = res.data.list || [];
// })
const { data } = await this.$axios('/base/factory/getTree');
this.formConfig[2].selectOptions = data
// console.log('this.removeLastLevelChildren(data)', this.transformAndRemoveLastChildren(data))
},
// 切换按产线/按产品监控
// 搜索/导出按钮点击
buttonClick(val) {
switch (val.btnName) {
case 'search':
this.listQuery.pageNo = 1;
this.listQuery.pageSize = 10;
this.searchBarData = val
// console.log('val.equipmentIds', val.equipmentIds);
this.getDataList();
// 关键:触发 SSE 连接(先断开旧连接,再循环连接新设备)
break;
case 'link':
this.disabled = true;
let equipmentIds = []
let arr = []
this.list.forEach(ele => {
console.log('ele',ele);
arr.push(ele.equipmentId)
});
console.log('arr', arr);
equipmentIds = arr
this.connectSSEBatch(this.listQuery.equipmentIds.length > 0 ? this.listQuery.equipmentIds : equipmentIds);
break;
default:
}
},
getDataList() {
if (this.disabled === true) {
this.closeAllSSE(); // 直接调用批量关闭方法
this.disabled = false;
this.isSSEConnected = false; // 重置连接状态
}
this.dataListLoading = true;
this.listQuery.equipmentIds = this.searchBarData.equipmentIds || [];
// this.listQuery.equipmentIds = [200301, 200302, 200303];
this.listQuery.lineId = this.searchBarData.lineId ? this.searchBarData.lineId : undefined;
this.listQuery.equipmentTypeId = this.searchBarData.equipmentTypeId ? this.searchBarData.equipmentTypeId : undefined
// this.listQuery.status = this.status ? this.status : undefined
this.urlOptions.getDataListURL(this.listQuery).then(res => {
this.list = res.data.dets
this.countEq = res.data.countEq
this.countRun = res.data.countRun
this.countStop = res.data.countStop
// console.log(this.tableDataCustom);
});
},
handleGetData(data) {
this.addOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(data);
});
},
handleAlarm(data) {
this.alarmOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.alarmOrUpdate.init(data);
});
},
handleTabClick() {
console.log('activeLabel', this.activeLabel);
if (this.activeLabel === 'running') {
this.status = 0
} else if (this.activeLabel === 'stop') {
this.status = 1
} else{
this.status = undefined
}
},
closeSSE() {
this.isDestroyed = true;
this.closeAllSSE(); // 调用批量关闭方法
console.log('组件销毁SSE 所有连接已强制关闭');
},
isValidData(data) {
return data.trim().startsWith('data:{') && !data.includes('heartbeat');
},
// 方案1箭头函数绑定 this推荐
connectSSEBatch(equipmentIds) {
console.log('equipmentIds'.equipmentIds);
this.closeAllSSE(); // 此时 this 指向组件实例
if (!equipmentIds || equipmentIds.length === 0) {
console.log('未选中任何设备,无需建立 SSE 连接');
return;
}
// 循环调用 getDatathis 正确指向组件
for (const equipmentId of equipmentIds) {
this.getData(equipmentId);
}
this.isSSEConnected = true;
},
// 批量关闭所有 SSE 连接
closeAllSSE() {
this.isSSEConnected = false;
if (this.abortController) {
this.abortController.abort();
this.abortController = null;
}
this.currentSSEReaders.forEach(reader => {
reader.cancel().catch(err => console.log('关闭 SSE 读取器失败:', err));
});
this.currentSSEReaders = [];
console.log('所有 SSE 连接已断开');
},
// 异步连接单个设备的 SSE
async getData(equipmentId) {
console.log('开始连接设备 SSE', equipmentId);
if (this.isDestroyed) return;
const url = process.env.VUE_APP_BASE_API +
`/admin-api/monitoring/equMonitorMessage/subscribe/${equipmentId}-${Date.now()}`;
const token = getAccessToken();
const headers = new Headers({
Authorization: `Bearer ${token}`,
'tenant-id': store.getters.userId,
'Content-Type': 'text/event-stream',
});
try {
if (!this.abortController) {
this.abortController = new AbortController();
}
const response = await fetch(url, {
method: 'GET',
headers: headers,
signal: this.abortController.signal,
});
if (!response.ok) {
console.error(`设备 ${equipmentId} SSE 连接失败:${response.status} - ${response.statusText}`);
return;
}
const sseReader = response.body.getReader();
this.currentSSEReaders.push(sseReader);
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await sseReader.read();
if (done) {
console.log(`设备 ${equipmentId} SSE 连接正常关闭`);
this.currentSSEReaders = this.currentSSEReaders.filter(reader => reader !== sseReader);
if (!this.isDestroyed) {
this.handleReconnect(equipmentId);
}
break;
}
const chunk = decoder.decode(value, { stream: true });
buffer += chunk;
const messages = buffer.split('\n\n');
buffer = messages.pop() || '';
for (const message of messages) {
if (this.isValidData(message)) {
this.upDateMsg(message, equipmentId);
}
}
}
} catch (error) {
if (error.name === 'AbortError') return;
console.error(`设备 ${equipmentId} SSE 连接异常:`, error);
if (!this.isDestroyed) {
this.handleReconnect(equipmentId);
}
}
},
// 处理 SSE 推送数据
upDateMsg(data, equipmentId) {
const jsonStr = data.replace(/^data:/, '').trim();
console.log(`设备 ${equipmentId} 收到 SSE 数据:`, jsonStr);
try {
const dataObj = JSON.parse(jsonStr);
this.dataObj = dataObj;
// 更新对应设备的报警数量
const targetEqIndex = this.list.findIndex(item => item.equipmentId === equipmentId);
if (targetEqIndex !== -1) {
const updatedEq = { ...this.list[targetEqIndex] };
updatedEq.paramAlarms = dataObj.paramAlarms ? dataObj.paramAlarms : [];
updatedEq.paramCount = dataObj.paramAlarms ? dataObj.paramAlarms.length : 0;
// 更新参数异常数量equipmentAlarms 长度)
updatedEq.equipmentAlarms = dataObj.equipmentAlarms ? dataObj.equipmentAlarms : [];
updatedEq.equipmentCount = dataObj.equipmentAlarms ? dataObj.equipmentAlarms.length : 0;
updatedEq.paramMonitors = dataObj.paramMonitors ? dataObj.paramMonitors : [];
this.list.splice(targetEqIndex, 1, updatedEq);
}
console.log(' this.list', this.list);
// if (this.list.length > 0) { // 确保数组有数据
// const firstEqIndex = 0; // 固定取第一个元素
// const updatedEq = { ...this.list[firstEqIndex] }; // 深拷贝第一个元素
// // 更新警告数量paramAlarms 长度)
// updatedEq.paramAlarms = dataObj.paramAlarms ? dataObj.paramAlarms : [];
// updatedEq.paramCount = dataObj.paramAlarms ? dataObj.paramAlarms.length : 0;
// // 更新参数异常数量equipmentAlarms 长度)
// updatedEq.equipmentAlarms = dataObj.equipmentAlarms ? dataObj.equipmentAlarms : [];
// updatedEq.equipmentCount = dataObj.equipmentAlarms ? dataObj.equipmentAlarms.length : 0;
// updatedEq.paramMonitors = dataObj.paramMonitors ? dataObj.paramMonitors : [];
// // 响应式更新数组第一个元素Vue 会检测到变化并刷新页面)
// this.list.splice(firstEqIndex, 1, updatedEq);
// }
// console.log('更新后的 list 数组:', this.list);
} catch (e) {
console.error(`设备 ${equipmentId} SSE 数据解析失败:`, e);
}
},
// 重连方法
handleReconnect(equipmentId) {
if (this.isDestroyed) return;
const maxRetries = 5;
if (this.retryCount < maxRetries) {
const delay = Math.pow(2, this.retryCount) * 1000;
setTimeout(() => {
this.retryCount++;
this.getData(equipmentId);
}, delay);
} else {
console.error(`设备 ${equipmentId} SSE 重连次数已达上限`);
this.retryCount = 0;
}
},
}
}
</script>
<style lang="scss" scoped>
// 全局容器占满屏幕高度减去90px可根据实际需求调整
.app-container {
width: 100%;
height: calc(100vh - 90px);
background: #f2f4f9;
padding: 8px 0px;
// display: flex;
// flex-direction: column;
// overflow: hidden;
}
// 顶部导航栏固定高度40px
.top-nav {
height: 40px;
width: 100%;
background: #f2f4f9;
}
// 搜索栏固定高度自身内容高度底部间距8px
.search-container {
width: 100%;
margin-bottom: 8px;
border-radius: 8px;
padding: 16px 16px 18px 16px;
background-color: #ffffff;
}
.content-container {
// width: 1640px;
// height: 730px;
// background: #FFFFFF;
border-radius: 8px;
margin-top: 8px;
height: calc(100vh - 350px);
padding: 16px 16px 18px 16px;
background-color: #ffffff;
.content {
position: relative;
height: calc(100% - 40px); // 减去标签栏/图例的占用高度40px 可微调)
overflow: hidden; // 隐藏父容器溢出,避免影响整体布局
// display: flex;
.legend {
display: flex;
position: absolute;
margin-left: 24px;
gap: 14px;
top: -3px;
left: 400px;
.legend-item {
display: flex;
gap: 6px;
align-items: center;
.circle {
width: 10px;
height: 10px;
border-radius: 50%;
}
.run {
background: #0BDBFF;
box-shadow: 0px 0px 6px 0px #0BDBFF;
}
.stop {
background: #FF760B;
box-shadow: 0px 0px 6px 0px #FF760B;
}
.title {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 16px;
color: #000000;
// line-height: 16px;
text-align: left;
font-style: normal;
}
}
}
.eqRun {
width: 100%;
display: flex;
gap: 8px;
flex-wrap: wrap; // 保留换行功能
height: 100%; // 继承父容器高度,作为滚动触发条件
overflow-y: auto; // 超出高度时显示滚动功能(可手动滑动)
overflow-x: hidden; // 禁止横向滚动,避免布局错乱
padding: 0px 0 16px 0; // 上下预留空间,避免最后一行被遮挡
// 隐藏滚动条(兼容主流浏览器,视觉更简洁)
&::-webkit-scrollbar {
width: 0; // Chrome/Safari 滚动条宽度设为0
height: 0;
}
scrollbar-width: none; // Firefox 隐藏滚动条
-ms-overflow-style: none; // IE/Edge 隐藏滚动条
.eq-item-disabled-false {
width: 394px !important;
height: 142px !important;
background: #FFFFFF !important;
border-radius: 8px !important;
border: 1px solid #E0E0E0 !important;
// 取消 hover 效果disabled=false 时不需要高亮)
// &:hover {
// box-shadow: none !important;
// .bottom {
// .runBottom {
// background: #E6F1FC !important;
// box-shadow: none !important;
// opacity: 0.4 !important;
// }
// }
// }
}
// 2. disabled 为 true 时的样式(保留原有样式,统一放在此类中)
.eq-item-disabled-true {
width: 394px;
height: 142px;
background: #FFFFFF;
border-radius: 8px;
border: 1px solid #C2D5FF;
&:hover {
box-shadow: 0px 2px 6px 0px #D7D7D7;
.bottom {
.runBottom {
background: #E6F1FC !important;
box-shadow: 0px 2px 6px 0px #D7D7D7 !important;
border-radius: 4px !important;
border: 1px solid #A3D0FD !important;
opacity: 1 !important;
cursor: pointer;
}
}
}
}
.eqItem {
// width: 394px;
// height: 142px;
// background: #FFFFFF;
// border-radius: 8px;
// border: 1px solid #C2D5FF;
// &:hover {
// box-shadow: 0px 2px 6px 0px #D7D7D7;
// // opacity: .1 !important; // 取消原有透明效果,让样式更清晰
// .bottom {
// .runBottom {
// background: #E6F1FC !important; // 覆盖原有背景
// box-shadow: 0px 2px 6px 0px #D7D7D7 !important; // 添加阴影
// border-radius: 4px !important;
// border: 1px solid #A3D0FD !important;
// opacity: 1 !important; // 取消原有透明效果,让样式更清晰
// cursor: pointer; // 可选:添加指针样式,提示可点击
// }
// }
// }
.title {
display: flex;
padding: 14px 22px 0 16px;
justify-content: space-between;
.eqName {
display: flex;
gap: 6px;
align-items: center;
.circle {
width: 10px;
height: 10px;
border-radius: 50%;
}
.run {
background: #0BDBFF;
box-shadow: 0px 0px 6px 0px #0BDBFF;
}
.stop {
background: #FF760B;
box-shadow: 0px 0px 6px 0px #FF760B;
}
.name {
// width: 133px;
// height: 16px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 16px;
color: #000000;
line-height: 16px;
text-shadow: 0px 2px 6px #D7D7D7;
text-align: left;
font-style: normal;
}
}
.alarmNum {
width: 67px;
height: 18px;
margin-bottom: -2px;
background: #FFEFEC;
// box-shadow: 0px 2px 6px 0px #D7D7D7;
border-radius: 3px;
border: 1px solid #FF5454;
// width: 58px;
// height: 16px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: #FF2020;
line-height: 16px;
text-shadow: 0px 2px 6px #D7D7D7;
text-align: center;
font-style: normal;
}
}
.warningHours {
display: flex;
padding: 9px 22px 0 16px;
justify-content: space-between;
.hours {
// width: 133px;
// height: 16px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 16px;
color: #000000;
// line-height: 16px;
text-shadow: 0px 2px 6px #D7D7D7;
text-align: left;
font-style: normal;
}
.warning {
margin-bottom: -2px;
width: 94px;
height: 18px;
background: #FFF7DF;
// box-shadow: 0px 2px 6px 0px #D7D7D7;
border-radius: 3px;
border: 1px solid #FFBD02;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: #FF760B;
line-height: 16px;
text-shadow: 0px 2px 6px #D7D7D7;
text-align: center;
font-style: normal;
}
}
.progress-container {
margin: 8px 24px 0 16px;
width: 348px;
height: 14px;
background: #F1F1F1;
// box-shadow: 0px 2px 6px 0px #D7D7D7;
border-radius: 9px;
overflow: hidden;
font-size: 14px;
color: #333;
position: relative;
}
.progress-running {
// width: 70%;
height: 100%;
background: #1677ff;
float: left;
line-height: 24px;
// padding-left: 8px;
}
.progress-text {
margin: 4px 24px 0 24px;
width: 348px;
display: flex;
justify-content: space-between;
margin-top: 4px;
.run {
// width: 133px;
// height: 16px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: #0B58FF;
line-height: 16px;
text-shadow: 0px 2px 6px #D7D7D7;
text-align: left;
font-style: normal;
}
.stop {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: #7D7D7D;
line-height: 16px;
text-shadow: 0px 2px 6px #D7D7D7;
text-align: right;
font-style: normal;
}
}
.bottom {
margin-top: 8px;
display: flex;
.runBottom {
width: 198px;
height: 32px;
background: #E6F1FC;
border-radius: 4px;
border: 1px solid #A3D0FD;
opacity: 0.4;
// width: 120px;
// height: 20px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: #0B58FF;
line-height: 30px;
text-align: center;
font-style: normal;
text-transform: uppercase;
letter-spacing: 2px;
}
}
}
}
}
}
.num-container {
width: 100%;
margin-top: 8px;
gap: 8px;
display: flex;
.equipmentNum {
background-image: url('../../../assets/images/equipmentNum.png');
background-size: 100% 100%;
width: 541px;
height: 104px;
.equipment-info {
margin-left: 110px;
display: flex;
.info {
margin-top: 24px;
display: flex;
flex-direction: column;
gap: 8px;
.num {
width: 143px;
font-family: PingFangSC, PingFang SC;
font-weight: 600;
font-size: 33px;
line-height: 28px;
letter-spacing: 2px;
text-align: left;
font-style: normal;
}
.equipment {
color: #0B58FF;
}
.title {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 16px;
color: #000000;
line-height: 19px;
letter-spacing: 2px;
text-align: left;
font-style: normal;
}
}
.numImg {
margin-top: 11px;
margin-left: 99px;
}
}
}
.runNum {
background-image: url('../../../assets/images/runNum.png');
background-size: 100% 100%;
width: 541px;
height: 104px;
.equipment-info {
margin-left: 110px;
display: flex;
.info {
margin-top: 24px;
display: flex;
flex-direction: column;
gap: 8px;
.num {
width: 143px;
font-family: PingFangSC, PingFang SC;
font-weight: 600;
font-size: 33px;
color: #0B58FF;
line-height: 28px;
letter-spacing: 2px;
text-align: left;
font-style: normal;
}
.run {
color: rgba(76, 208, 231, 1);
}
.title {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 16px;
color: #000000;
line-height: 19px;
letter-spacing: 2px;
text-align: left;
font-style: normal;
}
}
.numImg {
margin-top: 11px;
margin-left: 99px;
}
}
}
.stopNum {
background-image: url('../../../assets/images/stopNum.png');
background-size: 100% 100%;
width: 541px;
height: 104px;
.equipment-info {
margin-left: 110px;
display: flex;
.info {
margin-top: 24px;
display: flex;
flex-direction: column;
gap: 8px;
.num {
width: 143px;
font-family: PingFangSC, PingFang SC;
font-weight: 600;
font-size: 33px;
color: #0B58FF;
line-height: 28px;
letter-spacing: 2px;
text-align: left;
font-style: normal;
}
.stop {
color: rgba(255, 118, 11, 1);
}
.title {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 16px;
color: #000000;
line-height: 19px;
letter-spacing: 2px;
text-align: left;
font-style: normal;
}
}
.numImg {
margin-top: 11px;
margin-left: 99px;
}
}
}
}
:deep(.custom-tabs) {
.el-tabs__header {
margin-bottom: 8px;
display: inline-block;
transform: translateY(-12px);
}
.el-tabs__content {
overflow: visible;
}
.el-tabs__item {
padding-left: 0 !important;
padding-right: 0 !important;
line-height: 36px !important;
width: 132px;
letter-spacing: 2px;
height: 36px;
}
}
.tooltips-item {
width: 256px;
height: 56px;
display: flex;
align-items: center;
gap: 4px;
.text {
display: flex;
flex-direction: column;
gap: 4px;
.time {
// width: 225px;
// height: 40px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: #000000;
// line-height: 20px;
text-align: left;
font-style: normal;
}
.alarm {
// width: 225px;
// height: 40px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: #000000;
// line-height: 20px;
text-align: left;
font-style: normal;
}
}
.line {
width: 2px;
height: 34px;
background: #FF760B;
border-radius: 1px;
}
}
/* 更具体的选择器 */
</style>
<style lang="scss">
.el-tooltip__popper.is-light.no-border-tooltip {
border: none !important;
border-width: 0 !important;
padding: 12px 16px;
box-shadow: 0px 2px 6px 0px #D7D7D7 !important;
}
// 全局公共样式
.energyOverlimitLog {
.searchBarBox {
margin-bottom: 0;
}
.blue-block {
float: left;
display: inline-block;
width: 4px;
height: 16px;
background-color: #0b58ff;
border-radius: 1px;
margin-right: 8px;
margin-top: 6px;
}
.tip {
display: inline-block;
font-size: 16px;
margin-right: 8px;
// margin-top: 10px;
margin-bottom: 16px;
}
}
</style>