dust-detail.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. <template>
  2. <view class="dust-detail">
  3. <u-tabs
  4. class="devic-box-tab"
  5. :current="PageCur"
  6. :list="tabList"
  7. @click="NavChange"
  8. ></u-tabs>
  9. <view class="dust-content">
  10. <view class="top-area" v-for="(item, index) in topList" :key="index">
  11. <view class="top-title">
  12. <view style="font-weight: bold">{{ item.label }}</view>
  13. </view>
  14. <view class="top-card">
  15. <view class="card-item card-item1">
  16. <view class="item-box">
  17. <view class="item-icon">
  18. <image
  19. class="icon-img"
  20. src="/static/warndata/wendu.svg"
  21. mode="aspectFit"
  22. ></image>
  23. </view>
  24. <view class="item-info">
  25. <view class="box-val">{{ item.temperature || "--" }}</view>
  26. <view class="box-name">温度(℃)</view>
  27. </view>
  28. </view>
  29. </view>
  30. <view class="card-item card-item1">
  31. <view class="item-box">
  32. <view class="item-icon">
  33. <image
  34. class="icon-img"
  35. src="/static/warndata/fenchen-nongdu.svg"
  36. mode="aspectFit"
  37. ></image>
  38. </view>
  39. <view class="item-info">
  40. <view class="box-val">{{ item.dustval || "--" }}</view>
  41. <view class="box-name">粉尘浓度(mg/m³)</view>
  42. </view>
  43. </view>
  44. </view>
  45. <view class="card-item">
  46. <view class="item-box">
  47. <view class="item-icon">
  48. <image
  49. class="icon-img"
  50. src="/static/warndata/penwu-shuiya.svg"
  51. mode="aspectFit"
  52. ></image>
  53. </view>
  54. <view class="item-info">
  55. <view class="box-val">{{ item.waterPressure || "--" }}</view>
  56. <view class="box-name">喷雾水压(MPa)</view>
  57. </view>
  58. </view>
  59. </view>
  60. <view class="card-item">
  61. <view class="item-box">
  62. <view class="item-icon">
  63. <image
  64. class="icon-img"
  65. src="/static/warndata/zhuangtai.svg"
  66. mode="aspectFit"
  67. ></image>
  68. </view>
  69. <view class="item-info">
  70. <view
  71. class="box-val"
  72. :class="{ 'is-normal': item.atomizingState === 1 }"
  73. >{{ item.atomizingState === 1 ? "正常" : "异常" }}</view
  74. >
  75. <view class="box-name">喷雾状态</view>
  76. </view>
  77. </view>
  78. </view>
  79. </view>
  80. <view class="top-echart">
  81. <LineChart
  82. type="area"
  83. :chartData="option"
  84. :chartOpts="areaOpts"
  85. :style="{ width: '100%', height: '180px' }"
  86. ></LineChart>
  87. </view>
  88. </view>
  89. <!-- <view class="bot-area">
  90. <view class="bot-title">
  91. <view style="font-weight: bold">粉尘监控测点信息</view>
  92. </view>
  93. <view class="bot-content">
  94. <view
  95. class="card-b"
  96. v-for="(item, index) in cardListDust"
  97. :key="index"
  98. >
  99. <div class="item-l">
  100. <div class="label-l">{{ item.label || "--" }}</div>
  101. <div class="value-l">{{ item.value || "--" }}</div>
  102. </div>
  103. <div class="item-r">
  104. <div
  105. class="content-r"
  106. v-for="(items, ind) in item.listR"
  107. :key="ind"
  108. >
  109. <span>{{ `${items.label} : ` }}</span>
  110. <span
  111. :class="{
  112. 'status-f': items.value == 1,
  113. 'status-l': items.value == 0,
  114. }"
  115. >{{ `${items.value}${items.dw}` || "--" }}</span
  116. >
  117. </div>
  118. </div>
  119. </view>
  120. </view>
  121. </view> -->
  122. </view>
  123. </view>
  124. </template>
  125. <script>
  126. import api from "@/api/api";
  127. export default {
  128. name: "dustDetail",
  129. props: {},
  130. watch: {},
  131. data() {
  132. return {
  133. option: {},
  134. timer: "",
  135. PageCur: "0",
  136. tabList: [],
  137. //喷粉选项数据
  138. topList: [],
  139. flag: true,
  140. echartNow: [],
  141. echartYc: [],
  142. xData: [],
  143. yDataN: [],
  144. yDataY: [],
  145. yDataS: [],
  146. maxY: 0,
  147. cardListDust: [],
  148. };
  149. },
  150. beforeDestroy() {
  151. this.myChart.clear();
  152. this.myChart.dispose();
  153. this.timer = null;
  154. clearTimeout(this.timer);
  155. },
  156. mounted() {
  157. this.getTabList();
  158. this.getWindDeviceList();
  159. },
  160. methods: {
  161. NavChange: function (item) {
  162. clearTimeout(this.timer);
  163. this.PageCur = item.index;
  164. this.getMonitor(this.tabList[this.PageCur].deviceID, true);
  165. },
  166. getMonitor(deviceID, flag) {
  167. let than = this;
  168. than.timer = setTimeout(
  169. async () => {
  170. await than.getSysWarnList(deviceID, "dust");
  171. if (than.timer) {
  172. than.timer = null;
  173. }
  174. than.getMonitor(deviceID);
  175. },
  176. flag ? 0 : 3000
  177. );
  178. },
  179. //获取顶部tab选项数据
  180. getTabList() {
  181. this.tabList = [];
  182. new Promise((resolve, reject) => {
  183. api
  184. .sysTypeWarn({ type: "dust" })
  185. .then((response) => {
  186. if (response.data.code == 200 && response.data.result.length != 0) {
  187. let result = response.data.result;
  188. console.log(result, "粉尘菜单---------");
  189. result.forEach((el) => {
  190. this.tabList.push({
  191. name: el.systemname,
  192. warn: "低风险",
  193. deviceID: el.id,
  194. strtype: el.strtype,
  195. });
  196. });
  197. this.getMonitor(this.tabList[0].deviceID, true);
  198. } else {
  199. reject(response);
  200. }
  201. })
  202. .catch((error) => {
  203. console.log("catch===>response", response);
  204. reject(error);
  205. });
  206. });
  207. },
  208. //获取喷粉选项数据
  209. getSysWarnList(id, type) {
  210. let that = this;
  211. new Promise((resolve, reject) => {
  212. api
  213. .sysWarn({ sysid: id, type: type })
  214. .then((response) => {
  215. if (response.data.code == 200) {
  216. console.log(response.data, "喷粉选项数据---------");
  217. let data = response.data.result.dust;
  218. if (data.length != 0) {
  219. that.topList = data.map((el) => {
  220. return {
  221. label: el.strinstallpos,
  222. temperature: el.readData.temperature,
  223. dustval: el.readData.dustval,
  224. waterPressure: el.readData.waterPressure,
  225. atomizingState: el.readData.atomizingState,
  226. };
  227. });
  228. data.forEach((v, index) => {
  229. if (that.flag) {
  230. that.echartNow = JSON.parse(v.readData.expectInfo)["list"];
  231. that.flag = false;
  232. }
  233. that.echartYc.push({
  234. time: JSON.parse(v.readData.expectInfo)["nowTime"],
  235. value: JSON.parse(v.readData.expectInfo)["nowVal"],
  236. });
  237. let setData = [...that.echartNow, ...that.echartYc].sort(
  238. (a, b) =>
  239. Date.parse(new Date(a.time)) -
  240. Date.parse(new Date(b.time))
  241. );
  242. that.xData = [];
  243. that.yDataS = [];
  244. that.yDataY = [];
  245. that.yDataN = [];
  246. setData.forEach((el) => {
  247. if (el.value && el.value != "0") {
  248. that.xData.push(that.formatAxisTime(el.time));
  249. that.yDataS.push(el.value);
  250. that.yDataY.push(
  251. JSON.parse(v.readData.expectInfo)["aveVal"]
  252. );
  253. that.yDataN.push(
  254. JSON.parse(v.readData.expectInfo)["fmin"]
  255. ? JSON.parse(v.readData.expectInfo)["fmin"]
  256. : 0
  257. );
  258. }
  259. });
  260. let max = that.yDataS.reduce((acr, cur) => {
  261. return acr > cur ? acr : cur;
  262. });
  263. that.maxY = that.formatRoundNum(max * 1.5);
  264. // console.log(that.xData, that.yDataS, that.yDataY, that.yDataN, '000')
  265. this.option = {
  266. categories: this.xData.slice(-3) || [],
  267. series: [
  268. {
  269. name: "预测值",
  270. data: this.yDataY.slice(-3) || [],
  271. },
  272. {
  273. name: "最新值",
  274. data: this.yDataN.slice(-3) || [],
  275. },
  276. ],
  277. };
  278. });
  279. }
  280. } else {
  281. reject(response);
  282. }
  283. })
  284. .catch((error) => {
  285. console.log("catch===>response", response);
  286. reject(error);
  287. });
  288. });
  289. },
  290. //获取粉尘监控测点信息
  291. getWindDeviceList() {
  292. new Promise((resolve, reject) => {
  293. api
  294. .getDeviceVent({ devicetype: "dusting", pagetype: "normal" })
  295. .then((response) => {
  296. if (response.data.code == 200) {
  297. let data = response.data.result;
  298. if (data.msgTxt[0].datalist.length != 0) {
  299. let list = data.msgTxt[0].datalist;
  300. if (list.length > 0) {
  301. this.cardListDust = list.map((el) => {
  302. //lxh
  303. const readData = el.readData;
  304. el = Object.assign(el, readData);
  305. return {
  306. label: "通信状态",
  307. value: el.netStatus == "0" ? "断开" : "连接",
  308. listR: [
  309. {
  310. id: 0,
  311. label: "安装位置",
  312. dw: "",
  313. value: el.strinstallpos,
  314. },
  315. {
  316. id: 1,
  317. label: "粉尘浓度",
  318. dw: "(mg/m³)",
  319. value: el.dustval,
  320. },
  321. {
  322. id: 2,
  323. label: "巷道湿度",
  324. dw: "(RH)",
  325. value: el.humidity,
  326. },
  327. {
  328. id: 4,
  329. label: "巷道温度",
  330. dw: "(℃)",
  331. value: el.temperature,
  332. },
  333. {
  334. id: 3,
  335. label: "是否报警",
  336. dw: "",
  337. value:
  338. el.warnFlag == "0"
  339. ? "正常"
  340. : el.warnFlag == 1
  341. ? "报警"
  342. : el.warnFlag == 2
  343. ? "断开"
  344. : "未监测",
  345. },
  346. ],
  347. };
  348. });
  349. }
  350. }
  351. } else {
  352. reject(response);
  353. }
  354. })
  355. .catch((error) => {
  356. console.log("catch===>response", response);
  357. reject(error);
  358. });
  359. });
  360. },
  361. //横轴时间格式化,避免标签太长叠在一起
  362. formatAxisTime(time) {
  363. if (typeof time != "string") {
  364. return time;
  365. }
  366. //2024-03-11 17:58:11 -> 03-11 17:58
  367. return time.length > 16 ? time.slice(5, 16) : time;
  368. },
  369. formatRoundNum(num) {
  370. let interger = Math.ceil(num);
  371. let leng = String(interger).length;
  372. return (
  373. Math.ceil(interger / Math.pow(10, leng - 1)) * Math.pow(10, leng - 1)
  374. );
  375. },
  376. },
  377. computed: {
  378. //曲线通用配置:曲线下方渐变填充 + 横轴标签抽稀
  379. areaOpts() {
  380. return {
  381. xAxis: {
  382. disableGrid: true,
  383. labelCount: 3,
  384. fontSize: 10,
  385. },
  386. extra: {
  387. area: {
  388. type: "curve",
  389. opacity: 0.35,
  390. addLine: true,
  391. width: 2,
  392. gradient: true,
  393. activeType: "hollow",
  394. },
  395. },
  396. };
  397. },
  398. },
  399. };
  400. </script>
  401. <style lang="scss" scoped>
  402. .dust-detail {
  403. position: relative;
  404. // padding: 0px 10px;
  405. box-sizing: border-box;
  406. .devic-box-tab {
  407. position: sticky;
  408. top: 0;
  409. z-index: 10;
  410. padding: 0px 10px !important;
  411. margin-bottom: 10px;
  412. }
  413. .dust-content {
  414. // height: 704px;
  415. // padding: 0px 10px;
  416. // box-sizing: border-box;
  417. // overflow-y: auto;
  418. height: auto;
  419. box-sizing: border-box;
  420. overflow-y: auto;
  421. .top-area {
  422. width: 100%;
  423. padding: 10px;
  424. box-sizing: border-box;
  425. background-color: #f9f9fb;
  426. border: 1px solid #fff;
  427. margin-bottom: 12px;
  428. .top-title {
  429. height: 28px;
  430. margin-bottom: 10px;
  431. display: flex;
  432. justify-content: space-between;
  433. align-items: center;
  434. font-size: 14px;
  435. font-weight: 600;
  436. color: #000;
  437. }
  438. .top-card {
  439. display: flex;
  440. justify-content: space-between;
  441. align-items: flex-start;
  442. flex-wrap: wrap;
  443. width: 100%;
  444. margin-bottom: 10px;
  445. .card-item {
  446. width: calc(50% - 5px);
  447. height: 62px;
  448. border-radius: 10px;
  449. background: linear-gradient(
  450. 135deg,
  451. rgba(55, 135, 254, 0.12) 0%,
  452. rgba(4, 184, 255, 0.08) 100%
  453. );
  454. .item-box {
  455. width: 100%;
  456. height: 100%;
  457. padding: 0px 8px;
  458. box-sizing: border-box;
  459. display: flex;
  460. align-items: center;
  461. .item-icon {
  462. position: relative;
  463. flex-shrink: 0;
  464. width: 28px;
  465. height: 28px;
  466. border-radius: 8px;
  467. display: flex;
  468. align-items: center;
  469. justify-content: center;
  470. background: linear-gradient(135deg, #80b4ff, #347df3);
  471. .icon-img {
  472. position: relative;
  473. width: 16px;
  474. height: 16px;
  475. filter: brightness(0) invert(1);
  476. }
  477. }
  478. .item-info {
  479. flex: 1;
  480. min-width: 0px;
  481. padding-left: 8px;
  482. display: flex;
  483. flex-direction: column;
  484. justify-content: center;
  485. }
  486. .box-val {
  487. font-size: 13px;
  488. font-weight: bold;
  489. line-height: 16px;
  490. color: #2b7cff;
  491. }
  492. .box-val.is-normal {
  493. color: #39b54a;
  494. }
  495. .box-name {
  496. margin-top: 2px;
  497. font-size: 11px;
  498. line-height: 15px;
  499. color: #000;
  500. }
  501. }
  502. }
  503. .card-item1 {
  504. margin-bottom: 10px;
  505. }
  506. }
  507. .top-echart {
  508. height: 220px;
  509. }
  510. }
  511. .bot-area {
  512. width: 100%;
  513. padding: 10px;
  514. box-sizing: border-box;
  515. background-color: #f9f9fb;
  516. border: 1px solid #fff;
  517. margin-bottom: 2px;
  518. .bot-title {
  519. height: 28px;
  520. margin-bottom: 10px;
  521. display: flex;
  522. justify-content: space-between;
  523. align-items: center;
  524. font-size: 14px;
  525. font-weight: 600;
  526. color: #000;
  527. }
  528. .bot-content {
  529. // height: calc(100% - 38px);
  530. overflow-y: auto;
  531. .card-b {
  532. width: 100%;
  533. height: 100px;
  534. display: flex;
  535. justify-content: space-between;
  536. padding: 5px;
  537. margin-bottom: 5px;
  538. background: linear-gradient(
  539. to right,
  540. rgba(55, 135, 254, 0.08),
  541. rgba(4, 184, 255, 0.08),
  542. rgba(60, 161, 237, 0.08)
  543. );
  544. .item-l {
  545. position: relative;
  546. width: 80px;
  547. height: 100%;
  548. background: url("/static/bot-area.png") no-repeat center;
  549. background-size: 100% 100%;
  550. .label-l {
  551. width: 100%;
  552. position: absolute;
  553. top: 7px;
  554. font-size: 12px;
  555. text-align: center;
  556. }
  557. .value-l {
  558. width: 100%;
  559. position: absolute;
  560. top: 50px;
  561. color: #0eb4fc;
  562. text-align: center;
  563. }
  564. }
  565. .item-r {
  566. width: calc(100% - 100px);
  567. height: 100%;
  568. display: flex;
  569. flex-direction: column;
  570. justify-content: space-around;
  571. .content-r {
  572. display: flex;
  573. span {
  574. font-size: 12px;
  575. &:first-child {
  576. display: inline-block;
  577. width: 68px;
  578. }
  579. &:last-child {
  580. display: inline-block;
  581. width: calc(100% - 68px);
  582. overflow: hidden;
  583. white-space: nowrap;
  584. /* 不换行 */
  585. /* 超出部分隐藏 */
  586. text-overflow: ellipsis;
  587. /* 使用省略符号 */
  588. }
  589. }
  590. .status-f {
  591. color: #ff0000;
  592. }
  593. .status-l {
  594. color: #3df6ff;
  595. }
  596. }
  597. }
  598. }
  599. }
  600. }
  601. }
  602. }
  603. </style>