balancePressHomeBD.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. <template>
  2. <a-spin tip="Loading..." :spinning="loading">
  3. <div class="monitor-container">
  4. <div class="lr left-box">
  5. <Transition enter-active-class="animate__animated animate__fadeInLeft" leave-active-class="animate__animated animate__fadeOutLeft">
  6. <ventBox1 v-if="showModules">
  7. <template #title>
  8. <div>均压与低氧参数监测与设置</div>
  9. </template>
  10. <template #container>
  11. <div class="vent-flex-row-between auto-control mt-10px mb-10px">
  12. <div class="title">自动调节:</div>
  13. <a-radio-group :value="avePressSetting.isAuto" name="radioGroup" @change="changeAvePressState($event, 'isAuto')">
  14. <a-radio :value="false">关闭</a-radio>
  15. <a-radio :value="true">开启</a-radio>
  16. </a-radio-group>
  17. </div>
  18. <div class="vent-flex-row-between auto-control mt-10px mb-10px">
  19. <div class="title">调节类型:</div>
  20. <a-radio-group :value="avePressSetting.controlType" name="radioGroup" @change="changeAvePressState($event, 'controlType')">
  21. <a-radio value="o2">氧气</a-radio>
  22. <a-radio value="pressure">压差</a-radio>
  23. </a-radio-group>
  24. </div>
  25. <div>
  26. <!-- <div class="divider-line">开始条件</div>
  27. <div v-for="(item, index) in settingParam1" class="input-item" :key="index">
  28. <div class="title">{{ item.title }}:</div>
  29. <a-input-number class="input-value" v-model:value="formData[item.code]" placeholder="" />
  30. <div class="unit">{{ item.unit }}</div>
  31. </div> -->
  32. <div class="divider-line"></div>
  33. <div v-for="(item, index) in settingParam4" class="input-item" :key="index">
  34. <div class="title">{{ item.title }}:</div>
  35. <a-input-number class="input-value" v-model:value="avePressSetting[item.code]" placeholder="" :disabled="settingFormDisabled" />
  36. <div class="unit">&nbsp;{{ item.unit }}</div>
  37. </div>
  38. <!-- <div class="divider-line">结束时间</div>
  39. <div v-for="(item, index) in settingParam3" class="input-item" :key="index">
  40. <div class="title">{{ item.title }}:</div>
  41. <a-input-number class="input-value" v-model:value="formData[item.code]" placeholder="" />
  42. <div class="unit">{{ item.unit }}</div>
  43. </div> -->
  44. </div>
  45. <div class="btn-box flex" style="text-align: center">
  46. <div class="btn btn1 flex-1" @click="editSettingForm">{{ settingFormDisabled ? '编辑' : '取消' }}</div>
  47. <div class="btn btn1 flex-1" @click="submitSettingForm">提交</div>
  48. </div>
  49. </template>
  50. </ventBox1>
  51. </Transition>
  52. <Transition enter-active-class="animate__animated animate__fadeInLeft" leave-active-class="animate__animated animate__fadeOutLeft">
  53. <ventBox1 v-if="showModules" class="mt-10px">
  54. <template #title>
  55. <div>均压工作面联动控制</div>
  56. </template>
  57. <template #container>
  58. <div class="vent-flex-row-between auto-control mt-10px mb-10px">
  59. <div class="title">风门与风门自动调节:</div>
  60. <a-radio-group :value="gateLinkage.isAuto" name="radioGroup" @change="changeIsAuto($event, gateLinkage.id)">
  61. <a-radio :value="false">关闭</a-radio>
  62. <a-radio :value="true">开启</a-radio>
  63. </a-radio-group>
  64. <div class="btn btn1" @click="() => openModal(false, { id: gateLinkage.id })">密码修改</div>
  65. </div>
  66. <div class="vent-flex-row-between auto-control mt-10px mb-10px">
  67. <div class="title">风机与风门自动调节:</div>
  68. <a-radio-group :value="avePressLinkage.isAuto" name="radioGroup" @change="changeIsAuto($event, avePressLinkage.id)">
  69. <a-radio :value="false">关闭</a-radio>
  70. <a-radio :value="true">开启</a-radio>
  71. </a-radio-group>
  72. <div class="btn btn1" @click="() => openModal(false, { id: avePressLinkage.id })">密码修改</div>
  73. </div>
  74. <!-- <div class="btn-box" style="text-align: center">
  75. </div> -->
  76. </template>
  77. </ventBox1>
  78. </Transition>
  79. </div>
  80. <ModuleCommon
  81. v-for="(cfg, index) in configs"
  82. :key="`svvmbcb${index}`"
  83. :show-style="cfg.showStyle"
  84. :module-data="cfg.moduleData"
  85. :module-name="cfg.moduleName"
  86. :device-type="cfg.deviceType"
  87. :data="selectData"
  88. :visible="showModules"
  89. />
  90. </div>
  91. <PasswordModal z-index="2000" :modal-is-show="modalVisible" modal-title="提交" @handle-ok="handleResolve" @handle-cancel="handleReject" />
  92. <UpdatePassword @register="updatePwdRegister" @submit="handleChangePassword" />
  93. <!-- <BasicModal title="风门状态监测" :mask="false" :bodyStyle="{ height: '50px' }" style="top: 20px" :show-ok-btn="false" @register="warnRegister2">
  94. {{ warnModalText2 }}
  95. </BasicModal>
  96. <BasicModal
  97. title="压差状态监测"
  98. :mask="false"
  99. :bodyStyle="{ height: '50px' }"
  100. centered
  101. ok-text="下发联动控制指令"
  102. @ok="autoControl"
  103. @register="warnRegister1"
  104. >
  105. {{ warnModalText1 }}
  106. </BasicModal>
  107. <BasicModal title="局扇状态监测" :mask="false" :bodyStyle="{ height: '50px' }" style="top: 420px" :show-ok-btn="false" @register="warnRegister3">
  108. {{ warnModalText3 }}
  109. </BasicModal> -->
  110. <div class="switch-button icon-goto right-10px top-70px" :class="{ 'right-390px': showModules }" @click="showModules = !showModules"></div>
  111. <!-- <div v-else class="switch-button icon-goto right-10px top-70px" @click="showModules = true"></div> -->
  112. </a-spin>
  113. </template>
  114. <script setup lang="ts">
  115. import { ref, onMounted, onUnmounted, defineProps, h } from 'vue';
  116. import { mountedThree, destroy, setModelType, updateText, play } from '../balancePress.threejs';
  117. import { list } from '../balancePress.api';
  118. import ModuleCommon from '../../../home/configurable/components/ModuleCommon.vue';
  119. import { useInitConfigs } from '../../../home/configurable/hooks/useInit';
  120. import { useGlobSetting } from '/@/hooks/setting';
  121. import { settingParam4 } from '../balancePress.data';
  122. import { Modal } from 'ant-design-vue';
  123. import ventBox1 from '/@/components/vent/ventBox1.vue';
  124. import PasswordModal from '../../comment/components/PasswordModal.vue';
  125. import UpdatePassword from '../../comment/components/UpdatePassword.vue';
  126. import { useModal } from '/@/components/Modal';
  127. import { connectWebSocket, onWebSocket } from '/@/hooks/web/useWebSocket';
  128. import { getToken } from '/@/utils/auth';
  129. import { useUserStore } from '/@/store/modules/user';
  130. import { usePressControl } from '../hooks/useControl';
  131. import dayjs from 'dayjs';
  132. // import { Config } from '../../../deviceManager/configurationTable/types';
  133. const props = defineProps({
  134. deviceId: {
  135. type: String,
  136. require: true,
  137. },
  138. });
  139. const { sysOrgCode } = useGlobSetting();
  140. const loading = ref(false);
  141. const showModules = ref(true);
  142. // 监测数据
  143. const selectData = ref();
  144. // https获取监测数据
  145. let timer: any = null;
  146. function getMonitor(flag?) {
  147. if (Object.prototype.toString.call(timer) === '[object Null]') {
  148. timer = setTimeout(
  149. async () => {
  150. if (props.deviceId) {
  151. const data = await getDataSource(props.deviceId);
  152. // Object.assign(selectData, data);
  153. updateText(selectData);
  154. selectData.value = data;
  155. }
  156. if (timer) {
  157. timer = null;
  158. }
  159. await getMonitor();
  160. },
  161. flag ? 0 : 1000
  162. );
  163. }
  164. }
  165. async function getDataSource(systemID) {
  166. const res = await list({ devicetype: 'sys', systemID });
  167. const result = Array.from(res.msgTxt).reduce(
  168. (obj: any, e: any) => {
  169. obj[e.type] = e;
  170. // if (true) {
  171. if (sysOrgCode === 'sdmtjtswmk') {
  172. if (e.type.startsWith('fanlocal')) {
  173. obj.fanlocal.datalist.push(...e.datalist);
  174. }
  175. if (e.type.startsWith('safetymonitor')) {
  176. e.datalist.forEach((ele) => {
  177. if (ele.strinstallpos.includes('风门')) {
  178. obj.gate.datalist.push(ele);
  179. } else if (ele.strinstallpos.includes('风窗')) {
  180. obj.window.datalist.push(ele);
  181. } else if (ele.strinstallpos.includes('工作面')) {
  182. obj.work_surface.datalist.push(ele);
  183. } else {
  184. obj.others.datalist.push(ele);
  185. }
  186. });
  187. }
  188. }
  189. return obj;
  190. },
  191. {
  192. /** 用于归类fanlocal */
  193. fanlocal: { datalist: [] },
  194. /** 用于归类gate */
  195. gate: { datalist: [] },
  196. /** 用于归类window */
  197. window: { datalist: [] },
  198. /** 用于归类work_surface */
  199. work_surface: { datalist: [] },
  200. others: { datalist: [] },
  201. }
  202. );
  203. return result;
  204. }
  205. const {
  206. avePressSetting,
  207. avePressLinkage,
  208. gateLinkage,
  209. formData,
  210. getAvePress,
  211. changePassword,
  212. linkageControl,
  213. settingControl,
  214. autoControl,
  215. cancelControl,
  216. } = usePressControl();
  217. const modalVisible = ref(false);
  218. const { configs, fetchConfigs } = useInitConfigs();
  219. const [updatePwdRegister, { openModal, closeModal, setModalProps }] = useModal();
  220. function handleChangePassword(values) {
  221. setModalProps({ confirmLoading: true });
  222. changePassword(values).finally(() => {
  223. setModalProps({ confirmLoading: false });
  224. closeModal();
  225. });
  226. }
  227. function changeIsAuto({ target }, id) {
  228. formData.value.isAuto = target.value;
  229. modalVisible.value = true;
  230. resolver = (password) => {
  231. linkageControl(
  232. { password, id },
  233. {
  234. isAuto: formData.value.isAuto,
  235. }
  236. ).finally(() => {
  237. modalVisible.value = false;
  238. });
  239. };
  240. }
  241. function changeAvePressState({ target }, key) {
  242. formData.value.temp = target.value;
  243. modalVisible.value = true;
  244. resolver = (password) => {
  245. settingControl(
  246. { password, id: avePressSetting.value.id },
  247. {
  248. [key]: formData.value.temp,
  249. }
  250. ).finally(() => {
  251. modalVisible.value = false;
  252. });
  253. };
  254. }
  255. // function submitLinkageForm(password) {}
  256. function submitSettingForm() {
  257. modalVisible.value = true;
  258. resolver = (password) => {
  259. settingControl({ password, id: avePressSetting.value.id }, avePressSetting.value).finally(() => {
  260. modalVisible.value = false;
  261. settingFormDisabled.value = true;
  262. });
  263. };
  264. }
  265. let resolver: any = null;
  266. let rejecter: any = null;
  267. function handleResolve(password) {
  268. if (resolver) {
  269. resolver(password);
  270. } else {
  271. modalVisible.value = false;
  272. }
  273. resolver = null;
  274. rejecter = null;
  275. }
  276. function handleReject() {
  277. if (rejecter) {
  278. rejecter();
  279. } else {
  280. modalVisible.value = false;
  281. }
  282. resolver = null;
  283. rejecter = null;
  284. }
  285. // const [warnRegister1, warnModal1] = useModal();
  286. // const [warnRegister2, warnModal2] = useModal();
  287. // const [warnRegister3, warnModal3] = useModal();
  288. let warnModal1: { destroy: () => void } | null = null;
  289. let warnModal2: { destroy: () => void } | null = null;
  290. let warnModal3: { destroy: () => void } | null = null;
  291. // const warnModalText1 = ref('');
  292. // const warnModalText2 = ref('');
  293. // const warnModalText3 = ref('');
  294. // 初始化 WebSocket
  295. function initWebSocket() {
  296. const token = getToken();
  297. const userStore = useUserStore();
  298. const glob = useGlobSetting();
  299. // WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
  300. const url = `${glob.wsUrl?.replace('https://', 'wss://').replace('http://', 'ws://')}/websocket/${userStore.getUserInfo.id}?token=${token}`;
  301. connectWebSocket(url);
  302. onWebSocket((data: any) => {
  303. if (data.cmd !== 'topic' || data.topic !== 'warn') return;
  304. if (!data.msgTxt) return;
  305. const { info = '', type = '', avgPressureLogId, date } = JSON.parse(data.msgTxt);
  306. const datestr = dayjs(date).format('YYYY-MM-DD HH:mm:ss');
  307. switch (type) {
  308. case 'o2':
  309. // 如果已经存在报警模态框,则不需要处理
  310. if (warnModal1) break;
  311. warnModal1 = Modal.confirm({
  312. title: data.msgTitle,
  313. content: h('div', { style: { color: '#fff' } }, [h('p', datestr), h('p', info)]),
  314. centered: true,
  315. okText: '下发调节指令',
  316. mask: true,
  317. class: 'balancePress',
  318. onOk() {
  319. // 点击确定按钮后,执行调节指令。调节指令需要确认密码,所以需要先弹出密码框
  320. return new Promise((resolve, reject) => {
  321. modalVisible.value = true;
  322. // 弹出密码框后,输入密码并验证成功则关闭密码弹窗和报警弹窗,失败则关闭密码弹窗但不关闭报警弹窗
  323. resolver = (password) => {
  324. autoControl({ password, id: avePressSetting.value.id }, { avgPressLogId: avgPressureLogId })
  325. .then(() => {
  326. modalVisible.value = false;
  327. resolve(true);
  328. warnModal1?.destroy();
  329. warnModal1 = null;
  330. })
  331. .catch(() => {
  332. modalVisible.value = false;
  333. reject();
  334. });
  335. };
  336. // 弹出密码框取消操作则关闭密码弹窗但不关闭报警弹窗
  337. rejecter = () => {
  338. modalVisible.value = false;
  339. reject();
  340. };
  341. });
  342. },
  343. onCancel() {
  344. return cancelControl({}, { avgPressLogId: avgPressureLogId }).finally(() => {
  345. warnModal1?.destroy();
  346. warnModal1 = null;
  347. });
  348. },
  349. });
  350. // warnModalText1.value = info;
  351. // warnModal1.openModal();
  352. break;
  353. case 'pressure':
  354. // warnModalText1.value = info;
  355. // warnModal1.openModal();
  356. if (warnModal1) break;
  357. warnModal1 = Modal.confirm({
  358. title: data.msgTitle,
  359. content: h('div', { style: { color: '#fff' } }, [h('p', datestr), h('p', info)]),
  360. centered: true,
  361. okText: '下发调节指令',
  362. mask: true,
  363. class: 'balancePress',
  364. onOk() {
  365. // 点击确定按钮后,执行调节指令。调节指令需要确认密码,所以需要先弹出密码框
  366. return new Promise((resolve, reject) => {
  367. modalVisible.value = true;
  368. // 弹出密码框后,输入密码并验证成功则关闭密码弹窗和报警弹窗,失败则关闭密码弹窗但不关闭报警弹窗
  369. resolver = (password) => {
  370. autoControl({ password, id: avePressSetting.value.id }, { avgPressLogId: avgPressureLogId })
  371. .then(() => {
  372. modalVisible.value = false;
  373. resolve(true);
  374. warnModal1?.destroy();
  375. warnModal1 = null;
  376. })
  377. .catch(() => {
  378. modalVisible.value = false;
  379. reject();
  380. });
  381. };
  382. // 弹出密码框取消操作则关闭密码弹窗但不关闭报警弹窗
  383. rejecter = () => {
  384. modalVisible.value = false;
  385. reject();
  386. };
  387. });
  388. },
  389. onCancel() {
  390. return cancelControl({}, { avgPressLogId: avgPressureLogId }).finally(() => {
  391. warnModal1?.destroy();
  392. warnModal1 = null;
  393. });
  394. },
  395. });
  396. break;
  397. case 'gate':
  398. if (warnModal2) break;
  399. warnModal2 = Modal.warning({
  400. title: data.msgTitle,
  401. content: info,
  402. mask: true,
  403. class: 'balancePress',
  404. onOk: () => {
  405. warnModal2?.destroy();
  406. warnModal2 = null;
  407. },
  408. });
  409. break;
  410. case 'fansys':
  411. if (warnModal3) break;
  412. warnModal3 = Modal.warning({
  413. title: data.msgTitle,
  414. content: info,
  415. mask: true,
  416. class: 'balancePress',
  417. style: 'top: 700px',
  418. onOk: () => {
  419. warnModal3?.destroy();
  420. warnModal3 = null;
  421. },
  422. });
  423. break;
  424. default:
  425. break;
  426. }
  427. });
  428. }
  429. const settingFormDisabled = ref(true);
  430. function editSettingForm() {
  431. settingFormDisabled.value = !settingFormDisabled.value;
  432. /** 如果取消了编辑模式,那么需要重置表单 */
  433. if (settingFormDisabled.value) {
  434. getAvePress();
  435. }
  436. }
  437. onMounted(() => {
  438. initWebSocket();
  439. // getMonitor()
  440. fetchConfigs('balancePressHome');
  441. getAvePress();
  442. loading.value = true;
  443. mountedThree().then(async () => {
  444. await setModelType('balancePressTun'); //balancePressBase
  445. loading.value = false;
  446. timer = null;
  447. await getMonitor(true);
  448. play('startSmoke', 'top', 30, 'open', 0);
  449. });
  450. // loading.value = false;
  451. // timer = null;
  452. // getMonitor(true);
  453. });
  454. onUnmounted(() => {
  455. destroy();
  456. if (timer) {
  457. clearTimeout(timer);
  458. }
  459. });
  460. </script>
  461. <style lang="less" scoped>
  462. @import '/@/design/vent/modal.less';
  463. @import '../../comment/less/workFace.less';
  464. @ventSpace: zxm;
  465. .monitor-container {
  466. margin-top: 60px;
  467. }
  468. .switch-button {
  469. width: 34px;
  470. height: 34px;
  471. position: fixed;
  472. // right: 5px;
  473. // bottom: 300px;
  474. z-index: 1000;
  475. background-repeat: no-repeat;
  476. background-size: 100% 100%;
  477. pointer-events: auto;
  478. transition: right 1s;
  479. }
  480. .icon-goto {
  481. --image-monitor-goto: url('/@/assets/images/company/monitor-goto.png');
  482. background-image: var(--image-monitor-goto);
  483. }
  484. .divider-line {
  485. border-bottom: 1px solid white;
  486. }
  487. </style>
  488. <style>
  489. /* .balancePress .zxm-modal-confirm-title {
  490. font-size: 20px;
  491. } */
  492. .balancePress .zxm-modal-confirm-content {
  493. font-size: 22px;
  494. }
  495. </style>