vent-detail.vue 16 KB

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