You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1555 lines
48 KiB
1555 lines
48 KiB
<template>
|
|
<div class="env-container">
|
|
<div class="env-top-title">
|
|
<img src="@/assets/images/logo-2.png" alt="">
|
|
<p>档案库房智能管理系统</p>
|
|
</div>
|
|
<div class="header-date">
|
|
<div class="time">{{ nowDate.split(' ')[1] }}</div>
|
|
<div class="time-other">
|
|
<span>{{ currentWeek }}</span>
|
|
<span>{{ nowDate.split(' ')[0] }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="env-main">
|
|
<div class="env-main-left">
|
|
<div class="env-item container-wrap" style="height: 130px !important; ">
|
|
<span class="right-top-line" />
|
|
<span class="left-bottom-line" />
|
|
<h3>
|
|
<svg-icon icon-class="danganjieyue" style="margin-right:10px" />档案借阅
|
|
</h3>
|
|
<div class="chart-wrapper">
|
|
<lend-across :lend-data="lendData" :refreshtime="refreshtime" />
|
|
</div>
|
|
</div>
|
|
<div class="env-item container-wrap left-wrap">
|
|
<span class="right-top-line" />
|
|
<span class="left-bottom-line" />
|
|
<h3>
|
|
<i class="iconfont icon-kongqizhiliangshuju" />环控数据
|
|
</h3>
|
|
<!-- <ul
|
|
class="leakage-list"
|
|
:style="{
|
|
height: showScroll ? 'calc(100% - 40px)' : 'auto',
|
|
overflow: showScroll ? 'auto' : 'hidden'
|
|
}"
|
|
>
|
|
<li
|
|
v-for="item in validDisplayConfigData"
|
|
:key="item.id"
|
|
:class="{ 'leakage-warn': item.NetStatus === 0 }"
|
|
:style="{
|
|
width: liWidth,
|
|
height: liHeight,
|
|
marginRight: '12px',
|
|
marginBottom: '12px'
|
|
}"
|
|
>
|
|
<p><i class="iconfont icon-shebei" />{{ item.Name }}</p>
|
|
<span class="leakage-state-tip" />
|
|
</li>
|
|
</ul> -->
|
|
<ul v-if="newAlarm && newAlarm.length !== 0" class="screen-env-list">
|
|
<!-- <li :class="alarmStatus.infrared === '告警' ? 'li-warn alarm-status' : 'alarm-status'">
|
|
<div class="msg-txt">
|
|
|
|
<p class="msg-list-unit">红外 </p>
|
|
<span class="msg-list-num">{{ alarmStatus.infrared }}</span>
|
|
</div>
|
|
</li>
|
|
<li :class="alarmStatus.fire === '告警' ? 'li-warn alarm-status' : 'alarm-status'">
|
|
<div class="msg-txt">
|
|
<p class="msg-list-unit">消防 </p>
|
|
<span class="msg-list-num">{{ alarmStatus.fire }}</span>
|
|
</div>
|
|
</li>
|
|
<li :class="alarmStatus.waterLeak === '告警' ? 'li-warn alarm-status' : 'alarm-status'">
|
|
<div class="msg-txt">
|
|
<p class="msg-list-unit">漏水 </p>
|
|
<span class="msg-list-num">{{ alarmStatus.waterLeak }}</span>
|
|
</div>
|
|
</li>-->
|
|
<li>
|
|
<svg-icon icon-class="pm25" class-name="msg-list-svg" />
|
|
<div class="msg-txt">
|
|
<span class="msg-list-num">{{ avgData.pm25 }}</span>
|
|
<p class="msg-list-unit">PM2.5浓度 <br>{{ avgData.pm25Unit }}</p>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<svg-icon icon-class="pm10" class-name="msg-list-svg" />
|
|
<div class="msg-txt">
|
|
<span class="msg-list-num">{{ avgData.pm10 }}</span>
|
|
<p class="msg-list-unit">PM10浓度 <br>{{ avgData.pm10Unit }}</p>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<svg-icon icon-class="voc" class-name="msg-list-svg" />
|
|
<div class="msg-txt">
|
|
<span class="msg-list-num">{{ avgData.tvoc }}</span>
|
|
<p class="msg-list-unit">TVOC {{ avgData.tvocUnit }}</p>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<svg-icon icon-class="co2" class-name="msg-list-svg" />
|
|
<div class="msg-txt">
|
|
<span class="msg-list-num">{{ avgData.co2 }}</span>
|
|
<p class="msg-list-unit">二氧化碳 {{ avgData.co2Unit }}</p>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<svg-icon icon-class="jiaquan" class-name="msg-list-svg" style="font-size: 68px;" />
|
|
<div class="msg-txt">
|
|
<span class="msg-list-num">{{ avgData.formaldehyde }}</span>
|
|
<p class="msg-list-unit">甲醛 {{ avgData.formaldehydeUnit }}</p>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="env-item container-wrap left-wrap">
|
|
<span class="right-top-line" />
|
|
<span class="left-bottom-line" />
|
|
<h3>
|
|
<i class="iconfont icon-kongqizhiliangshuju" />设备联调状态
|
|
</h3>
|
|
<div style="display: flex; justify-content: space-between; height: calc(100% - 40px);">
|
|
<ul v-if="hasValidData" class="leakage-list">
|
|
<li :class="alarmStatus.infrared === '告警' ? 'leakage-warn' : ''">
|
|
<p><i class="iconfont icon-shebei" />红外</p>
|
|
<span class="leakage-state-tip" />
|
|
</li>
|
|
<li :class="alarmStatus.fire === '告警' ? 'leakage-warn' : ''">
|
|
<p><i class="iconfont icon-shebei" />消防</p>
|
|
<span class="leakage-state-tip" />
|
|
</li>
|
|
<li :class="alarmStatus.waterLeak === '告警' ? 'leakage-warn' : ''">
|
|
<p><i class="iconfont icon-shebei" />漏水</p>
|
|
<span class="leakage-state-tip" />
|
|
</li>
|
|
</ul>
|
|
<ul class="leakage-list">
|
|
<li
|
|
v-for="item in validDisplayConfigData.slice().sort((a, b) => {
|
|
// 空值处理
|
|
if (!a.Name) return 1;
|
|
if (!b.Name) return -1;
|
|
// 按长度升序,长度相同则按名称排序
|
|
if (a.Name.length === b.Name.length) {
|
|
return a.Name.localeCompare(b.Name, 'zh-CN', { numeric: true });
|
|
}
|
|
return a.Name.length - b.Name.length; // 升序;降序则 b.Name.length - a.Name.length
|
|
})"
|
|
:key="item.id"
|
|
:class="{ 'leakage-warn': item.NetStatus === 0 }"
|
|
>
|
|
<p><i class="iconfont icon-shebei" />{{ item.Name }}</p>
|
|
<span class="leakage-state-tip" />
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="env-main-middle">
|
|
<div class="env-3d">
|
|
<iframe id="myIframe" ref="myIframe" name="iframeMap" class="iframe_box" src="/web3D/index.html" frameborder="0" scrolling="no" style=" margin: 0 auto; display: block;" />
|
|
</div>
|
|
<div class="env-alarm-container">
|
|
<ul v-if="hasValidData" class="env-alarm-list env-alarm-list-first">
|
|
<li>
|
|
<svg-icon icon-class="temperature" class-name="msg-list-svg" />
|
|
<div>
|
|
<span>{{ avgData.temperature }} </span>
|
|
<p>温度 {{ avgData.temperatureUnit }}</p>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<svg-icon icon-class="shidu" class-name="msg-list-svg" />
|
|
<div>
|
|
<span>{{ avgData.humidity }}</span>
|
|
<p>湿度 {{ avgData.humidityUnit }}</p>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
<div
|
|
v-if="hasValidData"
|
|
class="air-quality"
|
|
:class="[
|
|
aqiStatus === '优' ? 'air-excellent' : '',
|
|
aqiStatus === '良' ? 'air-good' : '',
|
|
aqiStatus === '轻度污染' ? 'air-lightPollution' : '',
|
|
aqiStatus === '中度污染' ? 'air-mediumPollution' : '',
|
|
aqiStatus === '重度污染' ? 'air-heavyPollution' : '',
|
|
aqiStatus === '严重污染' ? 'air-severePollution' : ''
|
|
]"
|
|
>
|
|
<h3>空气质量指数</h3>
|
|
<div class="air-params">
|
|
<div class="air-left">
|
|
<span class="air-title">实时AQI</span>
|
|
<div class="air-result">{{ aqiValue }}</div>
|
|
</div>
|
|
<div class="air-right">
|
|
<span>空气质量</span>
|
|
<!-- <p>{{ aqiStatus }}</p> -->
|
|
<p class="air-status-text" v-html="formatAqiStatus" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- <el-row :gutter="10" class="panel-group" type="flex" justify="space-between">
|
|
<el-col class="card-panel-col">
|
|
<div class="card-panel zaixianshebei">
|
|
<div class="card-panel-icon-wrapper icon-shopping">
|
|
<svg-icon icon-class="zaixianshebei" class-name="card-panel-icon" />
|
|
</div>
|
|
<div class="card-panel-description">
|
|
<div class="card-panel-text">
|
|
<count-to v-if="getDeviceFlag" :start-val="0" :end-val="onlineDeviceNum" :duration="3200" class="card-panel-num" />
|
|
<div v-if="!getDeviceFlag" class="card-panel-text"><span class="card-panel-num">获取中...</span></div>
|
|
</div>
|
|
在线设备
|
|
</div>
|
|
</div>
|
|
</el-col>
|
|
<el-col class="card-panel-col">
|
|
<div class="card-panel lixianshebei">
|
|
<div class="card-panel-icon-wrapper icon-shopping">
|
|
<svg-icon icon-class="lixianshebei" class-name="card-panel-icon" />
|
|
</div>
|
|
<div class="card-panel-description">
|
|
<div class="card-panel-text">
|
|
<count-to v-if="getDeviceFlag" :start-val="0" :end-val="offlineDeviceNum" :duration="3200" class="card-panel-num" />
|
|
<div v-if="!getDeviceFlag" class="card-panel-text"><span class="card-panel-num">获取中...</span></div>
|
|
</div>
|
|
离线设备
|
|
</div>
|
|
</div>
|
|
</el-col>
|
|
</el-row> -->
|
|
<ul class="env-alarm-list env-alarm-list-first">
|
|
<li>
|
|
<svg-icon icon-class="zaixian" class-name="msg-list-svg" />
|
|
<div>
|
|
<!-- <span>{{ avgData.temperature }} </span> -->
|
|
<count-to v-if="getDeviceFlag" :start-val="0" :end-val="onlineDeviceNum" :duration="3200" class="card-panel-num" />
|
|
<div v-if="!getDeviceFlag" class="card-panel-text"><span class="card-panel-num">获取中...</span></div>
|
|
<p>在线设备</p>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<svg-icon icon-class="lixian" class-name="msg-list-svg" />
|
|
<div>
|
|
<!-- <span>{{ avgData.humidity }}</span> -->
|
|
<count-to v-if="getDeviceFlag" :start-val="0" :end-val="offlineDeviceNum" :duration="3200" class="card-panel-num" />
|
|
<div v-if="!getDeviceFlag" class="card-panel-text"><span class="card-panel-num">获取中...</span></div>
|
|
<p>离线设备</p>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="env-main-right">
|
|
<!-- 环控实时报警 -->
|
|
<warehouse-warning :height="'calc(100% - 38px)'" />
|
|
<!-- 门禁出入记录 -->
|
|
<AccessDoor :height="'calc(100% - 40px)'" size="4" />
|
|
<!-- 当日告警统计 -->
|
|
<div class="env-item container-wrap">
|
|
<span class="right-top-line" />
|
|
<span class="left-bottom-line" />
|
|
<h3>
|
|
<svg-icon icon-class="alerm" class-name="warehouse-svg" />当日告警统计
|
|
</h3>
|
|
<div class="chart-wrapper" style="height: calc(100% - 40px);">
|
|
<catePie :cate-data="alarmChartData" :refreshtime="refreshtime" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import CountTo from 'vue-count-to'
|
|
import { getCurrentTime } from '@/utils/index'
|
|
import lendAcross from '@/views/components/echarts/lendAcross.vue'
|
|
import catePie from './module/catePie.vue'
|
|
import WarehouseWarning from '@/views/components/WarehouseWarning'
|
|
import AccessDoor from '@/views/components/AccessDoor'
|
|
import { statisticsCrud } from '@/views/system/archiveStatistics/mixins/statistics'
|
|
import displayConfigApi from '@/api/storeManage/displayConfig'
|
|
import { getDeviceOnoff, getTodayHikAlarmLog } from '@/api/home/device'
|
|
import alarmApi from '@/api/home/alarm'
|
|
// import { allDeviceData, mockIpData } from './index.js'
|
|
|
|
// const mockFetchDataForIP = (params) => {
|
|
// return new Promise((resolve) => {
|
|
// setTimeout(() => {
|
|
// const ip = params.ip
|
|
// const result = mockIpData[ip] || { code: 200, message: '操作成功', data: [], timestamp: Date.now() }
|
|
// resolve(result.data)
|
|
// }, 500)
|
|
// })
|
|
// }
|
|
|
|
export default {
|
|
name: 'EnvironmentalScreen',
|
|
components: {
|
|
CountTo,
|
|
WarehouseWarning,
|
|
AccessDoor,
|
|
lendAcross,
|
|
catePie
|
|
},
|
|
mixins: [statisticsCrud],
|
|
data() {
|
|
return {
|
|
bannerRoomName: '3F 全景图',
|
|
nowDate: '',
|
|
currentWeek: this.getCurrentWeek(),
|
|
timer: null,
|
|
echartsTimer: null,
|
|
roomId: 'D6490DA3D4261E8C26D0E3',
|
|
allDisplayConfigData: [],
|
|
displayConfigData: [],
|
|
url: '',
|
|
allDeviceIds: [], // 所有设备IP(无排除)
|
|
refreshtime: 60000,
|
|
lendData: [],
|
|
typeData: [],
|
|
newAlarm: [], // 所有设备的原始数据
|
|
aqiValue: 45,
|
|
aqiStatus: '健康',
|
|
// 需展示的指标列表(包含所有要计算的指标)
|
|
keepIndicators: [
|
|
'二氧化碳',
|
|
'CO2浓度',
|
|
'甲醛',
|
|
'综合气体',
|
|
'PM2.5浓度',
|
|
'PM10浓度',
|
|
'温度',
|
|
'湿度',
|
|
'空气质量',
|
|
'TVOC',
|
|
'红外',
|
|
'消防',
|
|
'漏水'
|
|
],
|
|
iframeWin: null,
|
|
// 平均值数据(新增温湿度和单位字段)
|
|
avgData: {
|
|
temperature: '0.00', // 温度
|
|
temperatureUnit: '', // 温度单位
|
|
humidity: '0.00', // 湿度
|
|
humidityUnit: '', // 湿度单位
|
|
pm25: '0.00',
|
|
pm25Unit: '', // PM2.5单位
|
|
tvoc: '0.00',
|
|
tvocUnit: '', // TVOC单位
|
|
pm10: '0.00',
|
|
pm10Unit: '', // PM10单位
|
|
co2: '0.00',
|
|
co2Unit: '', // 二氧化碳单位
|
|
formaldehyde: '0.00',
|
|
formaldehydeUnit: '' // 甲醛单位
|
|
},
|
|
// 告警状态(ON=告警,OFF=正常)
|
|
alarmStatus: {
|
|
infrared: '正常',
|
|
fire: '正常',
|
|
waterLeak: '正常'
|
|
},
|
|
aqiGradeStandard: [
|
|
{ aqiMin: 0, aqiMax: 50, level: '优', colorKey: 'excellent' },
|
|
{ aqiMin: 51, aqiMax: 100, level: '良', colorKey: 'good' },
|
|
{ aqiMin: 101, aqiMax: 150, level: '轻度污染', colorKey: 'lightPollution' },
|
|
{ aqiMin: 151, aqiMax: 200, level: '中度污染', colorKey: 'mediumPollution' },
|
|
{ aqiMin: 201, aqiMax: 300, level: '重度污染', colorKey: 'heavyPollution' },
|
|
{ aqiMin: 301, aqiMax: 500, level: '严重污染', colorKey: 'severePollution' }
|
|
],
|
|
pollutantLimits: {
|
|
pm25: { excellent: 35, good: 75, light: 115, medium: 150, heavy: 250, severe: 500 }, // μg/m³
|
|
pm10: { excellent: 50, good: 150, light: 250, medium: 350, heavy: 420, severe: 600 } // μg/m³
|
|
},
|
|
hasValidData: false, // 是否有有效数据
|
|
hkConfig: {
|
|
'username': 'admin',
|
|
'password': 'ftzn83560792',
|
|
'ip': '192.168.99.125', // 固定为目标IP
|
|
'port': '554'
|
|
},
|
|
webRtcServer: null,
|
|
camera_ip: '127.0.0.1:9527',
|
|
cameraList: [], // 摄像头列表
|
|
targetDeviceIps: ['192.168.99.101:5003', '192.168.99.101:6003', '192.168.99.101:5004'],
|
|
getDeviceFlag: false,
|
|
totalDeviceNum: 0,
|
|
onlineDeviceNum: 0,
|
|
offlineDeviceNum: 0,
|
|
alarmStatisticsRaw: [], // 原始告警统计数据
|
|
alarmChartData: [], // 格式化后给饼图的数据源
|
|
alarmRefreshTimer: null // 告警数据刷新定时器
|
|
}
|
|
},
|
|
computed: {
|
|
// 处理空气质量状态换行的计算属性
|
|
formatAqiStatus() {
|
|
const status = this.aqiStatus
|
|
// 仅当文本长度为4时,在第2个字后插入<br/>换行
|
|
if (status && status.length === 4) {
|
|
return status.slice(0, 2) + '<br/>' + status.slice(2)
|
|
}
|
|
// 其他长度(2字/3字)直接返回原文本
|
|
return status || ''
|
|
},
|
|
validDisplayConfigData() {
|
|
return this.allDisplayConfigData.filter(item => {
|
|
return item && item.Name && this.targetDeviceIps.includes(item.IP?.trim())
|
|
})
|
|
},
|
|
itemsPerRow() {
|
|
const len = this.validDisplayConfigData.length
|
|
if (len === 0) return 0
|
|
return len <= 2 ? len : 2
|
|
},
|
|
liWidth() {
|
|
if (this.itemsPerRow === 0) return '0'
|
|
return `calc(100% / ${this.itemsPerRow} - 12px)`
|
|
},
|
|
liHeight() {
|
|
const len = this.validDisplayConfigData.length
|
|
if (len === 0) return '0'
|
|
const rows = Math.ceil(len / this.itemsPerRow)
|
|
return `calc(100% / ${rows} - 12px)`
|
|
},
|
|
showScroll() {
|
|
return this.validDisplayConfigData.length > 8
|
|
}
|
|
},
|
|
async created() {
|
|
// 时间更新定时器
|
|
this.timer = setInterval(() => {
|
|
this.nowDate = getCurrentTime()
|
|
}, 1000)
|
|
this.getDevice()
|
|
this.getTodayHikAlarmLog()
|
|
// 初始化
|
|
window.getIframeLoading = this.getIframeLoading
|
|
// this.allDisplayConfigData = allDeviceData
|
|
// this.handleDeviceIpList()
|
|
|
|
await alarmApi.FetchYpGetSite().then((data) => {
|
|
if (data && data.length > 0) {
|
|
this.allDisplayConfigData = data
|
|
this.handleDeviceIpList()
|
|
} else {
|
|
this.allDisplayConfigData = []
|
|
}
|
|
})
|
|
|
|
if (this.allDeviceIds.length > 0) {
|
|
await this.getAllDevicesData()
|
|
this.calcAllAvgData() // 计算所有指标平均值
|
|
this.calcAQIByAvg() // 根据平均值计算AQI
|
|
} else {
|
|
console.warn('无设备IP数据')
|
|
this.hasValidData = false
|
|
}
|
|
},
|
|
mounted() {
|
|
this.iframeWin = this.$refs.myIframe?.contentWindow
|
|
// Echarts数据刷新定时器
|
|
this.echartsTimer = setInterval(() => {
|
|
this.lendData = []
|
|
this.typeData = []
|
|
this.alarmChartData = []
|
|
this.getBorrowerNumSta()
|
|
this.getTodayHikAlarmLog()
|
|
}, this.refreshtime)
|
|
// this.getVideoUrl()
|
|
},
|
|
beforeDestroy() {
|
|
// 清理所有定时器
|
|
if (this.timer) clearInterval(this.timer)
|
|
if (this.echartsTimer) clearInterval(this.echartsTimer)
|
|
// 销毁视频流
|
|
if (this.webRtcServer) {
|
|
this.webRtcServer.disconnect()
|
|
this.webRtcServer = null
|
|
}
|
|
// if (this.alarmRefreshTimer) clearInterval(this.alarmRefreshTimer)
|
|
},
|
|
methods: {
|
|
getTodayHikAlarmLog() {
|
|
getTodayHikAlarmLog().then(data => {
|
|
console.log('今日海康告警日志', data)
|
|
this.alarmStatisticsRaw = this.transformAlarmData(data || {})
|
|
this.formatAlarmChartData()
|
|
}).catch(error => {
|
|
console.error('获取告警统计数据失败:', error)
|
|
this.alarmStatisticsRaw = []
|
|
this.alarmChartData = []
|
|
})
|
|
},
|
|
|
|
transformAlarmData(alarmObj) {
|
|
const fieldMap = [
|
|
{ key: 'accessAlarm', name: '门禁告警' },
|
|
{ key: 'firefightingAlarm', name: '消防告警' },
|
|
{ key: 'equipmentAlarm', name: '设备告警' },
|
|
{ key: 'environmentAlarm', name: '环境告警' }
|
|
]
|
|
|
|
return fieldMap.map(item => ({
|
|
alarmValue: item.name,
|
|
num: alarmObj[item.key] || 0
|
|
}))
|
|
},
|
|
|
|
formatAlarmChartData() {
|
|
// const allZero = this.alarmStatisticsRaw.every(item => item.num === 0)
|
|
// if (allZero) {
|
|
// this.alarmChartData = [{ name: '无告警', value: 1, itemStyle: { color: '#666666' }}]
|
|
// return
|
|
// }
|
|
const alarmColorMap = {
|
|
'门禁告警': '#F65164',
|
|
'消防告警': '#FFB800',
|
|
'设备告警': '#339CFF',
|
|
'环境告警': '#1CADAB'
|
|
}
|
|
// 备用颜色列表(防止新增告警类型没有配置颜色)
|
|
const colorList = ['#F65164', '#339CFF', '#FFB800', '#1CADAB', '#9B6BCC', '#FF7D00']
|
|
|
|
this.alarmChartData = this.alarmStatisticsRaw.map((item, index) => {
|
|
const alarmName = item.alarmValue
|
|
// 优先使用配置的颜色,没有则使用备用颜色
|
|
const color = alarmColorMap[alarmName] || colorList[index % colorList.length] || '#666666'
|
|
|
|
return {
|
|
name: alarmName,
|
|
value: item.num || 0, // 保留0值
|
|
itemStyle: {
|
|
color: color
|
|
},
|
|
...(item.num === 0 ? { itemStyle: { color: color, opacity: 0.5 }} : {})
|
|
}
|
|
})
|
|
},
|
|
getDevice() {
|
|
getDeviceOnoff().then(data => {
|
|
this.getDeviceFlag = true
|
|
this.totalDeviceNum = data.deviceall.length
|
|
this.onlineDeviceNum = data.online.length
|
|
this.offlineDeviceNum = data.offline.length
|
|
// this.onlineDevice = data.online
|
|
// this.offDevice = data.offline
|
|
})
|
|
},
|
|
getVideoUrl() {
|
|
displayConfigApi.list({ storeroomId: '01A1DC2123C2B75E1A579D', isQueryAll: 1 }).then((res) => {
|
|
console.log('摄像头列表', res)
|
|
if (res && res.length > 0) {
|
|
// 只筛选IP为192.168.99.125的摄像头
|
|
this.cameraList = res.filter(item =>
|
|
item.divPosition && item.divPosition.includes('cam') &&
|
|
item.deviceInfo && item.deviceInfo.deviceIp === '192.168.99.125'
|
|
)
|
|
|
|
if (this.cameraList.length > 0) {
|
|
const targetCamera = this.cameraList[0].deviceInfo
|
|
this.hkConfig = {
|
|
username: targetCamera.deviceAccount || 'admin',
|
|
password: targetCamera.devicePassword || 'ftzn83560792',
|
|
ip: targetCamera.deviceIp || '192.168.99.125',
|
|
port: targetCamera.devicePort || '554'
|
|
}
|
|
|
|
this.$nextTick(() => {
|
|
this.initVideo()
|
|
})
|
|
} else {
|
|
this.$message({
|
|
message: '未找到IP为192.168.99.125的摄像头配置',
|
|
type: 'warning'
|
|
})
|
|
}
|
|
} else {
|
|
this.$message({
|
|
message: '请先配置摄像头',
|
|
type: 'error'
|
|
})
|
|
}
|
|
}).catch(error => {
|
|
console.error('获取摄像头列表失败', error)
|
|
this.$message({
|
|
message: '获取摄像头配置失败',
|
|
type: 'error'
|
|
})
|
|
})
|
|
},
|
|
initVideo() {
|
|
const linkSrc = process.env.NODE_ENV === 'production' ? window.g.ApiWebRtcServerUrl : process.env.VUE_APP_WEBRTCSTREAMER_API
|
|
this.camera_ip = linkSrc
|
|
console.log('hkConfig', this.hkConfig)
|
|
|
|
// 先销毁已存在的视频流
|
|
if (this.webRtcServer) {
|
|
this.webRtcServer.disconnect()
|
|
}
|
|
|
|
// 初始化新的视频流
|
|
// eslint-disable-next-line no-undef
|
|
this.webRtcServer = new WebRtcStreamer('video', location.protocol + '//' + this.camera_ip)
|
|
// 拼接RTSP地址
|
|
const rtspUrl = `rtsp://${this.hkConfig.username}:${this.hkConfig.password}@${this.hkConfig.ip}:${this.hkConfig.port}/h264/1/1`
|
|
console.log('RTSP地址:', rtspUrl)
|
|
this.webRtcServer.connect(rtspUrl)
|
|
},
|
|
/**
|
|
* 获取当前星期
|
|
*/
|
|
getCurrentWeek() {
|
|
const days = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
|
|
const date = new Date()
|
|
return days[date.getDay()]
|
|
},
|
|
/**
|
|
* 处理设备IP列表(仅去重,无排除)
|
|
*/
|
|
handleDeviceIpList() {
|
|
const ipSet = new Set()
|
|
this.allDisplayConfigData.forEach(element => {
|
|
const ip = (element.IP || '').trim()
|
|
if (ip) { // 仅过滤空IP,无其他排除逻辑
|
|
ipSet.add(ip)
|
|
}
|
|
})
|
|
this.allDeviceIds = Array.from(ipSet)
|
|
},
|
|
/**
|
|
* 获取所有设备的实时数据
|
|
*/
|
|
async getAllDevicesData() {
|
|
const allData = []
|
|
for (const ip of this.allDeviceIds) {
|
|
try {
|
|
// const data = await mockFetchDataForIP({ ip })
|
|
// 真实请求
|
|
const data = await alarmApi.FetchDataForIP({ ip })
|
|
// 过滤需要的指标并添加到总数据
|
|
const filtered = data.filter(item => this.keepIndicators.includes(item.subName)).map(item => {
|
|
if (item.subName === 'CO2浓度') {
|
|
return { ...item, subName: '二氧化碳' }
|
|
}
|
|
return item
|
|
})
|
|
if (filtered.length > 0) {
|
|
allData.push(...filtered)
|
|
}
|
|
} catch (error) {
|
|
console.error(`获取IP【${ip}】数据失败:`, error)
|
|
}
|
|
}
|
|
this.newAlarm = allData
|
|
this.hasValidData = allData.length > 0
|
|
},
|
|
/**
|
|
* 计算所有设备指标的平均值(适配ON/OFF告警状态,保留单位)
|
|
*/
|
|
calcAllAvgData() {
|
|
if (!this.hasValidData) return
|
|
|
|
// 1. 初始化各指标的总和、计数和单位
|
|
const sumMap = {
|
|
temperature: { sum: 0, count: 0, unit: '' }, // 温度
|
|
humidity: { sum: 0, count: 0, unit: '' }, // 湿度
|
|
pm25: { sum: 0, count: 0, unit: '' },
|
|
tvoc: { sum: 0, count: 0, unit: '' },
|
|
pm10: { sum: 0, count: 0, unit: '' },
|
|
co2: { sum: 0, count: 0, unit: '' },
|
|
formaldehyde: { sum: 0, count: 0, unit: '' },
|
|
infrared: [], // 红外状态(ON/OFF)
|
|
fire: [], // 消防状态(ON/OFF)
|
|
waterLeak: [] // 漏水状态(ON/OFF)
|
|
}
|
|
|
|
// 2. 遍历所有数据累加,同时记录单位
|
|
this.newAlarm.forEach(item => {
|
|
const value = parseFloat(item.value) || 0
|
|
// 提取单位(优先使用数据中的dw字段,无则用默认值)
|
|
const unit = item.dw || this.getDefaultUnit(item.subName)
|
|
|
|
switch (item.subName) {
|
|
case '温度':
|
|
sumMap.temperature.sum += value
|
|
sumMap.temperature.count++
|
|
if (!sumMap.temperature.unit && unit) sumMap.temperature.unit = unit
|
|
break
|
|
case '湿度':
|
|
sumMap.humidity.sum += value
|
|
sumMap.humidity.count++
|
|
if (!sumMap.humidity.unit && unit) sumMap.humidity.unit = unit
|
|
break
|
|
case '二氧化碳':
|
|
case 'CO2浓度':
|
|
sumMap.co2.sum += value
|
|
sumMap.co2.count++
|
|
if (!sumMap.co2.unit && unit) sumMap.co2.unit = unit
|
|
break
|
|
case 'PM2.5浓度':
|
|
sumMap.pm25.sum += value
|
|
sumMap.pm25.count++
|
|
if (!sumMap.pm25.unit && unit) sumMap.pm25.unit = unit
|
|
break
|
|
case 'TVOC':
|
|
sumMap.tvoc.sum += value
|
|
sumMap.tvoc.count++
|
|
if (!sumMap.tvoc.unit && unit) sumMap.tvoc.unit = unit
|
|
break
|
|
case 'PM10浓度':
|
|
sumMap.pm10.sum += value
|
|
sumMap.pm10.count++
|
|
if (!sumMap.pm10.unit && unit) sumMap.pm10.unit = unit
|
|
break
|
|
case '甲醛':
|
|
sumMap.formaldehyde.sum += value
|
|
sumMap.formaldehyde.count++
|
|
if (!sumMap.formaldehyde.unit && unit) sumMap.formaldehyde.unit = unit
|
|
break
|
|
// 告警类指标直接存储状态
|
|
case '红外':
|
|
sumMap.infrared.push(item.value)
|
|
break
|
|
case '消防':
|
|
sumMap.fire.push(item.value)
|
|
break
|
|
case '漏水':
|
|
sumMap.waterLeak.push(item.value)
|
|
break
|
|
}
|
|
})
|
|
console.log('累加结果:', sumMap)
|
|
|
|
// 3. 计算平均值(保留两位小数),并赋值单位
|
|
this.avgData = {
|
|
temperature: sumMap.temperature.count ? (sumMap.temperature.sum / sumMap.temperature.count).toFixed(2) : '0.00',
|
|
temperatureUnit: sumMap.temperature.unit || '℃',
|
|
humidity: sumMap.humidity.count ? (sumMap.humidity.sum / sumMap.humidity.count).toFixed(2) : '0.00',
|
|
humidityUnit: sumMap.humidity.unit || '%',
|
|
pm25: sumMap.pm25.count ? (sumMap.pm25.sum / sumMap.pm25.count).toFixed(2) : '0.00',
|
|
pm25Unit: sumMap.pm25.unit || 'ug/立方米',
|
|
tvoc: sumMap.tvoc.count ? (sumMap.tvoc.sum / sumMap.tvoc.count).toFixed(2) : '0.00',
|
|
tvocUnit: sumMap.tvoc.unit || 'LuK',
|
|
pm10: sumMap.pm10.count ? (sumMap.pm10.sum / sumMap.pm10.count).toFixed(2) : '0.00',
|
|
pm10Unit: sumMap.pm10.unit || 'ug/立方米',
|
|
co2: sumMap.co2.count ? (sumMap.co2.sum / sumMap.co2.count).toFixed(2) : '0.00',
|
|
co2Unit: sumMap.co2.unit || 'ppm',
|
|
formaldehyde: sumMap.formaldehyde.count ? (sumMap.formaldehyde.sum / sumMap.formaldehyde.count).toFixed(2) : '0.00',
|
|
formaldehydeUnit: sumMap.formaldehyde.unit || 'ppm'
|
|
}
|
|
console.log('平均值:', this.avgData)
|
|
|
|
// 4. 处理告警状态(ON=告警,OFF=正常;只要有一个ON就显示告警)
|
|
this.alarmStatus = {
|
|
infrared: sumMap.infrared.some(s => s === 'ON') ? '告警' : '正常',
|
|
fire: sumMap.fire.some(s => s === 'ON') ? '告警' : '正常',
|
|
waterLeak: sumMap.waterLeak.some(s => s === 'ON') ? '告警' : '正常'
|
|
}
|
|
|
|
// 兼容无数据的情况
|
|
if (sumMap.infrared.length === 0) this.alarmStatus.infrared = '正常'
|
|
if (sumMap.fire.length === 0) this.alarmStatus.fire = '正常'
|
|
if (sumMap.waterLeak.length === 0) this.alarmStatus.waterLeak = '正常'
|
|
},
|
|
/**
|
|
* 获取指标默认单位(兜底用)
|
|
*/
|
|
getDefaultUnit(subName) {
|
|
const unitMap = {
|
|
'温度': '℃',
|
|
'湿度': '%',
|
|
'PM2.5浓度': 'ug/立方米',
|
|
'TVOC': 'LuK',
|
|
'PM10浓度': 'ug/立方米',
|
|
'二氧化碳': 'ppm',
|
|
'CO2浓度': 'ppm',
|
|
'甲醛': 'ppm',
|
|
'综合气体': '无量纲'
|
|
}
|
|
return unitMap[subName] || ''
|
|
},
|
|
/**
|
|
* 根据所有设备的平均值计算AQI
|
|
*/
|
|
calcAQIByAvg() {
|
|
if (!this.hasValidData) {
|
|
this.aqiValue = 45
|
|
this.aqiStatus = '优'
|
|
return
|
|
}
|
|
|
|
// 1. 提取PM2.5、PM10数值(转数字,兜底0),单位:μg/m³(国标24小时平均)
|
|
const pm25 = parseFloat(this.avgData.pm25) || 0
|
|
const pm10 = parseFloat(this.avgData.pm10) || 0
|
|
console.log('PM2.5平均值:', pm25, 'μg/m³ | PM10平均值:', pm10, 'μg/m³')
|
|
|
|
// 2. 国标AQI浓度限值区间(24小时平均,μg/m³)
|
|
const aqiBpTable = {
|
|
pm25: [
|
|
{ iaqiLo: 0, iaqiHi: 50, bpLo: 0, bpHi: 35 }, // 优
|
|
{ iaqiLo: 50, iaqiHi: 100, bpLo: 35, bpHi: 75 }, // 良
|
|
{ iaqiLo: 100, iaqiHi: 150, bpLo: 75, bpHi: 115 }, // 轻度污染
|
|
{ iaqiLo: 150, iaqiHi: 200, bpLo: 115, bpHi: 150 }, // 中度污染
|
|
{ iaqiLo: 200, iaqiHi: 300, bpLo: 150, bpHi: 250 }, // 重度污染
|
|
{ iaqiLo: 300, iaqiHi: 500, bpLo: 250, bpHi: 500 } // 严重污染
|
|
],
|
|
pm10: [
|
|
{ iaqiLo: 0, iaqiHi: 50, bpLo: 0, bpHi: 50 }, // 优
|
|
{ iaqiLo: 50, iaqiHi: 100, bpLo: 50, bpHi: 150 }, // 良
|
|
{ iaqiLo: 100, iaqiHi: 150, bpLo: 150, bpHi: 250 }, // 轻度污染
|
|
{ iaqiLo: 150, iaqiHi: 200, bpLo: 250, bpHi: 350 }, // 中度污染
|
|
{ iaqiLo: 200, iaqiHi: 300, bpLo: 350, bpHi: 420 }, // 重度污染
|
|
{ iaqiLo: 300, iaqiHi: 500, bpLo: 420, bpHi: 500 } // 严重污染
|
|
]
|
|
}
|
|
|
|
// 3. 计算单污染物分指数(IAQI)的核心方法
|
|
const calculateIAQI = (conc, pollutantType) => {
|
|
const bpTable = aqiBpTable[pollutantType]
|
|
// 浓度超过上限,直接返回500
|
|
if (conc > bpTable[bpTable.length - 1].bpHi) {
|
|
return 500
|
|
}
|
|
// 匹配浓度所在区间
|
|
const matchedBp = bpTable.find(item => conc >= item.bpLo && conc <= item.bpHi) || bpTable[0]
|
|
// 国标IAQI计算公式
|
|
const iaqi = ((matchedBp.iaqiHi - matchedBp.iaqiLo) / (matchedBp.bpHi - matchedBp.bpLo)) * (conc - matchedBp.bpLo) + matchedBp.iaqiLo
|
|
// 四舍五入,且不超过500
|
|
return Math.min(Math.round(iaqi), 500)
|
|
}
|
|
|
|
// 4. 计算PM2.5、PM10的分指数
|
|
const iaqiPm25 = calculateIAQI(pm25, 'pm25')
|
|
const iaqiPm10 = calculateIAQI(pm10, 'pm10')
|
|
|
|
// 5. AQI取分指数最大值
|
|
const finalAQI = Math.max(iaqiPm25, iaqiPm10)
|
|
this.aqiValue = finalAQI
|
|
|
|
// 6. 匹配AQI对应的空气质量等级
|
|
const matchedGrade = this.aqiGradeStandard.find(grade =>
|
|
finalAQI >= grade.aqiMin && finalAQI <= grade.aqiMax
|
|
)
|
|
this.aqiStatus = matchedGrade ? matchedGrade.level : '严重污染'
|
|
// this.aqiStatus = '严重污染'
|
|
|
|
console.log('IAQI-PM2.5:', iaqiPm25, 'IAQI-PM10:', iaqiPm10, '最终AQI:', finalAQI, '| 等级:', this.aqiStatus)
|
|
},
|
|
/**
|
|
* iframe加载回调(保留原有逻辑)
|
|
*/
|
|
getIframeLoading(value) {}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style rel="stylesheet/scss" lang="scss" scoped>
|
|
@import "~@/assets/styles/lend-manage.scss";
|
|
|
|
.env-container {
|
|
width: 100%;
|
|
height: calc(100vh);
|
|
background-color: #031435;
|
|
.env-top-title {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: flex-start;
|
|
font-size: 44px;
|
|
letter-spacing: 0.1em;
|
|
text-align: center;
|
|
color: #fff;
|
|
width: calc(100vw);
|
|
height: 130px;
|
|
background: url("~@/assets/images/largeScreen/top.png") no-repeat 0 -14px;
|
|
background-size: contain;
|
|
font-family: 'LianMengQiYiLuShuaiZhengRuiHei';
|
|
img{
|
|
display: block;
|
|
height: 50px;
|
|
margin-right: 10px;
|
|
margin-top: 26px;
|
|
}
|
|
p{
|
|
margin-top: 26px;
|
|
}
|
|
}
|
|
.header-date {
|
|
position: fixed;
|
|
top: 10px;
|
|
right: 80px;
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
align-items: center;
|
|
color: #fff;
|
|
.time {
|
|
font-size: 32px;
|
|
font-weight: bold;
|
|
line-height: 30px;
|
|
padding-right: 20px;
|
|
border-right: 1px solid rgba(255, 255, 255, 0.5);
|
|
}
|
|
.time-other {
|
|
font-size: 18px;
|
|
line-height: 22px;
|
|
padding-left: 20px;
|
|
span {
|
|
display: block;
|
|
}
|
|
}
|
|
}
|
|
.env-main {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 0 25px;
|
|
margin-top: -12px;
|
|
.env-main-left,
|
|
.env-main-right {
|
|
max-width: 22%;
|
|
flex: 1;
|
|
height: calc(100vh - 138px);
|
|
overflow: hidden;
|
|
z-index: 9999;
|
|
::v-deep .el-table .el-table__body-wrapper td.el-table__cell,
|
|
::v-deep .el-table .el-table__fixed-right td.el-table__cell{
|
|
font-size: 15px !important;
|
|
}
|
|
::v-deep .el-table .el-table__header .el-table__cell .cell,
|
|
::v-deep .el-table.warehose-el-table .el-table__header .el-table__cell .cell{
|
|
font-size: 16px !important;
|
|
}
|
|
}
|
|
.env-main-middle {
|
|
position: relative;
|
|
flex: 1;
|
|
margin: 0 20px;
|
|
height: calc(100vh - 138px);
|
|
overflow: hidden;
|
|
background: url("~@/assets/images/largeScreen/bg.png") no-repeat center center;
|
|
}
|
|
.env-main-left .container-wrap {
|
|
min-height: auto;
|
|
}
|
|
.env-main-left .container-wrap.left-wrap {
|
|
height: calc(100% / 2 - 85px);
|
|
}
|
|
.env-main-right .container-wrap {
|
|
height: calc(100% / 3 - 14px);
|
|
min-height: auto;
|
|
margin-bottom: 20px;
|
|
}
|
|
.env-item {
|
|
margin-bottom: 20px;
|
|
text-align: center;
|
|
h3 {
|
|
position: relative;
|
|
display: inline-block;
|
|
padding: 10px 70px;
|
|
font-size: 20px;
|
|
color: #fff;
|
|
.iconfont {
|
|
margin-right: 10px;
|
|
font-size: 14px;
|
|
color: #f65163;
|
|
}
|
|
&::before {
|
|
content: "";
|
|
position: absolute;
|
|
left: 0;
|
|
top: 50%;
|
|
width: 36px;
|
|
height: 12px;
|
|
margin-top: -6px;
|
|
background: url("~@/assets/images/largeScreen/item-left.png") no-repeat;
|
|
background-size: cover;
|
|
}
|
|
&::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: 50%;
|
|
right: 0;
|
|
width: 36px;
|
|
height: 12px;
|
|
margin-top: -6px;
|
|
background: url("~@/assets/images/largeScreen/item-right.png") no-repeat;
|
|
background-size: cover;
|
|
}
|
|
}
|
|
|
|
}
|
|
.env-3d {
|
|
position: fixed;
|
|
left: 0;
|
|
top: 10px;
|
|
width: 100%;
|
|
// height: calc(100%);
|
|
height: 100%;
|
|
// background: url("~@/assets/images/largeScreen/bg.png") no-repeat center 0;
|
|
overflow: hidden;
|
|
.iframe_box {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.iframe_box {
|
|
/* 移除原有width/height,改用内联样式或下面的样式 */
|
|
width: 100% !important;
|
|
height: 100% !important;
|
|
border: none;
|
|
}
|
|
.banner-top-name{
|
|
position: absolute;
|
|
left: 0;
|
|
top: 80px;
|
|
padding: 0 15px;
|
|
height: 34px;
|
|
line-height: 32px;
|
|
font-size: 18px;
|
|
color: #fff;
|
|
background-color: #113d72;
|
|
border: 1px solid #339cff;
|
|
border-radius: 4px;
|
|
}
|
|
.air-quality{
|
|
// position: absolute;
|
|
// bottom: 10px;
|
|
// right: 20px;
|
|
// width: calc(100% / 3 - 100px);
|
|
width: calc(20% + 100px);
|
|
color: #fff;
|
|
margin: 0 20px 0 0;
|
|
padding: 10px;
|
|
height: 90px;
|
|
background-image: linear-gradient(to bottom, rgba(24, 176, 143, .5), rgba(24, 176, 143, 0));
|
|
border-radius: 5px;
|
|
z-index: 9999;
|
|
h3{
|
|
font-size: 18px;
|
|
padding: 0 0 6px 0;
|
|
}
|
|
.air-params{
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
.air-left{
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
align-items: center;
|
|
.air-title{
|
|
position: relative;
|
|
padding-left: 12px;
|
|
font-size: 12px;
|
|
&::before{
|
|
content: "";
|
|
position: absolute;
|
|
left: 0;
|
|
top: 50%;
|
|
width: 6px;
|
|
height: 6px;
|
|
background-color: #18B08F;
|
|
border-radius: 50%;
|
|
margin-top: -3px;
|
|
}
|
|
}
|
|
.air-result{
|
|
font-size: 30px;
|
|
font-weight: 600;
|
|
padding: 0 0 0 10px;
|
|
// p{
|
|
// font-size: 22px;
|
|
// font-weight: 600;
|
|
// padding: 0 6px 0 10px;
|
|
// }
|
|
// span{
|
|
// display: block;
|
|
// font-size: 11px;
|
|
// opacity: .6;
|
|
// }
|
|
}
|
|
}
|
|
.air-right{
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
align-items: center;
|
|
span{
|
|
display: block;
|
|
font-size: 12px;
|
|
// padding: 8px 0;
|
|
margin-right: 6px;
|
|
}
|
|
p{
|
|
font-size: 24px;
|
|
font-weight: 600;
|
|
padding: 8px 15px;
|
|
background-color: rgba(24, 176, 143, .2);
|
|
border-radius: 5px;
|
|
margin-top: -4px;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 原有基础样式保留
|
|
&.air-excellent { // 优 - 绿色
|
|
background-image: linear-gradient(to bottom, rgba(40, 180, 40, .5), rgba(40, 180, 40, 0));
|
|
.air-params .air-right p {
|
|
background-color: rgba(40, 180, 40, .2);
|
|
}
|
|
}
|
|
&.air-good { // 良 - 黄色
|
|
background-image: linear-gradient(to bottom, rgba(255, 200, 0, .5), rgba(255, 200, 0, 0));
|
|
.air-params .air-right p {
|
|
background-color: rgba(255, 200, 0, .2);
|
|
}
|
|
}
|
|
&.air-lightPollution { // 轻度污染 - 橙色
|
|
h3{
|
|
padding: 0;
|
|
}
|
|
background-image: linear-gradient(to bottom, rgba(255, 140, 0, .5), rgba(255, 140, 0, 0));
|
|
.air-params {
|
|
margin-top: -6px;
|
|
.air-right p {
|
|
background-color: rgba(255, 140, 0, .2);
|
|
}
|
|
}
|
|
}
|
|
&.air-mediumPollution { // 中度污染 - 红色
|
|
h3{
|
|
padding: 0;
|
|
}
|
|
background-image: linear-gradient(to bottom, rgba(255, 0, 0, .5), rgba(255, 0, 0, 0));
|
|
.air-params {
|
|
margin-top: -6px;
|
|
.air-right p {
|
|
background-color: rgba(255, 0, 0, .2);
|
|
}
|
|
}
|
|
}
|
|
&.air-heavyPollution { // 重度污染 - 紫色
|
|
h3{
|
|
padding: 0;
|
|
}
|
|
background-image: linear-gradient(to bottom, rgba(150, 0, 200, .5), rgba(150, 0, 200, 0));
|
|
|
|
.air-params {
|
|
margin-top: -6px;
|
|
.air-right p {
|
|
background-color: rgba(150, 0, 200, .2);
|
|
}
|
|
}
|
|
}
|
|
&.air-severePollution { // 严重污染 - 褐红色
|
|
h3{
|
|
padding: 0;
|
|
}
|
|
background-image: linear-gradient(to bottom, rgba(120, 0, 0, .5), rgba(120, 0, 0, 0));
|
|
|
|
.air-params {
|
|
margin-top: -6px;
|
|
.air-right p {
|
|
background-color: rgba(120, 0, 0, .2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.air-warn{
|
|
background-image: linear-gradient(to bottom, rgba(246, 81, 99, .5), rgba(24, 176, 143, 0));
|
|
.air-params{
|
|
.air-right{
|
|
p{
|
|
background-color: rgba(246, 81, 99, .2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.leakage-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
flex-wrap: wrap;
|
|
text-align: left;
|
|
width: calc(100% / 2 - 14px);
|
|
height: calc(100%);
|
|
margin: 0 7px;
|
|
padding: 20px 0;
|
|
li {
|
|
position: relative;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 0 15px;
|
|
border: 1px solid #3581cc;
|
|
background-color: #02255f;
|
|
border-radius: 2px;
|
|
width: calc(100%);
|
|
height: calc(100% / 4 - 10px);
|
|
margin: 10px 0;
|
|
&::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: 4px;
|
|
left: 4px;
|
|
width: 0;
|
|
height: 0;
|
|
border-color: transparent #339cff;
|
|
border-width: 0 0 6px 6px;
|
|
border-style: solid;
|
|
}
|
|
p {
|
|
font-size: 16px;
|
|
color: #fff;
|
|
i {
|
|
margin-right: 4px;
|
|
}
|
|
}
|
|
span.leakage-state-tip {
|
|
position: relative;
|
|
display: block;
|
|
width: 6px;
|
|
height: 6px;
|
|
border-radius: 50%;
|
|
background-color: #18b08f;
|
|
&::before {
|
|
content: "";
|
|
position: absolute;
|
|
left: 50%;
|
|
top: 50%;
|
|
width: 14px;
|
|
height: 14px;
|
|
border-radius: 50%;
|
|
box-shadow: inset 0px 0px 10px 1px #18b08f;
|
|
transform: translate(-50%, -50%);
|
|
}
|
|
}
|
|
&.leakage-warn {
|
|
border-color: #f65164;
|
|
box-shadow: inset 0px 0px 15px 1px #f65164;
|
|
color: #f65164;
|
|
&::before {
|
|
border-color: transparent #f65164;
|
|
}
|
|
span.leakage-state-tip {
|
|
background-color: #f65164;
|
|
&::before {
|
|
box-shadow: inset 0px 0px 10px 1px #f65164;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.env-alarm-list{
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
// width: 100%;
|
|
width: calc(40%);
|
|
li{
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
align-content: center;
|
|
justify-content: center;
|
|
height: 66px;
|
|
margin-right: 24px;
|
|
background: url('~@/assets/images/data_border_default.png') no-repeat;
|
|
background-size: 100% 100%;
|
|
position: relative;
|
|
color: #fff;
|
|
font-size: 14px;
|
|
&.li-warn{
|
|
background: url('~@/assets/images/data_border_warn.png') no-repeat;
|
|
background-size: 100% 100%;
|
|
}
|
|
p{
|
|
color: #339CFF;
|
|
font-weight: bold;
|
|
}
|
|
span{
|
|
display: block;
|
|
margin-top: 12px;
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 告警列表容器
|
|
.env-alarm-container {
|
|
position: absolute;
|
|
top: 10px;
|
|
left: 10px;
|
|
width: calc(100%);
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
|
|
// 第一行:温湿度
|
|
.env-alarm-list-first {
|
|
margin-bottom: 20px;
|
|
li {
|
|
width: calc(100% / 2 - 10px);
|
|
height: 90px;
|
|
font-size: 24px;
|
|
flex-direction: row;
|
|
& .msg-list-svg{
|
|
font-size: 40px;
|
|
margin-right: 14px;
|
|
}
|
|
& div{
|
|
span{
|
|
margin-top: 0 !important;
|
|
}
|
|
p{
|
|
font-size: 18px;
|
|
margin-top: 6px;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.env-alarm-list-second {
|
|
position: absolute;
|
|
bottom: 10px;
|
|
left: 10px;
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
flex-wrap: wrap;
|
|
color: #fff;
|
|
width: 600px;
|
|
height: 130px;
|
|
font-size: 14px;
|
|
li {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
width: calc(100% / 3 - 10px);
|
|
height: calc(100% / 3 - 14px);
|
|
background: linear-gradient(
|
|
360deg,
|
|
rgba(51, 156, 255, 0.24) 0%,
|
|
rgba(56, 158, 225, 0) 70%,
|
|
rgba(56, 158, 225, 0) 100%
|
|
);
|
|
margin-left: 10px;
|
|
margin-bottom: 14px;
|
|
padding: 0 10px;
|
|
p{
|
|
font-size: 12px;
|
|
color: #339CFF;
|
|
}
|
|
span{
|
|
display: inline-block;
|
|
font-weight: bold;
|
|
}
|
|
&.li-warn{
|
|
background: linear-gradient(
|
|
360deg,
|
|
rgba(246, 81, 100, 0.5) 0%,
|
|
rgba(56, 158, 225, 0) 70%,
|
|
rgba(56, 158, 225, 0) 100%
|
|
);
|
|
span{
|
|
color: #F65164;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.device-container{
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 10px;
|
|
}
|
|
|
|
.new-leakage-list{
|
|
width: 660px;
|
|
height: 120px;
|
|
padding: 0;
|
|
li {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
width: calc(100% / 3 - 10px);
|
|
height: calc(100% / 2 - 14px);
|
|
margin-right: 10px;
|
|
margin-bottom: 14px;
|
|
p{
|
|
font-size: 14px;
|
|
}
|
|
}
|
|
}
|
|
|
|
.device-info {
|
|
width: 600px;
|
|
height: 30px;
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
margin-bottom: 14px;
|
|
li {
|
|
width: calc(100% / 2);
|
|
height: calc(100%);
|
|
background: linear-gradient(
|
|
360deg,
|
|
rgba(51, 156, 255, 0.24) 0%,
|
|
rgba(56, 158, 225, 0) 70%,
|
|
rgba(56, 158, 225, 0) 100%
|
|
);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-left: 10px;
|
|
.row-item {
|
|
display: flex;
|
|
align-items: center;
|
|
.svg-box {
|
|
margin-right: 10px;
|
|
.card-panel-icon {
|
|
font-size: 24px;
|
|
}
|
|
}
|
|
}
|
|
.row-num {
|
|
font-size: 18px;
|
|
color: #fff;
|
|
margin-right: 10px;
|
|
}
|
|
}
|
|
}
|
|
|
|
.screen-env-list {
|
|
flex-wrap: wrap;
|
|
height: calc(100% - 54px);
|
|
padding: 0 10px;
|
|
li {
|
|
position: relative;
|
|
flex: none;
|
|
width: calc(100% / 2 - 22px);
|
|
margin: 10px 10px 0 10px;
|
|
height: calc(100% / 3 - 10px) !important;
|
|
.msg-txt{
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding-left: 40px;
|
|
height: calc(100%);
|
|
.msg-list-num{
|
|
font-size: 18px;
|
|
position: static;
|
|
}
|
|
.msg-list-unit{
|
|
font-size: 12px;
|
|
position: static;
|
|
margin-top: 8px;
|
|
}
|
|
}
|
|
.msg-list-svg {
|
|
position: absolute;
|
|
left: 20px;
|
|
top: 0;
|
|
font-size: 40px;
|
|
margin-left: 0 !important;
|
|
}
|
|
&.msg-pm {
|
|
.msg-list-svg {
|
|
font-size: 46px;
|
|
}
|
|
}
|
|
&.alarm-status{
|
|
.msg-txt{
|
|
flex-direction: row;
|
|
}
|
|
&.li-warn{
|
|
span{
|
|
color: #F65164;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.panel-group {
|
|
// width: calc(100% / 3 + 60px);
|
|
width: calc(40% - 100px);
|
|
.card-panel-col {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.card-panel {
|
|
cursor: pointer;
|
|
height: 100px;
|
|
font-size: 15px;
|
|
position: relative;
|
|
overflow: hidden;
|
|
opacity: 0.86;
|
|
&.zaixianshebei {
|
|
color: #c4c859;
|
|
background: linear-gradient(
|
|
180deg,
|
|
rgba(196, 200, 89, 0.5) 0%,
|
|
rgba(196, 200, 89, 0) 100%
|
|
);
|
|
border-top: 2px #c4c859 solid;
|
|
& span.card-panel-num {
|
|
background: linear-gradient(180deg, #ffffff 0%, #bfc458 100%);
|
|
}
|
|
}
|
|
&.lixianshebei {
|
|
color: #f65164;
|
|
background: linear-gradient(
|
|
180deg,
|
|
rgba(246, 81, 100, 0.5) 0%,
|
|
rgba(247, 80, 100, 0) 100%
|
|
);
|
|
border-top: 2px #f65164 solid;
|
|
& span.card-panel-num {
|
|
background: linear-gradient(180deg, #ffffff 0%, #f55164 100%);
|
|
}
|
|
}
|
|
.card-panel-icon-wrapper {
|
|
float: left;
|
|
margin: 0 8px;
|
|
padding: 20px 0;
|
|
transition: all 0.38s ease-out;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.card-panel-icon {
|
|
float: left;
|
|
font-size: 40px;
|
|
}
|
|
|
|
.card-panel-description {
|
|
margin: 20px 4px;
|
|
margin-left: 0px;
|
|
|
|
.card-panel-text {
|
|
line-height: 30px;
|
|
color: rgba(0, 0, 0, 0.45);
|
|
font-size: 20px;
|
|
margin-bottom: 11px;
|
|
& span {
|
|
-webkit-background-clip: text;
|
|
color: transparent;
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
::v-deep .table-title{
|
|
font-size: 20px;
|
|
}
|
|
</style>
|