|
|
<template> <div class="warehouse"> <div class="warehouse-left"> <div class="left-3d"> <iframe id="myIframe" ref="myIframe" name="iframeMap" class="iframe_box" src="/web3D/index.html" frameborder="0" scrolling="no" />
<div style="position: absolute; top: 0; left: 0;"> <p style="font-size: 12px; margin-bottom: 10px;">{{ currentDeviceName }}</p> <ul v-if="newAlarm && newAlarm.length !== 0" class="msg-list"> <li v-for="item in newAlarm" :key="item.SUBID"> <svg-icon v-if="item.subName === '温度'" icon-class="temperature" class-name="msg-list-svg" /> <svg-icon v-if="item.subName === '湿度'" icon-class="shidu" class-name="msg-list-svg" /> <svg-icon v-if="item.subName === 'PM2.5浓度'" icon-class="pm25" class-name="msg-list-svg" /> <svg-icon v-if="item.subName === 'PM10浓度'" icon-class="pm10" class-name="msg-list-svg" /> <svg-icon v-if="item.subName === 'TVOC'" icon-class="voc" class-name="msg-list-svg" /> <svg-icon v-if="item.subName === '二氧化碳'" icon-class="co2" class-name="msg-list-svg" /> <svg-icon v-if="item.subName === '甲醛'" icon-class="jiaquan" class-name="msg-list-svg" style="width: 2em; margin-left: 20px;" /> <svg-icon v-if="item.subName === '综合气体'" icon-class="comprehensive-gas" class-name="msg-list-svg" /> <svg-icon v-if="item.subName === '空气质量'" icon-class="kongqi" class-name="msg-list-svg" /> <div class="msg-txt"> <span class="msg-list-num">{{ item.value }}</span> <p class="msg-list-unit">{{ item.subName }} {{ item.dw }}</p> </div> </li> </ul> </div> <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>实时空气质量指数(AQI)</h3> <div class="air-params"> <div class="air-left"> <span class="air-title">实时AQI</span> <div class="air-result"><p>{{ aqiValue }}</p><span>(AQI-中国标准)</span></div> </div> <div class="air-right"> <span>空气质量为</span> <p>{{ aqiStatus }}</p> </div> </div> </div>
</div> </div> <div class="warehouse-right"> <warehouse-warning :height="'calc(100% - 40px)'" :storeroom-id="roomId" /> <AccessDoor :height="'calc(100% - 40px)'" /> </div> <hkVideo ref="camera" :dialog-open.sync="open" /> </div></template>
<script>import WarehouseWarning from '@/views/components/WarehouseWarning'import AccessDoor from '@/views/components/AccessDoor'import hkVideo from '../module/hkVideo.vue'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: 'FullView', components: { WarehouseWarning, AccessDoor, hkVideo }, data() { return { roomId: '01A1DC2123C2B75E1A579D', allDisplayConfigData: [], displayConfigData: [], originalAllDeviceIds: [], allDeviceIds: [], currentIpIndex: 0, excludeIpList: [ '192.168.99.101:6003' ], timer: '', open: false, newAlarm: [], aqiValue: 45, aqiStatus: '优', keepIndicators: [ '二氧化碳', '甲醛', '综合气体', 'PM2.5浓度', 'PM10浓度', '温度', '湿度', '空气质量' ], 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' } ], 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 } // 严重污染
] }, // 新增:存储IP到设备名称的映射
ipToNameMap: {}, // 新增:当前显示的设备名称
currentDeviceName: '', aqiTimer: null, // 新增:存储所有设备的PM2.5/PM10数据
allDevicesPollutantData: [], hasValidData: false } }, async created() { 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 = [] } })
await this.getAllDevicesPollutantData() this.calcGlobalAQI()
if (this.allDeviceIds.length > 0) { this.currentDeviceName = this.ipToNameMap[this.allDeviceIds[0]] || '' await this.getRealTimeData(this.allDeviceIds[0])
// 轮询定时器(仅更新msg-list,完全不碰AQI)
this.timer = setInterval(async() => { const currentIp = this.getNextIp() this.currentDeviceName = this.ipToNameMap[currentIp] || '' await this.getRealTimeData(currentIp) }, 10000) console.log(`启动IP轮询,共${this.allDeviceIds.length}个有效IP:`, this.allDeviceIds) } else { console.warn('无有效设备IP,停止轮询') this.newAlarm = [] }
// 可选:开启AQI定时更新(如需实时刷新全量AQI)
// if (this.allDeviceIds.length > 0) {
// this.aqiTimer = setInterval(async() => {
// await this.getAllDevicesPollutantData()
// this.calcGlobalAQI()
// }, 30000)
// console.log('启动全量AQI计算定时器(30秒/次)')
// }
}, mounted() { const _this = this _this.iframeWin = this.$refs.myIframe.contentWindow window.addEventListener('message', this.handleMessageEvent) }, beforeDestroy() { // 清理轮询定时器
if (this.timer) { clearInterval(this.timer) console.log('停止IP轮询') } // 清理AQI定时器
if (this.aqiTimer) { clearInterval(this.aqiTimer) console.log('停止全量AQI计算定时器') } window.removeEventListener('message', this.handleMessageEvent) }, methods: { handleDeviceIpList() { const filteredIpSet = new Set() // 过滤后的IP(轮询用)
const originalIpSet = new Set() // 原始IP(AQI计算用)
this.ipToNameMap = {}
this.allDisplayConfigData.forEach(element => { const ip = (element.IP || '').trim() if (ip) { // 1. 原始IP列表:所有非空IP都加入(用于AQI计算)
originalIpSet.add(ip) // 2. 过滤后的IP列表:排除指定IP(用于轮询)
if (!this.excludeIpList.includes(ip)) { filteredIpSet.add(ip) } else { console.log('轮询排除IP:', ip, '设备名称:', element.Name) } // 存储IP到名称的映射(所有IP都存)
this.ipToNameMap[ip] = element.Name || `未知设备(${ip})` console.log('有效设备IP(AQI计算包含):', ip, '设备名称:', element.Name) } else { console.log('过滤空IP:', element.Name) } }) // 赋值:过滤后的IP(轮询)、原始IP(AQI)
this.allDeviceIds = Array.from(filteredIpSet) // 轮询用
this.originalAllDeviceIds = Array.from(originalIpSet) // AQI计算用
console.log(`轮询IP列表(过滤后):`, this.allDeviceIds) console.log(`AQI计算IP列表(原始):`, this.originalAllDeviceIds) },
/** * 获取下一个轮询IP(循环切换) */ getNextIp() { if (this.allDeviceIds.length === 0) return '' // 先重置索引(防止数组长度变化导致越界)
this.currentIpIndex = this.currentIpIndex % this.allDeviceIds.length const ip = this.allDeviceIds[this.currentIpIndex] this.currentIpIndex = (this.currentIpIndex + 1) % this.allDeviceIds.length console.log(`轮询切换 - 当前IP:${ip},下一个索引:${this.currentIpIndex}`) return ip }, /** * 处理iframe消息事件 */ handleMessageEvent(event) { if (event.data) { switch (event.data.type) { case 'autoRotationStatus': { const newValue = event.data.value this.$emit('update:isGetRotate', newValue) console.log('Received auto rotation status:', newValue) } break case 'cameraClick': { const deviceData = event.data.data console.log('cameraClick', deviceData) if (deviceData.toLowerCase().includes('cam')) { this.open = true this.$nextTick(() => { this.$refs.camera.getVideoUrl(this.roomId, deviceData) }) } } break case 'archCabinetsClick': { const data = event.data.data console.log('archCabinetsClick', data) console.log(data.split('-')[1]) if (data.includes('cabinet')) { this.deviceId = data.split('-')[1] this.$router.push('/storeManage/deseCabinet') localStorage.setItem('cabinetNum', this.deviceId) } } break default: console.log('未知消息类型:', event.data) } } },
/** * 请求指定IP的实时数据(过滤+设备名称关联) */ async getRealTimeData(targetIp) { if (!targetIp) { this.newAlarm = [] this.currentDeviceName = '' return }
try { console.log(`开始请求IP【${targetIp}】的实时数据(模拟)`) // const data = await mockFetchDataForIP({ ip: targetIp })
// 真实请求
const data = await alarmApi.FetchDataForIP({ ip: targetIp })
const filteredData = data.filter(item => this.keepIndicators.includes(item.subName) )
if (filteredData.length > 0) { this.newAlarm = filteredData console.log(`IP【${targetIp}】(${this.currentDeviceName}) 过滤后的数据:`, filteredData) } else { this.newAlarm = [] console.log(`IP【${targetIp}】(${this.currentDeviceName}) 无需要的指标数据`) } } catch (error) { this.newAlarm = [] // 移除AQI重置逻辑
this.currentDeviceName = '' console.error(`IP【${targetIp}】模拟数据请求失败:`, error) } }, calcAQI() {}, /** * 获取所有设备的PM2.5/PM10数据(用于计算全局AQI) */ async getAllDevicesPollutantData() { if (this.originalAllDeviceIds.length === 0) { this.allDevicesPollutantData = [] return }
const allPollutantData = [] // 遍历原始IP列表(和EnvironmentalScreen的allDeviceIds一致)
for (const ip of this.originalAllDeviceIds) { try { // const data = await mockFetchDataForIP({ ip })
// 真实请求
const data = await alarmApi.FetchDataForIP({ ip }) // 仅过滤PM2.5/PM10
const pm25Item = data.find(item => item.subName === 'PM2.5浓度') const pm10Item = data.find(item => item.subName === 'PM10浓度')
if (pm25Item) { allPollutantData.push({ ...pm25Item, value: parseFloat(pm25Item.value) || 0, // 确保数值类型
dw: pm25Item.dw || 'ug/立方米' // 对齐单位
}) } if (pm10Item) { allPollutantData.push({ ...pm10Item, value: parseFloat(pm10Item.value) || 0, dw: pm10Item.dw || 'ug/立方米' }) } } catch (error) { console.error(`获取IP【${ip}】污染物数据失败:`, error) } } this.allDevicesPollutantData = allPollutantData }, /** * 计算全局AQI(所有设备PM2.5/PM10平均值) */ calcGlobalAQI() { this.hasValidData = this.allDevicesPollutantData.length > 0
// 无数据时兜底(和EnvironmentalScreen一致:默认AQI=45,状态=优)
if (!this.hasValidData) { this.aqiValue = 45 this.aqiStatus = '优' return }
// 2. 提取所有设备的PM2.5/PM10数值(转数字,兜底0)
const pm25List = this.allDevicesPollutantData .filter(item => item.subName === 'PM2.5浓度') .map(item => parseFloat(item.value) || 0) const pm10List = this.allDevicesPollutantData .filter(item => item.subName === 'PM10浓度') .map(item => parseFloat(item.value) || 0)
// 3. 计算平均值(和EnvironmentalScreen一致:所有设备平均值)
const avgPm25 = pm25List.length > 0 ? (pm25List.reduce((sum, val) => sum + val, 0) / pm25List.length) : 0 const avgPm10 = pm10List.length > 0 ? (pm10List.reduce((sum, val) => sum + val, 0) / pm10List.length) : 0
// 4. 完全复用EnvironmentalScreen的国标AQI计算公式
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 } // 严重污染
] }
// 计算单污染物分指数(IAQI)
const calculateIAQI = (conc, pollutantType) => { const bpTable = aqiBpTable[pollutantType] if (conc > bpTable[bpTable.length - 1].bpHi) return 500 const matchedBp = bpTable.find(item => conc >= item.bpLo && conc <= item.bpHi) || bpTable[0] const iaqi = ((matchedBp.iaqiHi - matchedBp.iaqiLo) / (matchedBp.bpHi - matchedBp.bpLo)) * (conc - matchedBp.bpLo) + matchedBp.iaqiLo return Math.min(Math.round(iaqi), 500) }
// 计算PM2.5、PM10分指数,取最大值为最终AQI
const iaqiPm25 = calculateIAQI(avgPm25, 'pm25') const iaqiPm10 = calculateIAQI(avgPm10, 'pm10') const finalAQI = Math.max(iaqiPm25, iaqiPm10)
// 5. 匹配AQI等级
this.aqiValue = finalAQI const matchedGrade = this.aqiGradeStandard.find(grade => finalAQI >= grade.aqiMin && finalAQI <= grade.aqiMax ) this.aqiStatus = matchedGrade ? matchedGrade.level : '严重污染'
// 可选:打印日志,便于和EnvironmentalScreen对比
console.log('FullView AQI计算(对齐EnvironmentalScreen):', { avgPm25: avgPm25.toFixed(2), avgPm10: avgPm10.toFixed(2), iaqiPm25, iaqiPm10, finalAQI, aqiStatus: this.aqiStatus }) },
getIframeLoading(value) {}, deviceState(e) { this.iframeWin.postMessage({ data: this.oaoMessage }, '*') }, handleAQI() { this.oaoMessage.forEach(element => { window.frames['iframeMap'].setAlertValue(element.id, element.wendu, element.sidu) }) }, handleAlarm(deviceId) { window.frames['iframeMap'].Myalert(deviceId, true) }, handleHide(deviceId) { window.frames['iframeMap'].setYangGanCanshow(deviceId, true) } }}</script>
<style lang="scss" scoped>@import "~@/assets/styles/lend-manage.scss";.warehouse-left { position: relative;}.container-wrap { min-height: auto; height: calc(100% / 2 - 10px); overflow: hidden; margin-bottom: 20px;}
// 新增:设备名称标题样式
.device-name-title { position: absolute; top: 0; left: 0; color: #fff; font-size: 18px; font-weight: 600; background: rgba(0, 0, 0, 0.3); padding: 8px 16px; border-radius: 4px; z-index: 10;}
.air-quality{ position: absolute; bottom: 10px; right: 20px; color: #fff; padding: 20px 20px 10px 20px; border-radius: 5px; z-index: 999; h3{ padding: 30px 0; } .air-params{ display: flex; justify-content: space-between; align-items: last baseline; .air-left{ .air-title{ position: relative; padding-left: 12px; font-size: 14px; &::before{ content: ""; position: absolute; left: 0; top: 50%; width: 6px; height: 6px; background-color: #18B08F; border-radius: 50%; } } .air-result{ display: flex; justify-content: space-between; align-items: last baseline; padding-top: 10px; p{ font-size: 30px; font-weight: 600; padding: 0 6px 0 10px; } span{ display: block; font-size: 12px; opacity: .6; } } } .air-right{ text-align: center; span{ display: block; font-size: 12px; } p{ font-size: 18px; font-weight: 600; padding: 10px 30px; margin-top: 10px; border-radius: 5px; } } }
&.air-excellent { // 优 - 绿色
background-image: linear-gradient(to top, rgba(40, 180, 40, .5), rgba(40, 180, 40, 0)); .air-params .air-right p { background-color: rgba(40, 180, 40, .2); } .air-title::before { background-color: #28B428; } } &.air-good { // 良 - 黄色
background-image: linear-gradient(to top, rgba(255, 200, 0, .5), rgba(255, 200, 0, 0)); .air-params .air-right p { background-color: rgba(255, 200, 0, .2); } .air-title::before { background-color: #FFC800; } } &.air-lightPollution { // 轻度污染 - 橙色
background-image: linear-gradient(to top, rgba(255, 140, 0, .5), rgba(255, 140, 0, 0)); .air-params .air-right p { background-color: rgba(255, 140, 0, .2); } .air-title::before { background-color: #FF8C00; } } &.air-mediumPollution { // 中度污染 - 红色
background-image: linear-gradient(to top, rgba(255, 0, 0, .5), rgba(255, 0, 0, 0)); .air-params .air-right p { background-color: rgba(255, 0, 0, .2); } .air-title::before { background-color: #FF0000; } } &.air-heavyPollution { // 重度污染 - 紫色
background-image: linear-gradient(to top, rgba(150, 0, 200, .5), rgba(150, 0, 200, 0)); .air-params .air-right p { background-color: rgba(150, 0, 200, .2); } .air-title::before { background-color: #9600C8; } } &.air-severePollution { // 严重污染 - 褐红色
background-image: linear-gradient(to top, rgba(120, 0, 0, .5), rgba(120, 0, 0, 0)); .air-params .air-right p { background-color: rgba(120, 0, 0, .2); } .air-title::before { background-color: #780000; } }}
// 兼容原有air-warn类(可选,可删除)
.air-warn{ background-image: linear-gradient(to top, rgba(246, 81, 99, .5), rgba(24, 176, 143, 0)); .air-params{ .air-right{ p{ background-color: rgba(246, 81, 99, .2); } } }}
.msg-list{ position: static; flex-wrap: wrap !important; li{ margin-bottom: 20px; }}</style>
|