ventilateWarn.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. <template>
  2. <customHeader :options="options" @change="getSelectRow" :optionValue="optionValue"> 通风监测预警 </customHeader>
  3. <div class="ventilateWarn">
  4. <div class="ventilate-top">
  5. <a-button
  6. v-if="!hasPermission('ventilateWarn:return')"
  7. preIcon="ant-design:rollback-outlined"
  8. type="text"
  9. size="small"
  10. style="position: absolute; left: 15px; top: 15px; color: var(--vent-font-color)"
  11. @click="getBack"
  12. >
  13. 返回
  14. </a-button>
  15. <div class="alarm-menu">
  16. <div class="card-btn">
  17. <div :class="activeIndex1 == ind ? 'btn1' : 'btn'" v-for="(item, ind) in menuList" :key="ind" @click="cardClick(ind, item)">
  18. <div class="text">{{ item.name }}</div>
  19. <div class="warn">{{ item.warn }}</div>
  20. </div>
  21. </div>
  22. </div>
  23. <div class="ventilate-content">
  24. <div class="work-nav">
  25. <div class="nav" v-for="(item, index) in ventilateTopList" :key="index">
  26. <div class="pic" v-if="item.imgSrc"></div>
  27. <div class="content" v-if="item.label && item.value">
  28. <span>{{ item.label }}</span>
  29. <span>{{ item.value }}</span>
  30. </div>
  31. <div
  32. :style="{ color: item.text == '正常' ? 'var(--vent-table-action-link)' : '#ff2313' }"
  33. style="width: 100%; padding: 0px 10px; text-align: center; font-weight: bold"
  34. v-if="item.text"
  35. >
  36. {{ item.text }}
  37. </div>
  38. <div class="percent" v-if="item.list.length != 0">
  39. <div class="title">{{ item.label }}</div>
  40. <div class="value">
  41. <div class="content-box" v-for="(items, ind) in item.list" :key="ind">
  42. <span style="color: #b3b8cc">{{ `${items.label} :` }}</span>
  43. <span style="color: var(--vent-table-action-link); margin-left: 10px">{{ items.value }}</span>
  44. </div>
  45. </div>
  46. </div>
  47. </div>
  48. </div>
  49. <div class="bot-area">
  50. <div class="title-t">
  51. <div class="text-t">通风信息状态监测</div>
  52. </div>
  53. <div class="echart-boxd">
  54. <echartLine :echartDataGq="echartDataFc1" :maxY="maxY" :echartDw="echartDw" />
  55. </div>
  56. </div>
  57. </div>
  58. </div>
  59. <div class="ventilate-bottom">
  60. <div class="bot-area">
  61. <MeasurePoint title="通风监控测点信息" :timeout="1000" :cards="cardListTf" :charts="chartListTf" />
  62. </div>
  63. </div>
  64. </div>
  65. </template>
  66. <script setup lang="ts">
  67. import { ref, reactive, onMounted, onUnmounted } from 'vue';
  68. import { useSystemSelect } from '/@/hooks/vent/useSystemSelect';
  69. import { useRouter } from 'vue-router';
  70. import { sysTypeWarnList, sysWarn, getDevice } from '../common.api';
  71. import { ventilateTopList } from '../common.data';
  72. import CustomHeader from '/@/components/vent/customHeader.vue';
  73. import echartLine from '../common/echartLine.vue';
  74. import { usePermission } from '/@/hooks/web/usePermission';
  75. import MeasurePoint from '../common/measurePoint.vue';
  76. import moment from 'moment';
  77. const { hasPermission } = usePermission();
  78. const { options, optionValue, getSelectRow, getSysDataSource } = useSystemSelect('sys_surface_caimei'); // 参数为场景类型(设备类型管理中可以查询到)
  79. let router = useRouter();
  80. //左侧数据列表
  81. let menuList = reactive<any[]>([]);
  82. //当前左侧激活菜单的索引
  83. let activeIndex1 = ref(0);
  84. let maxY = ref<any>(0);
  85. let echartDw = ref('(m³/min)');
  86. //通风图表数据
  87. const echartDataFc1 = reactive({
  88. maxData: {
  89. lengedData: '进风量',
  90. data: [],
  91. },
  92. minData: {
  93. lengedData: '回风量',
  94. data: [],
  95. },
  96. aveValue: {
  97. lengedData: '需风量',
  98. data: [],
  99. },
  100. xData: [],
  101. });
  102. let cardListTf = reactive<any[]>([]);
  103. const chartListTf = reactive<any[]>([]);
  104. // https获取监测数据
  105. let timer: null | NodeJS.Timeout = null;
  106. function getMonitor(deviceID, flag?) {
  107. timer = setTimeout(
  108. async () => {
  109. await getSysWarnList(deviceID, 'vent');
  110. if (timer) {
  111. timer = null;
  112. }
  113. getMonitor(deviceID);
  114. },
  115. flag ? 0 : 1000
  116. );
  117. }
  118. //返回首页
  119. function getBack() {
  120. router.push('/monitorChannel/monitor-alarm-home');
  121. }
  122. //获取左侧数据列表
  123. async function getMenuList() {
  124. let res = await sysTypeWarnList({ type: 'vent' });
  125. if (res.length != 0) {
  126. menuList.length = 0;
  127. res.forEach((el) => {
  128. menuList.push({
  129. name: el.deviceName,
  130. warn: '低风险',
  131. deviceID: el.deviceID,
  132. strtype: el.deviceType,
  133. });
  134. });
  135. getMonitor(menuList[0].deviceID, true);
  136. }
  137. }
  138. //菜单选项切换
  139. function cardClick(ind, item) {
  140. activeIndex1.value = ind;
  141. clearTimeout(timer);
  142. getMonitor(item.deviceID, true);
  143. }
  144. //获取预警详情弹窗右侧数据
  145. function getSysWarnList(id, type) {
  146. sysWarn({ sysid: id, type: type }).then((res) => {
  147. echartDataFc1.maxData.data.length = 0;
  148. echartDataFc1.minData.data.length = 0;
  149. echartDataFc1.aveValue.data.length = 0;
  150. echartDataFc1.xData.length = 0;
  151. chartListTf.length = 0;
  152. if (JSON.stringify(res) != '{}') {
  153. ventilateTopList[0].value = res.jin || '--';
  154. ventilateTopList[1].value = res.hui || '--';
  155. ventilateTopList[2].value = res.xufengliang || '--';
  156. ventilateTopList[3].text = res.warnFlag ? res.warnDes : '正常';
  157. res.vent.forEach((el) => {
  158. // 初始化预测曲线配置,分别为x轴时间、平均、最大、最小、实时
  159. chartListTf.push({
  160. label: el.strinstallpos,
  161. time: new Date(),
  162. data: [moment().format('ss'), 15, 5, 10],
  163. });
  164. });
  165. if (res.history.length != 0) {
  166. res.history.forEach((v) => {
  167. echartDataFc1.maxData.data.push(parseFloat(v.jin));
  168. echartDataFc1.minData.data.push(parseFloat(v.hui));
  169. if (ventilateTopList[2].value && ventilateTopList[2].value != '--') {
  170. echartDataFc1.aveValue.data.push(ventilateTopList[2].value);
  171. } else {
  172. echartDataFc1.aveValue.data.push(0);
  173. }
  174. echartDataFc1.xData.push(v.time);
  175. });
  176. }
  177. let max1 = echartDataFc1.maxData.data.reduce((acr, cur) => {
  178. return acr > cur ? acr : cur;
  179. });
  180. let max2 = echartDataFc1.minData.data.reduce((acr1, cur1) => {
  181. return acr1 > cur1 ? acr1 : cur1;
  182. });
  183. maxY.value = max1 >= max2 ? max1 : max2;
  184. maxY.value =
  185. maxY.value.toString().indexOf('.') == -1 ? maxY.value.toString() : maxY.value.toString().substring(0, maxY.value.toString().indexOf('.'));
  186. if (maxY.value.length < 2 && Number(maxY.value) < 1) {
  187. maxY.value = 1;
  188. } else if (maxY.value.length < 2 && Number(maxY.value) >= 1) {
  189. maxY.value = 10;
  190. } else if (maxY.value.length < 3) {
  191. maxY.value = (Number(maxY.value[0]) + 1) * 10;
  192. } else if (maxY.value.length < 4) {
  193. maxY.value = (Number(maxY.value[0]) + 1) * 100;
  194. } else if (maxY.value.length < 5) {
  195. maxY.value = (Number(maxY.value[0]) + 1) * 1000;
  196. } else if (maxY.value.length < 6) {
  197. maxY.value = (Number(maxY.value[0]) + 1) * 10000;
  198. }
  199. }
  200. });
  201. }
  202. //获取通风监控测点信息
  203. async function getWindDeviceList() {
  204. cardListTf.length = 0;
  205. let res = await getDevice({ devicetype: 'windrect', pagetype: 'normal' });
  206. if (res && res.msgTxt[0]) {
  207. let list = res.msgTxt[0].datalist || [];
  208. if (list.length > 0) {
  209. list.forEach((el: any) => {
  210. const readData = el.readData;
  211. el = Object.assign(el, readData);
  212. cardListTf.push({
  213. label: '通信状态',
  214. value: el.netStatus == '0' ? '断开' : '连接',
  215. listR: [
  216. { id: 0, label: '安装位置', dw: '', value: el.strinstallpos },
  217. { id: 1, label: '风量', dw: '(m³/min)', value: el.m3 },
  218. { id: 2, label: '风速', dw: '(m/s)', value: el.va },
  219. { id: 4, label: '时间', dw: '', value: el.readTime },
  220. {
  221. id: 3,
  222. label: '是否报警',
  223. dw: '',
  224. value: el.warnFlag == '0' ? '正常' : el.warnFlag == 1 ? '报警' : el.warnFlag == 2 ? '断开' : '未监测',
  225. },
  226. ],
  227. });
  228. });
  229. }
  230. }
  231. }
  232. onMounted(() => {
  233. getMenuList();
  234. getWindDeviceList();
  235. });
  236. onUnmounted(() => {
  237. if (timer) {
  238. clearTimeout(timer);
  239. timer = undefined;
  240. }
  241. });
  242. </script>
  243. <style lang="less" scoped>
  244. @import '/@/design/theme.less';
  245. @{theme-deepblue} {
  246. .ventilateWarn {
  247. --image-border: url('/@/assets/images/themify/deepblue/fire/border.png');
  248. --image-no-choice: url('/@/assets/images/themify/deepblue/fire/no-choice.png');
  249. --image-choice: url('/@/assets/images/themify/deepblue/fire/choice.png');
  250. --image-bj1: url('/@/assets/images/themify/deepblue/fire/bj1.png');
  251. --image-jinfengliang: url('/@/assets/images/themify/deepblue/fire/jinfengliang.png');
  252. --image-huifengliang: url('/@/assets/images/themify/deepblue/fire/huifengliang.png');
  253. --image-xufengliang: url('/@/assets/images/themify/deepblue/fire/xufengliang.png');
  254. }
  255. }
  256. .ventilateWarn {
  257. --image-border: url('/@/assets/images/fire/border.png');
  258. --image-no-choice: url('/@/assets/images/fire/no-choice.png');
  259. --image-choice: url('/@/assets/images/fire/choice.png');
  260. --image-bj1: url('/@/assets/images/fire/bj1.png');
  261. --image-jinfengliang: url('/@/assets/images/fire/jinfengliang.png');
  262. --image-huifengliang: url('/@/assets/images/fire/huifengliang.png');
  263. --image-xufengliang: url('/@/assets/images/fire/xufengliang.png');
  264. --border-image-1: linear-gradient(to bottom, #2d74a0, #2d74a0, #2d74a0);
  265. --border-image-2: linear-gradient(to bottom, transparent, #024688, transparent);
  266. width: 100%;
  267. height: 100%;
  268. padding: 80px 10px 15px 10px;
  269. box-sizing: border-box;
  270. .ventilate-top {
  271. display: flex;
  272. justify-content: space-between;
  273. height: 50%;
  274. margin-bottom: 15px;
  275. background: var(--image-border) no-repeat center;
  276. background-size: 100% 100%;
  277. .alarm-menu {
  278. height: 100%;
  279. width: 15%;
  280. padding: 10px;
  281. box-sizing: border-box;
  282. .card-btn {
  283. width: 100%;
  284. height: 100%;
  285. overflow-y: auto;
  286. .btn {
  287. position: relative;
  288. width: 81%;
  289. height: 24%;
  290. margin-bottom: 6%;
  291. font-family: 'douyuFont';
  292. background: var(--image-no-choice) no-repeat;
  293. background-size: 100% 100%;
  294. cursor: pointer;
  295. .text {
  296. width: 80%;
  297. position: absolute;
  298. left: 50%;
  299. top: 28px;
  300. font-size: 14px;
  301. color: var(--vent-table-action-link);
  302. text-align: center;
  303. transform: translate(-50%, 0);
  304. }
  305. .warn {
  306. width: 100%;
  307. position: absolute;
  308. left: 50%;
  309. bottom: 11px;
  310. font-size: 12px;
  311. color: var(--vent-font-color);
  312. text-align: center;
  313. transform: translate(-50%, 0);
  314. }
  315. }
  316. .btn1 {
  317. position: relative;
  318. width: 100%;
  319. height: 24%;
  320. margin-bottom: 6%;
  321. font-family: 'douyuFont';
  322. background: var(--image-choice) no-repeat;
  323. background-size: 100% 100%;
  324. cursor: pointer;
  325. .text {
  326. width: 80%;
  327. position: absolute;
  328. left: 50%;
  329. top: 28px;
  330. font-size: 14px;
  331. color: var(--vent-table-action-link);
  332. text-align: center;
  333. transform: translate(-62%, 0);
  334. }
  335. .warn {
  336. width: 100%;
  337. position: absolute;
  338. left: 50%;
  339. bottom: 11px;
  340. font-size: 14px;
  341. color: var(--vent-font-color);
  342. text-align: center;
  343. transform: translate(-60%, 0);
  344. }
  345. }
  346. }
  347. }
  348. .ventilate-content {
  349. height: 100%;
  350. width: 85%;
  351. padding: 10px 0px;
  352. box-sizing: border-box;
  353. .work-nav {
  354. height: 30%;
  355. width: 100%;
  356. background: var(--image-bj1) no-repeat center;
  357. background-size: 100% 100%;
  358. display: flex;
  359. justify-content: space-between;
  360. align-items: center;
  361. border-bottom: 3px solid;
  362. border-image: var(--border-image-1) 1 1 1;
  363. .nav {
  364. display: flex;
  365. justify-content: center;
  366. align-items: center;
  367. &:nth-child(1) {
  368. flex: 1;
  369. height: 100%;
  370. border-right: 2px solid;
  371. border-image: var(--border-image-2) 1 1 1;
  372. }
  373. &:nth-child(2) {
  374. flex: 1;
  375. height: 100%;
  376. border-right: 2px solid;
  377. border-image: var(--border-image-2) 1 1 1;
  378. }
  379. &:nth-child(3) {
  380. flex: 1;
  381. height: 100%;
  382. border-right: 2px solid;
  383. border-image: var(--border-image-2) 1 1 1;
  384. }
  385. &:nth-child(4) {
  386. flex: 1;
  387. color: #b3b8cc;
  388. font-size: 16px;
  389. height: 100%;
  390. border-right: 2px solid;
  391. border-image: var(--border-image-2) 1 1 1;
  392. }
  393. /**
  394. &:nth-child(5) {
  395. flex: 1.4;
  396. height: 100%;
  397. .percent {
  398. width: 100%;
  399. height: 82%;
  400. padding: 0px 20px;
  401. box-sizing: border-box;
  402. display: flex;
  403. flex-direction: column;
  404. justify-content: space-around;
  405. .title {
  406. font-size: 14px;
  407. padding: 5px 0px;
  408. color: #b3b8cc;
  409. text-align: center;
  410. }
  411. .value {
  412. display: flex;
  413. justify-content: space-between;
  414. span {
  415. font-family: 'douyuFont';
  416. font-size: 18px;
  417. }
  418. }
  419. }
  420. }*/
  421. .pic {
  422. width: 30%;
  423. height: 82%;
  424. }
  425. .content {
  426. height: 82%;
  427. margin-left: 15px;
  428. color: var(--vent-font-color);
  429. display: flex;
  430. flex-direction: column;
  431. justify-content: space-around;
  432. span {
  433. font-size: 14px;
  434. &:nth-child(1) {
  435. padding: 5px 0px;
  436. color: #b3b8cc;
  437. }
  438. &:nth-child(2) {
  439. font-family: 'douyuFont';
  440. font-size: 16px;
  441. color: var(--vent-table-action-link);
  442. }
  443. }
  444. }
  445. }
  446. .nav:nth-child(1) .pic {
  447. background: var(--image-jinfengliang) no-repeat center;
  448. background-size: 100% 100%;
  449. }
  450. .nav:nth-child(2) .pic {
  451. background: var(--image-huifengliang) no-repeat center;
  452. background-size: 100% 100%;
  453. }
  454. .nav:nth-child(3) .pic {
  455. background: var(--image-xufengliang) no-repeat center;
  456. background-size: 100% 100%;
  457. }
  458. }
  459. .bot-area {
  460. height: calc(100% - 30% - 3px);
  461. padding: 10px;
  462. background: var(--image-bj1) no-repeat;
  463. background-size: 100% 100%;
  464. box-sizing: border-box;
  465. .title-t {
  466. height: 30px;
  467. display: flex;
  468. justify-content: space-between;
  469. align-items: center;
  470. .text-t {
  471. font-family: 'douyuFont';
  472. font-size: 14px;
  473. color: var(--vent-font-color);
  474. }
  475. }
  476. .echart-boxd {
  477. width: 100%;
  478. height: calc(100% - 30px);
  479. }
  480. }
  481. }
  482. }
  483. .ventilate-bottom {
  484. height: calc(50% - 15px);
  485. background: var(--image-border) no-repeat center;
  486. background-size: 100% 100%;
  487. padding: 10px;
  488. box-sizing: border-box;
  489. .bot-area {
  490. height: 100%;
  491. padding: 10px;
  492. background: var(--image-bj1) no-repeat center;
  493. background-size: 100% 100%;
  494. box-sizing: border-box;
  495. }
  496. }
  497. }
  498. </style>