黄陂项目
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.

1547 lines
48 KiB

2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
  1. <template>
  2. <div class="env-container">
  3. <div class="env-top-title" />
  4. <div class="header-date">
  5. <div class="time">{{ nowDate.split(' ')[1] }}</div>
  6. <div class="time-other">
  7. <span>{{ currentWeek }}</span>
  8. <span>{{ nowDate.split(' ')[0] }}</span>
  9. </div>
  10. </div>
  11. <div class="env-main">
  12. <div class="env-main-left">
  13. <div class="env-item container-wrap" style="height: 130px !important; ">
  14. <span class="right-top-line" />
  15. <span class="left-bottom-line" />
  16. <h3>
  17. <svg-icon icon-class="danganjieyue" style="margin-right:10px" />档案借阅
  18. </h3>
  19. <div class="chart-wrapper">
  20. <lend-across :lend-data="lendData" :refreshtime="refreshtime" />
  21. </div>
  22. </div>
  23. <div class="env-item container-wrap left-wrap">
  24. <span class="right-top-line" />
  25. <span class="left-bottom-line" />
  26. <h3>
  27. <i class="iconfont icon-kongqizhiliangshuju" />环控数据
  28. </h3>
  29. <!-- <ul
  30. class="leakage-list"
  31. :style="{
  32. height: showScroll ? 'calc(100% - 40px)' : 'auto',
  33. overflow: showScroll ? 'auto' : 'hidden'
  34. }"
  35. >
  36. <li
  37. v-for="item in validDisplayConfigData"
  38. :key="item.id"
  39. :class="{ 'leakage-warn': item.NetStatus === 0 }"
  40. :style="{
  41. width: liWidth,
  42. height: liHeight,
  43. marginRight: '12px',
  44. marginBottom: '12px'
  45. }"
  46. >
  47. <p><i class="iconfont icon-shebei" />{{ item.Name }}</p>
  48. <span class="leakage-state-tip" />
  49. </li>
  50. </ul> -->
  51. <ul v-if="newAlarm && newAlarm.length !== 0" class="screen-env-list">
  52. <!-- <li :class="alarmStatus.infrared === '告警' ? 'li-warn alarm-status' : 'alarm-status'">
  53. <div class="msg-txt">
  54. <p class="msg-list-unit">红外 </p>
  55. <span class="msg-list-num">{{ alarmStatus.infrared }}</span>
  56. </div>
  57. </li>
  58. <li :class="alarmStatus.fire === '告警' ? 'li-warn alarm-status' : 'alarm-status'">
  59. <div class="msg-txt">
  60. <p class="msg-list-unit">消防 </p>
  61. <span class="msg-list-num">{{ alarmStatus.fire }}</span>
  62. </div>
  63. </li>
  64. <li :class="alarmStatus.waterLeak === '告警' ? 'li-warn alarm-status' : 'alarm-status'">
  65. <div class="msg-txt">
  66. <p class="msg-list-unit">漏水 </p>
  67. <span class="msg-list-num">{{ alarmStatus.waterLeak }}</span>
  68. </div>
  69. </li>-->
  70. <li>
  71. <svg-icon icon-class="pm25" class-name="msg-list-svg" />
  72. <div class="msg-txt">
  73. <span class="msg-list-num">{{ avgData.pm25 }}</span>
  74. <p class="msg-list-unit">PM2.5浓度 {{ avgData.pm25Unit }}</p>
  75. </div>
  76. </li>
  77. <li>
  78. <svg-icon icon-class="pm10" class-name="msg-list-svg" />
  79. <div class="msg-txt">
  80. <span class="msg-list-num">{{ avgData.pm10 }}</span>
  81. <p class="msg-list-unit">PM10浓度 {{ avgData.pm10Unit }}</p>
  82. </div>
  83. </li>
  84. <li>
  85. <svg-icon icon-class="voc" class-name="msg-list-svg" />
  86. <div class="msg-txt">
  87. <span class="msg-list-num">{{ avgData.tvoc }}</span>
  88. <p class="msg-list-unit">TVOC {{ avgData.tvocUnit }}</p>
  89. </div>
  90. </li>
  91. <li>
  92. <svg-icon icon-class="co2" class-name="msg-list-svg" />
  93. <div class="msg-txt">
  94. <span class="msg-list-num">{{ avgData.co2 }}</span>
  95. <p class="msg-list-unit">二氧化碳 {{ avgData.co2Unit }}</p>
  96. </div>
  97. </li>
  98. <li>
  99. <svg-icon icon-class="jiaquan" class-name="msg-list-svg" style="font-size: 64px; margin-left: 10px;" />
  100. <div class="msg-txt">
  101. <span class="msg-list-num">{{ avgData.formaldehyde }}</span>
  102. <p class="msg-list-unit">甲醛 {{ avgData.formaldehydeUnit }}</p>
  103. </div>
  104. </li>
  105. </ul>
  106. </div>
  107. <div class="env-item container-wrap left-wrap">
  108. <span class="right-top-line" />
  109. <span class="left-bottom-line" />
  110. <h3>
  111. <svg-icon icon-class="jiankong" style="font-size: 18px; margin-right: 10px;" />视频监控
  112. </h3>
  113. <div style="height: calc(100% - 40px);">
  114. <video id="video" controls autoplay muted width="100%" height="100%" />
  115. </div>
  116. </div>
  117. </div>
  118. <div class="env-main-middle">
  119. <div class="env-3d">
  120. <iframe id="myIframe" ref="myIframe" name="iframeMap" class="iframe_box" src="/web3D/index.html" frameborder="0" scrolling="no" style=" margin: 0 auto; display: block;" />
  121. </div>
  122. <!-- 重构告警列表 - 分为两行 -->
  123. <div v-if="hasValidData" class="env-alarm-container">
  124. <!-- 第一行温湿度 -->
  125. <ul class="env-alarm-list env-alarm-list-first">
  126. <li>
  127. <svg-icon icon-class="temperature" class-name="msg-list-svg" />
  128. <div>
  129. <span>{{ avgData.temperature }} </span>
  130. <p>温度 {{ avgData.temperatureUnit }}</p>
  131. </div>
  132. </li>
  133. <li>
  134. <svg-icon icon-class="shidu" class-name="msg-list-svg" />
  135. <div>
  136. <span>{{ avgData.humidity }}</span>
  137. <p>湿度 {{ avgData.humidityUnit }}</p>
  138. </div>
  139. </li>
  140. </ul>
  141. <el-row :gutter="20" class="panel-group" type="flex" justify="space-between">
  142. <el-col class="card-panel-col">
  143. <div class="card-panel zaixianshebei">
  144. <div class="card-panel-icon-wrapper icon-shopping">
  145. <svg-icon icon-class="zaixianshebei" class-name="card-panel-icon" />
  146. </div>
  147. <div class="card-panel-description">
  148. <div class="card-panel-text">
  149. <count-to v-if="getDeviceFlag" :start-val="0" :end-val="onlineDeviceNum" :duration="3200" class="card-panel-num" />
  150. <div v-if="!getDeviceFlag" class="card-panel-text"><span class="card-panel-num">获取中...</span></div>
  151. </div>
  152. 在线设备
  153. </div>
  154. </div>
  155. </el-col>
  156. <el-col class="card-panel-col">
  157. <div class="card-panel lixianshebei">
  158. <div class="card-panel-icon-wrapper icon-shopping">
  159. <svg-icon icon-class="lixianshebei" class-name="card-panel-icon" />
  160. </div>
  161. <div class="card-panel-description">
  162. <div class="card-panel-text">
  163. <count-to v-if="getDeviceFlag" :start-val="0" :end-val="offlineDeviceNum" :duration="3200" class="card-panel-num" />
  164. <div v-if="!getDeviceFlag" class="card-panel-text"><span class="card-panel-num">获取中...</span></div>
  165. </div>
  166. 离线设备
  167. </div>
  168. </div>
  169. </el-col>
  170. </el-row>
  171. <!-- 第二行其他指标 -->
  172. <!-- <ul class="env-alarm-list-second">
  173. <li :class="alarmStatus.infrared === '告警' ? 'li-warn' : ''">
  174. <p>红外</p>
  175. <span>{{ alarmStatus.infrared }}</span>
  176. </li>
  177. <li :class="alarmStatus.fire === '告警' ? 'li-warn' : ''">
  178. <p>消防</p>
  179. <span>{{ alarmStatus.fire }}</span>
  180. </li>
  181. <li :class="alarmStatus.waterLeak === '告警' ? 'li-warn' : ''">
  182. <p>漏水</p>
  183. <span>{{ alarmStatus.waterLeak }}</span>
  184. </li>
  185. <li>
  186. <p>PM2.5浓度</p>
  187. <span>{{ avgData.pm25 }}{{ avgData.pm25Unit }}</span>
  188. </li>
  189. <li>
  190. <p>TVOC</p>
  191. <span>{{ avgData.tvoc }}{{ avgData.tvocUnit }}</span>
  192. </li>
  193. <li>
  194. <p>PM10浓度</p>
  195. <span>{{ avgData.pm10 }}{{ avgData.pm10Unit }}</span>
  196. </li>
  197. <li>
  198. <p>二氧化碳</p>
  199. <span>{{ avgData.co2 }}{{ avgData.co2Unit }}</span>
  200. </li>
  201. <li>
  202. <p>甲醛</p>
  203. <span>{{ avgData.formaldehyde }}{{ avgData.formaldehydeUnit }}</span>
  204. </li>
  205. </ul> -->
  206. </div>
  207. <div
  208. v-if="hasValidData"
  209. class="air-quality"
  210. :class="[
  211. aqiStatus === '优' ? 'air-excellent' : '',
  212. aqiStatus === '良' ? 'air-good' : '',
  213. aqiStatus === '轻度污染' ? 'air-lightPollution' : '',
  214. aqiStatus === '中度污染' ? 'air-mediumPollution' : '',
  215. aqiStatus === '重度污染' ? 'air-heavyPollution' : '',
  216. aqiStatus === '严重污染' ? 'air-severePollution' : ''
  217. ]"
  218. >
  219. <h3>实时空气质量指数AQI</h3>
  220. <div class="air-params">
  221. <div class="air-left">
  222. <span class="air-title">实时AQI</span>
  223. <div class="air-result"><p>{{ aqiValue }}</p><span>(AQI-中国标准)</span></div>
  224. </div>
  225. <div class="air-right">
  226. <span>空气质量为</span>
  227. <p>{{ aqiStatus }}</p>
  228. </div>
  229. </div>
  230. </div>
  231. <!-- <ul class="env-alarm-list-second">
  232. <li :class="alarmStatus.infrared === '告警' ? 'li-warn' : ''">
  233. <p><i class="iconfont icon-weihubaojing" />红外</p>
  234. <span>{{ alarmStatus.infrared }}</span>
  235. </li>
  236. <li :class="alarmStatus.fire === '告警' ? 'li-warn' : ''">
  237. <p><i class="iconfont icon-weihubaojing" />消防</p>
  238. <span>{{ alarmStatus.fire }}</span>
  239. </li>
  240. <li :class="alarmStatus.waterLeak === '告警' ? 'li-warn' : ''">
  241. <p><i class="iconfont icon-weihubaojing" />漏水</p>
  242. <span>{{ alarmStatus.waterLeak }}</span>
  243. </li>
  244. <li>
  245. <p><i class="iconfont icon-weihubaojing" />PM2.5浓度</p>
  246. <span>{{ avgData.pm25 }} {{ avgData.pm25Unit }}</span>
  247. </li>
  248. <li>
  249. <p><i class="iconfont icon-weihubaojing" />TVOC</p>
  250. <span>{{ avgData.tvoc }} {{ avgData.tvocUnit }}</span>
  251. </li>
  252. <li>
  253. <p><i class="iconfont icon-weihubaojing" />PM10浓度</p>
  254. <span>{{ avgData.pm10 }} {{ avgData.pm10Unit }}</span>
  255. </li>
  256. <li>
  257. <p><i class="iconfont icon-weihubaojing" />二氧化碳</p>
  258. <span>{{ avgData.co2 }} {{ avgData.co2Unit }}</span>
  259. </li>
  260. <li>
  261. <p><i class="iconfont icon-weihubaojing" />甲醛</p>
  262. <span>{{ avgData.formaldehyde }} {{ avgData.formaldehydeUnit }}</span>
  263. </li>
  264. </ul> -->
  265. <div class="device-container">
  266. <!-- container-wrap -->
  267. <!-- <span class="right-top-line" />
  268. <span class="left-bottom-line" />
  269. <ul class="device-info">
  270. <li>
  271. <div class="row-item">
  272. <div class="svg-box">
  273. <svg-icon icon-class="zaixianshebei" class-name="card-panel-icon" />
  274. </div>
  275. <span>在线设备</span>
  276. </div>
  277. <div class="card-panel-text">
  278. <count-to :start-val="0" :end-val="lineDevNum" :duration="3200" class="row-num" />
  279. </div>
  280. </li>
  281. <li>
  282. <div class="row-item">
  283. <div class="svg-box">
  284. <svg-icon icon-class="lixianshebei" class-name="card-panel-icon" />
  285. </div>
  286. <span>离线设备</span>
  287. </div>
  288. <div class="card-panel-text">
  289. <count-to :start-val="0" :end-val="offDevNum" :duration="3200" class="row-num" />
  290. </div>
  291. </li>
  292. </ul> -->
  293. <ul v-if="hasValidData" class="leakage-list new-leakage-list">
  294. <li :class="alarmStatus.infrared === '告警' ? 'leakage-warn' : ''">
  295. <p><i class="iconfont icon-shebei" />红外</p>
  296. <span class="leakage-state-tip" />
  297. </li>
  298. <li :class="alarmStatus.fire === '告警' ? 'leakage-warn' : ''">
  299. <p><i class="iconfont icon-shebei" />消防</p>
  300. <span class="leakage-state-tip" />
  301. </li>
  302. <li :class="alarmStatus.waterLeak === '告警' ? 'leakage-warn' : ''">
  303. <p><i class="iconfont icon-shebei" />漏水</p>
  304. <span class="leakage-state-tip" />
  305. </li>
  306. <li
  307. v-for="item in validDisplayConfigData"
  308. :key="item.id"
  309. :class="{ 'leakage-warn': item.NetStatus === 0 }"
  310. >
  311. <p><i class="iconfont icon-shebei" />{{ item.Name }}</p>
  312. <span class="leakage-state-tip" />
  313. </li>
  314. </ul>
  315. </div>
  316. </div>
  317. <div class="env-main-right">
  318. <!-- 环控实时报警 -->
  319. <warehouse-warning :height="'calc(100% - 38px)'" />
  320. <!-- 门禁出入记录 -->
  321. <AccessDoor :height="'calc(100% - 40px)'" size="5" />
  322. <!-- 当日告警统计 -->
  323. <div class="env-item container-wrap">
  324. <span class="right-top-line" />
  325. <span class="left-bottom-line" />
  326. <h3>
  327. <svg-icon icon-class="alerm" class-name="warehouse-svg" />当日告警统计
  328. </h3>
  329. <div class="chart-wrapper" style="height: calc(100% - 40px);">
  330. <catePie :cate-data="alarmChartData" />
  331. </div>
  332. </div>
  333. </div>
  334. </div>
  335. </div>
  336. </template>
  337. <script>
  338. import CountTo from 'vue-count-to'
  339. import { getCurrentTime } from '@/utils/index'
  340. import lendAcross from '@/views/components/echarts/lendAcross.vue'
  341. import catePie from './module/catePie.vue'
  342. import WarehouseWarning from '@/views/components/WarehouseWarning'
  343. import AccessDoor from '@/views/components/AccessDoor'
  344. import { statisticsCrud } from '@/views/system/archiveStatistics/mixins/statistics'
  345. import displayConfigApi from '@/api/storeManage/displayConfig'
  346. import { getDeviceOnoff, getTodayHikAlarmLog } from '@/api/home/device'
  347. import alarmApi from '@/api/home/alarm'
  348. // import { allDeviceData, mockIpData } from './index.js'
  349. // const mockFetchDataForIP = (params) => {
  350. // return new Promise((resolve) => {
  351. // setTimeout(() => {
  352. // const ip = params.ip
  353. // const result = mockIpData[ip] || { code: 200, message: '操作成功', data: [], timestamp: Date.now() }
  354. // resolve(result.data)
  355. // }, 500)
  356. // })
  357. // }
  358. export default {
  359. name: 'EnvironmentalScreen',
  360. components: {
  361. CountTo,
  362. WarehouseWarning,
  363. AccessDoor,
  364. lendAcross,
  365. catePie
  366. },
  367. mixins: [statisticsCrud],
  368. data() {
  369. return {
  370. bannerRoomName: '3F 全景图',
  371. nowDate: '',
  372. currentWeek: this.getCurrentWeek(),
  373. timer: null,
  374. echartsTimer: null,
  375. roomId: 'D6490DA3D4261E8C26D0E3',
  376. allDisplayConfigData: [],
  377. displayConfigData: [],
  378. url: '',
  379. allDeviceIds: [], // 所有设备IP(无排除)
  380. refreshtime: 60000,
  381. lendData: [],
  382. typeData: [],
  383. newAlarm: [], // 所有设备的原始数据
  384. aqiValue: 45,
  385. aqiStatus: '健康',
  386. // 需展示的指标列表(包含所有要计算的指标)
  387. keepIndicators: [
  388. '二氧化碳',
  389. 'CO2浓度',
  390. '甲醛',
  391. '综合气体',
  392. 'PM2.5浓度',
  393. 'PM10浓度',
  394. '温度',
  395. '湿度',
  396. '空气质量',
  397. 'TVOC',
  398. '红外',
  399. '消防',
  400. '漏水'
  401. ],
  402. iframeWin: null,
  403. // 平均值数据(新增温湿度和单位字段)
  404. avgData: {
  405. temperature: '0.00', // 温度
  406. temperatureUnit: '', // 温度单位
  407. humidity: '0.00', // 湿度
  408. humidityUnit: '', // 湿度单位
  409. pm25: '0.00',
  410. pm25Unit: '', // PM2.5单位
  411. tvoc: '0.00',
  412. tvocUnit: '', // TVOC单位
  413. pm10: '0.00',
  414. pm10Unit: '', // PM10单位
  415. co2: '0.00',
  416. co2Unit: '', // 二氧化碳单位
  417. formaldehyde: '0.00',
  418. formaldehydeUnit: '' // 甲醛单位
  419. },
  420. // 告警状态(ON=告警,OFF=正常)
  421. alarmStatus: {
  422. infrared: '正常',
  423. fire: '正常',
  424. waterLeak: '正常'
  425. },
  426. aqiGradeStandard: [
  427. { aqiMin: 0, aqiMax: 50, level: '优', colorKey: 'excellent' },
  428. { aqiMin: 51, aqiMax: 100, level: '良', colorKey: 'good' },
  429. { aqiMin: 101, aqiMax: 150, level: '轻度污染', colorKey: 'lightPollution' },
  430. { aqiMin: 151, aqiMax: 200, level: '中度污染', colorKey: 'mediumPollution' },
  431. { aqiMin: 201, aqiMax: 300, level: '重度污染', colorKey: 'heavyPollution' },
  432. { aqiMin: 301, aqiMax: 500, level: '严重污染', colorKey: 'severePollution' }
  433. ],
  434. pollutantLimits: {
  435. pm25: { excellent: 35, good: 75, light: 115, medium: 150, heavy: 250, severe: 500 }, // μg/m³
  436. pm10: { excellent: 50, good: 150, light: 250, medium: 350, heavy: 420, severe: 600 } // μg/m³
  437. },
  438. hasValidData: false, // 是否有有效数据
  439. hkConfig: {
  440. 'username': 'admin',
  441. 'password': 'ftzn83560792',
  442. 'ip': '192.168.99.125', // 固定为目标IP
  443. 'port': '554'
  444. },
  445. webRtcServer: null,
  446. camera_ip: '127.0.0.1:9527',
  447. cameraList: [], // 摄像头列表
  448. targetDeviceIps: ['192.168.99.101:5003', '192.168.99.101:6003', '192.168.99.101:5004'],
  449. getDeviceFlag: false,
  450. totalDeviceNum: 0,
  451. onlineDeviceNum: 0,
  452. offlineDeviceNum: 0,
  453. alarmStatisticsRaw: [], // 原始告警统计数据
  454. alarmChartData: [], // 格式化后给饼图的数据源
  455. alarmRefreshTimer: null // 告警数据刷新定时器
  456. }
  457. },
  458. computed: {
  459. // validDisplayConfigData() {
  460. // return this.allDisplayConfigData.filter(item => item && item.Name)
  461. // },
  462. validDisplayConfigData() {
  463. return this.allDisplayConfigData.filter(item => {
  464. return item && item.Name && this.targetDeviceIps.includes(item.IP?.trim())
  465. })
  466. },
  467. itemsPerRow() {
  468. const len = this.validDisplayConfigData.length
  469. if (len === 0) return 0
  470. return len <= 2 ? len : 2
  471. },
  472. liWidth() {
  473. if (this.itemsPerRow === 0) return '0'
  474. return `calc(100% / ${this.itemsPerRow} - 12px)`
  475. },
  476. liHeight() {
  477. const len = this.validDisplayConfigData.length
  478. if (len === 0) return '0'
  479. const rows = Math.ceil(len / this.itemsPerRow)
  480. return `calc(100% / ${rows} - 12px)`
  481. },
  482. showScroll() {
  483. return this.validDisplayConfigData.length > 8
  484. }
  485. },
  486. async created() {
  487. // 时间更新定时器
  488. this.timer = setInterval(() => {
  489. this.nowDate = getCurrentTime()
  490. }, 1000)
  491. this.getDevice()
  492. this.getTodayHikAlarmLog()
  493. this.alarmRefreshTimer = setInterval(() => {
  494. this.getTodayHikAlarmLog()
  495. }, 300000)
  496. // 初始化逻辑
  497. window.getIframeLoading = this.getIframeLoading
  498. // this.allDisplayConfigData = allDeviceData
  499. // this.handleDeviceIpList()
  500. await alarmApi.FetchYpGetSite().then((data) => {
  501. if (data && data.length > 0) {
  502. this.allDisplayConfigData = data
  503. this.handleDeviceIpList()
  504. } else {
  505. this.allDisplayConfigData = []
  506. }
  507. })
  508. if (this.allDeviceIds.length > 0) {
  509. await this.getAllDevicesData()
  510. this.calcAllAvgData() // 计算所有指标平均值
  511. this.calcAQIByAvg() // 根据平均值计算AQI
  512. } else {
  513. console.warn('无设备IP数据')
  514. this.hasValidData = false
  515. }
  516. },
  517. mounted() {
  518. this.iframeWin = this.$refs.myIframe?.contentWindow
  519. // Echarts数据刷新定时器
  520. this.echartsTimer = setInterval(() => {
  521. this.lendData = []
  522. this.typeData = []
  523. this.getBorrowerNumSta()
  524. }, this.refreshtime)
  525. this.getVideoUrl()
  526. },
  527. beforeDestroy() {
  528. // 清理所有定时器
  529. if (this.timer) clearInterval(this.timer)
  530. if (this.echartsTimer) clearInterval(this.echartsTimer)
  531. // 销毁视频流
  532. if (this.webRtcServer) {
  533. this.webRtcServer.disconnect()
  534. this.webRtcServer = null
  535. }
  536. if (this.alarmRefreshTimer) clearInterval(this.alarmRefreshTimer)
  537. },
  538. methods: {
  539. // 重构告警数据获取方法
  540. getTodayHikAlarmLog() {
  541. getTodayHikAlarmLog().then(data => {
  542. console.log('今日海康告警日志', data)
  543. this.alarmStatisticsRaw = data || []
  544. this.formatAlarmChartData()
  545. }).catch(error => {
  546. console.error('获取告警统计数据失败:', error)
  547. this.alarmStatisticsRaw = []
  548. this.alarmChartData = []
  549. })
  550. },
  551. // 格式化告警数据为饼图数据源
  552. formatAlarmChartData() {
  553. if (!this.alarmStatisticsRaw.length) {
  554. this.alarmChartData = [{ name: '无告警', value: 1, itemStyle: { color: '#666666' }}]
  555. return
  556. }
  557. // 固定颜色列表(按优先级排序)
  558. const colorList = ['#F65164', '#339CFF', '#FFB800', '#1CADAB', '#9B6BCC', '#FF7D00']
  559. const alarmColorMap = {
  560. '设备断线': '#F65164',
  561. '红外线报警': '#339CFF',
  562. '消防报警': '#FFB800',
  563. '漏水报警': '#1CADAB',
  564. 'PM2.5超标': '#9B6BCC',
  565. '甲醛超标': '#FF7D00'
  566. }
  567. this.alarmChartData = this.alarmStatisticsRaw.map((item, index) => {
  568. const alarmName = item.alarmValue || '未知告警'
  569. const color = alarmColorMap[alarmName] || colorList[index % colorList.length] || '#666666'
  570. return {
  571. name: alarmName,
  572. value: item.num || 0,
  573. itemStyle: {
  574. color: color
  575. }
  576. }
  577. })
  578. },
  579. getDevice() {
  580. getDeviceOnoff().then(data => {
  581. this.getDeviceFlag = true
  582. this.totalDeviceNum = data.deviceall.length
  583. this.onlineDeviceNum = data.online.length
  584. this.offlineDeviceNum = data.offline.length
  585. // this.onlineDevice = data.online
  586. // this.offDevice = data.offline
  587. })
  588. },
  589. getVideoUrl() {
  590. displayConfigApi.list({ storeroomId: '01A1DC2123C2B75E1A579D', isQueryAll: 1 }).then((res) => {
  591. console.log('摄像头列表', res)
  592. if (res && res.length > 0) {
  593. // 只筛选IP为192.168.99.125的摄像头
  594. this.cameraList = res.filter(item =>
  595. item.divPosition && item.divPosition.includes('cam') &&
  596. item.deviceInfo && item.deviceInfo.deviceIp === '192.168.99.125'
  597. )
  598. if (this.cameraList.length > 0) {
  599. const targetCamera = this.cameraList[0].deviceInfo
  600. this.hkConfig = {
  601. username: targetCamera.deviceAccount || 'admin',
  602. password: targetCamera.devicePassword || 'ftzn83560792',
  603. ip: targetCamera.deviceIp || '192.168.99.125',
  604. port: targetCamera.devicePort || '554'
  605. }
  606. this.$nextTick(() => {
  607. this.initVideo()
  608. })
  609. } else {
  610. this.$message({
  611. message: '未找到IP为192.168.99.125的摄像头配置',
  612. type: 'warning'
  613. })
  614. }
  615. } else {
  616. this.$message({
  617. message: '请先配置摄像头',
  618. type: 'error'
  619. })
  620. }
  621. }).catch(error => {
  622. console.error('获取摄像头列表失败', error)
  623. this.$message({
  624. message: '获取摄像头配置失败',
  625. type: 'error'
  626. })
  627. })
  628. },
  629. initVideo() {
  630. const linkSrc = process.env.NODE_ENV === 'production' ? window.g.ApiWebRtcServerUrl : process.env.VUE_APP_WEBRTCSTREAMER_API
  631. this.camera_ip = linkSrc
  632. console.log('hkConfig', this.hkConfig)
  633. // 先销毁已存在的视频流
  634. if (this.webRtcServer) {
  635. this.webRtcServer.disconnect()
  636. }
  637. // 初始化新的视频流
  638. // eslint-disable-next-line no-undef
  639. this.webRtcServer = new WebRtcStreamer('video', location.protocol + '//' + this.camera_ip)
  640. // 拼接RTSP地址
  641. const rtspUrl = `rtsp://${this.hkConfig.username}:${this.hkConfig.password}@${this.hkConfig.ip}:${this.hkConfig.port}/h264/1/1`
  642. console.log('RTSP地址:', rtspUrl)
  643. this.webRtcServer.connect(rtspUrl)
  644. },
  645. /**
  646. * 获取当前星期
  647. */
  648. getCurrentWeek() {
  649. const days = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
  650. const date = new Date()
  651. return days[date.getDay()]
  652. },
  653. /**
  654. * 处理设备IP列表仅去重无排除
  655. */
  656. handleDeviceIpList() {
  657. const ipSet = new Set()
  658. this.allDisplayConfigData.forEach(element => {
  659. const ip = (element.IP || '').trim()
  660. if (ip) { // 仅过滤空IP,无其他排除逻辑
  661. ipSet.add(ip)
  662. }
  663. })
  664. this.allDeviceIds = Array.from(ipSet)
  665. },
  666. /**
  667. * 获取所有设备的实时数据
  668. */
  669. async getAllDevicesData() {
  670. const allData = []
  671. for (const ip of this.allDeviceIds) {
  672. try {
  673. // const data = await mockFetchDataForIP({ ip })
  674. // 真实请求
  675. const data = await alarmApi.FetchDataForIP({ ip })
  676. // 过滤需要的指标并添加到总数据
  677. const filtered = data.filter(item => this.keepIndicators.includes(item.subName)).map(item => {
  678. if (item.subName === 'CO2浓度') {
  679. return { ...item, subName: '二氧化碳' }
  680. }
  681. return item
  682. })
  683. if (filtered.length > 0) {
  684. allData.push(...filtered)
  685. }
  686. } catch (error) {
  687. console.error(`获取IP【${ip}】数据失败:`, error)
  688. }
  689. }
  690. this.newAlarm = allData
  691. this.hasValidData = allData.length > 0
  692. },
  693. /**
  694. * 计算所有设备指标的平均值适配ON/OFF告警状态保留单位
  695. */
  696. calcAllAvgData() {
  697. if (!this.hasValidData) return
  698. // 1. 初始化各指标的总和、计数和单位
  699. const sumMap = {
  700. temperature: { sum: 0, count: 0, unit: '' }, // 温度
  701. humidity: { sum: 0, count: 0, unit: '' }, // 湿度
  702. pm25: { sum: 0, count: 0, unit: '' },
  703. tvoc: { sum: 0, count: 0, unit: '' },
  704. pm10: { sum: 0, count: 0, unit: '' },
  705. co2: { sum: 0, count: 0, unit: '' },
  706. formaldehyde: { sum: 0, count: 0, unit: '' },
  707. infrared: [], // 红外状态(ON/OFF)
  708. fire: [], // 消防状态(ON/OFF)
  709. waterLeak: [] // 漏水状态(ON/OFF)
  710. }
  711. // 2. 遍历所有数据累加,同时记录单位
  712. this.newAlarm.forEach(item => {
  713. const value = parseFloat(item.value) || 0
  714. // 提取单位(优先使用数据中的dw字段,无则用默认值)
  715. const unit = item.dw || this.getDefaultUnit(item.subName)
  716. switch (item.subName) {
  717. case '温度':
  718. sumMap.temperature.sum += value
  719. sumMap.temperature.count++
  720. if (!sumMap.temperature.unit && unit) sumMap.temperature.unit = unit
  721. break
  722. case '湿度':
  723. sumMap.humidity.sum += value
  724. sumMap.humidity.count++
  725. if (!sumMap.humidity.unit && unit) sumMap.humidity.unit = unit
  726. break
  727. case '二氧化碳':
  728. case 'CO2浓度':
  729. sumMap.co2.sum += value
  730. sumMap.co2.count++
  731. if (!sumMap.co2.unit && unit) sumMap.co2.unit = unit
  732. break
  733. case 'PM2.5浓度':
  734. sumMap.pm25.sum += value
  735. sumMap.pm25.count++
  736. if (!sumMap.pm25.unit && unit) sumMap.pm25.unit = unit
  737. break
  738. case 'TVOC':
  739. sumMap.tvoc.sum += value
  740. sumMap.tvoc.count++
  741. if (!sumMap.tvoc.unit && unit) sumMap.tvoc.unit = unit
  742. break
  743. case 'PM10浓度':
  744. sumMap.pm10.sum += value
  745. sumMap.pm10.count++
  746. if (!sumMap.pm10.unit && unit) sumMap.pm10.unit = unit
  747. break
  748. case '甲醛':
  749. sumMap.formaldehyde.sum += value
  750. sumMap.formaldehyde.count++
  751. if (!sumMap.formaldehyde.unit && unit) sumMap.formaldehyde.unit = unit
  752. break
  753. // 告警类指标直接存储状态
  754. case '红外':
  755. sumMap.infrared.push(item.value)
  756. break
  757. case '消防':
  758. sumMap.fire.push(item.value)
  759. break
  760. case '漏水':
  761. sumMap.waterLeak.push(item.value)
  762. break
  763. }
  764. })
  765. console.log('累加结果:', sumMap)
  766. // 3. 计算平均值(保留两位小数),并赋值单位
  767. this.avgData = {
  768. temperature: sumMap.temperature.count ? (sumMap.temperature.sum / sumMap.temperature.count).toFixed(2) : '0.00',
  769. temperatureUnit: sumMap.temperature.unit || '℃',
  770. humidity: sumMap.humidity.count ? (sumMap.humidity.sum / sumMap.humidity.count).toFixed(2) : '0.00',
  771. humidityUnit: sumMap.humidity.unit || '%',
  772. pm25: sumMap.pm25.count ? (sumMap.pm25.sum / sumMap.pm25.count).toFixed(2) : '0.00',
  773. pm25Unit: sumMap.pm25.unit || 'ug/立方米',
  774. tvoc: sumMap.tvoc.count ? (sumMap.tvoc.sum / sumMap.tvoc.count).toFixed(2) : '0.00',
  775. tvocUnit: sumMap.tvoc.unit || 'LuK',
  776. pm10: sumMap.pm10.count ? (sumMap.pm10.sum / sumMap.pm10.count).toFixed(2) : '0.00',
  777. pm10Unit: sumMap.pm10.unit || 'ug/立方米',
  778. co2: sumMap.co2.count ? (sumMap.co2.sum / sumMap.co2.count).toFixed(2) : '0.00',
  779. co2Unit: sumMap.co2.unit || 'ppm',
  780. formaldehyde: sumMap.formaldehyde.count ? (sumMap.formaldehyde.sum / sumMap.formaldehyde.count).toFixed(2) : '0.00',
  781. formaldehydeUnit: sumMap.formaldehyde.unit || 'ppm'
  782. }
  783. console.log('平均值:', this.avgData)
  784. // 4. 处理告警状态(ON=告警,OFF=正常;只要有一个ON就显示告警)
  785. this.alarmStatus = {
  786. infrared: sumMap.infrared.some(s => s === 'ON') ? '告警' : '正常',
  787. fire: sumMap.fire.some(s => s === 'ON') ? '告警' : '正常',
  788. waterLeak: sumMap.waterLeak.some(s => s === 'ON') ? '告警' : '正常'
  789. }
  790. // 兼容无数据的情况
  791. if (sumMap.infrared.length === 0) this.alarmStatus.infrared = '正常'
  792. if (sumMap.fire.length === 0) this.alarmStatus.fire = '正常'
  793. if (sumMap.waterLeak.length === 0) this.alarmStatus.waterLeak = '正常'
  794. },
  795. /**
  796. * 获取指标默认单位兜底用
  797. */
  798. getDefaultUnit(subName) {
  799. const unitMap = {
  800. '温度': '℃',
  801. '湿度': '%',
  802. 'PM2.5浓度': 'ug/立方米',
  803. 'TVOC': 'LuK',
  804. 'PM10浓度': 'ug/立方米',
  805. '二氧化碳': 'ppm',
  806. 'CO2浓度': 'ppm',
  807. '甲醛': 'ppm',
  808. '综合气体': '无量纲'
  809. }
  810. return unitMap[subName] || ''
  811. },
  812. /**
  813. * 根据所有设备的平均值计算AQI
  814. */
  815. calcAQIByAvg() {
  816. if (!this.hasValidData) {
  817. this.aqiValue = 45
  818. this.aqiStatus = '优'
  819. return
  820. }
  821. // 1. 提取PM2.5、PM10数值(转数字,兜底0),单位:μg/m³(国标24小时平均)
  822. const pm25 = parseFloat(this.avgData.pm25) || 0
  823. const pm10 = parseFloat(this.avgData.pm10) || 0
  824. console.log('PM2.5平均值:', pm25, 'μg/m³ | PM10平均值:', pm10, 'μg/m³')
  825. // 2. 国标AQI浓度限值区间(24小时平均,μg/m³)
  826. const aqiBpTable = {
  827. pm25: [
  828. { iaqiLo: 0, iaqiHi: 50, bpLo: 0, bpHi: 35 }, // 优
  829. { iaqiLo: 50, iaqiHi: 100, bpLo: 35, bpHi: 75 }, // 良
  830. { iaqiLo: 100, iaqiHi: 150, bpLo: 75, bpHi: 115 }, // 轻度污染
  831. { iaqiLo: 150, iaqiHi: 200, bpLo: 115, bpHi: 150 }, // 中度污染
  832. { iaqiLo: 200, iaqiHi: 300, bpLo: 150, bpHi: 250 }, // 重度污染
  833. { iaqiLo: 300, iaqiHi: 500, bpLo: 250, bpHi: 500 } // 严重污染
  834. ],
  835. pm10: [
  836. { iaqiLo: 0, iaqiHi: 50, bpLo: 0, bpHi: 50 }, // 优
  837. { iaqiLo: 50, iaqiHi: 100, bpLo: 50, bpHi: 150 }, // 良
  838. { iaqiLo: 100, iaqiHi: 150, bpLo: 150, bpHi: 250 }, // 轻度污染
  839. { iaqiLo: 150, iaqiHi: 200, bpLo: 250, bpHi: 350 }, // 中度污染
  840. { iaqiLo: 200, iaqiHi: 300, bpLo: 350, bpHi: 420 }, // 重度污染
  841. { iaqiLo: 300, iaqiHi: 500, bpLo: 420, bpHi: 500 } // 严重污染
  842. ]
  843. }
  844. // 3. 计算单污染物分指数(IAQI)的核心方法
  845. const calculateIAQI = (conc, pollutantType) => {
  846. const bpTable = aqiBpTable[pollutantType]
  847. // 浓度超过上限,直接返回500
  848. if (conc > bpTable[bpTable.length - 1].bpHi) {
  849. return 500
  850. }
  851. // 匹配浓度所在区间
  852. const matchedBp = bpTable.find(item => conc >= item.bpLo && conc <= item.bpHi) || bpTable[0]
  853. // 国标IAQI计算公式
  854. const iaqi = ((matchedBp.iaqiHi - matchedBp.iaqiLo) / (matchedBp.bpHi - matchedBp.bpLo)) * (conc - matchedBp.bpLo) + matchedBp.iaqiLo
  855. // 四舍五入,且不超过500
  856. return Math.min(Math.round(iaqi), 500)
  857. }
  858. // 4. 计算PM2.5、PM10的分指数
  859. const iaqiPm25 = calculateIAQI(pm25, 'pm25')
  860. const iaqiPm10 = calculateIAQI(pm10, 'pm10')
  861. // 5. AQI取分指数最大值
  862. const finalAQI = Math.max(iaqiPm25, iaqiPm10)
  863. this.aqiValue = finalAQI
  864. // 6. 匹配AQI对应的空气质量等级
  865. const matchedGrade = this.aqiGradeStandard.find(grade =>
  866. finalAQI >= grade.aqiMin && finalAQI <= grade.aqiMax
  867. )
  868. this.aqiStatus = matchedGrade ? matchedGrade.level : '严重污染'
  869. console.log('IAQI-PM2.5:', iaqiPm25, 'IAQI-PM10:', iaqiPm10, '最终AQI:', finalAQI, '| 等级:', this.aqiStatus)
  870. },
  871. /**
  872. * iframe加载回调保留原有逻辑
  873. */
  874. getIframeLoading(value) {}
  875. }
  876. }
  877. </script>
  878. <style rel="stylesheet/scss" lang="scss" scoped>
  879. @import "~@/assets/styles/lend-manage.scss";
  880. .env-container {
  881. width: 100%;
  882. height: calc(100vh);
  883. background-color: #031435;
  884. .env-top-title {
  885. width: calc(100vw);
  886. height: 130px;
  887. background: url("~@/assets/images/largeScreen/top-title.png") no-repeat 0 -14px;
  888. background-size: contain;
  889. }
  890. .header-date {
  891. position: fixed;
  892. top: 10px;
  893. right: 80px;
  894. display: flex;
  895. justify-content: flex-start;
  896. align-items: center;
  897. color: #fff;
  898. .time {
  899. font-size: 32px;
  900. font-weight: bold;
  901. line-height: 30px;
  902. padding-right: 20px;
  903. border-right: 1px solid rgba(255, 255, 255, 0.5);
  904. }
  905. .time-other {
  906. font-size: 18px;
  907. line-height: 22px;
  908. padding-left: 20px;
  909. span {
  910. display: block;
  911. }
  912. }
  913. }
  914. .env-main {
  915. display: flex;
  916. justify-content: space-between;
  917. padding: 0 25px;
  918. margin-top: -12px;
  919. .env-main-left,
  920. .env-main-right {
  921. max-width: 22%;
  922. flex: 1;
  923. height: calc(100vh - 138px);
  924. overflow: hidden;
  925. z-index: 9999;
  926. }
  927. .env-main-middle {
  928. position: relative;
  929. flex: 1;
  930. margin: 0 20px;
  931. height: calc(100vh - 138px);
  932. overflow: hidden;
  933. background: url("~@/assets/images/largeScreen/bg.png") no-repeat center center;
  934. }
  935. .env-main-left .container-wrap {
  936. min-height: auto;
  937. }
  938. .env-main-left .container-wrap.left-wrap {
  939. height: calc(100% / 2 - 85px);
  940. }
  941. .env-main-right .container-wrap {
  942. height: calc(100% / 3 - 14px);
  943. min-height: auto;
  944. margin-bottom: 20px;
  945. }
  946. .env-item {
  947. margin-bottom: 20px;
  948. text-align: center;
  949. h3 {
  950. position: relative;
  951. display: inline-block;
  952. padding: 10px 70px;
  953. font-size: 16px;
  954. color: #fff;
  955. .iconfont {
  956. margin-right: 10px;
  957. font-size: 14px;
  958. color: #f65163;
  959. }
  960. &::before {
  961. content: "";
  962. position: absolute;
  963. left: 0;
  964. top: 50%;
  965. width: 36px;
  966. height: 12px;
  967. margin-top: -6px;
  968. background: url("~@/assets/images/largeScreen/item-left.png") no-repeat;
  969. background-size: cover;
  970. }
  971. &::after {
  972. content: "";
  973. position: absolute;
  974. top: 50%;
  975. right: 0;
  976. width: 36px;
  977. height: 12px;
  978. margin-top: -6px;
  979. background: url("~@/assets/images/largeScreen/item-right.png") no-repeat;
  980. background-size: cover;
  981. }
  982. }
  983. }
  984. .env-3d {
  985. position: fixed;
  986. left: 0;
  987. top: -40px;
  988. width: 100%;
  989. // height: calc(100%);
  990. height: 100%;
  991. // background: url("~@/assets/images/largeScreen/bg.png") no-repeat center 0;
  992. overflow: hidden;
  993. .iframe_box {
  994. width: 100%;
  995. height: 100%;
  996. }
  997. }
  998. }
  999. }
  1000. .iframe_box {
  1001. /* 移除原有width/height,改用内联样式或下面的样式 */
  1002. width: 100% !important;
  1003. height: 100% !important;
  1004. border: none;
  1005. }
  1006. .banner-top-name{
  1007. position: absolute;
  1008. left: 0;
  1009. top: 80px;
  1010. padding: 0 15px;
  1011. height: 34px;
  1012. line-height: 32px;
  1013. font-size: 18px;
  1014. color: #fff;
  1015. background-color: #113d72;
  1016. border: 1px solid #339cff;
  1017. border-radius: 4px;
  1018. }
  1019. .air-quality{
  1020. position: absolute;
  1021. bottom: 10px;
  1022. right: 20px;
  1023. color: #fff;
  1024. padding: 20px 20px 10px 20px;
  1025. background-image: linear-gradient(to top, rgba(24, 176, 143, .5), rgba(24, 176, 143, 0));
  1026. border-radius: 5px;
  1027. z-index: 9999;
  1028. h3{
  1029. padding: 30px 0;
  1030. }
  1031. .air-params{
  1032. display: flex;
  1033. justify-content: space-between;
  1034. align-items: last baseline;
  1035. .air-left{
  1036. .air-title{
  1037. position: relative;
  1038. padding-left: 12px;
  1039. font-size: 14px;
  1040. &::before{
  1041. content: "";
  1042. position: absolute;
  1043. left: 0;
  1044. top: 50%;
  1045. width: 6px;
  1046. height: 6px;
  1047. background-color: #18B08F;
  1048. border-radius: 50%;
  1049. }
  1050. }
  1051. .air-result{
  1052. display: flex;
  1053. justify-content: space-between;
  1054. align-items: last baseline;
  1055. padding-top: 10px;
  1056. p{
  1057. font-size: 30px;
  1058. font-weight: 600;
  1059. padding: 0 6px 0 10px;
  1060. }
  1061. span{
  1062. display: block;
  1063. font-size: 12px;
  1064. opacity: .6;
  1065. }
  1066. }
  1067. }
  1068. .air-right{
  1069. text-align: center;
  1070. span{
  1071. display: block;
  1072. font-size: 12px;
  1073. }
  1074. p{
  1075. font-size: 18px;
  1076. font-weight: 600;
  1077. padding: 10px 30px;
  1078. margin-top: 10px;
  1079. background-color: rgba(24, 176, 143, .2);
  1080. border-radius: 5px;
  1081. }
  1082. }
  1083. }
  1084. // 原有基础样式保留
  1085. &.air-excellent { // 优 - 绿色
  1086. background-image: linear-gradient(to top, rgba(40, 180, 40, .5), rgba(40, 180, 40, 0));
  1087. .air-params .air-right p {
  1088. background-color: rgba(40, 180, 40, .2);
  1089. }
  1090. }
  1091. &.air-good { // 良 - 黄色
  1092. background-image: linear-gradient(to top, rgba(255, 200, 0, .5), rgba(255, 200, 0, 0));
  1093. .air-params .air-right p {
  1094. background-color: rgba(255, 200, 0, .2);
  1095. }
  1096. }
  1097. &.air-lightPollution { // 轻度污染 - 橙色
  1098. background-image: linear-gradient(to top, rgba(255, 140, 0, .5), rgba(255, 140, 0, 0));
  1099. .air-params .air-right p {
  1100. background-color: rgba(255, 140, 0, .2);
  1101. }
  1102. }
  1103. &.air-mediumPollution { // 中度污染 - 红色
  1104. background-image: linear-gradient(to top, rgba(255, 0, 0, .5), rgba(255, 0, 0, 0));
  1105. .air-params .air-right p {
  1106. background-color: rgba(255, 0, 0, .2);
  1107. }
  1108. }
  1109. &.air-heavyPollution { // 重度污染 - 紫色
  1110. background-image: linear-gradient(to top, rgba(150, 0, 200, .5), rgba(150, 0, 200, 0));
  1111. .air-params .air-right p {
  1112. background-color: rgba(150, 0, 200, .2);
  1113. }
  1114. }
  1115. &.air-severePollution { // 严重污染 - 褐红色
  1116. background-image: linear-gradient(to top, rgba(120, 0, 0, .5), rgba(120, 0, 0, 0));
  1117. .air-params .air-right p {
  1118. background-color: rgba(120, 0, 0, .2);
  1119. }
  1120. }
  1121. }
  1122. .air-warn{
  1123. background-image: linear-gradient(to top, rgba(246, 81, 99, .5), rgba(24, 176, 143, 0));
  1124. .air-params{
  1125. .air-right{
  1126. p{
  1127. background-color: rgba(246, 81, 99, .2);
  1128. }
  1129. }
  1130. }
  1131. }
  1132. .leakage-list {
  1133. display: flex;
  1134. justify-content: flex-start;
  1135. flex-wrap: wrap;
  1136. padding: 0 0 0 14px;
  1137. text-align: left;
  1138. &::-webkit-scrollbar {
  1139. width: 4px;
  1140. height: 4px;
  1141. }
  1142. &::-webkit-scrollbar-thumb {
  1143. background-color: #339cff;
  1144. border-radius: 2px;
  1145. }
  1146. &::-webkit-scrollbar-track {
  1147. background-color: #021941;
  1148. }
  1149. li {
  1150. position: relative;
  1151. display: flex;
  1152. justify-content: space-between;
  1153. align-items: center;
  1154. padding: 0 15px;
  1155. border: 1px solid #3581cc;
  1156. background-color: #02255f;
  1157. border-radius: 2px;
  1158. &::before {
  1159. content: "";
  1160. position: absolute;
  1161. top: 4px;
  1162. left: 4px;
  1163. width: 0;
  1164. height: 0;
  1165. border-color: transparent #339cff;
  1166. border-width: 0 0 6px 6px;
  1167. border-style: solid;
  1168. }
  1169. p {
  1170. font-size: 12px;
  1171. color: #fff;
  1172. i {
  1173. margin-right: 4px;
  1174. }
  1175. }
  1176. span.leakage-state-tip {
  1177. position: relative;
  1178. display: block;
  1179. width: 6px;
  1180. height: 6px;
  1181. border-radius: 50%;
  1182. background-color: #18b08f;
  1183. &::before {
  1184. content: "";
  1185. position: absolute;
  1186. left: 50%;
  1187. top: 50%;
  1188. width: 14px;
  1189. height: 14px;
  1190. border-radius: 50%;
  1191. box-shadow: inset 0px 0px 10px 1px #18b08f;
  1192. transform: translate(-50%, -50%);
  1193. }
  1194. }
  1195. &.leakage-warn {
  1196. border-color: #f65164;
  1197. box-shadow: inset 0px 0px 15px 1px #f65164;
  1198. color: #f65164;
  1199. &::before {
  1200. border-color: transparent #f65164;
  1201. }
  1202. span.leakage-state-tip {
  1203. background-color: #f65164;
  1204. &::before {
  1205. box-shadow: inset 0px 0px 10px 1px #f65164;
  1206. }
  1207. }
  1208. }
  1209. }
  1210. }
  1211. .env-alarm-list{
  1212. display: flex;
  1213. justify-content: flex-start;
  1214. // width: 100%;
  1215. width: calc(100% / 2);
  1216. li{
  1217. display: flex;
  1218. flex-direction: column;
  1219. align-items: center;
  1220. align-content: center;
  1221. justify-content: center;
  1222. height: 66px;
  1223. margin-right: 10px;
  1224. background: url('~@/assets/images/data_border_default.png') no-repeat;
  1225. background-size: 100% 100%;
  1226. position: relative;
  1227. color: #fff;
  1228. font-size: 14px;
  1229. &.li-warn{
  1230. background: url('~@/assets/images/data_border_warn.png') no-repeat;
  1231. background-size: 100% 100%;
  1232. }
  1233. p{
  1234. color: #339CFF;
  1235. }
  1236. span{
  1237. display: block;
  1238. margin-top: 12px;
  1239. font-weight: bold;
  1240. }
  1241. }
  1242. }
  1243. // 告警列表容器
  1244. .env-alarm-container {
  1245. position: absolute;
  1246. top: 10px;
  1247. left: 10px;
  1248. width: calc(100% - 20px);
  1249. display: flex;
  1250. justify-content: flex-start;
  1251. // 第一行:温湿度
  1252. .env-alarm-list-first {
  1253. margin-bottom: 20px;
  1254. li {
  1255. width: calc(100% / 2 - 10px);
  1256. height: 90px;
  1257. font-size: 24px;
  1258. flex-direction: row;
  1259. & .msg-list-svg{
  1260. font-size: 40px;
  1261. margin-right: 14px;
  1262. }
  1263. & div{
  1264. span{
  1265. margin-top: 0 !important;
  1266. }
  1267. p{
  1268. font-size: 18px;
  1269. margin-top: 6px;
  1270. }
  1271. }
  1272. }
  1273. }
  1274. // .env-alarm-list-second {
  1275. // li{
  1276. // width: calc(100% / 10 - 10px);
  1277. // }
  1278. // }
  1279. }
  1280. .env-alarm-list-second {
  1281. position: absolute;
  1282. bottom: 10px;
  1283. left: 10px;
  1284. display: flex;
  1285. justify-content: flex-start;
  1286. flex-wrap: wrap;
  1287. color: #fff;
  1288. width: 600px;
  1289. height: 130px;
  1290. font-size: 14px;
  1291. li {
  1292. display: flex;
  1293. align-items: center;
  1294. justify-content: space-between;
  1295. width: calc(100% / 3 - 10px);
  1296. height: calc(100% / 3 - 14px);
  1297. background: linear-gradient(
  1298. 360deg,
  1299. rgba(51, 156, 255, 0.24) 0%,
  1300. rgba(56, 158, 225, 0) 70%,
  1301. rgba(56, 158, 225, 0) 100%
  1302. );
  1303. margin-left: 10px;
  1304. margin-bottom: 14px;
  1305. padding: 0 10px;
  1306. p{
  1307. font-size: 12px;
  1308. color: #339CFF;
  1309. }
  1310. span{
  1311. display: inline-block;
  1312. font-weight: bold;
  1313. }
  1314. &.li-warn{
  1315. background: linear-gradient(
  1316. 360deg,
  1317. rgba(246, 81, 100, 0.5) 0%,
  1318. rgba(56, 158, 225, 0) 70%,
  1319. rgba(56, 158, 225, 0) 100%
  1320. );
  1321. span{
  1322. color: #F65164;
  1323. }
  1324. }
  1325. }
  1326. }
  1327. .device-container{
  1328. position: absolute;
  1329. bottom: 0;
  1330. left: 10px;
  1331. // height: 160px;
  1332. // padding: 14px 0 10px 14px;
  1333. // min-height: auto;
  1334. }
  1335. .new-leakage-list{
  1336. width: 660px;
  1337. height: 120px;
  1338. padding: 0;
  1339. li {
  1340. display: flex;
  1341. align-items: center;
  1342. justify-content: space-between;
  1343. width: calc(100% / 3 - 10px);
  1344. height: calc(100% / 2 - 14px);
  1345. margin-right: 10px;
  1346. margin-bottom: 14px;
  1347. p{
  1348. font-size: 14px;
  1349. }
  1350. }
  1351. }
  1352. .device-info {
  1353. width: 600px;
  1354. height: 30px;
  1355. display: flex;
  1356. flex-direction: row;
  1357. justify-content: space-between;
  1358. margin-bottom: 14px;
  1359. li {
  1360. width: calc(100% / 2);
  1361. height: calc(100%);
  1362. background: linear-gradient(
  1363. 360deg,
  1364. rgba(51, 156, 255, 0.24) 0%,
  1365. rgba(56, 158, 225, 0) 70%,
  1366. rgba(56, 158, 225, 0) 100%
  1367. );
  1368. display: flex;
  1369. align-items: center;
  1370. justify-content: space-between;
  1371. margin-left: 10px;
  1372. .row-item {
  1373. display: flex;
  1374. align-items: center;
  1375. .svg-box {
  1376. margin-right: 10px;
  1377. .card-panel-icon {
  1378. font-size: 24px;
  1379. }
  1380. }
  1381. }
  1382. .row-num {
  1383. font-size: 18px;
  1384. color: #fff;
  1385. margin-right: 10px;
  1386. }
  1387. }
  1388. }
  1389. .screen-env-list {
  1390. flex-wrap: wrap;
  1391. // justify-content: space-between;
  1392. // justify-content: center;
  1393. height: calc(100% - 54px);
  1394. padding: 0 10px;
  1395. li {
  1396. flex: none;
  1397. width: calc(100% / 2 - 22px);
  1398. margin: 10px 10px 0 10px;
  1399. height: calc(100% / 3 - 10px) !important;
  1400. .msg-txt{
  1401. display: flex;
  1402. flex-direction: column;
  1403. align-items: center;
  1404. justify-content: center;
  1405. height: calc(100%);
  1406. .msg-list-num{
  1407. font-size: 18px;
  1408. position: static;
  1409. }
  1410. .msg-list-unit{
  1411. font-size: 12px;
  1412. position: static;
  1413. margin-top: 4px;
  1414. }
  1415. }
  1416. .msg-list-svg {
  1417. font-size: 40px;
  1418. margin-left: 20px !important;
  1419. }
  1420. &.msg-pm {
  1421. .msg-list-svg {
  1422. font-size: 46px;
  1423. }
  1424. }
  1425. &.alarm-status{
  1426. .msg-txt{
  1427. flex-direction: row;
  1428. }
  1429. &.li-warn{
  1430. span{
  1431. color: #F65164;
  1432. }
  1433. }
  1434. }
  1435. }
  1436. }
  1437. .panel-group {
  1438. width: calc(100% / 2);
  1439. .card-panel-col {
  1440. margin-bottom: 20px;
  1441. }
  1442. .card-panel {
  1443. cursor: pointer;
  1444. height: 100px;
  1445. font-size: 15px;
  1446. position: relative;
  1447. overflow: hidden;
  1448. opacity: 0.86;
  1449. &.zaixianshebei {
  1450. color: #c4c859;
  1451. background: linear-gradient(
  1452. 180deg,
  1453. rgba(196, 200, 89, 0.5) 0%,
  1454. rgba(196, 200, 89, 0) 100%
  1455. );
  1456. border-top: 2px #c4c859 solid;
  1457. & span.card-panel-num {
  1458. background: linear-gradient(180deg, #ffffff 0%, #bfc458 100%);
  1459. }
  1460. }
  1461. &.lixianshebei {
  1462. color: #f65164;
  1463. background: linear-gradient(
  1464. 180deg,
  1465. rgba(246, 81, 100, 0.5) 0%,
  1466. rgba(247, 80, 100, 0) 100%
  1467. );
  1468. border-top: 2px #f65164 solid;
  1469. & span.card-panel-num {
  1470. background: linear-gradient(180deg, #ffffff 0%, #f55164 100%);
  1471. }
  1472. }
  1473. .card-panel-icon-wrapper {
  1474. float: left;
  1475. margin: 0 10px 0 12px;
  1476. padding: 20px;
  1477. transition: all 0.38s ease-out;
  1478. border-radius: 6px;
  1479. }
  1480. .card-panel-icon {
  1481. float: left;
  1482. font-size: 60px;
  1483. }
  1484. .card-panel-description {
  1485. margin: 19px;
  1486. margin-left: 0px;
  1487. .card-panel-text {
  1488. line-height: 30px;
  1489. color: rgba(0, 0, 0, 0.45);
  1490. font-size: 24px;
  1491. margin-bottom: 11px;
  1492. & span {
  1493. -webkit-background-clip: text;
  1494. color: transparent;
  1495. font-weight: bold;
  1496. }
  1497. }
  1498. }
  1499. }
  1500. }
  1501. </style>