index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. <template>
  2. <div class="bg" style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; overflow: hidden;">
  3. <a-spin :spinning="loading" />
  4. <div id="damper3D" v-show="!loading" style="width: 100%; height: 100%;; position: absolute; overflow: hidden;"> </div>
  5. <!-- <div id="damper3DCSS" v-show="!loading" style="width: 100%; height: 100%; top:0; left: 0; position: absolute; overflow: hidden;">
  6. <div>
  7. <div ref="elementContent" class="elementContent">
  8. <p><span class="data-title">压力(Pa):</span>{{selectData.frontRearDP}}</p>
  9. <p><span class="data-title">动力源压力(MPa):</span>{{selectData.sourcePressure}}</p>
  10. <p><span class="data-title">故障诊断:</span>
  11. <i
  12. :class="{'state-icon': true, 'open': selectData.messageBoxStatus, 'close': !selectData.messageBoxStatus}"
  13. ></i>{{selectData.fault}}</p>
  14. </div>
  15. </div>
  16. </div> -->
  17. </div>
  18. <div class="scene-box">
  19. <div class="top-box">
  20. <div class="top-left row">
  21. 井下风门远程集中管理
  22. </div>
  23. <div class="top-center row">
  24. <div class="button-box" :class="{'button-disable': backDoorIsOpen}" @click="playAnimation(1)">打开前门</div>
  25. <div class="button-box" :class="{'button-disable': backDoorIsOpen}" @click="playAnimation(2)">关闭前门</div>
  26. <div class="button-box" :class="{'button-disable': frontDoorIsOpen}" @click="playAnimation(3)">打开后门</div>
  27. <div class="button-box" :class="{'button-disable': frontDoorIsOpen}" @click="playAnimation(4)">关闭后门</div>
  28. <div class="button-box" :class="{'button-disable': frontDoorIsOpen || backDoorIsOpen}" @click="playAnimation(5)">打开前后门</div>
  29. <div class="button-box" :class="{'button-disable': (frontDoorIsOpen && !backDoorIsOpen) || (backDoorIsOpen && !frontDoorIsOpen)}" @click="playAnimation(6)">关闭前后门</div>
  30. <!-- <div class="button-box" @click="enterMY">漫游</div> -->
  31. </div>
  32. <div class="top-right row">
  33. <div class="control-type row">
  34. <div class="control-title">控制模式:</div>
  35. <a-radio-group v-model:value="selectData.autoRoManual" @change="changeType">
  36. <a-radio :value="`0`">就地</a-radio>
  37. <a-radio :value="`1`">远程</a-radio>
  38. </a-radio-group>
  39. </div>
  40. <div class="run-type row">
  41. <div class="control-title">运行状态:</div>
  42. <a-radio-group v-model:value="controlType">
  43. <a-radio :value="`1`">检修</a-radio>
  44. </a-radio-group>
  45. </div>
  46. <div class="run-state row">
  47. <div class="control-title">网络状态:</div>
  48. <a-radio-group v-model:value="selectData.netStatus">
  49. <a-radio :value="`1`">运行</a-radio>
  50. </a-radio-group>
  51. </div>
  52. </div>
  53. </div>
  54. <div class="title-box">
  55. 2-2煤主辅三联巷自动风门
  56. </div>
  57. <div class="tabs-box">
  58. <a-tabs v-model:activeKey="activeKey" @change="tabChange">
  59. <a-tab-pane key="1" tab="实时监测">
  60. <MonitorTable class="monitor-table" columnsType="gate_monitor" :dataSource="dataSource" design-scope="gate-monitor" @selectRow="getSelectRow" title="风门监测" />
  61. </a-tab-pane>
  62. <a-tab-pane key="2" tab="实时曲线图" force-render>
  63. <div class="tab-item" v-if="activeKey === '2'">
  64. <BarAndLine
  65. :chartData="dataSource"
  66. xAxisPropType="strname"
  67. :propTypeArr="propTypeArr"
  68. height="100%"
  69. :option="option"
  70. />
  71. </div>
  72. </a-tab-pane>
  73. <a-tab-pane key="3" tab="历史数据">
  74. <div class="tab-item">
  75. Content of Tab Pane 2
  76. </div>
  77. </a-tab-pane>
  78. <a-tab-pane key="4" tab="操作历史">
  79. <div class="tab-item">
  80. Content of Tab Pane 2
  81. </div>
  82. </a-tab-pane>
  83. <a-tab-pane key="5" tab="实时报警">
  84. <div class="tab-item">
  85. Content of Tab Pane 2
  86. </div>
  87. </a-tab-pane>
  88. </a-tabs>
  89. </div>
  90. </div>
  91. </template>
  92. <script setup lang="ts">
  93. import '/@/assets/less/modal.less';
  94. import {onBeforeMount, computed, onUnmounted, onMounted, ref, Ref, reactive, toRaw, nextTick } from 'vue';
  95. import BarAndLine from '/@/components/chart/BarAndLine.vue';
  96. import MonitorTable from '../comment/MonitorTable.vue';
  97. import { initWebSocket, getRecordList } from '/@/hooks/web/useVentWebSocket';
  98. import { mountedThree, addFmText, play, destroy} from './gate.threejs'
  99. import { deviceControlApi } from '/@/api/vent/index';
  100. import { message } from 'ant-design-vue';
  101. // import gsap from 'gsap';
  102. // import { flyLine } from '/@/utils/threejs/FlyLine'
  103. const elementContent = <Ref<HTMLElement>>ref()
  104. const activeKey = ref('1')
  105. const loading = ref(false);
  106. const propTypeArr = [
  107. {
  108. name: '气源压力(MPa)',
  109. type: 'line',
  110. filed: 'sourcePressure'
  111. },
  112. {
  113. name: '压差(Pa)',
  114. type: 'line',
  115. filed: 'frontRearDP'
  116. },
  117. {
  118. name: '测试数据1',
  119. type: 'bar',
  120. filed: 'test'
  121. }
  122. ];
  123. const frontDoorIsOpen = ref(false); //前门是否开启
  124. const backDoorIsOpen = ref(false); //后门是否开启
  125. // 监测数据
  126. const selectData = reactive({
  127. deviceID: '',
  128. deviceType: '',
  129. strname: '',
  130. frontRearDP: '-', //压差
  131. sourcePressure: '-', //气源压力
  132. netStatus: '0', //通信状态
  133. fault: '气源压力超限',
  134. autoRoManual: 0
  135. })
  136. const selectRowIndex = ref(0)
  137. // 实时监测数据
  138. const dataSource:any = computed(() => {
  139. // const data = [...getRecordList()].reverse() || []
  140. const data = [...getRecordList()] || []
  141. Object.assign(selectData, toRaw(data[selectRowIndex.value]))
  142. // console.log(data);
  143. addFmText(selectData)
  144. return data;
  145. });
  146. // echarts 图标样式
  147. const option = {
  148. grid: {
  149. show: false,
  150. left: 60,
  151. right: 50,
  152. bottom: 20,
  153. },
  154. xAxis: {
  155. axisLine: {
  156. show: true,
  157. lineStyle: {
  158. color: '#ffffffcc'
  159. }
  160. },
  161. },
  162. yAxis: {
  163. name: '气源压力(Pa)',
  164. nameTextStyle: {
  165. color: '#fff',
  166. fontSize: 14,
  167. },
  168. axisLine: {
  169. show: true,
  170. lineStyle: {
  171. color: '#ffffffcc'
  172. }
  173. },
  174. // axisTick: {
  175. // show: false
  176. // }
  177. splitLine: {
  178. show: false
  179. }
  180. },
  181. }
  182. // 设备数据
  183. const controlType = ref(1)
  184. const tabChange = (activeKeyVal) => {
  185. activeKey.value = activeKeyVal
  186. }
  187. // 切换检测数据
  188. const getSelectRow = (selectRow, index) => {
  189. selectRowIndex.value = index
  190. loading.value = true
  191. Object.assign(selectData, selectRow)
  192. setTimeout(() => {
  193. loading.value = false
  194. }, 300)
  195. }
  196. // 播放动画
  197. const playAnimation = (handlerState) => {
  198. const data = {
  199. deviceid: selectData.deviceID,
  200. devicetype: selectData.deviceType,
  201. paramcode: '',
  202. value: null,
  203. autoRoManual: selectData.autoRoManual
  204. };
  205. let handler = () => {};
  206. switch (handlerState) {
  207. case 1: // 打开前门
  208. if (!frontDoorIsOpen.value && !backDoorIsOpen.value) {
  209. handler = () => {
  210. frontDoorIsOpen.value = true;
  211. };
  212. data.paramcode = 'frontGateOpen_S';
  213. }
  214. break;
  215. case 2: // 关闭前门
  216. if (frontDoorIsOpen.value && !backDoorIsOpen.value) {
  217. handler = () => {
  218. frontDoorIsOpen.value = false;
  219. };
  220. data.paramcode = 'frontGateClose_S';
  221. }
  222. break;
  223. case 3: // 打开后门
  224. if (!backDoorIsOpen.value && !frontDoorIsOpen.value) {
  225. handler = () => {
  226. backDoorIsOpen.value = true;
  227. };
  228. data.paramcode = 'rearGateOpen_S';
  229. }
  230. break;
  231. case 4: // 关闭后门
  232. if (backDoorIsOpen.value && !frontDoorIsOpen.value) {
  233. handler = () => {
  234. backDoorIsOpen.value = false;
  235. };
  236. data.paramcode = 'rearGateClose_S';
  237. }
  238. break;
  239. case 5: // 打开前后门
  240. if (!frontDoorIsOpen.value && !backDoorIsOpen.value) {
  241. handler = () => {
  242. frontDoorIsOpen.value = true;
  243. backDoorIsOpen.value = true
  244. };
  245. data.paramcode = 'sameTimeOpen';
  246. }
  247. break;
  248. case 6: // 关闭前后门
  249. if (frontDoorIsOpen.value && backDoorIsOpen.value) {
  250. handler = () => {
  251. frontDoorIsOpen.value = false;
  252. backDoorIsOpen.value = false;
  253. };
  254. data.paramcode = 'sameTimeClose';
  255. }
  256. break;
  257. }
  258. if (data.paramcode) {
  259. deviceControlApi(data)
  260. .then((res) => {
  261. if (res.success) {
  262. }
  263. })
  264. .finally(() => {
  265. handler();
  266. play(handlerState)
  267. });
  268. }
  269. };
  270. // 远程、就地切换
  271. const changeType = () => {
  272. const data = {
  273. deviceid: selectData.deviceID,
  274. devicetype: selectData.deviceType,
  275. paramcode: 'autoRoManual',
  276. value: selectData.autoRoManual
  277. };
  278. deviceControlApi(data).then(() => {
  279. message.success('状态切换成功!')
  280. })
  281. }
  282. onBeforeMount(() => {
  283. const sendVal = JSON.stringify({ pagetype: 'normal', devicetype: 'gate', orgcode: '', ids: '', systemID: '' });
  284. initWebSocket(sendVal);
  285. });
  286. onMounted(() => {
  287. loading.value = true;
  288. mountedThree().then(() => {
  289. // addFmText(selectData)
  290. nextTick(() => {
  291. loading.value = false;
  292. })
  293. })
  294. });
  295. onUnmounted(() => {
  296. destroy()
  297. })
  298. </script>
  299. <style lang="less" scoped>
  300. :deep(.jeecg-basic-table .ant-table-wrapper){
  301. background-color: #ffffff00;
  302. }
  303. :deep(.ant-tabs-bar){
  304. margin: 0;
  305. }
  306. :deep(.ant-table){
  307. background-color: #ffffff00 !important;
  308. color: #fff;
  309. }
  310. :deep(.ant-table-header){
  311. background-color:transparent;
  312. // height: 42px;
  313. }
  314. :deep(.ant-table-thead > tr > th){
  315. background-color:transparent;
  316. border: none;
  317. }
  318. :deep(.ant-table-body > tr > th){
  319. background-color:transparent;
  320. border: none;
  321. }
  322. :deep(.ant-table-body > tr > td){
  323. border: none;
  324. }
  325. :deep(.ant-table-fixed-header > .ant-table-content > .ant-table-scroll > .ant-table-body){
  326. background-color:#ffffff05;
  327. margin-top: 8px;
  328. &::-webkit-scrollbar {
  329. display: none;
  330. }
  331. }
  332. :deep(.jeecg-basic-table .ant-table-wrapper .ant-table-title){
  333. padding: 0;
  334. }
  335. :deep(.jeecg-basic-table-row__striped td){
  336. background-color: transparent;
  337. }
  338. :deep(.ant-table-tbody > tr:hover.ant-table-row > td){
  339. background-color: #ffffff22;
  340. }
  341. :deep(.ant-table-tbody > tr:hover.ant-table-row > th){
  342. background-color: #ffffff22;
  343. }
  344. :deep(.ant-table-thead > tr:hover.ant-table-row > td){
  345. background-color: #ffffff22;
  346. }
  347. :deep(.ant-table-tbody > tr.ant-table-row-selected td){
  348. background-color: #ffffff22;
  349. }
  350. :deep(.ant-table-tbody > tr > td){
  351. border-color: #ffffff22;
  352. }
  353. :deep(.ant-table-thead > tr > th:hover){
  354. background-color: transparent !important;
  355. }
  356. :deep(.ant-table-thead > tr > th){
  357. color: #fff;
  358. }
  359. :deep(.ant-table-fixed-header .ant-table-scroll .ant-table-header){
  360. background: #ffffff44;
  361. position: relative;
  362. z-index: 999;
  363. padding: 4px 0 !important;
  364. &::-webkit-scrollbar {
  365. display: none;
  366. }
  367. }
  368. :deep(.ant-tabs-nav){
  369. color: #fff;
  370. }
  371. .ant-radio-group{
  372. color: #fff !important;
  373. .ant-radio-wrapper{
  374. color: #fff !important;
  375. }
  376. .ant-radio{
  377. color: #fff !important;
  378. }
  379. }
  380. .elementContent{
  381. background-color: rgb(20 143 221 / 40%);
  382. box-shadow: 0px 0px 12px rgb(0 128 255 / 75%);
  383. border: 1px solid rgb(127 177 255 / 75%);
  384. padding: 10px 20px 15px 20px;
  385. color: #efefef;
  386. p{
  387. // line-height: 1rem;
  388. }
  389. }
  390. .state-icon {
  391. display: inline-block;
  392. width: 11px;
  393. height: 11px;
  394. border-radius: 6px;
  395. margin-right: 6px;
  396. }
  397. .open {
  398. border: 2px solid #133a56;
  399. background: #4ecb73;
  400. }
  401. .close {
  402. border: 2px solid #ff1818;
  403. background: #ff8888;
  404. animation: close 1s infinite;
  405. }
  406. @keyframes close {
  407. 0% {
  408. border: 2px solid #ff8888;
  409. background: #ff3538;
  410. }
  411. 50% {
  412. border: 2px solid #ff0000;
  413. background: #c90000;
  414. }
  415. 100% {
  416. border: 2px solid #ff8888;
  417. background: #ff3538;
  418. }
  419. }
  420. .data-title{
  421. color: #eee;
  422. }
  423. </style>