dustWarn.vue 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. <template>
  2. <customHeader :options="options" @change="getSelectRow" :optionValue="optionValue"> 粉尘监测预警 </customHeader>
  3. <div class="dustWarn">
  4. <div class="top-dust">
  5. <a-button preIcon="ant-design:rollback-outlined" type="text" size="small"
  6. style="position: absolute;left:15px;top:15px;color: #fff;" @click="getBack">返回</a-button>
  7. <div class="alarm-menu">
  8. <div class="card-btn">
  9. <div :class="activeIndex1 == ind ? 'btn1' : 'btn'" v-for="(item, ind) in menuList" :key="ind"
  10. @click="cardClick(ind, item)">
  11. <div class="text">{{ item.name }}</div>
  12. <div class="warn">{{ item.warn }}</div>
  13. </div>
  14. </div>
  15. </div>
  16. <div class="dust-content">
  17. <div class="content-left">
  18. <div :class="activeIndex == index ? 'content-left-item' : 'content-left-item1'"
  19. v-for="(item, index) in topAreaList" :key="index" @click="topAreaClick(index)">
  20. <div class="content-title">{{ item.title }}</div>
  21. <div class="content-items" v-for="(ite, ind) in item.content" :key="ind">
  22. <span>{{ ite.label }}</span>
  23. <span style="color:#01fefc">{{ ite.value }}</span>
  24. </div>
  25. </div>
  26. </div>
  27. <div class="content-right">
  28. <div class="title-t">
  29. <div class="text-t">粉尘信息状态监测</div>
  30. </div>
  31. <div class="echart-boxd">
  32. <echartLine :echartDataGq="echartDataFc" :maxY="maxY" :echartDw="echartDw" :gridV="gridV" />
  33. </div>
  34. </div>
  35. </div>
  36. </div>
  37. <div class="bot-dust">
  38. <div class="bot-area">
  39. <div class="title-b">
  40. <div class="text-b">粉尘监控测点信息</div>
  41. </div>
  42. <div class="content-b">
  43. <div class="card-b" v-for="(item, index) in cardListTf" :key="index">
  44. <div class="item-l">
  45. <div class="label-l">{{ item.label }}</div>
  46. <div class="value-l">{{ item.value }}</div>
  47. </div>
  48. <div class="item-r">
  49. <div class="content-r" v-for="(items, ind) in item.listR" :key="ind">
  50. <span>{{ `${items.label} : ` }}</span>
  51. <span :class="{
  52. 'status-f': items.value == 1,
  53. 'status-l': items.value == 0,
  54. }">{{ items.value == 1 ? '异常' : items.value == 0 ? '正常' : `${items.value}${items.dw}`
  55. }}</span>
  56. </div>
  57. </div>
  58. </div>
  59. </div>
  60. </div>
  61. </div>
  62. </div>
  63. </template>
  64. <script setup lang="ts">
  65. import { ref, reactive, onMounted, onUnmounted } from 'vue'
  66. import { sysTypeWarnList, sysWarn, getDevice } from '../common.api'
  67. import echartLine from '../common/echartLine.vue';
  68. import { useSystemSelect } from '/@/hooks/vent/useSystemSelect';
  69. import { useRouter } from 'vue-router';
  70. import CustomHeader from '/@/components/vent/customHeader.vue';
  71. const { options, optionValue, getSelectRow, getSysDataSource } = useSystemSelect('sys_surface_caimei'); // 参数为场景类型(设备类型管理中可以查询到)
  72. //左侧数据列表
  73. let menuList = reactive<any[]>([])
  74. //当前左侧激活菜单的索引
  75. let activeIndex1 = ref(0);
  76. //顶部区域激活选项
  77. let activeIndex = ref(0);
  78. //顶部区域数据
  79. let topAreaList = reactive<any[]>([]);
  80. let choiceData = reactive<any[]>([]);
  81. //粉尘图表数据
  82. let echartDataFc = reactive({
  83. maxData: {
  84. lengedData: '实时值(mg/m³)',
  85. data: [],
  86. },
  87. minData: {
  88. lengedData: '预测值(mg/m³)',
  89. data: [],
  90. },
  91. aveValue: {
  92. lengedData: '预警值(mg/m³)',
  93. data: [],
  94. },
  95. xData: [],
  96. });
  97. let maxY = ref(0);
  98. let echartDw = ref('(mg/m³)');
  99. let gridV = reactive({
  100. top: '12%',
  101. left: '1%',
  102. bottom: '5%',
  103. right: '5%',
  104. containLabel: true,
  105. })
  106. let cardListTf = reactive<any[]>([])
  107. let router = useRouter()
  108. let echartNow = ref<any[]>([])
  109. let echartYc = reactive<any[]>([])
  110. let flag = ref(true)
  111. // https获取监测数据
  112. let timer: null | NodeJS.Timeout = null;
  113. function getMonitor(deviceID, flag?) {
  114. timer = setTimeout(
  115. async () => {
  116. await getSysWarnList(deviceID, 'dust');
  117. if (timer) {
  118. timer = null;
  119. }
  120. getMonitor(deviceID);
  121. },
  122. flag ? 0 : 3000
  123. );
  124. }
  125. //返回首页
  126. function getBack() {
  127. router.push('/monitorChannel/monitor-alarm-home')
  128. }
  129. //菜单选项切换
  130. function cardClick(ind, item) {
  131. activeIndex1.value = ind;
  132. clearTimeout(timer);
  133. getMonitor(item.deviceID, true);
  134. }
  135. //顶部区域选项切换
  136. function topAreaClick(index) {
  137. activeIndex.value = index;
  138. echartDataFc.maxData.data.length = 0;
  139. echartDataFc.minData.data.length = 0;
  140. echartDataFc.aveValue.data.length = 0;
  141. echartDataFc.xData.length = 0;
  142. echartYc.length = 0
  143. flag.value = true
  144. if (flag.value) {
  145. echartNow.value = JSON.parse(choiceData[index].readData.expectInfo)['list']
  146. flag.value = false
  147. }
  148. echartYc.push({ time: JSON.parse(choiceData[index].readData.expectInfo)['nowTime'], value: JSON.parse(choiceData[index].readData.expectInfo)['nowVal'], })
  149. let setData = [...echartNow.value, ...echartYc].sort((a, b) => Date.parse(new Date(a.time)) - Date.parse(new Date(b.time)))
  150. setData.forEach(el => {
  151. if (el.value && el.value != '0') {
  152. echartDataFc.xData.push(el.time);
  153. echartDataFc.maxData.data.push(el.value);
  154. echartDataFc.minData.data.push(JSON.parse(choiceData[index].readData.expectInfo)['aveVal']);
  155. echartDataFc.aveValue.data.push(JSON.parse(choiceData[activeIndex.value].readData.expectInfo)['fmin'] ? JSON.parse(choiceData[activeIndex.value].readData.expectInfo)['fmin'] : 0);
  156. }
  157. })
  158. }
  159. function formatRoundNum(num) {
  160. let interger = Math.ceil(num);
  161. let leng = String(interger).length;
  162. return Math.ceil(interger / Math.pow(10, leng - 1)) * Math.pow(10, leng - 1);
  163. }
  164. //获取左侧菜单列表
  165. async function getMenuList() {
  166. let res = await sysTypeWarnList({ type: 'dust' })
  167. if (res.length != 0) {
  168. menuList.length = 0
  169. res.forEach((el) => {
  170. menuList.push({
  171. name: el.systemname,
  172. warn: '低风险',
  173. deviceID: el.id,
  174. strtype: el.strtype,
  175. });
  176. });
  177. getMonitor(menuList[0].deviceID, true);
  178. }
  179. }
  180. //获取预警详情弹窗右侧数据
  181. function getSysWarnList(id, type) {
  182. sysWarn({ sysid: id, type: type }).then((res) => {
  183. // listData.common = res;
  184. topAreaList.length = 0;
  185. if (JSON.stringify(res) != '{}') {
  186. res.dust.forEach((el) => {
  187. topAreaList.push({
  188. title: el.strinstallpos,
  189. content: [
  190. { ids: 0, label: '温度(°C)', value: el.readData.temperature || '--' },
  191. { ids: 1, label: '粉尘浓度(mg/m³)', value: el.readData.dustval || '--' },
  192. { ids: 2, label: '喷雾水压(MPa)', value: el.readData.waterPressure || '--' },
  193. { ids: 3, label: '喷雾状态', value: el.readData.atomizingState || '--' },
  194. ],
  195. });
  196. });
  197. choiceData = res.dust;
  198. if (choiceData[activeIndex.value]) {
  199. if (flag.value) {
  200. echartNow.value = JSON.parse(choiceData[activeIndex.value].readData.expectInfo)['list']
  201. flag.value = false
  202. }
  203. echartYc.push({ time: JSON.parse(choiceData[activeIndex.value].readData.expectInfo)['nowTime'], value: JSON.parse(choiceData[activeIndex.value].readData.expectInfo)['nowVal'], })
  204. let setData = [...echartNow.value, ...echartYc].sort((a, b) => Date.parse(new Date(a.time)) - Date.parse(new Date(b.time)))
  205. echartDataFc.maxData.data.length = 0;
  206. echartDataFc.minData.data.length = 0;
  207. echartDataFc.aveValue.data.length = 0;
  208. echartDataFc.xData.length = 0;
  209. setData.forEach(el => {
  210. if (el.value && el.value != '0') {
  211. echartDataFc.xData.push(el.time);
  212. echartDataFc.maxData.data.push(el.value);
  213. echartDataFc.minData.data.push(JSON.parse(choiceData[activeIndex.value].readData.expectInfo)['aveVal']);
  214. echartDataFc.aveValue.data.push(JSON.parse(choiceData[activeIndex.value].readData.expectInfo)['fmin'] ? JSON.parse(choiceData[activeIndex.value].readData.expectInfo)['fmin'] : 0);
  215. }
  216. })
  217. let max1 = echartDataFc.maxData.data.reduce((acr, cur) => {
  218. return acr > cur ? acr : cur;
  219. });
  220. maxY.value = formatRoundNum(max1 * 1.5);
  221. } else {
  222. activeIndex.value = 0;
  223. if (flag.value) {
  224. echartNow.value = JSON.parse(choiceData[activeIndex.value].readData.expectInfo)['list']
  225. flag.value = false
  226. }
  227. echartYc.push({ time: JSON.parse(choiceData[activeIndex.value].readData.expectInfo)['nowTime'], value: JSON.parse(choiceData[activeIndex.value].readData.expectInfo)['nowVal'], })
  228. let setData = [...echartNow.value, ...echartYc].sort((a, b) => Date.parse(new Date(a.time)) - Date.parse(new Date(b.time)))
  229. echartDataFc.maxData.data.length = 0;
  230. echartDataFc.minData.data.length = 0;
  231. echartDataFc.aveValue.data.length = 0;
  232. echartDataFc.xData.length = 0;
  233. setData.forEach(el => {
  234. if (el.value && el.value != '0') {
  235. echartDataFc.xData.push(el.time);
  236. echartDataFc.maxData.data.push(el.value);
  237. echartDataFc.minData.data.push(JSON.parse(choiceData[activeIndex.value].readData.expectInfo)['aveVal']);
  238. echartDataFc.aveValue.data.push(JSON.parse(choiceData[activeIndex.value].readData.expectInfo)['fmin'] ? JSON.parse(choiceData[activeIndex.value].readData.expectInfo)['fmin'] : 0);
  239. }
  240. })
  241. let max1 = echartDataFc.maxData.data.reduce((acr, cur) => {
  242. return acr > cur ? acr : cur;
  243. });
  244. maxY.value = formatRoundNum(max1 * 1.5);
  245. }
  246. }
  247. });
  248. }
  249. //获取粉尘监控测点信息
  250. async function getWindDeviceList() {
  251. cardListTf.length = 0
  252. let res = await getDevice({ devicetype: 'dusting', pagetype: 'normal' })
  253. if (res && res.msgTxt[0]) {
  254. let list = res.msgTxt[0].datalist || [];
  255. if (list.length > 0) {
  256. list.forEach((el: any) => {
  257. const readData = el.readData;
  258. el = Object.assign(el, readData);
  259. cardListTf.push({
  260. label: '通信状态',
  261. value: el.netStatus == '0' ? '断开' : '连接',
  262. listR: [
  263. { id: 0, label: '安装位置', dw: '', value: el.strinstallpos || '-' },
  264. { id: 1, label: '粉尘浓度', dw: '(mg/m³)', value: el.dustval || '-' },
  265. { id: 2, label: '巷道湿度', dw: el.humidity ? '(RH)' : '', value: el.humidity || '-' },
  266. { id: 4, label: '巷道温度', dw: el.humidity ? '(℃)' : '', value: el.temperature || '-' },
  267. { id: 3, label: '是否报警', dw: '', value: el.warnFlag == '0' ? '正常' : el.warnFlag == 1 ? '报警' : el.warnFlag == 2 ? '断开' : '未监测' },
  268. ],
  269. })
  270. });
  271. }
  272. }
  273. }
  274. onMounted(() => {
  275. getMenuList()
  276. getWindDeviceList()
  277. })
  278. onUnmounted(() => {
  279. if (timer) {
  280. clearTimeout(timer);
  281. timer = undefined;
  282. }
  283. });
  284. </script>
  285. <style lang="less" scoped>
  286. .dustWarn {
  287. width: 100%;
  288. height: 100%;
  289. padding: 80px 10px 15px 10px;
  290. box-sizing: border-box;
  291. .top-dust {
  292. display: flex;
  293. justify-content: space-between;
  294. height: 50%;
  295. margin-bottom: 15px;
  296. background: url('../../../../../assets/images/fire/border.png') no-repeat center;
  297. background-size: 100% 100%;
  298. .alarm-menu {
  299. height: 100%;
  300. width: 15%;
  301. padding: 10px;
  302. box-sizing: border-box;
  303. .card-btn {
  304. width: 100%;
  305. height: 100%;
  306. overflow-y: auto;
  307. .btn {
  308. position: relative;
  309. width: 81%;
  310. height: 24%;
  311. margin-bottom: 6%;
  312. font-family: 'douyuFont';
  313. background: url('../../../../../assets/images/fire/no-choice.png') no-repeat;
  314. background-size: 100% 100%;
  315. cursor: pointer;
  316. .text {
  317. width: 80%;
  318. position: absolute;
  319. left: 50%;
  320. top: 28px;
  321. font-size: 14px;
  322. color: #01fefc;
  323. text-align: center;
  324. transform: translate(-50%, 0);
  325. }
  326. .warn {
  327. width: 100%;
  328. position: absolute;
  329. left: 50%;
  330. bottom: 11px;
  331. font-size: 12px;
  332. color: #fff;
  333. text-align: center;
  334. transform: translate(-50%, 0);
  335. }
  336. }
  337. .btn1 {
  338. position: relative;
  339. width: 100%;
  340. height: 24%;
  341. margin-bottom: 6%;
  342. font-family: 'douyuFont';
  343. background: url('../../../../../assets/images/fire/choice.png') no-repeat;
  344. background-size: 100% 100%;
  345. cursor: pointer;
  346. .text {
  347. width: 80%;
  348. position: absolute;
  349. left: 50%;
  350. top: 28px;
  351. font-size: 14px;
  352. color: #01fefc;
  353. text-align: center;
  354. transform: translate(-62%, 0);
  355. }
  356. .warn {
  357. width: 100%;
  358. position: absolute;
  359. left: 50%;
  360. bottom: 11px;
  361. font-size: 14px;
  362. color: #fff;
  363. text-align: center;
  364. transform: translate(-60%, 0);
  365. }
  366. }
  367. }
  368. }
  369. .dust-content {
  370. display: flex;
  371. justify-content: space-between;
  372. height: 100%;
  373. width: 85%;
  374. padding: 10px 0px;
  375. box-sizing: border-box;
  376. .content-left {
  377. width: 280px;
  378. height: 100%;
  379. display: flex;
  380. flex-direction: column;
  381. justify-content: space-around;
  382. align-items: flex-start;
  383. overflow-y: auto;
  384. overflow-x: hidden;
  385. .content-left-item {
  386. position: relative;
  387. width: 272px;
  388. height: 173px;
  389. flex-shrink: 0;
  390. background: url('../../../../../assets/images/fire/dust-choice.png') no-repeat center;
  391. background-size: 100% 100%;
  392. margin: 5px 0px;
  393. .content-title {
  394. width: 85%;
  395. position: absolute;
  396. top: 2px;
  397. left: 50%;
  398. transform: translate(-55%, 0);
  399. font-size: 14px;
  400. color: #fff;
  401. text-align: center
  402. }
  403. .content-items {
  404. position: absolute;
  405. left: 50%;
  406. transform: translate(-54%, 0);
  407. display: flex;
  408. justify-content: space-between;
  409. align-items: center;
  410. width: 240px;
  411. height: 26px;
  412. color: #fff;
  413. font-size: 14px;
  414. padding: 0px 5px;
  415. box-sizing: border-box;
  416. background: url('../../../../../assets/images/fire/dust-content.png') no-repeat center;
  417. background-size: 100% 100%;
  418. &:nth-child(2) {
  419. top: 32px;
  420. }
  421. &:nth-child(3) {
  422. top: 67px;
  423. }
  424. &:nth-child(4) {
  425. top: 102px;
  426. }
  427. &:nth-child(5) {
  428. top: 136px;
  429. }
  430. }
  431. }
  432. .content-left-item1 {
  433. position: relative;
  434. width: 250px;
  435. height: 173px;
  436. flex-shrink: 0;
  437. background: url('../../../../../assets/images/fire/dust-choice1.png') no-repeat center;
  438. background-size: 100% 100%;
  439. margin: 5px 0px;
  440. .content-title {
  441. width: 85%;
  442. position: absolute;
  443. top: 2px;
  444. left: 50%;
  445. transform: translate(-50%, 0);
  446. font-size: 14px;
  447. color: #fff;
  448. text-align: center
  449. }
  450. .content-items {
  451. position: absolute;
  452. left: 50%;
  453. transform: translate(-54%, 0);
  454. display: flex;
  455. justify-content: space-between;
  456. align-items: center;
  457. width: 215px;
  458. height: 26px;
  459. color: #fff;
  460. font-size: 14px;
  461. padding: 0px 5px;
  462. box-sizing: border-box;
  463. background: url('../../../../../assets/images/fire/dust-content.png') no-repeat center;
  464. background-size: 100% 100%;
  465. &:nth-child(2) {
  466. top: 32px;
  467. }
  468. &:nth-child(3) {
  469. top: 67px;
  470. }
  471. &:nth-child(4) {
  472. top: 102px;
  473. }
  474. &:nth-child(5) {
  475. top: 136px;
  476. }
  477. }
  478. }
  479. }
  480. .content-right {
  481. width: calc(100% - 280px);
  482. height: 100%;
  483. .title-t {
  484. height: 30px;
  485. margin-bottom: 10px;
  486. display: flex;
  487. justify-content: space-between;
  488. align-items: center;
  489. .text-t {
  490. font-family: 'douyuFont';
  491. font-size: 14px;
  492. color: #fff;
  493. }
  494. }
  495. .echart-boxd {
  496. width: 100%;
  497. height: calc(100% - 40px);
  498. }
  499. }
  500. }
  501. }
  502. .bot-dust {
  503. height: calc(50% - 15px);
  504. background: url('../../../../../assets/images/fire/border.png') no-repeat center;
  505. background-size: 100% 100%;
  506. padding: 10px;
  507. box-sizing: border-box;
  508. .bot-area {
  509. height: 100%;
  510. padding: 10px;
  511. background: url('../../../../../assets/images/fire/bj1.png') no-repeat center;
  512. background-size: 100% 100%;
  513. box-sizing: border-box;
  514. .title-b {
  515. height: 30px;
  516. margin-bottom: 10px;
  517. display: flex;
  518. justify-content: space-between;
  519. align-items: center;
  520. .text-b {
  521. font-family: 'douyuFont';
  522. font-size: 14px;
  523. color: #fff;
  524. }
  525. }
  526. .content-b {
  527. height: calc(100% - 40px);
  528. display: flex;
  529. justify-content: flex-start;
  530. align-items: flex-start;
  531. flex-wrap: wrap;
  532. overflow-y: auto;
  533. .card-b {
  534. position: relative;
  535. width: 24%;
  536. height: 128px;
  537. margin: 0px 9px 10px 9px;
  538. background: url(/src/assets/images/fire/bot-area.png) no-repeat center;
  539. background-size: 100% 100%;
  540. .item-l {
  541. position: absolute;
  542. left: 32px;
  543. top: 50%;
  544. transform: translate(0, -50%);
  545. width: 89px;
  546. height: 98px;
  547. background: url('../../../../../assets/images/fire/bot-area1.png') no-repeat center;
  548. .label-l {
  549. width: 100%;
  550. position: absolute;
  551. top: 7px;
  552. color: #fff;
  553. font-size: 12px;
  554. text-align: center;
  555. }
  556. .value-l {
  557. width: 100%;
  558. position: absolute;
  559. top: 50px;
  560. font-family: 'douyuFont';
  561. font-size: 14px;
  562. color: #3df6ff;
  563. text-align: center;
  564. }
  565. }
  566. .item-r {
  567. position: absolute;
  568. left: 132px;
  569. top: 50%;
  570. transform: translate(0, -50%);
  571. height: 128px;
  572. padding: 5px 0px;
  573. display: flex;
  574. flex-direction: column;
  575. justify-content: space-around;
  576. box-sizing: border-box;
  577. .content-r {
  578. display: flex;
  579. span {
  580. font-size: 14px;
  581. color: #fff;
  582. &:first-child {
  583. display: inline-block;
  584. width: 68px;
  585. }
  586. &:last-child {
  587. display: inline-block;
  588. width: calc(100% - 68px);
  589. color: #01fefc;
  590. overflow: hidden;
  591. white-space: nowrap;
  592. /* 不换行 */
  593. /* 超出部分隐藏 */
  594. text-overflow: ellipsis;
  595. /* 使用省略符号 */
  596. }
  597. }
  598. .status-f {
  599. color: #ff0000;
  600. }
  601. .status-l {
  602. color: #3df6ff;
  603. }
  604. }
  605. }
  606. }
  607. }
  608. }
  609. }
  610. }
  611. </style>