vent-detail.vue 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  1. <template>
  2. <view class="vent-detail">
  3. <u-tabs class="devic-box-tab" :current="PageCur" :list="tabList" @click="NavChange"></u-tabs>
  4. <view class="vent-content">
  5. <view class="top-area">
  6. <view class="top-title">
  7. <view style="font-weight: bold;">{{ mainTitle || '--' }}</view>
  8. <view class="title-icon">
  9. <image src="/static/model/alarmTrue.svg" alt="" class="icon-style" />
  10. <text class="icon-text">低风险</text>
  11. </view>
  12. </view>
  13. <view class="top-card">
  14. <view class="card-box" v-for="(item, index) in cardList" :key="index">
  15. <view class="box-item">
  16. <view class="box-val">{{ item.value }}</view>
  17. <view class="box-name">{{ item.name }}</view>
  18. </view>
  19. </view>
  20. </view>
  21. <view class="top-card1">
  22. <view class="card-box" v-for="(item, index) in cardList1" :key="index">
  23. <view class="box-item">
  24. <view class="box-val">{{ item.value }}</view>
  25. <view class="box-name">{{ item.name }}</view>
  26. </view>
  27. </view>
  28. </view>
  29. </view>
  30. <view class="center-area">
  31. <view class="top-title">
  32. <view style="font-weight: bold;">通风信息状态监测</view>
  33. </view>
  34. <view class="echartBox">
  35. <canvas id="myChart" :style="{ width: '100%', height: '180px' }"></canvas>
  36. </view>
  37. </view>
  38. <view class="bot-area">
  39. <view class="top-title">
  40. <view style="font-weight: bold;">通风监控测点信息</view>
  41. </view>
  42. <view class="bot-content">
  43. <view 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}${items.dw}` }}</span>
  55. </div>
  56. </div>
  57. </view>
  58. </view>
  59. </view>
  60. </view>
  61. </view>
  62. </template>
  63. <script>
  64. import api from "@/api/api";
  65. export default {
  66. name: 'ventDetail',
  67. props: {},
  68. watch: {},
  69. data() {
  70. return {
  71. mainTitle: '',
  72. myChart: null,
  73. timer: '',
  74. PageCur: "0",
  75. tabList: [],
  76. cardList: [
  77. { name: '进风量(m³/min)', value: 0 },
  78. { name: '回风量(m³/min)', value: 0 },
  79. { name: '需风量(m³/min)', value: 0 },
  80. ],
  81. cardList1: [
  82. { name: 'O₂(%)', value: 0 },
  83. { name: 'CO(%)', value: 0 },
  84. ],
  85. //echarts图表数据
  86. maxY: 0,
  87. xData: [],
  88. yDataJ: [],
  89. yDataH: [],
  90. yDataX: [],
  91. cardListTf: [],
  92. };
  93. },
  94. mounted() {
  95. this.getTabList()
  96. // than.initChart();
  97. this.getWindDeviceList()
  98. },
  99. beforeDestroy() {
  100. this.myChart.clear()
  101. this.myChart.dispose()
  102. this.timer = null
  103. clearTimeout(this.timer)
  104. },
  105. methods: {
  106. NavChange: function (item) {
  107. clearTimeout(this.timer)
  108. this.PageCur = item.index;
  109. this.mainTitle = this.tabList[this.PageCur].name
  110. this.getMonitor(this.tabList[this.PageCur].deviceID, true);
  111. },
  112. getMonitor(deviceID, flag) {
  113. let than = this
  114. than.timer = setTimeout(
  115. async () => {
  116. await than.getSysWarnList(deviceID, 'vent');
  117. if (than.timer) {
  118. than.timer = null;
  119. }
  120. than.getMonitor(deviceID);
  121. },
  122. flag ? 0 : 3000
  123. );
  124. },
  125. //获取顶部tab选项数据
  126. getTabList() {
  127. new Promise((resolve, reject) => {
  128. api
  129. .sysTypeWarn({ type: 'vent' })
  130. .then((response) => {
  131. if (response.data.code == 200 && response.data.result.length != 0) {
  132. let result = response.data.result
  133. this.tabList = result.map((el) => {//lxh
  134. return {
  135. name: el.deviceName,
  136. warn: '低风险',
  137. deviceID: el.deviceID,
  138. strtype: el.deviceType,
  139. };
  140. });
  141. this.mainTitle = this.tabList[0].name
  142. this.getMonitor(this.tabList[0].deviceID, true);
  143. } else {
  144. reject(response);
  145. }
  146. })
  147. .catch((error) => {
  148. console.log("catch===>response", response);
  149. reject(error);
  150. });
  151. });
  152. },
  153. formatRoundNum(num) {
  154. let interger = Math.ceil(num)
  155. let leng = String(interger).length
  156. return Math.ceil(interger / Math.pow(10, leng - 1)) * Math.pow(10, leng - 1)
  157. },
  158. //初始化echarts实例
  159. initChart() {
  160. let canvas = document.getElementById('myChart');
  161. this.myChart = this.$echarts.init(canvas)
  162. let option = {
  163. grid: {
  164. top: '24%',
  165. left: '0%',
  166. bottom: '6%',
  167. right: '2%',
  168. containLabel: true,
  169. },
  170. tooltip: {
  171. trigger: 'item',
  172. backgroundColor: 'rgba(0, 0, 0, .6)',
  173. textStyle: {
  174. color: '#fff',
  175. fontSize: 12,
  176. },
  177. },
  178. legend: {
  179. // align: 'center',
  180. right: '0%',
  181. top: '0%',
  182. type: 'plain',
  183. textStyle: {
  184. color: '#0eb4fc',
  185. fontSize: 12,
  186. },
  187. // icon:'rect',
  188. itemGap: 25,
  189. itemWidth: 20,
  190. icon: 'path://M0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v0a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z',
  191. data: [
  192. {
  193. name: '进风量',
  194. },
  195. {
  196. name: '回风量',
  197. },
  198. {
  199. name: '需风量'
  200. }
  201. ],
  202. },
  203. xAxis: [
  204. {
  205. type: 'category',
  206. boundaryGap: false,
  207. axisLabel: {
  208. // formatter: '{value}',
  209. fontSize: 12,
  210. margin: 10,
  211. textStyle: {
  212. color: '#b3b8cc',
  213. },
  214. // formatter: function (params) {
  215. // let newParamsName = ref('') // 最终拼接成的字符串
  216. // let paramsNameNumber = ref(params.length) // 实际标签的个数
  217. // let provideNumber = ref(10) // 每行能显示的字的个数
  218. // let rowNumber = Math.ceil(paramsNameNumber.value / provideNumber.value) // 换行的话,需要显示几行,向上取整
  219. // /**
  220. // * 判断标签的个数是否大于规定的个数, 如果大于,则进行换行处理 如果不大于,即等于或小于,就返回原标签
  221. // */
  222. // // 条件等同于rowNumber>1
  223. // if (paramsNameNumber.value > provideNumber.value) {
  224. // /** 循环每一行,p表示行 */
  225. // for (var p = 0; p < rowNumber; p++) {
  226. // var tempStr = '' // 表示每一次截取的字符串
  227. // var start = p * provideNumber.value // 开始截取的位置
  228. // var end = start + provideNumber.value // 结束截取的位置
  229. // // 此处特殊处理最后一行的索引值
  230. // if (p == rowNumber - 1) {
  231. // // 最后一次不换行
  232. // tempStr = params.substring(start, paramsNameNumber.value)
  233. // } else {
  234. // // 每一次拼接字符串并换行
  235. // tempStr = params.substring(start, end) + '\n'
  236. // }
  237. // newParamsName.value += tempStr // 最终拼成的字符串
  238. // }
  239. // } else {
  240. // // 将旧标签的值赋给新标签
  241. // newParamsName.value = params
  242. // }
  243. // //将最终的字符串返回
  244. // return newParamsName.value
  245. // }
  246. },
  247. axisLine: {
  248. lineStyle: {
  249. color: '#f1f5f6',
  250. },
  251. },
  252. axisTick: {
  253. show: false,
  254. },
  255. data: this.xData || [],
  256. },
  257. ],
  258. yAxis: [
  259. {
  260. boundaryGap: false,
  261. type: 'value',
  262. max: this.maxY,
  263. axisLabel: {
  264. textStyle: {
  265. color: '#b3b8cc',
  266. },
  267. formatter: '{value}'
  268. },
  269. name: '(m³/min)',
  270. nameTextStyle: {
  271. color: '#b3b8cc',
  272. fontSize: 12,
  273. lineHeight: 0,
  274. },
  275. splitLine: {
  276. lineStyle: {
  277. color: '#f1f5f6',
  278. },
  279. },
  280. axisLine: {
  281. show: true,
  282. lineStyle: {
  283. color: '#f1f5f6',
  284. },
  285. },
  286. axisTick: {
  287. show: false,
  288. },
  289. },
  290. {
  291. boundaryGap: false,
  292. type: 'value',
  293. max: this.maxY,
  294. axisLabel: {
  295. textStyle: {
  296. color: '#b3b8cc',
  297. },
  298. formatter: '{value}'
  299. },
  300. name: '(m³/min)',
  301. nameTextStyle: {
  302. color: '#fff',
  303. fontSize: 12,
  304. lineHeight: 10,
  305. },
  306. splitLine: {
  307. lineStyle: {
  308. color: '#f1f5f6',
  309. },
  310. },
  311. axisLine: {
  312. show: true,
  313. lineStyle: {
  314. color: '#f1f5f6',
  315. },
  316. },
  317. axisTick: {
  318. show: false,
  319. },
  320. },
  321. ],
  322. series: [
  323. {
  324. name: '进风量',
  325. type: 'line',
  326. smooth: true,
  327. showSymbol: true,
  328. symbolSize: 8,
  329. zlevel: 3,
  330. itemStyle: {
  331. color: '#19a3df',
  332. borderColor: '#a3c8d8',
  333. },
  334. lineStyle: {
  335. normal: {
  336. width: 2,
  337. color: '#19a3df',
  338. },
  339. },
  340. data: this.yDataJ || [],
  341. },
  342. {
  343. name: '回风量',
  344. type: 'line',
  345. smooth: true,
  346. showSymbol: true,
  347. symbolSize: 8,
  348. zlevel: 3,
  349. itemStyle: {
  350. color: '#4fffad',
  351. borderColor: '#a3c8d8',
  352. },
  353. lineStyle: {
  354. normal: {
  355. width: 2,
  356. color: '#4fffad',
  357. },
  358. },
  359. data: this.yDataH || [],
  360. },
  361. {
  362. name: '需风量',
  363. type: 'line',
  364. smooth: true,
  365. showSymbol: true,
  366. symbolSize: 8,
  367. zlevel: 3,
  368. itemStyle: {
  369. color: '#fc8452',
  370. borderColor: '#a3c8d8',
  371. },
  372. lineStyle: {
  373. normal: {
  374. width: 2,
  375. color: '#fc8452',
  376. },
  377. },
  378. data: this.yDataX || [],
  379. },
  380. ],
  381. };
  382. this.myChart.setOption(option)
  383. window.addEventListener('resize', () => {
  384. this.myChart.resize()
  385. })
  386. },
  387. //获取选项详情数据
  388. getSysWarnList(id, type) {
  389. new Promise((resolve, reject) => {
  390. api
  391. .sysWarn({ sysid: id, type: type })
  392. .then((response) => {
  393. if (response.data.code == 200) {
  394. this.xData = []
  395. this.yDataH = []
  396. this.yDataJ = []
  397. this.yDataX = []
  398. let data = response.data.result
  399. this.cardList[0].value = data.jin || '--'
  400. this.cardList[1].value = data.hui || '--'
  401. this.cardList[2].value = data.xufengliang || '--'
  402. if (data.history.length != 0) {
  403. data.history.forEach((v) => {//lxh
  404. this.yDataJ.push(parseFloat(v.jin));
  405. this.yDataH.push(parseFloat(v.hui));
  406. if (this.cardList[2].value && this.cardList[2].value != '--') {
  407. this.yDataX.push(this.cardList[2].value);
  408. } else {
  409. this.yDataX.push(0);
  410. }
  411. this.xData.push(v.time);
  412. });
  413. }
  414. let max1 = this.yDataJ.reduce((acr, cur) => {
  415. return acr > cur ? acr : cur
  416. })
  417. let max2 = this.yDataH.reduce((acr1, cur1) => {
  418. return acr1 > cur1 ? acr1 : cur1
  419. })
  420. this.maxY = max1 >= max2 ? this.formatRoundNum(max1 * 1.5) : this.formatRoundNum(max2 * 1.5)
  421. this.initChart()
  422. } else {
  423. reject(response);
  424. }
  425. })
  426. .catch((error) => {
  427. console.log("catch===>response", response);
  428. reject(error);
  429. });
  430. });
  431. },
  432. //获取通风监控测点信息
  433. getWindDeviceList() {
  434. new Promise((resolve, reject) => {
  435. api
  436. .getDeviceVent({ devicetype: 'windrect', pagetype: 'normal' })
  437. .then((response) => {
  438. if (response.data.code == 200) {
  439. let data = response.data.result
  440. if (data.msgTxt[0].datalist.length != 0) {
  441. let list = data.msgTxt[0].datalist;
  442. if (list.length > 0) {
  443. this.cardListTf = list.map((el) => {//lxh
  444. const readData = el.readData;
  445. el = Object.assign(el, readData);
  446. return {
  447. label: '通信状态',
  448. value: el.netStatus == '0' ? '断开' : '连接',
  449. listR: [
  450. { id: 0, label: '安装位置', dw: '', value: el.strinstallpos },
  451. { id: 1, label: '风量', dw: '(m³/min)', value: el.m3 },
  452. { id: 2, label: '风速', dw: '(m/s)', value: el.va },
  453. { id: 4, label: '时间', dw: '', value: el.readTime },
  454. { id: 3, label: '是否报警', dw: '', value: el.warnFlag == '0' ? '正常' : el.warnFlag == 1 ? '报警' : el.warnFlag == 2 ? '断开' : '未监测' },
  455. ],
  456. }
  457. });
  458. }
  459. }
  460. } else {
  461. reject(response);
  462. }
  463. })
  464. .catch((error) => {
  465. console.log("catch===>response", response);
  466. reject(error);
  467. });
  468. });
  469. }
  470. },
  471. computed: {},
  472. };
  473. </script>
  474. <style lang="scss" scoped>
  475. .vent-detail {
  476. position: relative;
  477. box-sizing: border-box;
  478. .devic-box-tab {
  479. padding: 0px 10px !important;
  480. }
  481. .vent-content {
  482. height: 704px;
  483. box-sizing: border-box;
  484. overflow-y: auto;
  485. .top-area {
  486. width: 100%;
  487. padding: 10px;
  488. box-sizing: border-box;
  489. background-color: #FFF;
  490. margin-bottom: 2px;
  491. .top-card {
  492. width: 100%;
  493. height: 60px;
  494. margin-bottom: 10px;
  495. display: flex;
  496. justify-content: space-between;
  497. align-items: center;
  498. .card-box {
  499. width: 32%;
  500. height: 100%;
  501. border-radius: 5px;
  502. background: linear-gradient(to right, rgba(55, 135, 254, 0.08), rgba(4, 184, 255, 0.08), rgba(60, 161, 237, 0.08));
  503. }
  504. .card-box:nth-child(1) .box-item {
  505. display: flex;
  506. flex-direction: column;
  507. justify-content: center;
  508. align-items: flex-start;
  509. width: 100%;
  510. height: 100%;
  511. padding: 0px 15px;
  512. background: url('/static/jinfeng.png') no-repeat center;
  513. background-size: 75% 70%;
  514. }
  515. .card-box:nth-child(2) .box-item {
  516. display: flex;
  517. flex-direction: column;
  518. justify-content: center;
  519. align-items: flex-start;
  520. width: 100%;
  521. height: 100%;
  522. padding: 0px 15px;
  523. background: url('/static/huifeng.png') no-repeat center;
  524. background-size: 75% 70%;
  525. }
  526. .card-box:nth-child(3) .box-item {
  527. display: flex;
  528. flex-direction: column;
  529. justify-content: center;
  530. align-items: flex-start;
  531. width: 100%;
  532. height: 100%;
  533. padding: 0px 15px;
  534. background: url('/static/xufeng.png') no-repeat center;
  535. background-size: 75% 70%;
  536. }
  537. }
  538. .top-card1 {
  539. width: 100%;
  540. height: 60px;
  541. display: flex;
  542. justify-content: space-between;
  543. align-items: center;
  544. .card-box {
  545. width: 49%;
  546. height: 100%;
  547. border-radius: 5px;
  548. background: linear-gradient(to right, rgba(55, 135, 254, 0.08), rgba(4, 184, 255, 0.08), rgba(60, 161, 237, 0.08));
  549. }
  550. .card-box:nth-child(1) .box-item {
  551. display: flex;
  552. flex-direction: column;
  553. justify-content: center;
  554. align-items: flex-start;
  555. width: 100%;
  556. height: 100%;
  557. padding: 0px 15px;
  558. background: url('/static/O₂.png') no-repeat right;
  559. background-size: 40% 80%;
  560. }
  561. .card-box:nth-child(2) .box-item {
  562. display: flex;
  563. flex-direction: column;
  564. justify-content: center;
  565. align-items: flex-start;
  566. width: 100%;
  567. height: 100%;
  568. padding: 0px 15px;
  569. background: url('/static/CO.png') no-repeat right;
  570. background-size: 40% 80%;
  571. }
  572. }
  573. }
  574. .center-area {
  575. width: 100%;
  576. height: 220px;
  577. padding: 0px 10px;
  578. box-sizing: border-box;
  579. background-color: #FFF;
  580. margin-bottom: 2px;
  581. }
  582. .bot-area {
  583. width: 100%;
  584. padding: 0px 10px;
  585. box-sizing: border-box;
  586. background-color: #FFF;
  587. margin-bottom: 2px;
  588. .bot-content {
  589. overflow-y: auto;
  590. .card-b {
  591. width: 100%;
  592. height: 100px;
  593. display: flex;
  594. justify-content: space-between;
  595. padding: 5px;
  596. margin-bottom: 5px;
  597. background: linear-gradient(to right, rgba(55, 135, 254, 0.08), rgba(4, 184, 255, 0.08), rgba(60, 161, 237, 0.08));
  598. .item-l {
  599. position: relative;
  600. width: 80px;
  601. height: 100%;
  602. background: url('/static/bot-area.png') no-repeat center;
  603. background-size: 100% 100%;
  604. .label-l {
  605. width: 100%;
  606. position: absolute;
  607. top: 7px;
  608. font-size: 12px;
  609. text-align: center;
  610. }
  611. .value-l {
  612. width: 100%;
  613. position: absolute;
  614. top: 50px;
  615. color: #0eb4fc;
  616. text-align: center;
  617. }
  618. }
  619. .item-r {
  620. width: calc(100% - 100px);
  621. height: 100%;
  622. display: flex;
  623. flex-direction: column;
  624. justify-content: space-around;
  625. .content-r {
  626. display: flex;
  627. span {
  628. font-size: 12px;
  629. &:first-child {
  630. display: inline-block;
  631. width: 68px;
  632. }
  633. &:last-child {
  634. display: inline-block;
  635. width: calc(100% - 68px);
  636. overflow: hidden;
  637. white-space: nowrap;
  638. /* 不换行 */
  639. /* 超出部分隐藏 */
  640. text-overflow: ellipsis;
  641. /* 使用省略符号 */
  642. }
  643. }
  644. .status-f {
  645. color: #ff0000;
  646. }
  647. .status-l {
  648. color: #3df6ff;
  649. }
  650. }
  651. }
  652. }
  653. }
  654. }
  655. .top-title {
  656. height: 28px;
  657. margin-bottom: 10px;
  658. display: flex;
  659. justify-content: space-between;
  660. align-items: center;
  661. .title-icon {
  662. display: flex;
  663. justify-content: space-between;
  664. align-items: center;
  665. width: 90px;
  666. height: 28px;
  667. padding: 0px 10px;
  668. border-radius: 5px;
  669. background: #d4ecff;
  670. .icon-text {
  671. font-size: 12px;
  672. font-weight: bold;
  673. color: #0eb4fc;
  674. }
  675. }
  676. }
  677. .box-item .box-val {
  678. height: 28px;
  679. line-height: 28px;
  680. color: #0eb4fc;
  681. font-weight: bold;
  682. }
  683. .box-item .box-name {
  684. font-size: 12px;
  685. }
  686. .icon-style {
  687. width: 14px;
  688. height: 14px;
  689. }
  690. }
  691. }
  692. </style>