fire-detail-out.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. <template>
  2. <!-- 外因火灾 -->
  3. <view class="fire-detail-out">
  4. <view class="top-area">
  5. <view class="top-card">
  6. <view class="card-box" v-for="(item, index) in cardList" :key="index">
  7. <view class="box-item">
  8. <view class="item-icon">
  9. <image class="icon-img" :src="item.icon" mode="aspectFit"></image>
  10. </view>
  11. <view class="item-info">
  12. <view
  13. class="box-val"
  14. :class="{ 'is-normal': item.value == '正常' }"
  15. >{{ item.value }}</view
  16. >
  17. <view class="box-name">{{ item.name }}</view>
  18. </view>
  19. </view>
  20. </view>
  21. </view>
  22. </view>
  23. <view class="center-area">
  24. <view class="bot-title">
  25. <view style="font-weight: bold">光纤测温系统温度实时监测</view>
  26. </view>
  27. <view class="echartBox">
  28. <LineChart
  29. type="area"
  30. :chartData="option"
  31. :chartOpts="chartOpts"
  32. :style="{ width: '100%' }"
  33. ></LineChart>
  34. </view>
  35. </view>
  36. <view class="bot-area" v-for="(item, index) in botList" :key="index">
  37. <view class="bot-title">{{ item.title }}</view>
  38. <view class="bot-content">
  39. <view class="h-table">
  40. <view class="table-head">
  41. <view
  42. class="head-item"
  43. v-for="(col, cIndex) in item.head"
  44. :key="cIndex"
  45. :style="{ width: col.width }"
  46. >{{ col.title }}</view
  47. >
  48. </view>
  49. <view class="table-content">
  50. <view class="content-tr" v-for="(ite, ind) in item.list" :key="ind">
  51. <view
  52. class="content-td"
  53. v-for="(col, cIndex) in item.head"
  54. :key="cIndex"
  55. :style="{ width: col.width }"
  56. >{{ cellText(ite, col.key) }}</view
  57. >
  58. </view>
  59. </view>
  60. </view>
  61. </view>
  62. </view>
  63. </view>
  64. </template>
  65. <script>
  66. import api from "@/api/api";
  67. export default {
  68. name: "fireDetailOut",
  69. props: {
  70. detailWy: {
  71. type: Object,
  72. default: () => {
  73. return {};
  74. },
  75. },
  76. },
  77. data() {
  78. return {
  79. option: {
  80. categories: [],
  81. series: [{ name: "当前温度", data: [] }],
  82. },
  83. myChart: null,
  84. xData: [],
  85. yData: [],
  86. cardList: [
  87. {
  88. name: "最高温度(℃)",
  89. value: 0,
  90. icon: "/static/warndata/zuigao-wendu.svg",
  91. },
  92. {
  93. name: "最低温度(℃)",
  94. value: 0,
  95. icon: "/static/warndata/zuidi-wendu.svg",
  96. },
  97. {
  98. name: "平均温度(℃)",
  99. value: 0,
  100. icon: "/static/warndata/pingjun-wendu.svg",
  101. },
  102. {
  103. name: "状态",
  104. value: "正常",
  105. icon: "/static/warndata/zhuangtai.svg",
  106. },
  107. ],
  108. //表头:设备名称/值/状态/时间
  109. headList: [
  110. { title: "设备名称", key: "strinstallpos", width: "25%" },
  111. { title: "值", key: "val", width: "20%" },
  112. { title: "状态", key: "warnLevel_str", width: "20%" },
  113. { title: "时间", key: "readTime", width: "35%" },
  114. ],
  115. //表头:设备名称/状态/时间
  116. headList1: [
  117. { title: "设备名称", key: "strinstallpos", width: "40%" },
  118. { title: "状态", key: "warnLevel_str", width: "30%" },
  119. { title: "时间", key: "readTime", width: "30%" },
  120. ],
  121. //表头:值/状态/时间
  122. headList2: [
  123. { title: "值", key: "val", width: "30%" },
  124. { title: "状态", key: "warnLevel_str", width: "30%" },
  125. { title: "时间", key: "readTime", width: "40%" },
  126. ],
  127. tableDataYw: [],
  128. tableDataCo: [],
  129. tableDataPl: [],
  130. tableDataWd: [],
  131. };
  132. },
  133. watch: {
  134. detailWy: {
  135. handler(newW, oldW) {
  136. console.log(newW, "newW--------------");
  137. let that = this;
  138. //接口未就绪或类型不对时不处理,避免报错中断
  139. if (!newW || typeof newW != "object" || Array.isArray(newW)) {
  140. return;
  141. }
  142. that.xData = [];
  143. that.yData = [];
  144. if (JSON.stringify(newW) != "{}") {
  145. //排查用:接口实际返回的字段
  146. console.log("外因火灾接口字段:", Object.keys(newW));
  147. //光纤测温:最高/最低/平均温度 + 温度曲线
  148. let fiber = that.getList(newW, "fiber");
  149. if (fiber.length != 0) {
  150. let readData = fiber[0].readData || {};
  151. that.cardList[0].value = that.valueOr(readData.fmax);
  152. that.cardList[1].value = that.valueOr(readData.fmin);
  153. that.cardList[2].value = that.valueOr(readData.favg);
  154. that.cardList[3].value = fiber[0].warnFlag ? "存在风险" : "正常";
  155. that.parseJson(readData.fibreTemperature).forEach((el) => {
  156. that.xData.push(el.pos);
  157. that.yData.push(el.value);
  158. });
  159. } else {
  160. console.warn("外因火灾:接口未返回光纤测温数据 fiber");
  161. that.cardList[0].value = "--";
  162. that.cardList[1].value = "--";
  163. that.cardList[2].value = "--";
  164. that.cardList[3].value = "正常";
  165. }
  166. that.initChart();
  167. //烟雾传感器
  168. that.tableDataYw = that.getList(newW, "smoke").map((el) => {
  169. return {
  170. strinstallpos: el.strinstallpos,
  171. val: el.readData ? el.readData.smokeval : "",
  172. warnLevel_str:
  173. el.warnLevel_str == "正常" ? "低风险" : el.warnLevel_str,
  174. readTime: el.readTime,
  175. };
  176. });
  177. //温度传感器
  178. that.tableDataWd = that.getList(newW, "temperature").map((el) => {
  179. return {
  180. val: el.readData ? el.readData.fireval : "",
  181. warnLevel_str:
  182. el.warnLevel_str == "正常" ? "低风险" : el.warnLevel_str,
  183. readTime: el.readTime,
  184. };
  185. });
  186. //一氧化碳传感器
  187. that.tableDataCo = that.getList(newW, "co").map((el) => {
  188. return {
  189. strinstallpos: el.strinstallpos,
  190. val: el.readData ? el.readData.coval : "",
  191. warnLevel_str:
  192. el.warnLevel_str == "正常" ? "低风险" : el.warnLevel_str,
  193. readTime: el.readTime,
  194. };
  195. });
  196. //自动喷淋灭火装置
  197. that.tableDataPl = that.getList(newW, "spray");
  198. }
  199. },
  200. immediate: true,
  201. deep: true,
  202. },
  203. },
  204. mounted() {},
  205. methods: {
  206. //初始化echarts实例
  207. initChart() {
  208. this.option = {
  209. categories: this.xData.slice(-10) || [],
  210. series: [
  211. {
  212. name: "当前温度",
  213. data: this.yData.slice(-10) || [],
  214. },
  215. ],
  216. };
  217. },
  218. //取出接口里的数组字段,字段不存在时返回空数组
  219. getList(data, key) {
  220. const list = data ? data[key] : null;
  221. return Array.isArray(list) ? list : [];
  222. },
  223. //解析JSON字符串(光纤测温数据),解析失败返回空数组
  224. parseJson(str) {
  225. if (!str) {
  226. return [];
  227. }
  228. try {
  229. const data = typeof str == "string" ? JSON.parse(str) : str;
  230. return Array.isArray(data) ? data : [];
  231. } catch (e) {
  232. console.warn("光纤测温数据解析失败:", str, e);
  233. return [];
  234. }
  235. },
  236. //空值统一显示--
  237. valueOr(value) {
  238. return value === null || value === undefined || value === ""
  239. ? "--"
  240. : value;
  241. },
  242. //表格取值,空值统一显示--
  243. cellText(item, key) {
  244. return this.valueOr(item ? item[key] : "");
  245. },
  246. },
  247. computed: {
  248. //光纤测温曲线:曲线下方渐变填充
  249. chartOpts() {
  250. return {
  251. extra: {
  252. area: {
  253. type: "curve",
  254. opacity: 0.35,
  255. addLine: true,
  256. width: 2,
  257. gradient: true,
  258. activeType: "hollow",
  259. },
  260. },
  261. };
  262. },
  263. //各监测项表格
  264. botList() {
  265. return [
  266. {
  267. title: "温度传感器监测",
  268. head: this.headList2,
  269. list: this.tableDataWd,
  270. },
  271. {
  272. title: "烟雾传感器监测",
  273. head: this.headList,
  274. list: this.tableDataYw,
  275. },
  276. {
  277. title: "一氧化碳传感器监测",
  278. head: this.headList,
  279. list: this.tableDataCo,
  280. },
  281. {
  282. title: "自动喷淋灭火装置监测",
  283. head: this.headList1,
  284. list: this.tableDataPl,
  285. },
  286. ];
  287. },
  288. },
  289. };
  290. </script>
  291. <style lang="scss" scoped>
  292. .fire-detail-out {
  293. width: 100%;
  294. height: 100%;
  295. // box-sizing: border-box;
  296. // overflow-y: auto;
  297. .top-area {
  298. width: 100%;
  299. padding: 10px;
  300. background-color: #f9f9fb;
  301. border: 1px solid #fff;
  302. margin-bottom: 12px;
  303. box-sizing: border-box;
  304. .top-card {
  305. width: 100%;
  306. display: flex;
  307. justify-content: space-between;
  308. align-items: flex-start;
  309. flex-wrap: wrap;
  310. .card-box {
  311. width: calc(50% - 5px);
  312. height: 62px;
  313. border-radius: 10px;
  314. background: linear-gradient(
  315. to bottom,
  316. rgba(175, 195, 252, 0.6) 0%,
  317. rgba(175, 195, 252, 0.1) 100%
  318. );
  319. &:nth-child(1),
  320. &:nth-child(2) {
  321. margin-bottom: 10px;
  322. }
  323. .box-item {
  324. width: 100%;
  325. height: 100%;
  326. padding: 0px 10px;
  327. box-sizing: border-box;
  328. display: flex;
  329. align-items: center;
  330. .item-icon {
  331. position: relative;
  332. flex-shrink: 0;
  333. width: 34px;
  334. height: 34px;
  335. border-radius: 8px;
  336. display: flex;
  337. align-items: center;
  338. justify-content: center;
  339. background: linear-gradient(135deg, #80b4ff, #347df3);
  340. .icon-img {
  341. position: relative;
  342. width: 18px;
  343. height: 18px;
  344. filter: brightness(0) invert(1);
  345. }
  346. }
  347. .item-info {
  348. flex: 1;
  349. min-width: 0px;
  350. padding-left: 10px;
  351. display: flex;
  352. flex-direction: column;
  353. justify-content: center;
  354. }
  355. }
  356. }
  357. }
  358. .box-item .box-val {
  359. font-size: 15px;
  360. font-weight: bold;
  361. line-height: 18px;
  362. color: #2b7cff;
  363. }
  364. .box-item .box-val.is-normal {
  365. color: #39b54a;
  366. }
  367. .box-item .box-name {
  368. margin-top: 2px;
  369. font-size: 12px;
  370. line-height: 16px;
  371. color: #000000;
  372. }
  373. }
  374. .center-area {
  375. width: 100%;
  376. padding: 10px;
  377. background-color: #f9f9fb;
  378. border: 1px solid #fff;
  379. margin-bottom: 12px;
  380. box-sizing: border-box;
  381. .bot-title {
  382. height: 28px;
  383. margin-bottom: 5px;
  384. display: flex;
  385. justify-content: space-between;
  386. align-items: center;
  387. }
  388. .echartBox {
  389. width: 100%;
  390. height: 220px;
  391. }
  392. }
  393. .bot-area {
  394. width: 100%;
  395. padding: 10px;
  396. background-color: #f9f9fb;
  397. border: 1px solid #fff;
  398. border-radius: 10px;
  399. box-shadow: 0 2px 8px rgba(43, 124, 255, 0.08);
  400. margin-bottom: 10px;
  401. box-sizing: border-box;
  402. .bot-title {
  403. height: 28px;
  404. margin-bottom: 5px;
  405. display: flex;
  406. align-items: center;
  407. font-size: 14px;
  408. font-weight: 600;
  409. color: #333;
  410. }
  411. .bot-content {
  412. width: 100%;
  413. height: 240px;
  414. .h-table {
  415. display: flex;
  416. flex-direction: column;
  417. width: 100%;
  418. height: 100%;
  419. box-sizing: border-box;
  420. border: 1px solid #bcd7fb;
  421. border-radius: 4px;
  422. overflow: hidden;
  423. .table-head {
  424. flex-shrink: 0;
  425. display: flex;
  426. align-items: center;
  427. height: 38px;
  428. border-bottom: 1px solid #bcd7fb;
  429. .head-item {
  430. height: 100%;
  431. display: flex;
  432. justify-content: center;
  433. align-items: center;
  434. box-sizing: border-box;
  435. font-size: 12px;
  436. font-weight: 600;
  437. color: #1a6dff;
  438. text-align: center;
  439. border-right: 1px solid #bcd7fb;
  440. &:last-child {
  441. border-right: none;
  442. }
  443. }
  444. }
  445. .table-content {
  446. flex: 1;
  447. min-height: 0;
  448. overflow-y: auto;
  449. .content-tr {
  450. display: flex;
  451. align-items: stretch;
  452. min-height: 44px;
  453. box-sizing: border-box;
  454. border-bottom: 1px solid #cfe1fa;
  455. &:last-child {
  456. border-bottom: none;
  457. }
  458. &:nth-child(odd) {
  459. background-color: #f5f9ff;
  460. }
  461. &:nth-child(even) {
  462. background-color: #e9f3ff;
  463. }
  464. .content-td {
  465. display: flex;
  466. justify-content: center;
  467. align-items: center;
  468. box-sizing: border-box;
  469. padding: 6px 4px;
  470. font-size: 12px;
  471. color: #333;
  472. text-align: center;
  473. word-break: break-all;
  474. border-right: 1px solid #cfe1fa;
  475. &:last-child {
  476. border-right: none;
  477. }
  478. }
  479. }
  480. }
  481. }
  482. }
  483. }
  484. }
  485. </style>