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

787 lines
24 KiB

3 days ago
  1. <template>
  2. <div class="env-container">
  3. <div class="env-top-title" />
  4. <div class="current-date">{{ nowDate }}</div>
  5. <div class="env-main">
  6. <div class="env-main-left">
  7. <div class="env-item container-wrap">
  8. <span class="right-top-line" />
  9. <span class="left-bottom-line" />
  10. <h3>
  11. <svg-icon icon-class="danganjieyue" style="margin-right:10px" />档案借阅
  12. </h3>
  13. <div class="chart-wrapper" style="height: calc(100% - 40px);">
  14. <lend-across :lend-data="lendData" :refreshtime="refreshtime" />
  15. </div>
  16. </div>
  17. <div class="env-item container-wrap" style="height: calc(100% - 170px) !important;">
  18. <span class="right-top-line" />
  19. <span class="left-bottom-line" />
  20. <h3>
  21. <i class="iconfont icon-kongqizhiliangshuju" />档案库空气质量数据
  22. </h3>
  23. <span style="display:block; text-align: center; font-size: 12px; margin-left: 10px; color: #3a99fd;">
  24. {{ currentDeviceName || '' }}
  25. </span>
  26. <ul v-if="newAlarm && newAlarm.length !== 0" class="screen-env-list">
  27. <li v-for="item in newAlarm" :key="item.SUBID">
  28. <svg-icon v-if="item.subName === '温度'" icon-class="temperature" class-name="msg-list-svg" />
  29. <svg-icon v-if="item.subName === '湿度'" icon-class="shidu" class-name="msg-list-svg" />
  30. <svg-icon v-if="item.subName === 'PM2.5浓度'" icon-class="pm25" class-name="msg-list-svg" />
  31. <svg-icon v-if="item.subName === 'PM10浓度'" icon-class="pm10" class-name="msg-list-svg" />
  32. <svg-icon v-if="item.subName === 'TVOC'" icon-class="voc" class-name="msg-list-svg" />
  33. <svg-icon v-if="item.subName === '二氧化碳'" icon-class="co2" class-name="msg-list-svg" />
  34. <svg-icon v-if="item.subName === '甲醛'" icon-class="jiaquan" class-name="msg-list-svg" style="font-size: 64px; margin-left: 10px;" />
  35. <!-- <svg-icon v-if="item.subName === '综合气体'" icon-class="comprehensive-gas" class-name="msg-list-svg" /> -->
  36. <svg-icon v-if="item.subName === '空气质量'" icon-class="kongqi" class-name="msg-list-svg" />
  37. <div class="msg-txt">
  38. <span class="msg-list-num">{{ item.value }}</span>
  39. <p class="msg-list-unit">{{ item.subName }} {{ item.dw }}</p>
  40. </div>
  41. </li>
  42. </ul>
  43. <!-- 空状态提示 -->
  44. <div v-else class="empty-tip" style="display:flex; justify-content: center; height: calc(100% - 80px); color: #909399; align-items: center; font-size: 12px;">
  45. 暂无数据
  46. </div>
  47. </div>
  48. </div>
  49. <div class="env-main-middle" :style="{ height: allDisplayConfigData?.length ? 'calc(100vh - 138px)' : 'calc(100vh - 138px)', overflow: 'hidden' }">
  50. <div class="env-3d" :style="{ height: allDisplayConfigData?.length ? 'calc(100% - 80px)' : 'calc(100% + 80px)'}">
  51. <div class="banner-top-name">{{ bannerRoomName }}</div>
  52. <iframe id="myIframe" ref="myIframe" name="iframeMap" class="iframe_box" src="/web3D/index.html" frameborder="0" scrolling="no" />
  53. </div>
  54. <div v-if="newAlarm && newAlarm.length !== 0" class="air-quality" :class="{'air-warn': aqiStatus === '污染'}">
  55. <h3>实时空气质量指数AQI</h3>
  56. <div class="air-params">
  57. <div class="air-left">
  58. <span class="air-title">实时AQI</span>
  59. <div class="air-result"><p>{{ aqiValue }}</p><span>(AQI-US)</span></div>
  60. </div>
  61. <div class="air-right">
  62. <span>空气质量为</span>
  63. <p>{{ aqiStatus }}</p>
  64. </div>
  65. </div>
  66. </div>
  67. <!-- <div v-if="allDisplayConfigData && allDisplayConfigData.length !==0 " class="middle-bottom">
  68. <span class="right-top-line" />
  69. <span class="left-bottom-line" />
  70. <ul class="leakage-list">
  71. <li v-for="item in allDisplayConfigData" :key="item.id" :class="{ 'leakage-warn': item.NetStatus === 0 }">
  72. <p><i class="iconfont icon-weihubaojing" />{{ item.Name }}</p>
  73. <span class="leakage-state-tip" />
  74. </li>
  75. </ul>
  76. </div> -->
  77. <div v-if="validDisplayConfigData.length" class="middle-bottom">
  78. <span class="right-top-line" />
  79. <span class="left-bottom-line" />
  80. <ul
  81. class="leakage-list"
  82. :style="{
  83. height: showScroll ? '147px' : 'auto', // 数量>6时固定高度,否则自适应
  84. overflow: showScroll ? 'auto' : 'hidden' // 数量>6时显示滚动
  85. }"
  86. >
  87. <li
  88. v-for="item in validDisplayConfigData"
  89. :key="item.id"
  90. :class="{ 'leakage-warn': item.NetStatus === 0 }"
  91. :style="{
  92. width: liWidth,
  93. height: liHeight,
  94. marginRight: '14px',
  95. marginBottom: '14px'
  96. }"
  97. >
  98. <p><i class="iconfont icon-shebei" />{{ item.Name }}</p>
  99. <span class="leakage-state-tip" />
  100. </li>
  101. </ul>
  102. </div>
  103. </div>
  104. <div class="env-main-right">
  105. <!-- 门禁记录 -->
  106. <!-- <security-door :height="'calc(100% - 38px)'" style="margin-bottom: 15px;" /> -->
  107. <AccessDoor :height="'calc(100% - 40px)'" style="margin-bottom: 15px;" />
  108. <!-- 报警记录 -->
  109. <warehouse-warning :height="'calc(100% - 38px)'" />
  110. </div>
  111. </div>
  112. </div>
  113. </template>
  114. <script>
  115. import { getCurrentTime } from '@/utils/index'
  116. import lendAcross from '@/views/components/echarts/lendAcross.vue'
  117. import WarehouseWarning from '@/views/components/WarehouseWarning'
  118. // import SecurityDoor from '@/views/components/SecurityDoor'
  119. import AccessDoor from '@/views/components/AccessDoor'
  120. import displayConfigApi from '@/api/storeManage/displayConfig'
  121. // import thirdApi from '@/api/thirdApi'
  122. import { statisticsCrud } from '@/views/system/archiveStatistics/mixins/statistics'
  123. import alarmApi from '@/api/home/alarm'
  124. // import { allDeviceData, mockIpData } from './index.js'
  125. // // 同步mock方法
  126. // const mockFetchDataForIP = (params) => {
  127. // return new Promise((resolve) => {
  128. // setTimeout(() => {
  129. // const ip = params.ip
  130. // const result = mockIpData[ip] || { code: 200, message: '操作成功', data: [], timestamp: Date.now() }
  131. // resolve(result.data)
  132. // }, 500)
  133. // })
  134. // }
  135. export default {
  136. name: 'EnvironmentalScreen',
  137. components: {
  138. WarehouseWarning,
  139. // SecurityDoor,
  140. AccessDoor,
  141. lendAcross
  142. },
  143. mixins: [statisticsCrud],
  144. data() {
  145. return {
  146. bannerRoomName: '3F 全景图',
  147. nowDate: '',
  148. timer: null,
  149. echartsTimer: null,
  150. roomId: 'D6490DA3D4261E8C26D0E3',
  151. allDisplayConfigData: [],
  152. displayConfigData: [],
  153. url: '',
  154. allDeviceIds: [],
  155. oaoMessage: [],
  156. topDisplayData: {
  157. DAK_DIV_TOP_001: { show: true, curValue: '23.10', unit: '℃', curstatus: 0 },
  158. DAK_DIV_TOP_002: { show: true, curValue: '48.90', unit: '%', curstatus: 0 },
  159. DAK_DIV_TOP_003: { show: true, curValue: '619', unit: 'ppm', curstatus: 0 },
  160. DAK_DIV_TOP_004: { show: true, curValue: '0.21', unit: 'mg/m³', curstatus: 0 },
  161. DAK_DIV_TOP_005: { show: true, curValue: '26.00', unit: 'ug/m³', curstatus: 0 },
  162. DAK_DIV_TOP_006: { show: true, curValue: '38.00', unit: 'ug/m³', curstatus: 0 }
  163. },
  164. refreshtime: 60000,
  165. lendData: [],
  166. typeData: [],
  167. // 同步FullView的核心数据
  168. newAlarm: [],
  169. aqiValue: 45,
  170. aqiStatus: '健康',
  171. keepIndicators: [
  172. '二氧化碳',
  173. '甲醛',
  174. '综合气体',
  175. 'PM2.5浓度',
  176. 'PM10浓度',
  177. '温度',
  178. '湿度',
  179. '空气质量'
  180. ],
  181. ipToNameMap: {}, // IP到设备名称映射
  182. currentDeviceName: '', // 当前显示设备名称
  183. currentIpIndex: 0, // IP轮询索引
  184. excludeIpList: ['192.168.99.101:6003'], // 排除IP列表
  185. dataTimer: null, // 数据轮询定时器
  186. iframeWin: null // iframe窗口对象
  187. }
  188. },
  189. computed: {
  190. // 过滤有效数据(排除空值/无效项)
  191. validDisplayConfigData() {
  192. return this.allDisplayConfigData.filter(item => item && item.Name)
  193. },
  194. // 每行显示数量(优先3个,数量不足时自动调整)
  195. itemsPerRow() {
  196. const len = this.validDisplayConfigData.length
  197. if (len === 0) return 0
  198. // 规则:总数<=3 → 每行显示总数;3<总数<=6 → 每行3个;总数>6 → 每行3个(最多显示2行,超出滚动)
  199. return len <= 3 ? len : 3
  200. },
  201. // li的宽度(百分比,预留间距)
  202. liWidth() {
  203. if (this.itemsPerRow === 0) return '0'
  204. // 每行n个 → 宽度 = 100%/n - 间距(14px)
  205. return `calc(100% / ${this.itemsPerRow} - 14px)`
  206. },
  207. // li的高度(百分比,根据总行数均分)
  208. liHeight() {
  209. const len = this.validDisplayConfigData.length
  210. if (len === 0) return '0'
  211. // 总行数 = 向上取整(总数/每行数量)
  212. const rows = Math.ceil(len / this.itemsPerRow)
  213. // 高度 = 100%/总行数 - 间距(14px)
  214. return `calc(100% / ${rows} - 14px)`
  215. },
  216. // 是否显示滚动(数量>6时,限制高度并显示滚动)
  217. showScroll() {
  218. return this.validDisplayConfigData.length > 6
  219. }
  220. },
  221. async created() {
  222. // 时间更新
  223. this.timer = setInterval(() => {
  224. this.nowDate = getCurrentTime()
  225. }, 1000)
  226. // 同步FullView的初始化逻辑
  227. window.getIframeLoading = this.getIframeLoading
  228. // 真实请求请替换:
  229. await alarmApi.FetchYpGetSite().then((data) => {
  230. if (data && data.length > 0) {
  231. this.allDisplayConfigData = data
  232. this.handleDeviceIpList()
  233. } else {
  234. this.allDisplayConfigData = []
  235. }
  236. })
  237. // this.allDisplayConfigData = allDeviceData
  238. // this.handleDeviceIpList()
  239. // 初始加载+创建定时器
  240. if (this.allDeviceIds.length > 0) {
  241. this.currentDeviceName = this.ipToNameMap[this.allDeviceIds[0]] || ''
  242. await this.getRealTimeData(this.allDeviceIds[0])
  243. // 移到这里创建定时器
  244. this.dataTimer = setInterval(async() => {
  245. const currentIp = this.getNextIp()
  246. this.currentDeviceName = this.ipToNameMap[currentIp] || ''
  247. await this.getRealTimeData(currentIp)
  248. }, 10000)
  249. // 10000
  250. console.log(`启动IP轮询,共${this.allDeviceIds.length}个有效IP:`, this.allDeviceIds)
  251. } else {
  252. console.warn('无有效设备IP,停止轮询')
  253. this.newAlarm = []
  254. }
  255. },
  256. mounted() {
  257. this.iframeWin = this.$refs.myIframe?.contentWindow
  258. // if (this.allDeviceIds.length > 0) {
  259. // this.dataTimer = setInterval(async() => {
  260. // const currentIp = this.getNextIp()
  261. // this.currentDeviceName = this.ipToNameMap[currentIp] || ''
  262. // await this.getRealTimeData(currentIp)
  263. // }, 10000)
  264. // console.log(`启动IP轮询,共${this.allDeviceIds.length}个有效IP:`, this.allDeviceIds)
  265. // }
  266. // 原有echarts刷新逻辑
  267. this.echartsTimer = setInterval(() => {
  268. this.lendData = []
  269. this.typeData = []
  270. this.getBorrowerNumSta()
  271. }, this.refreshtime)
  272. },
  273. beforeDestroy() {
  274. // 清理所有定时器
  275. if (this.timer) clearInterval(this.timer)
  276. if (this.echartsTimer) clearInterval(this.echartsTimer)
  277. if (this.dataTimer) {
  278. clearInterval(this.dataTimer)
  279. console.log('停止IP轮询')
  280. }
  281. },
  282. methods: {
  283. /**
  284. * 处理设备IP列表去重+排除+名称映射
  285. */
  286. handleDeviceIpList() {
  287. const ipSet = new Set()
  288. this.ipToNameMap = {}
  289. this.allDisplayConfigData.forEach(element => {
  290. const ip = (element.IP || '').trim()
  291. if (ip && !this.excludeIpList.includes(ip)) {
  292. ipSet.add(ip)
  293. this.ipToNameMap[ip] = element.Name || `未知设备(${ip})`
  294. console.log('有效设备IP:', ip, '设备名称:', element.Name)
  295. } else if (this.excludeIpList.includes(ip)) {
  296. console.log('排除指定IP:', ip, '设备名称:', element.Name)
  297. } else if (!ip) {
  298. console.log('过滤空IP:', element.Name)
  299. }
  300. })
  301. this.allDeviceIds = Array.from(ipSet)
  302. },
  303. /**
  304. * 获取下一个轮询IP循环切换
  305. */
  306. getNextIp() {
  307. if (this.allDeviceIds.length === 0) return ''
  308. // 先重置索引(防止数组长度变化导致越界)
  309. this.currentIpIndex = this.currentIpIndex % this.allDeviceIds.length
  310. const ip = this.allDeviceIds[this.currentIpIndex]
  311. this.currentIpIndex = (this.currentIpIndex + 1) % this.allDeviceIds.length
  312. console.log(`轮询切换 - 当前IP:${ip},下一个索引:${this.currentIpIndex}`)
  313. return ip
  314. },
  315. /**
  316. * 请求指定IP的实时数据过滤+AQI计算
  317. */
  318. async getRealTimeData(targetIp) {
  319. if (!targetIp) {
  320. this.newAlarm = []
  321. this.currentDeviceName = ''
  322. return
  323. }
  324. try {
  325. console.log(`开始请求IP【${targetIp}】的实时数据(模拟)`)
  326. // 模拟
  327. // const data = await mockFetchDataForIP({ ip: targetIp })
  328. // 真实请求
  329. const data = await alarmApi.FetchDataForIP({ ip: targetIp })
  330. // 过滤需要的指标
  331. const filteredData = data.filter(item =>
  332. this.keepIndicators.includes(item.subName)
  333. )
  334. if (filteredData.length > 0) {
  335. this.newAlarm = filteredData
  336. console.log(`IP【${targetIp}】(${this.currentDeviceName}) 过滤后的数据:`, filteredData)
  337. this.calcAQI(filteredData)
  338. } else {
  339. this.newAlarm = []
  340. this.aqiValue = 45
  341. this.aqiStatus = '健康'
  342. console.log(`IP【${targetIp}】(${this.currentDeviceName}) 无需要的指标数据`)
  343. }
  344. } catch (error) {
  345. this.newAlarm = []
  346. this.aqiValue = 45
  347. this.aqiStatus = '健康'
  348. this.currentDeviceName = ''
  349. console.error(`IP【${targetIp}】数据请求失败:`, error)
  350. }
  351. },
  352. getIframeLoading(value) {},
  353. // 原有切换房间方法(同步更新数据)
  354. async changeRoomGetDeivce() {
  355. this.allDeviceIds = []
  356. this.allDisplayConfigData = await displayConfigApi.list({ storeroomId: this.roomId })
  357. this.handleDeviceIpList() // 重新处理IP列表
  358. console.log('allDeviceIds2', this.allDeviceIds)
  359. this.displayConfigData = this.allDisplayConfigData.filter((item) => {
  360. return item.isDisplay && item.bindState && item.deviceInfo && (item.divPosition.includes('OAO') || item.divPosition.includes('TOP') || item.divPosition.includes('LS'))
  361. })
  362. console.log('displayConfigData2', this.displayConfigData)
  363. // 重新加载数据
  364. if (this.allDeviceIds.length > 0) {
  365. this.currentDeviceName = this.ipToNameMap[this.allDeviceIds[0]] || ''
  366. await this.getRealTimeData(this.allDeviceIds[0])
  367. } else {
  368. this.newAlarm = []
  369. this.currentDeviceName = ''
  370. }
  371. this.handleAQI()
  372. },
  373. /**
  374. * 计算AQI
  375. */
  376. calcAQI(filteredData) {
  377. const pm25 = parseFloat(filteredData.find(item => item.subName === 'PM2.5浓度')?.value || 0)
  378. const pm10 = parseFloat(filteredData.find(item => item.subName === 'PM10浓度')?.value || 0)
  379. const formaldehyde = parseFloat(filteredData.find(item => item.subName === '甲醛')?.value || 0)
  380. const co2 = parseFloat(filteredData.find(item => item.subName === '二氧化碳')?.value || 0)
  381. let aqi = 0
  382. if (pm25 > 50 || pm10 > 100 || formaldehyde > 30 || co2 > 1000) {
  383. aqi = Math.floor(Math.random() * 50) + 100
  384. this.aqiStatus = '污染'
  385. } else if (pm25 > 25 || pm10 > 50 || formaldehyde > 10 || co2 > 800) {
  386. aqi = Math.floor(Math.random() * 50) + 50
  387. this.aqiStatus = '一般'
  388. } else {
  389. aqi = Math.floor(Math.random() * 50) + 1
  390. this.aqiStatus = '健康'
  391. }
  392. this.aqiValue = aqi
  393. }
  394. }
  395. }
  396. </script>
  397. <style rel="stylesheet/scss" lang="scss" scoped>
  398. @import "~@/assets/styles/lend-manage.scss";
  399. .env-container {
  400. width: 100%;
  401. height: calc(100vh);
  402. background-color: #031435;
  403. .env-top-title {
  404. width: calc(100vw);
  405. height: 130px;
  406. background: url("~@/assets/images/largeScreen/top-title.png") no-repeat 0 -14px;
  407. background-size: contain;
  408. }
  409. .current-date {
  410. position: fixed;
  411. top: 25px;
  412. right: 150px;
  413. font-size: 16px;
  414. color: #3a99fd;
  415. }
  416. .env-main {
  417. display: flex;
  418. justify-content: space-between;
  419. padding: 0 25px;
  420. // margin-top: -12px;
  421. .env-main-left,
  422. .env-main-right {
  423. max-width: 24%;
  424. flex: 1;
  425. height: calc(100vh - 138px);
  426. overflow: hidden;
  427. }
  428. .env-main-middle {
  429. position: relative;
  430. flex: 1;
  431. margin: 0 20px;
  432. height: calc(100vh - 138px);
  433. overflow: hidden;
  434. }
  435. .env-main-left .container-wrap {
  436. min-height: auto;
  437. }
  438. .env-main-right .container-wrap {
  439. height: calc(100% / 2 - 14px);
  440. min-height: auto;
  441. }
  442. .env-item {
  443. margin-bottom: 20px;
  444. text-align: center;
  445. h3 {
  446. position: relative;
  447. display: inline-block;
  448. padding: 10px 70px;
  449. font-size: 16px;
  450. color: #fff;
  451. .iconfont {
  452. margin-right: 10px;
  453. font-size: 14px;
  454. color: #f65163;
  455. }
  456. &::before {
  457. content: "";
  458. position: absolute;
  459. left: 0;
  460. top: 50%;
  461. width: 36px;
  462. height: 12px;
  463. margin-top: -6px;
  464. background: url("~@/assets/images/largeScreen/item-left.png") no-repeat;
  465. background-size: cover;
  466. }
  467. &::after {
  468. content: "";
  469. position: absolute;
  470. top: 50%;
  471. right: 0;
  472. width: 36px;
  473. height: 12px;
  474. margin-top: -6px;
  475. background: url("~@/assets/images/largeScreen/item-right.png") no-repeat;
  476. background-size: cover;
  477. }
  478. }
  479. .msg-list {
  480. flex-wrap: wrap !important;
  481. padding: 0 20px;
  482. li {
  483. margin-bottom: 20px;
  484. display: flex;
  485. align-items: center;
  486. .msg-list-svg {
  487. font-size: 40px;
  488. margin-left: 20px;
  489. }
  490. .msg-txt {
  491. margin-left: 15px;
  492. text-align: left;
  493. .msg-list-num {
  494. font-size: 24px;
  495. color: #fff;
  496. font-weight: 600;
  497. }
  498. .msg-list-unit {
  499. font-size: 14px;
  500. color: #ccc;
  501. margin: 5px 0 0 0;
  502. }
  503. }
  504. }
  505. }
  506. .empty-tip {
  507. font-size: 14px;
  508. color: #999;
  509. padding: 40px 0;
  510. }
  511. }
  512. .screen-env-list {
  513. flex-wrap: wrap;
  514. // justify-content: space-between;
  515. justify-content: flex-start;
  516. height: calc(100% - 54px);
  517. padding: 0 10px;
  518. li {
  519. flex: none;
  520. width: calc(100% / 2 - 22px);
  521. margin: 20px 10px;
  522. height: calc(100% / 4 - 40px);
  523. .msg-list-svg {
  524. font-size: 40px;
  525. margin-left: 20px;
  526. }
  527. &.msg-pm {
  528. .msg-list-svg {
  529. font-size: 46px;
  530. }
  531. }
  532. }
  533. }
  534. .env-3d {
  535. position: relative;
  536. width: 100%;
  537. // height: calc(100% + 80px);
  538. height: calc(100% - 80px);
  539. background: url("~@/assets/images/largeScreen/bg.png") no-repeat center -130px;
  540. overflow: hidden;
  541. margin-top: -80px;
  542. .iframe_box {
  543. width: 100%;
  544. height: 100%;
  545. }
  546. .screen-env-list {
  547. position: absolute;
  548. right: 0;
  549. bottom: 0;
  550. flex-wrap: wrap;
  551. justify-content: space-between;
  552. padding: 0;
  553. width: 165px;
  554. height: 200px;
  555. z-index: 99999;
  556. li {
  557. width: 100%;
  558. margin: 20px 0 0 0;
  559. height: calc(100% / 2 - 20px);
  560. }
  561. }
  562. }
  563. }
  564. }
  565. .banner-top-name{
  566. position: absolute;
  567. left: 0;
  568. top: 80px;
  569. padding: 0 15px;
  570. height: 34px;
  571. line-height: 32px;
  572. font-size: 18px;
  573. color: #fff;
  574. background-color: #113d72;
  575. border: 1px solid #339cff;
  576. border-radius: 4px;
  577. }
  578. .air-quality{
  579. position: absolute;
  580. top: 10px;
  581. right: 20px;
  582. color: #fff;
  583. padding: 20px 20px 10px 20px;
  584. background-image: linear-gradient(to top, rgba(24, 176, 143, .5), rgba(24, 176, 143, 0));
  585. border-radius: 5px;
  586. z-index: 9999;
  587. h3{
  588. padding: 30px 0;
  589. }
  590. .air-params{
  591. display: flex;
  592. justify-content: space-between;
  593. align-items: last baseline;
  594. .air-left{
  595. .air-title{
  596. position: relative;
  597. padding-left: 12px;
  598. font-size: 14px;
  599. &::before{
  600. content: "";
  601. position: absolute;
  602. left: 0;
  603. top: 50%;
  604. width: 6px;
  605. height: 6px;
  606. background-color: #18B08F;
  607. border-radius: 50%;
  608. }
  609. }
  610. .air-result{
  611. display: flex;
  612. justify-content: space-between;
  613. align-items: last baseline;
  614. padding-top: 10px;
  615. p{
  616. font-size: 30px;
  617. font-weight: 600;
  618. padding: 0 6px 0 10px;
  619. }
  620. span{
  621. display: block;
  622. font-size: 12px;
  623. opacity: .6;
  624. }
  625. }
  626. }
  627. .air-right{
  628. text-align: center;
  629. span{
  630. display: block;
  631. font-size: 12px;
  632. }
  633. p{
  634. font-size: 18px;
  635. font-weight: 600;
  636. padding: 10px 30px;
  637. margin-top: 10px;
  638. background-color: rgba(24, 176, 143, .2);
  639. border-radius: 5px;
  640. }
  641. }
  642. }
  643. }
  644. .air-warn{
  645. background-image: linear-gradient(to top, rgba(246, 81, 99, .5), rgba(24, 176, 143, 0));
  646. .air-params{
  647. .air-right{
  648. p{
  649. background-color: rgba(246, 81, 99, .2);
  650. }
  651. }
  652. }
  653. }
  654. .middle-bottom {
  655. width: 100%;
  656. position: relative;
  657. padding: 0 !important;
  658. background-color: #021941;
  659. border: 1px solid #113d72;
  660. color: #339cff;
  661. font-size: 14px;
  662. &::before,
  663. &::after{
  664. content: "";
  665. position: absolute;
  666. width: 17px;
  667. height: 17px;
  668. z-index: 99;
  669. }
  670. &::before{
  671. top: -1px;
  672. left: -1px;
  673. border-top: 1px solid #339CFF;
  674. border-left: 1px solid #339CFF;
  675. }
  676. &::after{
  677. right: -1px;
  678. bottom: -1px;
  679. border-right: 1px solid #339CFF;
  680. border-bottom: 1px solid #339CFF;
  681. }
  682. }
  683. .leakage-list {
  684. display: flex;
  685. justify-content: flex-start;
  686. flex-wrap: wrap;
  687. padding: 14px 0 0 14px;
  688. height: auto;
  689. text-align: left;
  690. &::-webkit-scrollbar {
  691. width: 4px;
  692. height: 4px;
  693. }
  694. &::-webkit-scrollbar-thumb {
  695. background-color: #339cff;
  696. border-radius: 2px;
  697. }
  698. &::-webkit-scrollbar-track {
  699. background-color: #021941;
  700. }
  701. li {
  702. position: relative;
  703. display: flex;
  704. justify-content: space-between;
  705. align-items: center;
  706. // width: calc( 100% / 3 - 14px);
  707. // height: calc(100% / 3 - 14px);
  708. width: auto;
  709. height: auto;
  710. // margin: 0 14px 14px 0;
  711. padding: 0 30px;
  712. border: 1px solid #3581cc;
  713. background-color: #02255f;
  714. border-radius: 2px;
  715. &::before {
  716. content: "";
  717. position: absolute;
  718. top: 4px;
  719. left: 4px;
  720. width: 0;
  721. height: 0;
  722. border-color: transparent #339cff;
  723. border-width: 0 0 6px 6px;
  724. border-style: solid;
  725. }
  726. p {
  727. i {
  728. margin-right: 8px;
  729. font-size: 20px;
  730. }
  731. }
  732. span.leakage-state-tip {
  733. position: relative;
  734. display: block;
  735. width: 6px;
  736. height: 6px;
  737. border-radius: 50%;
  738. background-color: #18b08f;
  739. &::before {
  740. content: "";
  741. position: absolute;
  742. left: 50%;
  743. top: 50%;
  744. width: 14px;
  745. height: 14px;
  746. border-radius: 50%;
  747. box-shadow: inset 0px 0px 10px 1px #18b08f;
  748. transform: translate(-50%, -50%);
  749. }
  750. }
  751. &.leakage-warn {
  752. border-color: #f65164;
  753. box-shadow: inset 0px 0px 15px 1px #f65164;
  754. color: #f65164;
  755. &::before {
  756. border-color: transparent #f65164;
  757. }
  758. span.leakage-state-tip {
  759. background-color: #f65164;
  760. &::before {
  761. box-shadow: inset 0px 0px 10px 1px #f65164;
  762. }
  763. }
  764. }
  765. }
  766. }
  767. </style>