nitrogenHomeNew.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. <template>
  2. <div
  3. id="nitrogenCss3D"
  4. class="threejs-Object-CSS"
  5. style="width: 100%; height: 100%; position: absolute; pointer-events: none; overflow: hidden; z-index: 2; top: 0px; left: 0px"
  6. >
  7. <a-spin :spinning="loading" />
  8. <template>
  9. <div
  10. v-for="card in floatCards"
  11. :key="card.id"
  12. :id="card.domId"
  13. class="modal-monitor model-float-card"
  14. :style="{ visibility: moduleTypeName === 'ZDJ' ? 'visible' : 'hidden' }"
  15. >
  16. <fourBorderBg>
  17. <div class="title">{{ card.title }}</div>
  18. <div class="monitor-item" v-for="param in card.params" :key="param.code">
  19. <span class="monitor-title">{{ param.unit ? `${param.label}(${param.unit})` : param.label }}:</span>
  20. <span class="monitor-val"
  21. ><span class="val">{{ param.value }}</span></span
  22. >
  23. </div>
  24. </fourBorderBg>
  25. </div>
  26. </template>
  27. </div>
  28. <div id="nitrogen3D" style="width: 100%; height: 100%; position: absolute; overflow: hidden"></div>
  29. <div class="nitrogen-home">
  30. <div class="nitrogen-container">
  31. <div class="top-box">
  32. <!-- 左边监测数据 -->
  33. <div class="lr-box left-box" :class="{ 'left-box-kyj': moduleTypeName === 'KYJ', 'left-box-zdj': moduleTypeName === 'ZDJ' }">
  34. <div class="item item-l" v-for="card in unitCards" :key="card.id">
  35. <ventBox1>
  36. <template #title>
  37. <div>{{ card.title }}</div>
  38. </template>
  39. <template #container>
  40. <div class="monitor-box">
  41. <div class="parameter-title group-parameter-title"
  42. ><SvgIcon class="icon" size="38" name="device-group-paramer" /><span>机组参数</span></div
  43. >
  44. <div class="parameter-scroll">
  45. <div class="state-item" v-for="(row, index) in card.rows" :key="index">
  46. <div class="item-col" v-for="param in row" :key="param.code">
  47. <span class="state-title">{{ param.unit ? `${param.label}(${param.unit})` : param.label }} :</span>
  48. <span class="state-val">{{ param.value }}</span>
  49. </div>
  50. </div>
  51. </div>
  52. </div>
  53. </template>
  54. </ventBox1>
  55. </div>
  56. </div>
  57. <!-- 右边监测曲线:空压机房不展示 -->
  58. <div class="lr-box right-box" v-if="moduleTypeName !== 'KYJ'">
  59. <ventBox1>
  60. <template #title>
  61. <div>设备实时监测曲线</div>
  62. </template>
  63. <template #container>
  64. <BarAndLineCustom xAxisPropType="readTime" :chartData="flowChartData" height="240px" :propTypeArr="['flowRate']" :option="flowChartOption" />
  65. </template>
  66. </ventBox1>
  67. </div>
  68. </div>
  69. </div>
  70. </div>
  71. </template>
  72. <script lang="ts" setup name="nitrogenHome">
  73. import { onMounted, onUnmounted, ref, watch, nextTick } from 'vue';
  74. import ventBox1 from '/@/components/vent/ventBox1.vue';
  75. import fourBorderBg from '../../../comment/components/fourBorderBg.vue';
  76. import { mountedThree, destroy, attachMonitorCards } from '../nitrogen.threejs';
  77. import { list } from '../nitrogen.api';
  78. import { SvgIcon } from '/@/components/Icon';
  79. import BarAndLineCustom from '/@/components/chart/BarAndLineCustom.vue';
  80. import { zhudanOption } from '../nitrogen.data';
  81. interface PointField {
  82. code: string;
  83. label: string;
  84. unit: string;
  85. }
  86. interface UnitParam {
  87. code: string;
  88. label: string;
  89. value: string;
  90. unit: string;
  91. }
  92. interface UnitCard {
  93. id: string;
  94. title: string;
  95. rows: UnitParam[][];
  96. }
  97. interface FlowPoint {
  98. readTime: string;
  99. flowRate: number;
  100. }
  101. interface FloatCard {
  102. id: string;
  103. domId: string;
  104. title: string;
  105. params: UnitParam[];
  106. }
  107. const props = defineProps({
  108. deviceId: {
  109. type: String,
  110. require: true,
  111. },
  112. modalType: {
  113. type: String,
  114. require: true,
  115. },
  116. moduleTypeName: {
  117. type: String,
  118. default: '',
  119. },
  120. });
  121. const loading = ref(true);
  122. const nitrogenFields: PointField[] = [
  123. { code: 'dqyl', label: '氮气压力', unit: 'MPa' },
  124. { code: 'ysj_cw', label: '压缩机出温', unit: '℃' },
  125. { code: 'ysj_jw', label: '压缩机进温', unit: '℃' },
  126. { code: 'ld_wd', label: '露点温度', unit: '℃' },
  127. { code: 'jrqx_wd', label: '加热器温度', unit: '℃' },
  128. { code: 'lgj_ckyl', label: '冷干机出口压力', unit: 'MPa' },
  129. { code: 'lgj_rkyl', label: '冷干机入口压力', unit: 'MPa' },
  130. { code: 'ljyxsj', label: '累计运行时间', unit: 'h' },
  131. { code: 'bcljsj', label: '本次累计时间', unit: 'h' },
  132. { code: 'dqwd', label: '氮气温度', unit: '℃' },
  133. { code: 'ljll', label: '累计流量', unit: 'm³' },
  134. { code: 'bcljll', label: '本次累计流量', unit: 'm³' },
  135. { code: 'hyl', label: '含氧量', unit: '%' },
  136. { code: 'ssll', label: '瞬时流量', unit: 'm³/h' },
  137. ];
  138. const compressorFields: PointField[] = [
  139. { code: 'pqwd', label: '排气温度', unit: '℃' },
  140. { code: 'pqyl', label: '排气压力', unit: 'MPa' },
  141. ];
  142. const modelFloatFields: PointField[] = [
  143. { code: 'ljyxsj', label: '累计运行时间', unit: 'h' },
  144. { code: 'bcljsj', label: '本次累计时间', unit: 'h' },
  145. { code: 'dqwd', label: '氮气温度', unit: '℃' },
  146. { code: 'ljll', label: '累计流量', unit: 'm³' },
  147. { code: 'ssll', label: '瞬时流量', unit: 'm³/h' },
  148. ];
  149. const modelFloatCodes = modelFloatFields.map((field) => field.code);
  150. const unitCards = ref<UnitCard[]>([]);
  151. const floatCards = ref<FloatCard[]>(buildFloatCards({}));
  152. const flowChartData = ref<FlowPoint[]>([]);
  153. const flowPointLimit = 12;
  154. const flowYAxis = { ...zhudanOption.yAxis[0], min: 0 };
  155. delete flowYAxis.max;
  156. const flowChartOption = {
  157. ...zhudanOption,
  158. yAxis: [flowYAxis],
  159. };
  160. // https获取监测数据
  161. let timer: null | NodeJS.Timeout = null;
  162. async function getMonitor(flag?) {
  163. if (Object.prototype.toString.call(timer) === '[object Null]') {
  164. timer = await setTimeout(
  165. async () => {
  166. if (props.deviceId) {
  167. await getDataSource();
  168. }
  169. if (timer) {
  170. timer = null;
  171. }
  172. await getMonitor();
  173. },
  174. flag ? 0 : 1000
  175. );
  176. }
  177. }
  178. async function getReadData() {
  179. const res = await list({ devicetype: 'sys', systemID: props.deviceId, type: 'all' });
  180. const msgTxt = res?.msgTxt || [];
  181. const nitrogenBlock = msgTxt.find((item) => item.type === 'nitrogen_auto');
  182. return nitrogenBlock?.datalist?.[0]?.readData || {};
  183. }
  184. function toRows(params: UnitParam[]) {
  185. const rows: UnitParam[][] = [];
  186. for (let index = 0; index < params.length; index += 2) {
  187. rows.push(params.slice(index, index + 2));
  188. }
  189. return rows;
  190. }
  191. function readValue(readData: Record<string, string>, code: string) {
  192. const value = readData[code];
  193. if (value === undefined || value === null || value === '') return '-';
  194. return String(value);
  195. }
  196. function buildFloatCards(readData: Record<string, string>) {
  197. return [1, 2, 3].map((unitNo) => ({
  198. id: `float-jz${unitNo}`,
  199. domId: `nitrogenMonitor${unitNo}`,
  200. title: `${unitNo}号制氮机`,
  201. params: modelFloatFields.map((field) => ({
  202. code: `jz${unitNo}_${field.code}`,
  203. label: field.label,
  204. value: readValue(readData, `jz${unitNo}_${field.code}`),
  205. unit: field.unit,
  206. })),
  207. }));
  208. }
  209. function buildNitrogenCards(readData: Record<string, string>) {
  210. const unitNos = new Set<number>();
  211. Object.keys(readData).forEach((key) => {
  212. const matched = key.match(/^jz(\d+)_/);
  213. if (matched) unitNos.add(Number(matched[1]));
  214. });
  215. return Array.from(unitNos)
  216. .sort((prev, next) => prev - next)
  217. .map((unitNo) => {
  218. const params = nitrogenFields
  219. .filter((field) => !modelFloatCodes.includes(field.code))
  220. .map((field) => ({
  221. code: `jz${unitNo}_${field.code}`,
  222. label: field.label,
  223. value: readValue(readData, `jz${unitNo}_${field.code}`),
  224. unit: field.unit,
  225. }));
  226. return {
  227. id: `jz${unitNo}`,
  228. title: `${unitNo}号制氮机`,
  229. rows: toRows(params),
  230. };
  231. });
  232. }
  233. function buildCompressorCards(readData: Record<string, string>) {
  234. const unitNos = new Set<number>();
  235. Object.keys(readData).forEach((key) => {
  236. const matched = key.match(/^kyj_(\d+)h_/);
  237. if (matched) unitNos.add(Number(matched[1]));
  238. });
  239. return Array.from(unitNos)
  240. .sort((prev, next) => prev - next)
  241. .map((unitNo) => {
  242. const params = compressorFields.map((field) => ({
  243. code: `kyj_${unitNo}h_${field.code}`,
  244. label: field.label,
  245. value: readValue(readData, `kyj_${unitNo}h_${field.code}`),
  246. unit: field.unit,
  247. }));
  248. return {
  249. id: `kyj${unitNo}`,
  250. title: `${unitNo}号空压机`,
  251. rows: toRows(params),
  252. };
  253. });
  254. }
  255. function formatClock(date: Date) {
  256. const pad = (num: number) => String(num).padStart(2, '0');
  257. return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
  258. }
  259. function appendFlowPoint(readData: Record<string, string>) {
  260. const flowList = [1, 2, 3]
  261. .map((unitNo) => Number(readData[`jz${unitNo}_ssll`]))
  262. .filter((value) => !Number.isNaN(value));
  263. if (!flowList.length) return;
  264. const flowRate = Number(flowList.reduce((sum, value) => sum + value, 0).toFixed(2));
  265. const readTime = formatClock(new Date());
  266. const points = flowChartData.value.slice();
  267. const lastPoint = points[points.length - 1];
  268. if (lastPoint && lastPoint.readTime === readTime) {
  269. points[points.length - 1] = { readTime, flowRate };
  270. flowChartData.value = points;
  271. return;
  272. }
  273. flowChartData.value = points.concat({ readTime, flowRate }).slice(-flowPointLimit);
  274. }
  275. async function buildUnitCards() {
  276. const readData = await getReadData();
  277. floatCards.value = buildFloatCards(readData);
  278. if (props.moduleTypeName === 'KYJ') {
  279. unitCards.value = buildCompressorCards(readData);
  280. } else if (props.moduleTypeName === 'ZDJ') {
  281. unitCards.value = buildNitrogenCards(readData);
  282. } else {
  283. unitCards.value = [];
  284. }
  285. return readData;
  286. }
  287. async function getDataSource() {
  288. const readData = await buildUnitCards();
  289. if (props.moduleTypeName !== 'KYJ') {
  290. appendFlowPoint(readData);
  291. }
  292. }
  293. watch(
  294. () => props.moduleTypeName,
  295. () => {
  296. buildUnitCards();
  297. },
  298. { immediate: true }
  299. );
  300. onMounted(async () => {
  301. await getMonitor(true);
  302. await mountedThree('nitrogen_bjhz').then(() => {
  303. loading.value = false;
  304. });
  305. await nextTick();
  306. attachMonitorCards();
  307. });
  308. onUnmounted(() => {
  309. destroy();
  310. if (timer) {
  311. clearTimeout(timer);
  312. timer = undefined;
  313. }
  314. });
  315. </script>
  316. <style lang="less" scoped>
  317. #nitrogenCss3D {
  318. .model-float-card {
  319. :deep(.win) {
  320. margin: 0;
  321. }
  322. }
  323. .modal-monitor {
  324. width: 200px;
  325. position: absolute;
  326. left: 0px;
  327. top: 0px;
  328. }
  329. &:deep(.win) {
  330. margin: 0 !important;
  331. background: #00000044;
  332. }
  333. }
  334. .nitrogen-home {
  335. width: 100%;
  336. height: 100%;
  337. position: fixed;
  338. z-index: 9999;
  339. display: flex;
  340. flex-direction: column;
  341. justify-content: center;
  342. align-items: center;
  343. pointer-events: none;
  344. top: 60px;
  345. .nitrogen-container {
  346. width: 100%;
  347. height: calc(100% - 100px);
  348. display: flex;
  349. justify-content: space-between;
  350. margin-bottom: 100px;
  351. .top-box {
  352. width: 100%;
  353. padding: 10px;
  354. overflow: hidden;
  355. display: flex;
  356. justify-content: space-between;
  357. .lr-box {
  358. height: fit-content;
  359. display: flex;
  360. flex-direction: column;
  361. position: relative;
  362. overflow: hidden;
  363. z-index: 9999;
  364. pointer-events: auto;
  365. }
  366. .item {
  367. width: 335px;
  368. height: auto;
  369. position: relative;
  370. border-radius: 5px;
  371. margin-top: 10px;
  372. margin-bottom: 0px;
  373. pointer-events: auto;
  374. color: #fff;
  375. overflow: hidden;
  376. &:first-child {
  377. margin-top: 0px;
  378. }
  379. .state-item {
  380. display: grid;
  381. grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  382. column-gap: 8px;
  383. padding: 3px 2px;
  384. .item-col {
  385. width: auto;
  386. min-width: 0;
  387. min-height: 28px;
  388. display: flex;
  389. justify-content: space-between;
  390. align-items: center;
  391. gap: 8px;
  392. margin-right: 0;
  393. padding: 3px 6px;
  394. background-image: linear-gradient(to right, #39a3ff14, #39a3ff05);
  395. .state-title {
  396. color: #ffffffcc;
  397. flex: 1;
  398. min-width: 0;
  399. font-size: 12px;
  400. line-height: 1.35;
  401. }
  402. .state-val {
  403. flex: none;
  404. color: #00eefffe;
  405. text-align: right;
  406. font-size: 13px;
  407. line-height: 1.35;
  408. white-space: nowrap;
  409. }
  410. }
  411. }
  412. }
  413. .item-l {
  414. width: 100%;
  415. .monitor-box {
  416. width: 100%;
  417. .parameter-title {
  418. position: relative;
  419. width: 100%;
  420. height: 14px;
  421. margin-top: 10px;
  422. .icon,
  423. span {
  424. position: absolute;
  425. top: -10px;
  426. }
  427. }
  428. .group-parameter-title {
  429. background-image: linear-gradient(to right, #39a3ff50, #39a3ff00);
  430. .icon {
  431. left: -12px;
  432. top: -17px;
  433. }
  434. span {
  435. left: 18px;
  436. }
  437. .item-col {
  438. background-image: linear-gradient(to right, #39a3ff00, #39a3ff10);
  439. }
  440. }
  441. }
  442. }
  443. .right-box {
  444. width: 330px;
  445. margin-top: 0;
  446. }
  447. .left-box {
  448. width: 460px;
  449. margin-top: 80px;
  450. max-height: calc(100vh - 250px);
  451. overflow-x: hidden;
  452. overflow-y: auto;
  453. padding-right: 6px;
  454. padding-bottom: 12px;
  455. .item {
  456. overflow: visible;
  457. }
  458. :deep(.vent-box1-bg) {
  459. overflow: visible;
  460. }
  461. :deep(.box1-top),
  462. :deep(.box1-bottom) {
  463. background-size: 100% 100%;
  464. background-repeat: no-repeat;
  465. }
  466. :deep(.box1-bottom) {
  467. position: relative;
  468. bottom: auto;
  469. flex-shrink: 0;
  470. }
  471. :deep(.box-container) {
  472. padding-bottom: 10px;
  473. }
  474. }
  475. .left-box.left-box-kyj,
  476. .left-box.left-box-zdj {
  477. gap: 16px;
  478. .item {
  479. margin-top: 0;
  480. margin-bottom: 0;
  481. }
  482. }
  483. .left-box.left-box-kyj {
  484. margin-top: 0;
  485. max-height: 816px;
  486. padding-bottom: 8px;
  487. }
  488. .left-box.left-box-zdj {
  489. gap: 8px;
  490. margin-top: 0;
  491. max-height: none;
  492. overflow: visible;
  493. padding-right: 0;
  494. padding-bottom: 8px;
  495. :deep(.box-container) {
  496. padding: 10px 10px 0;
  497. }
  498. .parameter-title {
  499. height: 16px;
  500. margin-top: 8px;
  501. }
  502. .parameter-scroll {
  503. max-height: none;
  504. overflow: visible;
  505. }
  506. .state-item {
  507. padding: 3px 0;
  508. .item-col {
  509. min-height: 26px;
  510. padding: 2px 6px;
  511. }
  512. }
  513. }
  514. }
  515. &:deep(.win) {
  516. width: 100%;
  517. margin: 0 !important;
  518. }
  519. }
  520. }
  521. &:deep(.main) {
  522. .title {
  523. height: 34px;
  524. text-align: center;
  525. font-weight: 600;
  526. color: #7af5ff;
  527. // background-image: url('../../../assets/img/yfj/light.png');
  528. background-repeat: no-repeat;
  529. background-position-x: center;
  530. background-position-y: 100%;
  531. background-size: 80%;
  532. font-size: 16px;
  533. }
  534. .monitor-item {
  535. width: 200px;
  536. display: flex;
  537. flex-direction: row;
  538. width: auto;
  539. margin-bottom: 3px;
  540. .monitor-val {
  541. color: #ffb700;
  542. display: flex;
  543. width: auto;
  544. .val {
  545. width: 80px;
  546. font-size: 14px;
  547. }
  548. }
  549. }
  550. .monitor-title {
  551. width: 132px;
  552. color: #7af5ff;
  553. font-weight: 400;
  554. font-size: 14px;
  555. }
  556. }
  557. </style>