index.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  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" style="width: 100%; height: 100%; position: absolute; overflow: hidden"></div>
  5. </div>
  6. <div class="scene-box">
  7. <div class="top-box">
  8. <div class="top-center row">
  9. <div v-if="hasPermission('btn:control')" class="button-box" @click="setControl('frontGateOpen_S', '打开')">打开防火门</div>
  10. <div v-if="hasPermission('btn:control')" class="button-box" @click="setControl('frontGateClose_S', '关闭')">关闭防火门</div>
  11. <div v-if="hasPermission('btn:stopControl')" class="button-box" @click="setControl('frontGateStop_S', '停止')">关闭防火门</div>
  12. </div>
  13. <!-- 控制模式 -->
  14. <div class="top-right row">
  15. <div class="vent-flex-m row" v-if="selectData.contrlMod == 'loopCtrl'">
  16. <div class="control-title">控制模式:</div>
  17. <a-radio-group v-model:value="selectData.autoRoManual">
  18. <template v-for="(item, index) in modelList" :key="index">
  19. <a-radio :value="item.value" :disabled="true">{{ item.text }}</a-radio>
  20. </template>
  21. </a-radio-group>
  22. <div class="button-box" @click="setControl('autoRoManualControl', '控制模式切换')">切换模式</div>
  23. </div>
  24. <div class="vent-flex-m row" v-else>
  25. <div class="control-title">控制模式:</div>
  26. <a-radio-group v-model:value="selectData.autoRoManual">
  27. <template v-for="(item, index) in modelList" :key="index">
  28. <a-radio :value="item.value" :disabled="true">{{ item.text }}</a-radio>
  29. </template>
  30. </a-radio-group>
  31. <div
  32. class="button-box"
  33. v-for="(item, index) in modelList"
  34. @click="setControl('autoRoManualControl', '控制模式切换', item.value)"
  35. :key="index"
  36. >{{ item.text }}</div
  37. >
  38. </div>
  39. <!-- <div class="run-type row">
  40. <div class="control-title">运行状态:</div>
  41. <a-radio-group v-model:value="selectData.runRoRecondition">
  42. <a-radio :value="`0`">检修</a-radio>
  43. <a-radio :value="`1`">运行</a-radio>
  44. </a-radio-group>
  45. </div> -->
  46. </div>
  47. </div>
  48. <div class="title-text">
  49. {{ selectData.supplyAirAddr || selectData.strinstallpos || selectData.strname }}
  50. </div>
  51. <div class="bottom-tabs-box" @mousedown="setDivHeight($event, 300, scroll)">
  52. <dv-border-box8 :dur="5" :style="`padding: 5px; height: ${scroll.y + 120}px`">
  53. <a-tabs class="tabs-box" v-model:activeKey="activeKey" @change="tabChange">
  54. <a-tab-pane key="1" tab="实时监测">
  55. <MonitorTable
  56. v-if="activeKey === '1'"
  57. ref="MonitorDataTable"
  58. class="monitor-table"
  59. :columnsType="deviceType"
  60. :isShowActionColumn="true"
  61. :dataSource="dataSource"
  62. design-scope="gate-monitor"
  63. @selectRow="getSelectRow"
  64. :scroll="{ y: scroll.y - 40 }"
  65. title="风门监测"
  66. :isShowPagination="true"
  67. >
  68. <template #filterCell="{ column, record }">
  69. <a-tag v-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == '0' && record.frontGateClose == '0'" color="red"
  70. >正在运行</a-tag
  71. >
  72. <a-tag v-else-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == '0' && record.frontGateClose == 1" color="default"
  73. >关闭</a-tag
  74. >
  75. <a-tag v-else-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == '1' && record.frontGateClose == '0'" color="#46C66F"
  76. >打开</a-tag
  77. >
  78. <a-tag v-else-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == '1' && record.frontGateClose == '1'" color="#FF0000"
  79. >点位异常</a-tag
  80. >
  81. <template v-else-if="column.dataIndex === 'warnLevel'">
  82. <a-tag v-if="record.warnLevel == '101'" color="green">低风险</a-tag>
  83. <a-tag v-else-if="record.warnLevel == '102'" color="#FF5812">一般风险</a-tag>
  84. <a-tag v-else-if="record.warnLevel == '103'" color="#FF5812">较大风险</a-tag>
  85. <a-tag v-else-if="record.warnLevel == '104'" color="#FF5812">重大风险</a-tag>
  86. <a-tag v-else-if="record.warnLevel == '201'" color="#FF0000">报警</a-tag>
  87. <a-tag v-else-if="record.warnLevel == '10000'" color="#FF5812">数据超限</a-tag>
  88. <a-tag v-else-if="record.warnLevel == '1001'" color="default">网络中断</a-tag>
  89. <a-tag v-else color="green">正常</a-tag>
  90. </template>
  91. <a-tag v-else-if="column.dataIndex === 'warnFlag'" :color="record.warnFlag == '0' ? 'green' : 'red'">{{
  92. record.warnFlag == '0' ? '正常' : '报警'
  93. }}</a-tag>
  94. <a-tag v-if="column.dataIndex === 'netStatus'" :color="record.netStatus == '0' ? '#f00' : 'green'">{{
  95. record.netStatus == '0' ? '断开' : '连接'
  96. }}</a-tag>
  97. </template>
  98. <template #action="{ record }">
  99. <a v-if="globalConfig?.showReport" class="table-action-link" @click="deviceEdit($event, 'reportInfo', record)">报表录入</a>
  100. <a class="table-action-link" @click="deviceEdit($event, 'deviceInfo', record)">设备编辑</a>
  101. </template>
  102. </MonitorTable>
  103. </a-tab-pane>
  104. <a-tab-pane key="3" tab="历史数据">
  105. <div class="tab-item" v-if="activeKey === '3'">
  106. <HistoryTable :columnsType="deviceType" :device-type="deviceType" designScope="gate-history" :scroll="scroll">
  107. <template #filterCell="{ column, record }">
  108. <a-tag v-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == '0' && record.frontGateClose == '0'" color="red"
  109. >正在运行</a-tag
  110. >
  111. <a-tag v-else-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == '0' && record.frontGateClose == 1" color="default"
  112. >关闭</a-tag
  113. >
  114. <a-tag
  115. v-else-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == '1' && record.frontGateClose == '0'"
  116. color="#46C66F"
  117. >打开</a-tag
  118. >
  119. <a-tag
  120. v-else-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == '1' && record.frontGateClose == '1'"
  121. color="#FF0000"
  122. >点位异常</a-tag
  123. >
  124. <template v-else-if="column.dataIndex === 'warnLevel'">
  125. <a-tag v-if="record.warnLevel == '101'" color="green">低风险</a-tag>
  126. <a-tag v-else-if="record.warnLevel == '102'" color="#FF5812">一般风险</a-tag>
  127. <a-tag v-else-if="record.warnLevel == '103'" color="#FF5812">较大风险</a-tag>
  128. <a-tag v-else-if="record.warnLevel == '104'" color="#FF5812">重大风险</a-tag>
  129. <a-tag v-else-if="record.warnLevel == '201'" color="#FF0000">报警</a-tag>
  130. <a-tag v-else-if="record.warnLevel == '10000'" color="#FF5812">数据超限</a-tag>
  131. <a-tag v-else-if="record.warnLevel == '1001'" color="default">网络中断</a-tag>
  132. <a-tag v-else color="green">正常</a-tag>
  133. </template>
  134. </template>
  135. </HistoryTable>
  136. </div>
  137. </a-tab-pane>
  138. <a-tab-pane key="4" tab="报警历史">
  139. <div class="tab-item" v-if="activeKey === '4'">
  140. <AlarmHistoryTable
  141. columns-type="alarm"
  142. :device-type="deviceType"
  143. :device-list-api="getTableList"
  144. designScope="alarm-history"
  145. :scroll="scroll"
  146. >
  147. <template #filterCell="{ column, record }">
  148. <a-tag v-if="column.dataIndex === 'warnFlag'" :color="record.warnFlag == '0' ? 'green' : record.warnFlag == 1 ? '#FF5812' : 'gray'">
  149. {{ record.warnFlag == '0' ? '正常' : record.warnFlag == 1 ? '报警' : record.warnFlag == 2 ? '断开' : '未监测' }}</a-tag
  150. >
  151. <a-tag v-if="column.dataIndex === 'netStatus'" :color="record.netStatus == '0' ? '#f00' : 'green'">{{
  152. record.netStatus == '0' ? '断开' : '连接'
  153. }}</a-tag>
  154. </template>
  155. </AlarmHistoryTable>
  156. </div>
  157. </a-tab-pane>
  158. <a-tab-pane key="5" tab="操作历史">
  159. <div class="tab-item" v-if="activeKey === '5'">
  160. <HandlerHistoryTable
  161. columns-type="operator_history"
  162. :device-type="deviceType"
  163. :device-list-api="getTableList"
  164. designScope="operator_history"
  165. :scroll="scroll"
  166. />
  167. </div>
  168. </a-tab-pane>
  169. </a-tabs>
  170. </dv-border-box8>
  171. </div>
  172. </div>
  173. <div ref="playerRef" style="z-index: 999; position: absolute; top: 100px; right: 15px; width: 300px; height: 280px; margin: auto"> </div>
  174. <HandleModal
  175. v-if="!globalConfig?.simulatedPassword"
  176. :modal-is-show="modalIsShow"
  177. :modal-title="modalTitle"
  178. :modal-type="modalType"
  179. @handle-ok="handleOK"
  180. @handle-cancel="handleCancel"
  181. />
  182. <DeviceBaseInfo @register="registerModal" :device-type="selectData['deviceType']" />
  183. </template>
  184. <script setup lang="ts">
  185. import { onBeforeUnmount, onUnmounted, onMounted, ref, reactive, nextTick, inject, unref } from 'vue';
  186. import MonitorTable from '../comment/MonitorTable.vue';
  187. import HistoryTable from '../comment/HistoryTable.vue';
  188. import AlarmHistoryTable from '../comment/AlarmHistoryTable.vue';
  189. import HandlerHistoryTable from '../comment/HandlerHistoryTable.vue';
  190. import HandleModal from './modal.vue';
  191. import DeviceBaseInfo from '../comment/components/DeviceBaseInfo.vue';
  192. import { mountedThree, play, destroy, setModelType } from './fireDoor.threejs';
  193. import { deviceControlApi } from '/@/api/vent/index';
  194. import { message } from 'ant-design-vue';
  195. import { list, getTableList } from './fireDoor.api';
  196. import lodash from 'lodash';
  197. import { setDivHeight } from '/@/utils/event';
  198. import { BorderBox8 as DvBorderBox8 } from '@kjgl77/datav-vue3';
  199. import { useRouter } from 'vue-router';
  200. import { useModal } from '/@/components/Modal';
  201. import { useCamera } from '/@/hooks/system/useCamera';
  202. import { usePermission } from '/@/hooks/web/usePermission';
  203. import { getDictItems } from '/@/api/common/api';
  204. const { hasPermission } = usePermission();
  205. const globalConfig = inject('globalConfig');
  206. const { currentRoute } = useRouter();
  207. const MonitorDataTable = ref();
  208. let contrlValue = '';
  209. const playerRef = ref();
  210. const deviceType = ref('door');
  211. // const deviceType = ref('gate');
  212. // const deviceType = ref('firedoor');
  213. const activeKey = ref('1'); // tab
  214. const loading = ref(false);
  215. const scroll = reactive({
  216. y: 230,
  217. });
  218. const modelList = ref<{ text: string; value: string }[]>([]);
  219. const doorIsOpen = ref(false); //前门是否开启
  220. const modalIsShow = ref<boolean>(false); // 是否显示模态框
  221. const modalTitle = ref(''); // 模态框标题显示内容,根据设备操作类型决定
  222. const modalType = ref(''); // 模态框内容显示类型,设备操作类型
  223. const selectRowIndex = ref(-1); // 选中行
  224. const dataSource = ref([]);
  225. const deviceBaseList = ref([]); // 设备基本信息
  226. const [registerModal, { openModal, closeModal }] = useModal();
  227. const { getCamera, removeCamera } = useCamera();
  228. const tabChange = (activeKeyVal) => {
  229. activeKey.value = activeKeyVal;
  230. if (activeKeyVal == 1) {
  231. nextTick(() => {
  232. if (MonitorDataTable.value) MonitorDataTable.value.setSelectedRowKeys([selectData.deviceID]);
  233. });
  234. }
  235. };
  236. const initData = {
  237. deviceID: '',
  238. deviceType: '',
  239. strname: '',
  240. frontRearDP: '-', //压差
  241. // sourcePressure: '-', //气源压力
  242. runRoRecondition: null,
  243. autoRoManual: null,
  244. netStatus: '0', //通信状态
  245. frontGateOpen: '0',
  246. frontGateClose: '1',
  247. rearGateOpen: '0',
  248. rearGateClose: '1',
  249. midGateOpen: '0',
  250. midGateClose: '1',
  251. fault: '气源压力超限',
  252. masterComputer: 0,
  253. frontGateOpenCtrl: false,
  254. rearGateOpenCtrl: false,
  255. cameras: [],
  256. };
  257. // 监测数据
  258. const selectData = reactive(lodash.cloneDeep(initData));
  259. function deviceEdit(e: Event, type: string, record) {
  260. e.stopPropagation();
  261. openModal(true, {
  262. type,
  263. deviceId: record['deviceID'],
  264. });
  265. }
  266. // 获取设备基本信息列表
  267. function getDeviceBaseList() {
  268. getTableList({ pageSize: 1000 }).then((res) => {
  269. deviceBaseList.value = res.records;
  270. });
  271. }
  272. // https获取监测数据
  273. let timer: null | NodeJS.Timeout = null;
  274. async function getMonitor(flag?) {
  275. if (Object.prototype.toString.call(timer) === '[object Null]') {
  276. timer = await setTimeout(
  277. async () => {
  278. const res = await list({ devicetype: deviceType.value, pagetype: 'normal' });
  279. if (res.msgTxt && res.msgTxt[0]) {
  280. dataSource.value = res.msgTxt[0].datalist || [];
  281. dataSource.value.forEach((data: any) => {
  282. const readData = data.readData;
  283. data = Object.assign(data, readData);
  284. });
  285. if (dataSource.value.length > 0 && selectRowIndex.value == -1 && MonitorDataTable.value) {
  286. // 初始打开页面
  287. if (currentRoute.value && currentRoute.value['query'] && currentRoute.value['query']['id']) {
  288. MonitorDataTable.value.setSelectedRowKeys([currentRoute.value['query']['id']]);
  289. } else {
  290. MonitorDataTable.value.setSelectedRowKeys([dataSource.value[0]['deviceID']]);
  291. }
  292. }
  293. Object.assign(selectData, dataSource.value[selectRowIndex.value]);
  294. monitorAnimation(selectData);
  295. if (timer) {
  296. timer = null;
  297. }
  298. getMonitor();
  299. }
  300. },
  301. flag ? 0 : 1000
  302. );
  303. }
  304. }
  305. // 切换检测数据
  306. async function getSelectRow(selectRow, index) {
  307. if (!selectRow) return;
  308. loading.value = true;
  309. selectRowIndex.value = index;
  310. const baseData: any = deviceBaseList.value.find((baseData: any) => baseData.id === selectRow.deviceID);
  311. Object.assign(selectData, initData, selectRow, baseData);
  312. doorDeviceState = 0; //记录设备状态,为了与下一次监测数据做比较
  313. isdoorOpenRunning = false; //开关门动作是否在进行
  314. let type = 'fireDoorRed';
  315. if (selectData.modelType == 'bd_qt') {
  316. type = 'fireDoor';
  317. } else if (selectData.modelType == 'bd_kj') {
  318. type = 'fireDoorF';
  319. } else if (selectData.modelType) {
  320. type = selectData.modelType;
  321. }
  322. await setModelType(type);
  323. loading.value = false;
  324. await getCamera(selectRow.deviceID, playerRef.value);
  325. }
  326. const setControl = (flag, title, value?) => {
  327. modalType.value = flag + '';
  328. modalTitle.value = title;
  329. modalIsShow.value = true;
  330. if (value) contrlValue = value;
  331. if (globalConfig?.simulatedPassword) {
  332. handleOK('', handlerState + '');
  333. }
  334. };
  335. function handleOK(passWord, handlerState) {
  336. if (!passWord && !globalConfig?.simulatedPassword) {
  337. message.warning('请输入密码');
  338. return;
  339. }
  340. if (isOpenRunning) {
  341. return;
  342. }
  343. const data = {
  344. deviceid: selectData.deviceID,
  345. devicetype: selectData.deviceType,
  346. paramcode: '',
  347. value: contrlValue,
  348. password: passWord || globalConfig?.simulatedPassword,
  349. masterComputer: selectData.masterComputer,
  350. };
  351. data.paramcode = handlerState;
  352. if (handlerState == 'autoRoManualControl') {
  353. data.value = selectData.contrlMod != 'loopCtrl' ? contrlValue : '';
  354. selectData.autoRoManual = null;
  355. }
  356. if (data.paramcode) {
  357. deviceControlApi(data).then((res) => {
  358. // 模拟时开启
  359. if (res.success) {
  360. modalIsShow.value = false;
  361. if (globalConfig.History_Type == 'remote') {
  362. message.success('指令已下发至生产管控平台成功!');
  363. } else {
  364. message.success('指令已下发成功!');
  365. }
  366. } else {
  367. message.error(res.message);
  368. }
  369. });
  370. }
  371. }
  372. let isOpenRunning = false; //开关门动作是否在进行
  373. /** 开关门动画调用 */
  374. let isdoorOpenRunning = false; //开关门动作是否在进行
  375. // let isMidCloseRunning = false; //中间门动作是否在进行
  376. // 0 关闭 1 正在打开 2 打开 3正在关闭
  377. let doorDeviceState = 0; //记录设备状态,为了与下一次监测数据做比较
  378. function monitorAnimation(selectData) {
  379. const timeScale = 0.005;
  380. // 打开
  381. if (selectData.frontGateOpen == '1' && !isdoorOpenRunning) {
  382. isdoorOpenRunning = true;
  383. if (doorDeviceState != 1) {
  384. // import.meta.env.VITE_GLOB_IS_SIMULATE ? play(1, timeScale) : play(1);
  385. play(1, timeScale);
  386. doorDeviceState = 1;
  387. doorIsOpen.value = true;
  388. }
  389. }
  390. // 关闭
  391. if (selectData.frontGateOpen == '0' && isdoorOpenRunning) {
  392. isdoorOpenRunning = false;
  393. if (doorDeviceState != 0) {
  394. // import.meta.env.VITE_GLOB_IS_SIMULATE ? play(1, timeScale) : play(1);
  395. play(2, timeScale);
  396. doorDeviceState = 0;
  397. doorIsOpen.value = false;
  398. }
  399. }
  400. }
  401. function handleCancel() {
  402. modalIsShow.value = false;
  403. modalTitle.value = '';
  404. modalType.value = '';
  405. selectData.autoRoManual = null;
  406. }
  407. onMounted(async () => {
  408. const { query } = unref(currentRoute);
  409. if (query['deviceType']) deviceType.value = query['deviceType'] as string;
  410. modelList.value = await getDictItems('fireDoorModel');
  411. loading.value = true;
  412. mountedThree().then(async () => {
  413. await getMonitor(true);
  414. loading.value = false;
  415. });
  416. });
  417. onBeforeUnmount(() => {
  418. getDeviceBaseList();
  419. });
  420. onUnmounted(() => {
  421. removeCamera();
  422. if (timer) {
  423. clearTimeout(timer);
  424. timer = undefined;
  425. }
  426. destroy();
  427. });
  428. </script>
  429. ,
  430. <style lang="less" scoped>
  431. @import '/@/design/vent/modal.less';
  432. .scene-box {
  433. .bottom-tabs-box {
  434. height: 300px;
  435. }
  436. }
  437. .button-box {
  438. border: none !important;
  439. height: 34px !important;
  440. &:hover {
  441. background: linear-gradient(#2cd1ff55, #1eb0ff55) !important;
  442. }
  443. &::before {
  444. height: 27px !important;
  445. background: linear-gradient(#1fa6cb, #127cb5) !important;
  446. }
  447. &::after {
  448. top: 35px !important;
  449. }
  450. }
  451. :deep(.@{ventSpace}-tabs-tabpane-active) {
  452. height: 100%;
  453. }
  454. ::-webkit-scrollbar-thumb {
  455. -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
  456. background: #4288a444;
  457. }
  458. :deep(.zxm-radio-disabled + span) {
  459. color: #fff !important;
  460. }
  461. :deep(.zxm-radio-disabled .zxm-radio-inner::after) {
  462. background-color: #127cb5 !important;
  463. }
  464. </style>