浏览代码

Merge branch 'master' of http://182.92.126.35:3000/hrx/mky-vent-base

wangkeyi 16 小时之前
父节点
当前提交
4dacb6f61a

+ 45 - 27
src/hooks/vent/useSvgAnimation.ts

@@ -6,7 +6,7 @@ import _ from 'lodash';
  *
  * 备注:一个元素的动画仅有两种状态,正常播放、倒放;例如:`triggerAnimation(id1, false)`代表触发id1对应的动画,false代表触发正常播放的动画
  */
-export function useSvgAnimation(elementInfo: Map<string, { key: string; transforms: string[] }>) {
+export function useSvgAnimation(elementInfo: Map<string, { key: string; transforms?: string[]; opacity?: string[] }>) {
   /** 所有动画元素 */
   const animationElements = new Map<string, HTMLElement>();
   /** 管理节点是否处于初始状态 */
@@ -19,10 +19,17 @@ export function useSvgAnimation(elementInfo: Map<string, { key: string; transfor
    *
    * @param id 标识符号(可以在页面中使用元素选择器选择具体元素后查询其id),可以传数组
    * @param reverse 是否需要反向执行动画,如果id传了数组该参数可以传数组以一一匹配,默认为false
-   * @param duration 动画持续时长,越长动画执行的越慢
-   * @param progress 指定动画执行的进度,默认为1,即动画执行到100%,该数字范围为0-1
+   * @param config.duration 动画持续时长,越长动画执行的越慢
+   * @param config.progress 指定动画执行的进度,默认为1,即动画执行到100%,该数字范围为0-1
    */
-  function triggerAnimation(id: string | string[], reverse: boolean | boolean[] = false, duration = 3000, progress = 1) {
+  function triggerAnimation(
+    id: string | string[],
+    reverse: boolean | boolean[] = false,
+    config: {
+      duration?: number;
+      progress?: number;
+    } = {}
+  ) {
     const idArray = typeof id === 'string' ? [id] : id;
     const reverseArray = typeof reverse === 'boolean' ? idArray.map(() => reverse) : reverse;
 
@@ -36,54 +43,65 @@ export function useSvgAnimation(elementInfo: Map<string, { key: string; transfor
       // 不指定反向播放且group处于初始状态时播放正常动画
       if (!reverse && unchanged) {
         animationManager.value[id] = false;
-        animateByKey(id, true, duration, progress);
+        animateByKey(id, false, config);
         return;
       }
       if (reverse && !unchanged) {
         animationManager.value[id] = true;
-        animateByKey(id, false, duration, progress);
+        animateByKey(id, true, config);
         return;
       }
     });
   }
 
   // 直接控制动画的方法
-  const animateElement = (elementId: string, toEnd: boolean, duration = 3000, progress = 1) => {
+  const animateElement = (
+    elementId: string,
+    reverse: boolean = false,
+    config: {
+      duration?: number;
+      progress?: number;
+    } = {}
+  ) => {
+    const { duration = 3000, progress = 1 } = config;
     const el = animationElements.get(elementId);
     const info = elementInfo.get(elementId);
-    progress = _.clamp(progress, 0, 1);
+    const percentage = _.clamp(progress, 0, 1);
 
-    if (el && info && info.transforms.length > 1) {
-      const endTransform = info.transforms[Math.floor((info.transforms.length - 1) * progress)];
-      const startTransform = info.transforms[Math.floor((info.transforms.length - 1) * (1 - progress))];
+    if (!el || !info) return;
+
+    // 应用动画
+    if (info.transforms && info.transforms.length > 1) {
+      const endTransform = info.transforms[Math.floor((info.transforms.length - 1) * percentage)];
+      const startTransform = info.transforms[Math.floor((info.transforms.length - 1) * (1 - percentage))];
       el.style.transition = `transform ${duration}ms`;
-      el.setAttribute('transform', toEnd ? endTransform : startTransform);
+      el.setAttribute('transform', reverse ? startTransform : endTransform);
+    }
+    if (info.opacity && info.opacity.length > 1) {
+      const endOpacity = info.opacity[Math.floor((info.opacity.length - 1) * percentage)];
+      const startOpacity = info.opacity[Math.floor((info.opacity.length - 1) * (1 - percentage))];
+      el.style.transition = `opacity ${duration}ms`;
+      el.setAttribute('opacity', reverse ? startOpacity : endOpacity);
     }
   };
 
   // 批量控制同一key的所有元素
-  const animateByKey = (key: string, toEnd: boolean, duration = 3000, progress = 1) => {
+  const animateByKey = (
+    key: string,
+    reverse: boolean = false,
+    config: {
+      duration?: number;
+      progress?: number;
+    } = {}
+  ) => {
     animationElements.forEach((__, elementId) => {
       const info = elementInfo.get(elementId);
       if (info && info.key === key) {
-        animateElement(elementId, toEnd, duration, progress);
+        animateElement(elementId, reverse, config);
       }
     });
   };
 
-  // watch(
-  //   () => animationManager,
-  //   () => {
-  //     Object.keys(animationManager).forEach((key) => {
-  //       const unchanged = animationManager[key];
-
-  //       // 找到所有属于这个key的元素
-  //       animateByKey(key, !unchanged);
-  //     });
-  //   },
-  //   { deep: true }
-  // );
-
   return {
     animationElements,
     triggerAnimation,

+ 174 - 0
src/views/vent/monitorManager/fireDoorMonitor/fireDoor.threejs.ssl.ts

@@ -0,0 +1,174 @@
+import * as THREE from 'three';
+import { useAppStore } from '/@/store/modules/app';
+
+// import * as dat from 'dat.gui';
+// const gui = new dat.GUI();
+// gui.domElement.style = 'position:absolute;top:100px;left:10px;z-index:99999999999999';
+
+class FireDoor {
+  modelName = 'fireDoor';
+  model; //
+  group;
+  isLRAnimation = true; // 是否开启左右摇摆动画
+  direction = 1; // 摇摆方向
+  animationTimer: NodeJS.Timeout | null = null; // 摇摆开启定时器
+  player1;
+  player2;
+  deviceDetailCSS3D;
+  playerStartClickTime1 = new Date().getTime();
+  playerStartClickTime2 = new Date().getTime();
+
+  fmClock = new THREE.Clock();
+  mixers: THREE.AnimationMixer | undefined;
+  appStore = useAppStore();
+  damperOpenMesh;
+  damperClosedMesh;
+
+  clipActionArr: Record<string, THREE.AnimationAction | undefined> = {
+    door: undefined,
+  };
+
+  constructor(model) {
+    this.model = model;
+  }
+
+  addLight() {
+    const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5);
+    directionalLight.position.set(344, 690, 344);
+    this.group?.add(directionalLight);
+    directionalLight.target = this.group as THREE.Object3D;
+
+    const pointLight2 = new THREE.PointLight(0xffeeee, 1, 300);
+    pointLight2.position.set(-4, 10, 1.8);
+    pointLight2.shadow.bias = 0.05;
+    this.group?.add(pointLight2);
+
+    const pointLight3 = new THREE.PointLight(0xffeeee, 1, 200);
+    pointLight3.position.set(-0.5, -0.5, 0.75);
+    pointLight3.shadow.bias = 0.05;
+    this.group?.add(pointLight3);
+  }
+  resetCamera() {
+    this.model.camera.far = 274;
+    this.model.orbitControls?.update();
+    this.model.camera.updateProjectionMatrix();
+  }
+  // 设置模型位置
+  setModalPosition() {
+    this.group?.scale.set(22, 22, 22);
+    this.group?.position.set(-20, 20, 9);
+  }
+
+  /* 风门动画 */
+  render() {
+    if (!this.model) {
+      return;
+    }
+    if (this.mixers && this.fmClock.running) {
+      this.mixers.update(2);
+    }
+  }
+
+  /* 点击风门 */
+  mousedownModel(intersects: THREE.Intersection[]) {
+    console.log('摄像头控制信息', intersects);
+  }
+
+  mouseUpModel() {}
+
+  /* 提取风门序列帧,初始化前后门动画 */
+  initAnimation() {
+    const fireGroup = this.group.children[0]?.getObjectByName('Fire-door');
+    if (fireGroup) {
+      const tracks = fireGroup.animations[0].tracks;
+
+      this.mixers = new THREE.AnimationMixer(fireGroup);
+
+      const door = new THREE.AnimationClip('door', 100, tracks);
+      const frontClipAction = this.mixers.clipAction(door, fireGroup);
+      frontClipAction.clampWhenFinished = true;
+      frontClipAction.loop = THREE.LoopOnce;
+      this.clipActionArr.door = frontClipAction;
+    }
+  }
+
+  // 播放动画
+  play(handlerState, timeScale = 0.01) {
+    let handler = () => {};
+    switch (handlerState) {
+      case 1: // 打开门
+        handler = () => {
+          if (!this.clipActionArr.door) return;
+          this.clipActionArr.door.paused = true;
+          this.clipActionArr.door.reset();
+          this.clipActionArr.door.time = 1.7;
+          this.clipActionArr.door.timeScale = -timeScale;
+          // this.clipActionArr.door.clampWhenFinished = true;
+          this.clipActionArr.door.play();
+          this.fmClock.start();
+
+          // 显示打开前门文字
+          if (this.damperOpenMesh) this.damperOpenMesh.visible = true;
+        };
+        break;
+      case 2: // 关闭门
+        handler = () => {
+          if (!this.clipActionArr.door) return;
+          this.clipActionArr.door.paused = true;
+          this.clipActionArr.door.reset(); //
+          this.clipActionArr.door.time = 0;
+          this.clipActionArr.door.timeScale = timeScale;
+          // this.clipActionArr.door.clampWhenFinished = true;
+          this.clipActionArr.door.play();
+          this.fmClock.start();
+
+          if (this.damperOpenMesh) this.damperOpenMesh.visible = false;
+        };
+        break;
+      default:
+    }
+    handler();
+  }
+
+  mountedThree() {
+    this.group = new THREE.Object3D();
+    this.group.name = this.modelName;
+
+    return new Promise((resolve) => {
+      if (!this.model) {
+        resolve(null);
+      }
+      this.model.setGLTFModel(['Fire-door-ssl'], this.group).then(() => {
+        this.setModalPosition();
+        // 初始化左右摇摆动画;
+        this.initAnimation();
+        // this.addLight();
+        // this.model.animate();
+        // resolve(this.model);
+
+        // this.damperOpenMesh = this.group.getObjectByName('Damper_Open_2');
+        // if (this.damperOpenMesh) this.damperOpenMesh.visible = false;
+        // this.damperClosedMesh = this.group.getObjectByName('Damper_Closed_2');
+        // if (this.damperClosedMesh) this.damperClosedMesh.visible = true;
+      });
+    });
+  }
+
+  destroy() {
+    if (!this.model) return;
+    if (this.mixers && this.clipActionArr.door) {
+      this.mixers.uncacheClip(this.clipActionArr.door.getClip());
+      this.mixers.uncacheAction(this.clipActionArr.door.getClip(), this.group);
+      this.mixers.uncacheRoot(this.group);
+
+      if (this.model.animations[0]) this.model.animations[0].tracks = [];
+    }
+    this.model.clearGroup(this.group);
+    this.clipActionArr.door = undefined;
+
+    this.mixers = undefined;
+
+    // document.getElementById('damper3D').parentElement.remove(document.getElementById('damper3D'))
+  }
+}
+export default FireDoor;

+ 60 - 13
src/views/vent/monitorManager/fireDoorMonitor/fireDoor.threejs.ts

@@ -2,16 +2,17 @@ import * as THREE from 'three';
 import UseThree from '../../../../utils/threejs/useThree';
 import FireDoor from './fireDoor.threejs.fire';
 import FireDoorF from './fireDoor.threejs.fireF';
+import FireDoorSsl from './fireDoor.threejs.ssl';
 import { animateCamera } from '/@/utils/threejs/util';
 import useEvent from '../../../../utils/threejs/useEvent';
-import { useGlobSetting } from '/@/hooks/setting';
 
 // 模型对象、 文字对象
 let model,
   fireDoor, //液压风门
   fireDoorF, //液压风门
+  fireDoorSsl, // 思山岭防火门
   group: THREE.Object3D,
-  fmType = '';
+  fhmType = '';
 
 const { mouseDownFn } = useEvent();
 
@@ -22,12 +23,15 @@ const startAnimation = () => {
   model.canvasContainer?.addEventListener('pointerup', (event) => {
     event.stopPropagation();
     // 单道、 双道
-    if (fmType === 'fireDoor') {
+    if (fhmType === 'fireDoor') {
       fireDoor?.mouseUpModel.call(fireDoor);
     }
-    if (fmType === 'fireDoorF') {
+    if (fhmType === 'fireDoorF') {
       fireDoorF?.mouseUpModel.call(fireDoorF);
     }
+    if (fhmType === 'fireDoorSsl') {
+      fireDoorSsl?.mouseUpModel.call(fireDoorSsl);
+    }
   });
 };
 
@@ -35,32 +39,38 @@ const startAnimation = () => {
 const mouseEvent = (event) => {
   if (event.button == 0) {
     mouseDownFn(model, group, event, (intersects) => {
-      if (fmType === 'fireDoor' && fireDoor) {
+      if (fhmType === 'fireDoor' && fireDoor) {
         fireDoor?.mousedownModel.call(fireDoor, intersects);
       }
-      if (fmType === 'fireDoorF' && fireDoorF) {
+      if (fhmType === 'fireDoorF' && fireDoorF) {
         fireDoorF?.mousedownModel.call(fireDoorF, intersects);
       }
+      if (fhmType === 'fireDoorSsl' && fireDoorSsl) {
+        fireDoorSsl?.mousedownModel.call(fireDoorSsl, intersects);
+      }
     });
     // console.log('摄像头控制信息', model.orbitControls, model.camera);
   }
 };
 
 export const play = (handlerState, flag?) => {
-  if (fmType === 'fireDoor' && fireDoor) {
+  if (fhmType === 'fireDoor' && fireDoor) {
     return fireDoor.play.call(fireDoor, handlerState, flag);
   }
-  if (fmType === 'fireDoorF' && fireDoorF) {
+  if (fhmType === 'fireDoorF' && fireDoorF) {
     return fireDoorF.play.call(fireDoorF, handlerState, flag);
   }
+  if (fhmType === 'fireDoorSsl' && fireDoorSsl) {
+    return fireDoorSsl.play.call(fireDoorSsl, handlerState, flag);
+  }
 };
 
 // 切换风门类型
 export const setModelType = (type) => {
-  fmType = type;
+  fhmType = type;
   return new Promise((resolve) => {
     // 暂停风门1动画
-    if (fmType === 'fireDoor' && fireDoor && fireDoor.group) {
+    if (fhmType === 'fireDoor' && fireDoor && fireDoor.group) {
       if (fireDoor.clipActionArr.door) {
         fireDoor.clipActionArr.door.reset();
         fireDoor.clipActionArr.door.time = 0.5;
@@ -86,7 +96,7 @@ export const setModelType = (type) => {
           0.8
         );
       }, 300);
-    } else if (fmType === 'fireDoorF' && fireDoorF && fireDoorF.group) {
+    } else if (fhmType === 'fireDoorF' && fireDoorF && fireDoorF.group) {
       if (fireDoorF.clipActionArr.door) {
         fireDoorF.clipActionArr.door.reset();
         fireDoorF.clipActionArr.door.time = 0;
@@ -109,15 +119,48 @@ export const setModelType = (type) => {
           0.8
         );
       }, 300);
+    } else if (fhmType === 'fireDoorSsl' && fireDoorSsl && fireDoorSsl.group) {
+      // if (fireDoorSsl.clipActionArr.door) {
+      //   fireDoorSsl.clipActionArr.door.reset();
+      //   fireDoorSsl.clipActionArr.door.time = 0;
+      //   fireDoorSsl.clipActionArr.door.stop();
+      // }
+      // model.scene.remove(group);
+      // model.startAnimation = fireDoorSsl.render.bind(fireDoorSsl);
+      group = fireDoorSsl.group;
+      group.rotation.y = 0;
+      const oldCameraPosition = { x: -1000, y: 100, z: 500 };
+      setTimeout(async () => {
+        resolve(null);
+        model.scene.add(fireDoorSsl.group);
+        await animateCamera(
+          oldCameraPosition,
+          { x: 0, y: 0, z: 0 },
+          {
+            x: 342.74781900192056,
+            y: 183.50210411099545,
+            z: 451.0806333923029,
+          },
+          {
+            x: 72.33938301176254,
+            y: -35.03891296652319,
+            z: -37.91742549963208,
+          },
+          model,
+          0.8
+        );
+      }, 300);
     }
   });
 };
 
 export const initCameraCanvas = async (playerVal1) => {
-  if (fmType === 'fireDoor' && fireDoor) {
+  if (fhmType === 'fireDoor' && fireDoor) {
     return await fireDoor.initCamera.call(fireDoor, playerVal1);
-  } else if (fmType === 'fireDoorF' && fireDoorF) {
+  } else if (fhmType === 'fireDoorF' && fireDoorF) {
     return await fireDoorF.initCamera.call(fireDoorF, playerVal1);
+  } else if (fhmType === 'fireDoorSsl' && fireDoorSsl) {
+    return await fireDoorSsl.initCamera.call(fireDoorSsl, playerVal1);
   }
 };
 const setControls = () => {
@@ -140,6 +183,9 @@ export const mountedThree = () => {
 
     fireDoorF = new FireDoorF(model);
     fireDoorF.mountedThree();
+
+    fireDoorSsl = new FireDoorSsl(model);
+    fireDoorSsl.mountedThree();
     resolve(null);
     setControls();
     model.animate();
@@ -158,6 +204,7 @@ export const destroy = () => {
     model.isRender = false;
     if (fireDoor) fireDoor.destroy();
     fireDoor = null;
+    // @ts-ignore-next-line
     group = null;
     model.mixers = [];
     model.destroy();

+ 296 - 296
src/views/vent/monitorManager/fireDoorMonitor/index.vue

@@ -177,342 +177,342 @@
 </template>
 
 <script setup lang="ts">
-import { onBeforeUnmount, onUnmounted, onMounted, ref, reactive, nextTick, inject, unref } from 'vue';
-import MonitorTable from '../comment/MonitorTable.vue';
-import HistoryTable from '../comment/HistoryTable.vue';
-import AlarmHistoryTable from '../comment/AlarmHistoryTable.vue';
-import HandlerHistoryTable from '../comment/HandlerHistoryTable.vue';
-import HandleModal from './modal.vue';
-import DeviceBaseInfo from '../comment/components/DeviceBaseInfo.vue';
-import { mountedThree, play, destroy, setModelType } from './fireDoor.threejs';
-import { deviceControlApi } from '/@/api/vent/index';
-import { message } from 'ant-design-vue';
-import { list, getTableList } from './fireDoor.api';
-import lodash from 'lodash';
-import { setDivHeight } from '/@/utils/event';
-import { BorderBox8 as DvBorderBox8 } from '@kjgl77/datav-vue3';
-import { useRouter } from 'vue-router';
-import { useModal } from '/@/components/Modal';
-import { useCamera } from '/@/hooks/system/useCamera';
-import { usePermission } from '/@/hooks/web/usePermission';
-import { getDictItems } from '/@/api/common/api';
+  import { onBeforeUnmount, onUnmounted, onMounted, ref, reactive, nextTick, inject, unref } from 'vue';
+  import MonitorTable from '../comment/MonitorTable.vue';
+  import HistoryTable from '../comment/HistoryTable.vue';
+  import AlarmHistoryTable from '../comment/AlarmHistoryTable.vue';
+  import HandlerHistoryTable from '../comment/HandlerHistoryTable.vue';
+  import HandleModal from './modal.vue';
+  import DeviceBaseInfo from '../comment/components/DeviceBaseInfo.vue';
+  import { mountedThree, play, destroy, setModelType } from './fireDoor.threejs';
+  import { deviceControlApi } from '/@/api/vent/index';
+  import { message } from 'ant-design-vue';
+  import { list, getTableList } from './fireDoor.api';
+  import lodash from 'lodash';
+  import { setDivHeight } from '/@/utils/event';
+  import { BorderBox8 as DvBorderBox8 } from '@kjgl77/datav-vue3';
+  import { useRouter } from 'vue-router';
+  import { useModal } from '/@/components/Modal';
+  import { useCamera } from '/@/hooks/system/useCamera';
+  import { usePermission } from '/@/hooks/web/usePermission';
+  import { getDictItems } from '/@/api/common/api';
 
-const { hasPermission } = usePermission();
+  const { hasPermission } = usePermission();
 
-const globalConfig = inject('globalConfig');
+  const globalConfig = inject('globalConfig');
 
-const { currentRoute } = useRouter();
-const MonitorDataTable = ref();
-let contrlValue = '';
-const playerRef = ref();
-const deviceType = ref('door');
-// const deviceType = ref('firedoor');
-const activeKey = ref('1'); // tab
-const loading = ref(false);
+  const { currentRoute } = useRouter();
+  const MonitorDataTable = ref();
+  let contrlValue = '';
+  const playerRef = ref();
+  const deviceType = ref('door');
+  // const deviceType = ref('firedoor');
+  const activeKey = ref('1'); // tab
+  const loading = ref(false);
 
-const scroll = reactive({
-  y: 230,
-});
-const modelList = ref<{ text: string; value: string }[]>([]);
-const doorIsOpen = ref(false); //前门是否开启
-const modalIsShow = ref<boolean>(false); // 是否显示模态框
-const modalTitle = ref(''); // 模态框标题显示内容,根据设备操作类型决定
-const modalType = ref(''); // 模态框内容显示类型,设备操作类型
+  const scroll = reactive({
+    y: 230,
+  });
+  const modelList = ref<{ text: string; value: string }[]>([]);
+  const doorIsOpen = ref(false); //前门是否开启
+  const modalIsShow = ref<boolean>(false); // 是否显示模态框
+  const modalTitle = ref(''); // 模态框标题显示内容,根据设备操作类型决定
+  const modalType = ref(''); // 模态框内容显示类型,设备操作类型
 
-const selectRowIndex = ref(-1); // 选中行
-const dataSource = ref([]);
+  const selectRowIndex = ref(-1); // 选中行
+  const dataSource = ref([]);
 
-const deviceBaseList = ref([]); // 设备基本信息
-const [registerModal, { openModal, closeModal }] = useModal();
+  const deviceBaseList = ref([]); // 设备基本信息
+  const [registerModal, { openModal, closeModal }] = useModal();
 
-const { getCamera, removeCamera } = useCamera();
+  const { getCamera, removeCamera } = useCamera();
 
-const tabChange = (activeKeyVal) => {
-  activeKey.value = activeKeyVal;
-  if (activeKeyVal == 1) {
-    nextTick(() => {
-      if (MonitorDataTable.value) MonitorDataTable.value.setSelectedRowKeys([selectData.deviceID]);
-    });
-  }
-};
+  const tabChange = (activeKeyVal) => {
+    activeKey.value = activeKeyVal;
+    if (activeKeyVal == 1) {
+      nextTick(() => {
+        if (MonitorDataTable.value) MonitorDataTable.value.setSelectedRowKeys([selectData.deviceID]);
+      });
+    }
+  };
 
-const initData = {
-  deviceID: '',
-  deviceType: '',
-  strname: '',
-  frontRearDP: '-', //压差
-  // sourcePressure: '-', //气源压力
-  runRoRecondition: null,
-  autoRoManual: null,
-  netStatus: '0', //通信状态
-  frontGateOpen: '0',
-  frontGateClose: '1',
-  rearGateOpen: '0',
-  rearGateClose: '1',
-  midGateOpen: '0',
-  midGateClose: '1',
-  fault: '气源压力超限',
-  masterComputer: 0,
-  frontGateOpenCtrl: false,
-  rearGateOpenCtrl: false,
-  cameras: [],
-};
+  const initData = {
+    deviceID: '',
+    deviceType: '',
+    strname: '',
+    frontRearDP: '-', //压差
+    // sourcePressure: '-', //气源压力
+    runRoRecondition: null,
+    autoRoManual: null,
+    netStatus: '0', //通信状态
+    frontGateOpen: '0',
+    frontGateClose: '1',
+    rearGateOpen: '0',
+    rearGateClose: '1',
+    midGateOpen: '0',
+    midGateClose: '1',
+    fault: '气源压力超限',
+    masterComputer: 0,
+    frontGateOpenCtrl: false,
+    rearGateOpenCtrl: false,
+    cameras: [],
+  };
 
-// 监测数据
-const selectData = reactive(lodash.cloneDeep(initData));
-function deviceEdit(e: Event, type: string, record) {
-  e.stopPropagation();
-  openModal(true, {
-    type,
-    deviceId: record['deviceID'],
-  });
-}
-// 获取设备基本信息列表
-function getDeviceBaseList() {
-  getTableList({ pageSize: 1000 }).then((res) => {
-    deviceBaseList.value = res.records;
-  });
-}
+  // 监测数据
+  const selectData = reactive(lodash.cloneDeep(initData));
+  function deviceEdit(e: Event, type: string, record) {
+    e.stopPropagation();
+    openModal(true, {
+      type,
+      deviceId: record['deviceID'],
+    });
+  }
+  // 获取设备基本信息列表
+  function getDeviceBaseList() {
+    getTableList({ pageSize: 1000 }).then((res) => {
+      deviceBaseList.value = res.records;
+    });
+  }
 
-// https获取监测数据
-let timer: null | NodeJS.Timeout = null;
-async function getMonitor(flag?) {
-  if (Object.prototype.toString.call(timer) === '[object Null]') {
-    timer = await setTimeout(
-      async () => {
-        const res = await list({ devicetype: deviceType.value, pagetype: 'normal' });
-        if (res.msgTxt && res.msgTxt[0]) {
-          dataSource.value = res.msgTxt[0].datalist || [];
-          dataSource.value.forEach((data: any) => {
-            const readData = data.readData;
-            data = Object.assign(data, readData);
-          });
-          if (dataSource.value.length > 0 && selectRowIndex.value == -1 && MonitorDataTable.value) {
-            // 初始打开页面
-            if (currentRoute.value && currentRoute.value['query'] && currentRoute.value['query']['id']) {
-              MonitorDataTable.value.setSelectedRowKeys([currentRoute.value['query']['id']]);
-            } else {
-              MonitorDataTable.value.setSelectedRowKeys([dataSource.value[0]['deviceID']]);
+  // https获取监测数据
+  let timer: null | NodeJS.Timeout = null;
+  async function getMonitor(flag?) {
+    if (Object.prototype.toString.call(timer) === '[object Null]') {
+      timer = await setTimeout(
+        async () => {
+          const res = await list({ devicetype: deviceType.value, pagetype: 'normal' });
+          if (res.msgTxt && res.msgTxt[0]) {
+            dataSource.value = res.msgTxt[0].datalist || [];
+            dataSource.value.forEach((data: any) => {
+              const readData = data.readData;
+              data = Object.assign(data, readData);
+            });
+            if (dataSource.value.length > 0 && selectRowIndex.value == -1 && MonitorDataTable.value) {
+              // 初始打开页面
+              if (currentRoute.value && currentRoute.value['query'] && currentRoute.value['query']['id']) {
+                MonitorDataTable.value.setSelectedRowKeys([currentRoute.value['query']['id']]);
+              } else {
+                MonitorDataTable.value.setSelectedRowKeys([dataSource.value[0]['deviceID']]);
+              }
             }
+            Object.assign(selectData, dataSource.value[selectRowIndex.value]);
+            monitorAnimation(selectData);
+            if (timer) {
+              timer = null;
+            }
+            getMonitor();
           }
-          Object.assign(selectData, dataSource.value[selectRowIndex.value]);
-          monitorAnimation(selectData);
-          if (timer) {
-            timer = null;
-          }
-          getMonitor();
-        }
-      },
-      flag ? 0 : 1000
-    );
+        },
+        flag ? 0 : 1000
+      );
+    }
   }
-}
 
-// 切换检测数据
-async function getSelectRow(selectRow, index) {
-  if (!selectRow) return;
-  loading.value = true;
-  selectRowIndex.value = index;
-  const baseData: any = deviceBaseList.value.find((baseData: any) => baseData.id === selectRow.deviceID);
-  Object.assign(selectData, initData, selectRow, baseData);
-  doorDeviceState = 1; //记录设备状态,为了与下一次监测数据做比较
-  let type = 'fireDoor';
-  if (selectData.modelType == 'bd_qt') {
-    type = 'fireDoor';
-  } else if (selectData.modelType == 'bd_kj') {
-    type = 'fireDoorF';
+  // 切换检测数据
+  async function getSelectRow(selectRow, index) {
+    if (!selectRow) return;
+    loading.value = true;
+    selectRowIndex.value = index;
+    const baseData: any = deviceBaseList.value.find((baseData: any) => baseData.id === selectRow.deviceID);
+    Object.assign(selectData, initData, selectRow, baseData);
+    doorDeviceState = 1; //记录设备状态,为了与下一次监测数据做比较
+    let type = 'fireDoor';
+    if (selectData.modelType == 'bd_qt') {
+      type = 'fireDoor';
+    } else if (selectData.modelType == 'bd_kj') {
+      type = 'fireDoorF';
+    }
+    await setModelType(type);
+    loading.value = false;
+    isdoorOpenRunning = true; //开关门动作是否在进行
+    await getCamera(selectRow.deviceID, playerRef.value);
   }
-  await setModelType(type);
-  loading.value = false;
-  isdoorOpenRunning = true; //开关门动作是否在进行
-  await getCamera(selectRow.deviceID, playerRef.value);
-}
 
-function playAnimation(handlerState, data: any = null) {
-  const value = data;
-  switch (handlerState) {
-    case 1: // 打开前门
-      modalTitle.value = '打开';
-      modalType.value = '1';
-      modalIsShow.value = true;
-      break;
-    case 2: // 关闭前门
-      modalTitle.value = '关闭';
-      modalType.value = '2';
-      modalIsShow.value = true;
-      break;
-    case 7: // 控制模式切换
-      modalTitle.value = '控制模式切换';
-      modalType.value = '7';
-      modalIsShow.value = true;
-      break;
-  }
+  function playAnimation(handlerState, data: any = null) {
+    const value = data;
+    switch (handlerState) {
+      case 1: // 打开前门
+        modalTitle.value = '打开';
+        modalType.value = '1';
+        modalIsShow.value = true;
+        break;
+      case 2: // 关闭前门
+        modalTitle.value = '关闭';
+        modalType.value = '2';
+        modalIsShow.value = true;
+        break;
+      case 7: // 控制模式切换
+        modalTitle.value = '控制模式切换';
+        modalType.value = '7';
+        modalIsShow.value = true;
+        break;
+    }
 
-  if (globalConfig?.simulatedPassword) {
-    handleOK('', handlerState + '');
+    if (globalConfig?.simulatedPassword) {
+      handleOK('', handlerState + '');
+    }
+    contrlValue = value;
   }
-  contrlValue = value;
-}
 
-function handleOK(passWord, handlerState) {
-  if (!passWord && !globalConfig?.simulatedPassword) {
-    message.warning('请输入密码');
-    return;
-  }
-  if (isOpenRunning) {
-    return;
-  }
-  const data = {
-    deviceid: selectData.deviceID,
-    devicetype: selectData.deviceType,
-    paramcode: '',
-    value: contrlValue,
-    password: passWord || globalConfig?.simulatedPassword,
-    masterComputer: selectData.masterComputer,
-  };
-  switch (handlerState) {
-    case '1': // 打开前门
-      if (selectData.doorOpen == '0' && selectData.doorClose == '1') {
-        data.paramcode = 'frontGateOpen_S';
-      }
-      break;
-    case '2': // 关闭前门
-      if (selectData.doorOpen == '1' && selectData.doorClose == '0') {
-        data.paramcode = 'frontGateClose_S';
-      }
-      break;
-    case '7': // 远程与就地
-      data.paramcode = 'autoRoManualControl';
-      data.value = selectData.contrlMod != 'loopCtrl' ? contrlValue : '';
-      selectData.autoRoManual = null;
-  }
+  function handleOK(passWord, handlerState) {
+    if (!passWord && !globalConfig?.simulatedPassword) {
+      message.warning('请输入密码');
+      return;
+    }
+    if (isOpenRunning) {
+      return;
+    }
+    const data = {
+      deviceid: selectData.deviceID,
+      devicetype: selectData.deviceType,
+      paramcode: '',
+      value: contrlValue,
+      password: passWord || globalConfig?.simulatedPassword,
+      masterComputer: selectData.masterComputer,
+    };
+    switch (handlerState) {
+      case '1': // 打开前门
+        if (selectData.doorOpen == '0' && selectData.doorClose == '1') {
+          data.paramcode = 'frontGateOpen_S';
+        }
+        break;
+      case '2': // 关闭前门
+        if (selectData.doorOpen == '1' && selectData.doorClose == '0') {
+          data.paramcode = 'frontGateClose_S';
+        }
+        break;
+      case '7': // 远程与就地
+        data.paramcode = 'autoRoManualControl';
+        data.value = selectData.contrlMod != 'loopCtrl' ? contrlValue : '';
+        selectData.autoRoManual = null;
+    }
 
-  if (data.paramcode) {
-    deviceControlApi(data).then((res) => {
-      // 模拟时开启
-      if (res.success) {
-        modalIsShow.value = false;
-        if (globalConfig.History_Type == 'remote') {
-          message.success('指令已下发至生产管控平台成功!');
+    if (data.paramcode) {
+      deviceControlApi(data).then((res) => {
+        // 模拟时开启
+        if (res.success) {
+          modalIsShow.value = false;
+          if (globalConfig.History_Type == 'remote') {
+            message.success('指令已下发至生产管控平台成功!');
+          } else {
+            message.success('指令已下发成功!');
+          }
         } else {
-          message.success('指令已下发成功!');
+          message.error(res.message);
         }
-      } else {
-        message.error(res.message);
-      }
-    });
-  }
-}
-let isOpenRunning = false; //开关门动作是否在进行
-/** 开关门动画调用 */
-let isdoorOpenRunning = false; //开关门动作是否在进行
-// let isMidCloseRunning = false; //中间门动作是否在进行
-// 0 关闭 1 正在打开 2 打开 3正在关闭
-let doorDeviceState = 1; //记录设备状态,为了与下一次监测数据做比较
-function monitorAnimation(selectData) {
-  const timeScale = 0.005;
-  // 打开
-  if (selectData.frontGateOpen == '1' && !isdoorOpenRunning) {
-    isdoorOpenRunning = true;
-    if (doorDeviceState != 1) {
-      // import.meta.env.VITE_GLOB_IS_SIMULATE ? play(1, timeScale) : play(1);
-      play(1, timeScale);
-      doorDeviceState = 1;
-      doorIsOpen.value = true;
+      });
     }
   }
-  // 关闭
-  if (selectData.frontGateOpen == '0' && isdoorOpenRunning) {
-    isdoorOpenRunning = false;
-    if (doorDeviceState != 0) {
-      // import.meta.env.VITE_GLOB_IS_SIMULATE ? play(1, timeScale) : play(1);
-      play(2, timeScale);
-      doorDeviceState = 0;
-      doorIsOpen.value = false;
+  let isOpenRunning = false; //开关门动作是否在进行
+  /** 开关门动画调用 */
+  let isdoorOpenRunning = false; //开关门动作是否在进行
+  // let isMidCloseRunning = false; //中间门动作是否在进行
+  // 0 关闭 1 正在打开 2 打开 3正在关闭
+  let doorDeviceState = 1; //记录设备状态,为了与下一次监测数据做比较
+  function monitorAnimation(selectData) {
+    const timeScale = 0.005;
+    // 打开
+    if (selectData.frontGateOpen == '1' && !isdoorOpenRunning) {
+      isdoorOpenRunning = true;
+      if (doorDeviceState != 1) {
+        // import.meta.env.VITE_GLOB_IS_SIMULATE ? play(1, timeScale) : play(1);
+        play(1, timeScale);
+        doorDeviceState = 1;
+        doorIsOpen.value = true;
+      }
+    }
+    // 关闭
+    if (selectData.frontGateOpen == '0' && isdoorOpenRunning) {
+      isdoorOpenRunning = false;
+      if (doorDeviceState != 0) {
+        // import.meta.env.VITE_GLOB_IS_SIMULATE ? play(1, timeScale) : play(1);
+        play(2, timeScale);
+        doorDeviceState = 0;
+        doorIsOpen.value = false;
+      }
     }
-  }
 
-  // if (selectData.frontGateClose == '1' && selectData.frontGateOpen == '0' && isFrontOpenRunning) {
-  //   isFrontOpenRunning = false;
-  //   if (frontDeviceState != 0) {
-  //     // import.meta.env.VITE_GLOB_IS_SIMULATE ? play(2, timeScale) : play(2);
-  //     play(2, timeScale);
-  //     frontDeviceState = 0;
-  //     frontDoorIsOpen.value = false;
-  //     // backDoorIsOpen.value = false
-  //   }
-  // }
-}
+    // if (selectData.frontGateClose == '1' && selectData.frontGateOpen == '0' && isFrontOpenRunning) {
+    //   isFrontOpenRunning = false;
+    //   if (frontDeviceState != 0) {
+    //     // import.meta.env.VITE_GLOB_IS_SIMULATE ? play(2, timeScale) : play(2);
+    //     play(2, timeScale);
+    //     frontDeviceState = 0;
+    //     frontDoorIsOpen.value = false;
+    //     // backDoorIsOpen.value = false
+    //   }
+    // }
+  }
 
-function handleCancel() {
-  modalIsShow.value = false;
-  modalTitle.value = '';
-  modalType.value = '';
-  selectData.autoRoManual = null;
-}
+  function handleCancel() {
+    modalIsShow.value = false;
+    modalTitle.value = '';
+    modalType.value = '';
+    selectData.autoRoManual = null;
+  }
 
-onMounted(async () => {
-  const { query } = unref(currentRoute);
-  if (query['deviceType']) deviceType.value = query['deviceType'] as string;
-  modelList.value = await getDictItems('fireDoorModel');
-  loading.value = true;
-  mountedThree().then(async () => {
-    await getMonitor(true);
-    loading.value = false;
+  onMounted(async () => {
+    const { query } = unref(currentRoute);
+    if (query['deviceType']) deviceType.value = query['deviceType'] as string;
+    modelList.value = await getDictItems('fireDoorModel');
+    loading.value = true;
+    mountedThree().then(async () => {
+      await getMonitor(true);
+      loading.value = false;
+    });
   });
-});
 
-onBeforeUnmount(() => {
-  getDeviceBaseList();
-});
+  onBeforeUnmount(() => {
+    getDeviceBaseList();
+  });
 
-onUnmounted(() => {
-  removeCamera();
-  if (timer) {
-    clearTimeout(timer);
-    timer = undefined;
-  }
-  destroy();
-});
+  onUnmounted(() => {
+    removeCamera();
+    if (timer) {
+      clearTimeout(timer);
+      timer = undefined;
+    }
+    destroy();
+  });
 </script>
 ,
 <style lang="less" scoped>
-@import '/@/design/vent/modal.less';
-.scene-box {
-  .bottom-tabs-box {
-    height: 300px;
+  @import '/@/design/vent/modal.less';
+  .scene-box {
+    .bottom-tabs-box {
+      height: 300px;
+    }
   }
-}
-.button-box {
-  border: none !important;
-  height: 34px !important;
+  .button-box {
+    border: none !important;
+    height: 34px !important;
 
-  &:hover {
-    background: linear-gradient(#2cd1ff55, #1eb0ff55) !important;
-  }
+    &:hover {
+      background: linear-gradient(#2cd1ff55, #1eb0ff55) !important;
+    }
 
-  &::before {
-    height: 27px !important;
-    background: linear-gradient(#1fa6cb, #127cb5) !important;
-  }
+    &::before {
+      height: 27px !important;
+      background: linear-gradient(#1fa6cb, #127cb5) !important;
+    }
 
-  &::after {
-    top: 35px !important;
+    &::after {
+      top: 35px !important;
+    }
   }
-}
 
-:deep(.@{ventSpace}-tabs-tabpane-active) {
-  height: 100%;
-}
+  :deep(.@{ventSpace}-tabs-tabpane-active) {
+    height: 100%;
+  }
 
-::-webkit-scrollbar-thumb {
-  -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
-  background: #4288a444;
-}
-:deep(.zxm-radio-disabled + span) {
-  color: #fff !important;
-}
-:deep(.zxm-radio-disabled .zxm-radio-inner::after) {
-  background-color: #127cb5 !important;
-}
+  ::-webkit-scrollbar-thumb {
+    -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
+    background: #4288a444;
+  }
+  :deep(.zxm-radio-disabled + span) {
+    color: #fff !important;
+  }
+  :deep(.zxm-radio-disabled .zxm-radio-inner::after) {
+    background-color: #127cb5 !important;
+  }
 </style>

+ 249 - 0
src/views/vent/monitorManager/mainFanMonitor/components/entryThree.vue

@@ -0,0 +1,249 @@
+<template>
+  <div class="bg" style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; overflow: hidden">
+    <!-- <a-spin :spinning="loading" /> -->
+    <div
+      id="main3DCSS"
+      class="threejs-Object-CSS"
+      style="width: 100%; height: 100%; position: absolute; pointer-events: none; overflow: hidden; z-index: 1; top: 0"
+    >
+      <div style="position: relative" v-if="selectData['modalTyoe']">
+        <div class="elementTag" id="inputBox1" v-if="backMonitorIsShow && false">
+          <div class="elementContent elementContent-r">
+            <!-- <div class="element-item"><span class="data-title">风机气压(Pa):</span><span>{{ selectData.DataPa ? selectData.DataPa : '-' }}</span></div> -->
+            <div class="element-item"
+              ><span class="data-title">风机负压(Pa):</span><span>{{ selectData.Fan2FanPre ? selectData.Fan2FanPre : '-' }}</span></div
+            >
+            <div class="element-item"
+              ><span class="data-title">风机风量(m³/s):</span><span>{{ selectData.Fan2m3 ? selectData.Fan2m3 : '-' }}</span></div
+            >
+          </div>
+        </div>
+        <div class="elementTag" id="inputBox" v-if="frontMonitorIsShow && false">
+          <div class="elementContent elementContent-r">
+            <!-- <div class="element-item"><span class="data-title">风机全压(Pa):</span><span>{{ selectData.DataPa ? selectData.DataPa : '-' }}</span></div> -->
+            <div class="element-item"
+              ><span class="data-title">风机负压(Pa):</span><span>{{ selectData.Fan1FanPre ? selectData.Fan1FanPre : '-' }}</span></div
+            >
+            <div class="element-item"
+              ><span class="data-title">风机风量(m³/s):</span><span>{{ selectData.Fan1m3 ? selectData.Fan1m3 : '-' }}</span></div
+            >
+          </div>
+        </div>
+        <div class="elementTag" id="inputBox2" v-if="centerMonitorIsShow && false">
+          <div class="elementContent elementContent-r">
+            <!-- <div class="element-item"><span class="data-title">风机气压(Pa):</span><span>{{ selectData.DataPa ? selectData.DataPa : '-' }}</span></div> -->
+            <div class="element-item"
+              ><span class="data-title">风机负压(Pa):</span><span>{{ selectData.Fan3FanPre ? selectData.Fan3FanPre : '-' }}</span></div
+            >
+            <div class="element-item"
+              ><span class="data-title">风机风量(m³/s):</span><span>{{ selectData.Fan3m3 ? selectData.Fan3m3 : '-' }}</span></div
+            >
+          </div>
+        </div>
+        <!-- 18模拟反风锁井 -->
+        <!-- <div v-if="hasPermission('mainFan:ffsjMonitor')" class="elementTag" id="fbm">
+          <div class="elementContent elementContent-r fbm-box">
+            <div class="fbm-video">
+              <LivePlayer id="main-player2" ref="player2" :videoUrl="flvURL2()" muted live />
+              <div class="vent-flex-row-between vent-margin-t-20">
+                <span class="data-title">风门开启状态:</span>
+                <template v-if="selectData['ExplosionVentOpen'] == 1 && selectData['ExplosionVentClose'] == 0">
+                  <span class="data-title"><span class="signal-round signal-round-blue vent-margin-r-8"></span>开启</span>
+                </template>
+                <template v-else-if="selectData['ExplosionVentOpen'] == 0 && selectData['ExplosionVentClose'] == 1">
+                  <span class="data-title"><span class="signal-round signal-round-gry vent-margin-r-8"></span>关闭</span>
+                </template>
+                <template v-else>
+                  <div class="vent-margin-l-10"
+                    ><span class="signal-round signal-round-warning vent-margin-r-8"></span>防爆门正在运行 或 数据异常</div
+                  >
+                </template>
+              </div>
+              <div class="vent-flex-row-between vent-margin-t-10">
+                <span class="data-title">反风锁紧状态:</span>
+                <template
+                  v-if="
+                    selectData['Lock1Open'] == 1 && selectData['Lock1Close'] == 0 && selectData['Lock2Open'] == 1 && selectData['Lock2Close'] == '0'
+                  "
+                >
+                  <span class="data-title"><span class="signal-round signal-round-blue vent-margin-r-8"></span>锁1开</span>
+                  <span class="data-title"><span class="signal-round signal-round-blue vent-margin-r-8"></span>锁2开</span>
+                </template>
+                <template
+                  v-else-if="
+                    selectData['Lock1Open'] == '0' && selectData['Lock1Close'] == 1 && selectData['Lock2Open'] == '0' && selectData['Lock2Close'] == 1
+                  "
+                >
+                  <span class="data-title"><span class="signal-round signal-round-gry vent-margin-r-8"></span>锁1关</span>
+                  <span class="data-title"><span class="signal-round signal-round-gry vent-margin-r-8"></span>锁2关</span>
+                </template>
+                <template v-else>
+                  <div class="vent-margin-l-10"
+                    ><span class="signal-round signal-round-warning vent-margin-r-8"></span>反风锁紧正在运行 或 数据异常</div
+                  >
+                </template>
+              </div>
+            </div>
+            <div class="fbm-data">
+              <div class="element-item"
+                ><span class="data-title">井口负压(kPa):</span
+                ><span>{{ selectData.Fan1FanPre ? selectData.Fan1FanPre : selectData.Fan2FanPre ? selectData.Fan2FanPre : '-' }}</span></div
+              >
+              <div class="element-item"><span class="data-title">井口正压(kPa):</span><span>0</span></div>
+              <div class="element-item"><span class="data-title">井口温度(℃):</span><span>19.132</span></div>
+              <div class="element-item"><span class="data-title">甲烷浓度(%):</span><span>0.36</span></div>
+              <div class="element-item"><span class="data-title">CO浓度(%):</span><span>0</span></div>
+              <div class="vent-flex-row-between">
+                <span class="data-title">操作方式:</span>
+                <span class="data-title"><span class="signal-round signal-round-blue vent-margin-r-8"></span>远程</span>
+                <span class="data-title"><span class="signal-round signal-round-gry vent-margin-r-8"></span>就地</span>
+              </div>
+            </div>
+          </div>
+        </div> -->
+        <div v-if="hasPermission('mainFan:ffsjMonitor')">
+          <div class="elementContent elementContent-r fbm-box">
+            <div class="fbm-video">
+              <div class="vent-flex-row-between vent-margin-t-20">
+                <span class="data-title">风门开启状态:</span>
+                <template v-if="explosionDoorData['gate_1_kai'] == 1">
+                  <span class="data-title"
+                    ><span
+                      class="signal-round vent-margin-r-8"
+                      :class="{ 'signal-round-blue': explosionDoorData['gate_1_kai'] == 1, 'signal-round-gry': explosionDoorData['gate_1_kai'] == 0 }"
+                    ></span
+                    >门1开启</span
+                  >
+                </template>
+                <template v-else-if="explosionDoorData['gate_2_kai'] == 1">
+                  <span class="data-title"
+                    ><span
+                      class="signal-round vent-margin-r-8"
+                      :class="{ 'signal-round-blue': explosionDoorData['gate_1_kai'] == 1, 'signal-round-gry': explosionDoorData['gate_1_kai'] == 0 }"
+                    ></span
+                    >门2开启</span
+                  >
+                </template>
+                <template v-else>
+                  <div class="vent-margin-l-10"
+                    ><span class="signal-round signal-round-warning vent-margin-r-8"></span>防爆门正在运行 或 数据异常</div
+                  >
+                </template>
+              </div>
+              <div class="vent-flex-row-between vent-margin-t-10">
+                <span class="data-title">反风锁紧状态:</span>
+                <div>
+                  <span class="data-title"
+                    ><span
+                      class="signal-round vent-margin-r-8"
+                      :class="{ 'signal-round-blue': explosionDoorData['suo1_kai'] == 1, 'signal-round-gry': explosionDoorData['suo1_kai'] == 0 }"
+                    ></span
+                    >锁1开</span
+                  >
+                  <span class="data-title"
+                    ><span
+                      class="signal-round vent-margin-r-8"
+                      :class="{ 'signal-round-blue': explosionDoorData['suo1_guan'] == 1, 'signal-round-gry': explosionDoorData['suo1_guan'] == 0 }"
+                    ></span
+                    >锁1关</span
+                  >
+                </div>
+                <div>
+                  <span class="data-title"
+                    ><span
+                      class="signal-round vent-margin-r-8"
+                      :class="{ 'signal-round-blue': explosionDoorData['suo2_kai'] == 1, 'signal-round-gry': explosionDoorData['suo2_kai'] == 0 }"
+                    ></span
+                    >锁1开</span
+                  >
+                  <span class="data-title"
+                    ><span
+                      class="signal-round vent-margin-r-8"
+                      :class="{ 'signal-round-blue': explosionDoorData['suo2_guan'] == 1, 'signal-round-gry': explosionDoorData['suo2_guan'] == 0 }"
+                    ></span
+                    >锁2关</span
+                  >
+                </div>
+                <div>
+                  <span class="data-title"
+                    ><span
+                      class="signal-round vent-margin-r-8"
+                      :class="{ 'signal-round-blue': explosionDoorData['suo3_kai'] == 1, 'signal-round-gry': explosionDoorData['suo3_kai'] == 0 }"
+                    ></span
+                    >锁3开</span
+                  >
+                  <span class="data-title"
+                    ><span
+                      class="signal-round vent-margin-r-8"
+                      :class="{ 'signal-round-blue': explosionDoorData['suo3_guan'] == 1, 'signal-round-gry': explosionDoorData['suo3_guan'] == 0 }"
+                    ></span
+                    >锁3关</span
+                  >
+                </div>
+                <div>
+                  <span class="data-title"
+                    ><span
+                      class="signal-round vent-margin-r-8"
+                      :class="{ 'signal-round-blue': explosionDoorData['suo4_kai'] == 1, 'signal-round-gry': explosionDoorData['suo4_kai'] == 0 }"
+                    ></span
+                    >锁4开</span
+                  >
+                  <span class="data-title"
+                    ><span
+                      class="signal-round vent-margin-r-8"
+                      :class="{ 'signal-round-blue': explosionDoorData['suo4_guan'] == 1, 'signal-round-gry': explosionDoorData['suo4_guan'] == 0 }"
+                    ></span
+                    >锁4关</span
+                  >
+                </div>
+              </div>
+            </div>
+            <div class="fbm-data">
+              <div class="vent-flex-row-between">
+                <span class="data-title">操作方式:</span>
+                <span class="data-title"
+                  ><span
+                    class="signal-round signal-round-blue vent-margin-r-8"
+                    :class="{ 'signal-round-blue': explosionDoorData['jd_yc'] == 1 }"
+                  ></span
+                  >远程</span
+                >
+                <span class="data-title"
+                  ><span class="signal-round vent-margin-r-8" :class="{ 'signal-round-blue': explosionDoorData['jd_yc'] == 0 }"></span>就地</span
+                >
+              </div>
+              <div class="vent-flex-row-between">
+                <span class="data-title">是否检修:</span>
+                <span class="data-title"
+                  ><span
+                    class="signal-round signal-round-blue vent-margin-r-8"
+                    :class="{ 'signal-round-blue': explosionDoorData['zc_jx'] == 1 }"
+                  ></span
+                  >正常</span
+                >
+                <span class="data-title"
+                  ><span class="signal-round vent-margin-r-8" :class="{ 'signal-round-blue': explosionDoorData['zc_jx'] == 0 }"></span>检修</span
+                >
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div id="main3D" style="width: 100%; height: 100%; position: absolute; overflow: hidden"> </div>
+    <FanEchrats id="fan-echarts" :chartData="selectData" style="position: absolute; z-index: -1" />
+  </div>
+</template>
+<script lang="ts" setup>
+  import { usePermission } from '/@/hooks/web/usePermission';
+
+  defineProps<{
+    loading: boolean;
+    selectData: Record<string, any>;
+    explosionDoorData: Record<string, any>;
+    centerMonitorIsShow: boolean;
+    frontMonitorIsShow: boolean;
+    backMonitorIsShow: boolean;
+  }>();
+  const { hasPermission } = usePermission();
+</script>

文件差异内容过多而无法显示
+ 699 - 0
src/views/vent/monitorManager/mainFanMonitor/components/mainFanSVG.vue


+ 18 - 236
src/views/vent/monitorManager/mainFanMonitor/index.vue

@@ -1,238 +1,14 @@
 <template>
-  <div class="bg" style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; overflow: hidden">
-    <!-- <a-spin :spinning="loading" /> -->
-    <div
-      id="main3DCSS"
-      class="threejs-Object-CSS"
-      style="width: 100%; height: 100%; position: absolute; pointer-events: none; overflow: hidden; z-index: 1; top: 0"
-    >
-      <div style="position: relative" v-if="selectData['modalTyoe']">
-        <div class="elementTag" id="inputBox1" v-if="backMonitorIsShow && false">
-          <div class="elementContent elementContent-r">
-            <!-- <div class="element-item"><span class="data-title">风机气压(Pa):</span><span>{{ selectData.DataPa ? selectData.DataPa : '-' }}</span></div> -->
-            <div class="element-item"
-              ><span class="data-title">风机负压(Pa):</span><span>{{ selectData.Fan2FanPre ? selectData.Fan2FanPre : '-' }}</span></div
-            >
-            <div class="element-item"
-              ><span class="data-title">风机风量(m³/s):</span><span>{{ selectData.Fan2m3 ? selectData.Fan2m3 : '-' }}</span></div
-            >
-          </div>
-        </div>
-        <div class="elementTag" id="inputBox" v-if="frontMonitorIsShow && false">
-          <div class="elementContent elementContent-r">
-            <!-- <div class="element-item"><span class="data-title">风机全压(Pa):</span><span>{{ selectData.DataPa ? selectData.DataPa : '-' }}</span></div> -->
-            <div class="element-item"
-              ><span class="data-title">风机负压(Pa):</span><span>{{ selectData.Fan1FanPre ? selectData.Fan1FanPre : '-' }}</span></div
-            >
-            <div class="element-item"
-              ><span class="data-title">风机风量(m³/s):</span><span>{{ selectData.Fan1m3 ? selectData.Fan1m3 : '-' }}</span></div
-            >
-          </div>
-        </div>
-        <div class="elementTag" id="inputBox2" v-if="centerMonitorIsShow && false">
-          <div class="elementContent elementContent-r">
-            <!-- <div class="element-item"><span class="data-title">风机气压(Pa):</span><span>{{ selectData.DataPa ? selectData.DataPa : '-' }}</span></div> -->
-            <div class="element-item"
-              ><span class="data-title">风机负压(Pa):</span><span>{{ selectData.Fan3FanPre ? selectData.Fan3FanPre : '-' }}</span></div
-            >
-            <div class="element-item"
-              ><span class="data-title">风机风量(m³/s):</span><span>{{ selectData.Fan3m3 ? selectData.Fan3m3 : '-' }}</span></div
-            >
-          </div>
-        </div>
-        <!-- 18模拟反风锁井 -->
-        <!-- <div v-if="hasPermission('mainFan:ffsjMonitor')" class="elementTag" id="fbm">
-          <div class="elementContent elementContent-r fbm-box">
-            <div class="fbm-video">
-              <LivePlayer id="main-player2" ref="player2" :videoUrl="flvURL2()" muted live />
-              <div class="vent-flex-row-between vent-margin-t-20">
-                <span class="data-title">风门开启状态:</span>
-                <template v-if="selectData['ExplosionVentOpen'] == 1 && selectData['ExplosionVentClose'] == 0">
-                  <span class="data-title"><span class="signal-round signal-round-blue vent-margin-r-8"></span>开启</span>
-                </template>
-                <template v-else-if="selectData['ExplosionVentOpen'] == 0 && selectData['ExplosionVentClose'] == 1">
-                  <span class="data-title"><span class="signal-round signal-round-gry vent-margin-r-8"></span>关闭</span>
-                </template>
-                <template v-else>
-                  <div class="vent-margin-l-10"
-                    ><span class="signal-round signal-round-warning vent-margin-r-8"></span>防爆门正在运行 或 数据异常</div
-                  >
-                </template>
-              </div>
-              <div class="vent-flex-row-between vent-margin-t-10">
-                <span class="data-title">反风锁紧状态:</span>
-                <template
-                  v-if="
-                    selectData['Lock1Open'] == 1 && selectData['Lock1Close'] == 0 && selectData['Lock2Open'] == 1 && selectData['Lock2Close'] == '0'
-                  "
-                >
-                  <span class="data-title"><span class="signal-round signal-round-blue vent-margin-r-8"></span>锁1开</span>
-                  <span class="data-title"><span class="signal-round signal-round-blue vent-margin-r-8"></span>锁2开</span>
-                </template>
-                <template
-                  v-else-if="
-                    selectData['Lock1Open'] == '0' && selectData['Lock1Close'] == 1 && selectData['Lock2Open'] == '0' && selectData['Lock2Close'] == 1
-                  "
-                >
-                  <span class="data-title"><span class="signal-round signal-round-gry vent-margin-r-8"></span>锁1关</span>
-                  <span class="data-title"><span class="signal-round signal-round-gry vent-margin-r-8"></span>锁2关</span>
-                </template>
-                <template v-else>
-                  <div class="vent-margin-l-10"
-                    ><span class="signal-round signal-round-warning vent-margin-r-8"></span>反风锁紧正在运行 或 数据异常</div
-                  >
-                </template>
-              </div>
-            </div>
-            <div class="fbm-data">
-              <div class="element-item"
-                ><span class="data-title">井口负压(kPa):</span
-                ><span>{{ selectData.Fan1FanPre ? selectData.Fan1FanPre : selectData.Fan2FanPre ? selectData.Fan2FanPre : '-' }}</span></div
-              >
-              <div class="element-item"><span class="data-title">井口正压(kPa):</span><span>0</span></div>
-              <div class="element-item"><span class="data-title">井口温度(℃):</span><span>19.132</span></div>
-              <div class="element-item"><span class="data-title">甲烷浓度(%):</span><span>0.36</span></div>
-              <div class="element-item"><span class="data-title">CO浓度(%):</span><span>0</span></div>
-              <div class="vent-flex-row-between">
-                <span class="data-title">操作方式:</span>
-                <span class="data-title"><span class="signal-round signal-round-blue vent-margin-r-8"></span>远程</span>
-                <span class="data-title"><span class="signal-round signal-round-gry vent-margin-r-8"></span>就地</span>
-              </div>
-            </div>
-          </div>
-        </div> -->
-        <div v-if="hasPermission('mainFan:ffsjMonitor')">
-          <div class="elementContent elementContent-r fbm-box">
-            <div class="fbm-video">
-              <div class="vent-flex-row-between vent-margin-t-20">
-                <span class="data-title">风门开启状态:</span>
-                <template v-if="explosionDoorData['gate_1_kai'] == 1">
-                  <span class="data-title"
-                    ><span
-                      class="signal-round vent-margin-r-8"
-                      :class="{ 'signal-round-blue': explosionDoorData['gate_1_kai'] == 1, 'signal-round-gry': explosionDoorData['gate_1_kai'] == 0 }"
-                    ></span
-                    >门1开启</span
-                  >
-                </template>
-                <template v-else-if="explosionDoorData['gate_2_kai'] == 1">
-                  <span class="data-title"
-                    ><span
-                      class="signal-round vent-margin-r-8"
-                      :class="{ 'signal-round-blue': explosionDoorData['gate_1_kai'] == 1, 'signal-round-gry': explosionDoorData['gate_1_kai'] == 0 }"
-                    ></span
-                    >门2开启</span
-                  >
-                </template>
-                <template v-else>
-                  <div class="vent-margin-l-10"
-                    ><span class="signal-round signal-round-warning vent-margin-r-8"></span>防爆门正在运行 或 数据异常</div
-                  >
-                </template>
-              </div>
-              <div class="vent-flex-row-between vent-margin-t-10">
-                <span class="data-title">反风锁紧状态:</span>
-                <div>
-                  <span class="data-title"
-                    ><span
-                      class="signal-round vent-margin-r-8"
-                      :class="{ 'signal-round-blue': explosionDoorData['suo1_kai'] == 1, 'signal-round-gry': explosionDoorData['suo1_kai'] == 0 }"
-                    ></span
-                    >锁1开</span
-                  >
-                  <span class="data-title"
-                    ><span
-                      class="signal-round vent-margin-r-8"
-                      :class="{ 'signal-round-blue': explosionDoorData['suo1_guan'] == 1, 'signal-round-gry': explosionDoorData['suo1_guan'] == 0 }"
-                    ></span
-                    >锁1关</span
-                  >
-                </div>
-                <div>
-                  <span class="data-title"
-                    ><span
-                      class="signal-round vent-margin-r-8"
-                      :class="{ 'signal-round-blue': explosionDoorData['suo2_kai'] == 1, 'signal-round-gry': explosionDoorData['suo2_kai'] == 0 }"
-                    ></span
-                    >锁1开</span
-                  >
-                  <span class="data-title"
-                    ><span
-                      class="signal-round vent-margin-r-8"
-                      :class="{ 'signal-round-blue': explosionDoorData['suo2_guan'] == 1, 'signal-round-gry': explosionDoorData['suo2_guan'] == 0 }"
-                    ></span
-                    >锁2关</span
-                  >
-                </div>
-                <div>
-                  <span class="data-title"
-                    ><span
-                      class="signal-round vent-margin-r-8"
-                      :class="{ 'signal-round-blue': explosionDoorData['suo3_kai'] == 1, 'signal-round-gry': explosionDoorData['suo3_kai'] == 0 }"
-                    ></span
-                    >锁3开</span
-                  >
-                  <span class="data-title"
-                    ><span
-                      class="signal-round vent-margin-r-8"
-                      :class="{ 'signal-round-blue': explosionDoorData['suo3_guan'] == 1, 'signal-round-gry': explosionDoorData['suo3_guan'] == 0 }"
-                    ></span
-                    >锁3关</span
-                  >
-                </div>
-                <div>
-                  <span class="data-title"
-                    ><span
-                      class="signal-round vent-margin-r-8"
-                      :class="{ 'signal-round-blue': explosionDoorData['suo4_kai'] == 1, 'signal-round-gry': explosionDoorData['suo4_kai'] == 0 }"
-                    ></span
-                    >锁4开</span
-                  >
-                  <span class="data-title"
-                    ><span
-                      class="signal-round vent-margin-r-8"
-                      :class="{ 'signal-round-blue': explosionDoorData['suo4_guan'] == 1, 'signal-round-gry': explosionDoorData['suo4_guan'] == 0 }"
-                    ></span
-                    >锁4关</span
-                  >
-                </div>
-              </div>
-            </div>
-            <div class="fbm-data">
-              <div class="vent-flex-row-between">
-                <span class="data-title">操作方式:</span>
-                <span class="data-title"
-                  ><span
-                    class="signal-round signal-round-blue vent-margin-r-8"
-                    :class="{ 'signal-round-blue': explosionDoorData['jd_yc'] == 1 }"
-                  ></span
-                  >远程</span
-                >
-                <span class="data-title"
-                  ><span class="signal-round vent-margin-r-8" :class="{ 'signal-round-blue': explosionDoorData['jd_yc'] == 0 }"></span>就地</span
-                >
-              </div>
-              <div class="vent-flex-row-between">
-                <span class="data-title">是否检修:</span>
-                <span class="data-title"
-                  ><span
-                    class="signal-round signal-round-blue vent-margin-r-8"
-                    :class="{ 'signal-round-blue': explosionDoorData['zc_jx'] == 1 }"
-                  ></span
-                  >正常</span
-                >
-                <span class="data-title"
-                  ><span class="signal-round vent-margin-r-8" :class="{ 'signal-round-blue': explosionDoorData['zc_jx'] == 0 }"></span>检修</span
-                >
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-    <div id="main3D" style="width: 100%; height: 100%; position: absolute; overflow: hidden"> </div>
-    <FanEchrats id="fan-echarts" :chartData="selectData" style="position: absolute; z-index: -1" />
-  </div>
+  <component
+    ref="modelRef"
+    :loading="loading"
+    :is="modelComponent"
+    :centerMonitorIsShow="centerMonitorIsShow"
+    :frontMonitorIsShow="frontMonitorIsShow"
+    :backMonitorIsShow="backMonitorIsShow"
+    :explosionDoorData="explosionDoorData"
+    :selectData="selectData"
+  />
   <!-- 控制模式 -->
   <div v-if="hasPermission('fan:remote')" class="top-right">
     <div class="control-title">控制模式:</div>
@@ -852,13 +628,13 @@
   import FanDeviceEcharts from '../comment/FanDeviceEcharts.vue';
   import BarAndLine from '../../../../components/chart/BarAndLine.vue';
   import FanEchrats from '/@/views/vent/monitorManager/mainFanMonitor/fanEchrats.vue';
-  import { onBeforeMount, unref, ref, onMounted, inject, onUnmounted, reactive, toRaw, watch, nextTick, defineAsyncComponent } from 'vue';
+  import { onBeforeMount, unref, ref, onMounted, inject, onUnmounted, reactive, toRaw, watch, nextTick, defineAsyncComponent, shallowRef } from 'vue';
   import GroupMonitorTable from '../comment/GroupMonitorTable.vue';
   // // import HistoryTable from '../comment/HistoryTable.vue';
   // import HistoryTable from '../../../vent/comment/history/HistoryTable.vue';
   import AlarmHistoryTable from '../comment/AlarmHistoryTable.vue';
   import HandlerHistoryTable from '../comment/HandlerHistoryTable.vue';
-  import { modalTypeArr, fbmControlData, faultDeviceHeader, PointMonitorType } from './main.data';
+  import { modalTypeArr, fbmControlData, faultDeviceHeader, PointMonitorType, getModelComponent } from './main.data';
   import { deviceControlApi } from '/@/api/vent/index';
   import { mountedThree, destroy, addText, setModelType, playAnimate, resetEcharts } from './main.threejs';
   import LivePlayer from '@liveqing/liveplayer-v3';
@@ -902,6 +678,11 @@
     y: 180,
   });
   const modelList = ref<{ text: string; value: string }[]>([]);
+
+  const modelRef = ref();
+  /** 模型对应的组件,根据实际情况分为二维三维 */
+  const modelComponent = shallowRef(getModelComponent(globalConfig.is2DModel));
+
   let modeValue: null | string | number = null;
   const playerRef = ref();
   const isSimulation = true; // 是否模拟状态
@@ -1225,6 +1006,7 @@
 
             addText();
             playAnimate(selectData);
+            modelRef.value?.animate?.(selectData);
           }
 
           if (timer) {

+ 25 - 2
src/views/vent/monitorManager/mainFanMonitor/main.data.ts

@@ -1,10 +1,10 @@
 import { BasicColumn } from '/@/components/Table';
 import { FormSchema } from '/@/components/Table';
-import { rules } from '/@/utils/helper/validator';
-import { reactive } from 'vue';
+import { reactive, defineAsyncComponent } from 'vue';
 import type { EChartsOption } from 'echarts';
 import { useGlobSetting } from '/@/hooks/setting';
 import { cloneDeep } from 'lodash-es';
+import EntryThree from './components/entryThree.vue';
 
 type CtrlLockOpenType = {
   CtrlLockOpen: boolean | undefined;
@@ -2020,3 +2020,26 @@ export const lineFormData = reactive({
   min: null,
   max: null,
 });
+
+const componentsCaches = new Map<string, any>();
+export function getModelComponent(is2DModel: boolean = false, type: string = '') {
+  if (!is2DModel) return EntryThree;
+  // @ts-ignore
+  return defineAsyncComponent(() => {
+    // 为了支持SVG组件切换时不闪烁,先行下载并缓存
+    if (!componentsCaches.has('mainFanSVG')) componentsCaches.set('mainFanSVG', import('./components/mainFanSVG.vue'));
+
+    switch (type) {
+      case 'mainWindRect':
+        return componentsCaches.get('mainFanSVG');
+      case 'mainXjWindRect':
+        return componentsCaches.get('mainFanSVG');
+      case 'mainLjWindRect':
+        return componentsCaches.get('mainFanSVG');
+      case 'mainWindRect3':
+        return componentsCaches.get('mainFanSVG');
+      default:
+        return componentsCaches.get('mainFanSVG');
+    }
+  });
+}

+ 1 - 0
src/views/vent/monitorManager/mainFanMonitor/main.threejs.ts

@@ -536,6 +536,7 @@ export const setModelType = (type) => {
 export const mountedThree = (playerVal1) => {
   return new Promise(async (resolve) => {
     model = new UseThree('#main3D', '#main3DCSS');
+    if (!model || !model.renderer || !model.camera) return;
     model.setEnvMap('test1.hdr');
     model.renderer.toneMappingExposure = 1.0;
     if (model.renderer) {

+ 11 - 4
src/views/vent/monitorManager/windowMonitor/components/windowDualSVG.vue

@@ -2283,7 +2283,9 @@
     if (data.OpenDegree) {
       const progress = _.round(data.OpenDegree / 90, 2);
       if (progress > 0) {
-        triggerAnimation(['Chuang2_shanye_0_Layer0_0_FILL'], false, 3000, progress);
+        triggerAnimation(['Chuang2_shanye_0_Layer0_0_FILL'], false, {
+          progress,
+        });
       } else {
         triggerAnimation(['Chuang2_shanye_0_Layer0_0_FILL'], true);
       }
@@ -2292,7 +2294,9 @@
     if (data.OpenDegree1) {
       const progress = _.round(data.OpenDegree1 / 90, 2);
       if (progress > 0) {
-        triggerAnimation(['Chuang2_shanye_0_Layer0_0_FILL'], false, 3000, progress);
+        triggerAnimation(['Chuang2_shanye_0_Layer0_0_FILL'], false, {
+          progress,
+        });
       } else {
         triggerAnimation(['Chuang2_shanye_0_Layer0_0_FILL'], true);
       }
@@ -2300,7 +2304,9 @@
     if (data.OpenDegree2) {
       const progress = _.round(data.OpenDegree2 / 90, 2);
       if (progress > 0) {
-        triggerAnimation(['Chuang1_shanye_0_Layer0_0_FILL'], false, 3000, progress);
+        triggerAnimation(['Chuang1_shanye_0_Layer0_0_FILL'], false, {
+          progress,
+        });
       } else {
         triggerAnimation(['Chuang1_shanye_0_Layer0_0_FILL'], true);
       }
@@ -2308,7 +2314,8 @@
     // if (data.OpenDegree3) {
     //   const progress = _.round(data.OpenDegree3 / 90, 2);
     //   if (progress > 0) {
-    //     triggerAnimation(['Chuang2_shanye_0_Layer0_0_FILL'], false, 3000, progress);
+    //     triggerAnimation(['Chuang2_shanye_0_Layer0_0_FILL'], false, {
+    // progress});
     //   } else {
     //     triggerAnimation(['Chuang2_shanye_0_Layer0_0_FILL'], true);
     //   }

+ 3 - 1
src/views/vent/monitorManager/windowMonitor/components/windowSVG.vue

@@ -1638,7 +1638,9 @@
     // 当前面积 / 最大面积 = 风窗开度 = 动画进度
     const progress = _.round(parseFloat(data.frontArea) / parseFloat(maxarea), 2);
     if (progress > 0) {
-      triggerAnimation(['Chuang1_shanye_0_Layer0_0_FILL'], false, 3000, progress);
+      triggerAnimation(['Chuang1_shanye_0_Layer0_0_FILL'], false, {
+        progress,
+      });
     } else {
       triggerAnimation(['Chuang1_shanye_0_Layer0_0_FILL'], true);
     }

部分文件因为文件数量过多而无法显示