Jelajahi Sumber

瓦斯进尺与涌出分析-打包更新

lxh 2 hari lalu
induk
melakukan
5a27e55c7c

+ 1 - 0
package.json

@@ -80,6 +80,7 @@
     "vue": "^3.2.0",
     "vue-cropper": "^0.6.2",
     "vue-cropperjs": "^5.0.0",
+    "vue-draggable-resizable": "^3.0.0",
     "vue-i18n": "9.2.2",
     "vue-infinite-scroll": "^2.0.2",
     "vue-json-pretty": "^2.2.4",

+ 2 - 2
public/js/config.js

@@ -20,5 +20,5 @@ const VENT_PARAM = {
   gasControlMock: true, // 项目关于瓦斯自主调控,是否模拟演示, true: 真实调控; false 模拟调控
   historyIsMultiple: false, // 设备历史数据是否支持多选
   isShowQy: true, // 是否显示气压
-  is2DModel: false, // 是否尽最大可能使用2D模型
-}
+  is2DModel: true, // 是否尽最大可能使用2D模型
+}

TEMPAT SAMPAH
src/assets/images/camera.png


+ 1 - 1
src/views/vent/home/configurable/ceshi.json

@@ -1,4 +1,4 @@
-{
+ {
         "cmd": "monitordata",
         "msgTxt": [
             {

+ 19 - 20
src/views/vent/home/configurable/configurable.api.ts

@@ -16,7 +16,7 @@ enum Api {
   getTotal = '/safety/ventanalyAlarmLog/total',
   sysTypeWarnList = '/safety/ventanalyAlarmLog/sysTypeWarn',
   getDisasterProportion = '/safety/ventanalyAlarmLog/getDisasterProportion',
-  system = '/ventanaly-device/monitor/system'
+  system = '/ventanaly-device/monitor/device'
 }
 
 // 搞这个缓存是由于:目前代码上的设计是多个模块发出多次请求,每个模块自己负责消费前者的响应。
@@ -627,28 +627,27 @@ export const getElectroData = (params) => {
   return (cache.get(key) as Promise<any>).then(async (res) => {
     let data = res.msgTxt.find(el => el.type == 'ballvalve_auto')
     data.tempData = data?.datalist?.map((el, index) => {
-    return {
-      areaName: el.readData.areaName,
-      tempStart: el.readData.tempStart,
-      tempStop: el.readData.tempStop,
-      CORealtime: el.readData.CORealtime
-    }
-  })
-
-  if (data?.datalist) {
-    data.datalist.forEach(el => {
-      el.cardData = {
-        title: el.strinstallpos, areaText: '区域', areaVal: el.readData.areaName, moduleText: '模式', moduleVal: el.readData.smokePattern, statusText: '烟雾传感器状态', statusVal: el.readData.smokeSensorStatus == 'False' ? '正常-低电平' : '异常', phoneText: '机号', phoneVal: el.readData.deviceName, tempNowText: '实时测温', tempNowVal: el.readData.tempRealtime, tempOpenText: '开启温度', tempOpenVal: el.readData.tempStart, timeText: '延时t1', timeVal: 0, tempMaxText: '最高温度', tempMaxVal: el.readData.tempMax, tempCloseText: '关闭温度', tempCloseVal: el.readData.tempMin, time3Text: '延时t3', time3Val: 0, deviceSTAT: el.readData.deviceSTAT == '1' ? true : false
-      }
-    })
-    data.chartData = data.datalist.map(el => {
       return {
-        time: el.readData.areaName,
-        coRealTime: el.readData.CORealtime,
-        coWarn: el.readData.COWarn,
+        areaName: el.readData.areaName,
+        tempStart: el.readData.tempStart,
+        tempStop: el.readData.tempStop,
+        CORealtime: el.readData.CORealtime
       }
     })
-  }
+    if (data?.datalist) {
+      data.datalist.forEach(el => {
+        el.cardData = {
+          title: el.strinstallpos, areaText: '区域', areaVal: el.readData.areaName, moduleText: '模式', moduleVal: el.readData.smokePattern, statusText: '烟雾传感器状态', statusVal: el.readData.smokeSensorStatus == 'False' ? '正常-低电平' : '异常', phoneText: '机号', phoneVal: el.readData.deviceName, tempNowText: '实时测温', tempNowVal: el.readData.tempRealtime, tempOpenText: '开启温度', tempOpenVal: el.readData.tempStart, timeText: '延时t1', timeVal: 0, tempMaxText: '最高温度', tempMaxVal: el.readData.tempMax, tempCloseText: '关闭温度', tempCloseVal: el.readData.tempMin, time3Text: '延时t3', time3Val: 0, deviceSTAT: el.readData.deviceSTAT == '1' ? true : false
+        }
+      })
+      data.chartData = data.datalist.map(el => {
+        return {
+          time: el.readData.areaName,
+          coRealTime: el.readData.CORealtime,
+          coWarn: el.readData.COWarn,
+        }
+      })
+    }
     return data;
   });
 };

+ 17 - 0
src/views/vent/monitorManager/airDoor/airdoor.api.ts

@@ -0,0 +1,17 @@
+import { defHttp } from '/@/utils/http/axios';
+
+
+enum Api {
+getDevice = '/monitor/device',
+getCameraUrl = '/monitor/camera/queryByCameraCode',
+}
+
+
+
+/**
+* 风门列表
+* @param params
+*/
+export const getDevice = (params) => defHttp.post({ url: Api.getDevice,params });
+
+export const cameraAddr = (params) => defHttp.get({ url: Api.getCameraUrl, params });

+ 17 - 0
src/views/vent/monitorManager/airDoor/airdoor.data.ts

@@ -0,0 +1,17 @@
+import { defineAsyncComponent } from 'vue';
+import EntryThree from './components/entryThree.vue';
+
+export function getModelComponent(is2DModel: boolean = false, sysOrgCode?: string) {
+  if (!is2DModel) return EntryThree;
+  return defineAsyncComponent(() => {
+    // return import('./components/gateTripleSVG.vue');
+    switch (sysOrgCode) {
+      // case '000000':
+      //   双道风门
+      //   return import('./components/gateDualSVG.vue');
+      default:
+        // return import('./components/gateTripleSVG.vue');
+        return import('./components/gateDualSVG.vue');
+    }
+  });
+}

+ 863 - 0
src/views/vent/monitorManager/airDoor/airdoor.threejs.ts

@@ -0,0 +1,863 @@
+import * as THREE from 'three';
+import UseThree from '../../../../utils/threejs/useThree';
+// import Fm1 from './gate.threejs.yy';
+// import Fm3 from './gate.threejs.qd';
+import FmXR from './gate.threejs.xr';
+// import Fm2 from './gate.threejs.three';
+// import FmTwoSs from './gate.threejs.two.ss';
+// import FmThreeTl from './gate.threejs.three.tl';
+// import FmDc from './gate.threejs.window';
+// import FmDcHJG from './gate.threejs.window.hjg';
+// import FmDcZHQ from './gate.threejs.window.zhq';
+// import FmHsw3 from './gate.threejs.three.hsw';
+// import FmYjXr from './gate.threejs.two.yj'; // 窑街行人
+// import FmYj from './gate.threejs.yj'; // 窑街
+// import FmSp1 from './gate.threejs.one.sp';
+import { animateCamera } from '/@/utils/threejs/util';
+import useEvent from '../../../../utils/threejs/useEvent';
+import { getDictItemsByCode } from '/@/utils/dict';
+import { useGlobSetting } from '/@/hooks/setting';
+
+// 模型对象、 文字对象
+let model,
+  fm1, //液压风门
+  fm2, //三道风门收缩
+  fm3, //气动风门
+  fmXr: FmXR, //行人风门
+  fmTwoSs, //
+  fmThreeTl, // 三道推拉
+  fmWindowHjg, // 带风窗
+  fmWindowZhq, // 带风窗 沼和泉
+  fmWindow, // 带风窗
+  fmHsw3, // 海石湾拱形三道风门
+  fmYjXr, // 窑街拱形行人风门
+  fmYj, // 窑街拱形行车风门
+  fmSp1, // 沙坪一道风门
+  group: THREE.Object3D,
+  fmType = '',
+  windowType = 'singleWindow';
+
+const rotationParam = {
+  frontLeftDeg0: 0, // 前门初始
+  frontLeftDeg1: 0, // 前门目标
+  backLeftDeg0: 0, // 后门初始
+  backLeftDeg1: 0, // 后门目标
+  frontRightDeg0: 0, // 前门初始
+  frontRightDeg1: 0, // 前门目标
+  backRightDeg0: 0, // 后门初始
+  backRightDeg1: 0, // 后门目标
+};
+
+const { mouseDownFn } = useEvent();
+
+// 初始化左右摇摆动画
+const startAnimation = () => {
+  if (!model) return;
+  // 定义鼠标点击事件
+  model.canvasContainer?.addEventListener('mousedown', mouseEvent.bind(null));
+  model.canvasContainer?.addEventListener('pointerup', (event) => {
+    event.stopPropagation();
+    // 单道、 双道
+    if (fmType === 'fm1') {
+      fm1?.mouseUpModel.call(fm1);
+    } else if (fmType === 'fm2') {
+      fm2?.mouseUpModel.call(fm2);
+    } else if (fmType === 'fmThreeTl') {
+      fmThreeTl?.mouseUpModel.call(fmThreeTl);
+    } else if (fmType === 'fm3') {
+      fm3?.mouseUpModel.call(fm3);
+    } else if (fmType === 'fmXr') {
+      fmXr?.mouseUpModel.call(fmXr);
+    } else if (fmType === 'fmTwoSs') {
+      fmTwoSs?.mouseUpModel.call(fmTwoSs);
+    } else if (fmType === 'fmWindow') {
+      fmWindow.mouseUpModel.call(fmWindow);
+    } else if (fmType === 'fmWindowHjg') {
+      fmWindowHjg.mouseUpModel();
+    } else if (fmType === 'fmWindowZhq') {
+      fmWindowZhq.mouseUpModel();
+    } else if (fmType === 'fmHsw3') {
+      fmHsw3.mouseUpModel();
+    } else if (fmType === 'fmYjXr') {
+      fmYjXr.mouseUpModel();
+    } else if (fmType === 'fmYj') {
+      fmYj.mouseUpModel();
+    } else if (fmType === 'fmSp1') {
+      fmSp1.mouseUpModel();
+    }
+  });
+};
+
+// 鼠标点击、松开事件
+const mouseEvent = (event) => {
+  if (!model) return;
+  if (event.button == 0) {
+    mouseDownFn(model, group, event, (intersects) => {
+      if (fmType === 'fm1' && fm1) {
+        fm1?.mousedownModel.call(fm1, intersects);
+      } else if (fmType === 'fm2' && fm2) {
+        fm2?.mousedownModel.call(fm2, intersects);
+      } else if (fmType === 'fm3' && fm3) {
+        fm3?.mousedownModel.call(fm3, intersects);
+      } else if (fmType === 'fmXr' && fmXr) {
+        fmXr?.mousedownModel.call(fmXr, intersects);
+      } else if (fmType === 'fmTwoSs' && fmTwoSs) {
+        fmTwoSs?.mousedownModel.call(fmTwoSs, intersects);
+      } else if (fmType === 'fmThreeTl') {
+        fmThreeTl?.mousedownModel.call(fmThreeTl, intersects);
+      } else if (fmType === 'fmWindow' && fmWindow) {
+        fmWindow.mousedownModel.call(fmWindow, intersects);
+      } else if (fmType === 'fmWindowHjg' && fmWindowHjg) {
+        fmWindowHjg.mousedownModel(intersects);
+      } else if (fmType === 'fmWindowZhq' && fmWindowZhq) {
+        fmWindowZhq.mousedownModel(intersects);
+      } else if (fmType === 'fmHsw3' && fmHsw3) {
+        fmHsw3.mousedownModel(intersects);
+      } else if (fmType === 'fmYjXr' && fmYjXr) {
+        fmYjXr.mousedownModel(intersects);
+      } else if (fmType === 'fmYj' && fmYj) {
+        fmYj.mousedownModel(intersects);
+      } else if (fmType === 'fmSp1') {
+        fmSp1.mousedownModel(intersects);
+      }
+    });
+    console.log('摄像头控制信息', model.orbitControls, model.camera);
+  }
+};
+
+export const addMonitorText = (selectData) => {
+  if (!model) return;
+  if (fmType === 'fm1' && fm1) {
+    return fm1?.addMonitorText.call(fm1, selectData);
+  } else if (fmType === 'fm2' && fm2) {
+    return fm2?.addMonitorText.call(fm2, selectData);
+  } else if (fmType === 'fm3' && fm3) {
+    return fm3?.addMonitorText.call(fm3, selectData);
+  } else if (fmType === 'fmXr' && fmXr) {
+    return fmXr?.addMonitorText.call(fmXr, selectData);
+  } else if (fmType === 'fmTwoSs' && fmTwoSs) {
+    return fmTwoSs?.addMonitorText.call(fmTwoSs, selectData);
+  } else if (fmType === 'fmThreeTl') {
+    fmThreeTl?.addMonitorText.call(fmThreeTl, selectData);
+  } else if (fmType === 'fmWindow' && fmWindow) {
+    fmWindow.addMonitorText.call(fmWindow, selectData);
+  } else if (fmType === 'fmWindowHjg' && fmWindowHjg) {
+    fmWindowHjg.addMonitorText(selectData);
+  } else if (fmType === 'fmWindowZhq' && fmWindowZhq) {
+    fmWindowZhq.addMonitorText(selectData);
+  } else if (fmType === 'fmHsw3' && fmHsw3) {
+    fmHsw3.addMonitorText(selectData);
+  } else if (fmType === 'fmYjXr' && fmYjXr) {
+    fmYjXr.addMonitorText(selectData);
+  } else if (fmType === 'fmYj' && fmYj) {
+    fmYj.addMonitorText(selectData);
+  } else if (fmType === 'fmSp1' && fmSp1) {
+    fmSp1.addMonitorText(selectData);
+  }
+};
+
+export const deviceDetailCard = () => {
+  if (!model) return;
+  if (fmType === 'fm1') {
+    return fm1?.deviceDetailCard.call(fm1);
+  } else if (fmType === 'fm3') {
+    return fm3?.deviceDetailCard.call(fm3);
+  } else if (fmType === 'fmXr') {
+    return fmXr?.deviceDetailCard.call(fmXr);
+  } else {
+    // return fm2.addMonitorText.call(fm2);
+  }
+};
+
+export const play = (handlerState, flag?) => {
+  if (!model) return;
+  if (fmType === 'fm1' && fm1) {
+    return fm1.play.call(fm1, handlerState, flag);
+  } else if (fmType === 'fm2' && fm2) {
+    return fm2.play.call(fm2, handlerState, flag);
+  } else if (fmType === 'fmWindow' && fmWindow) {
+    return fmWindow.play.call(fmWindow, handlerState, flag);
+  } else if (fmType === 'fmWindowHjg' && fmWindowHjg) {
+    return fmWindowHjg.play.call(fmWindowHjg, handlerState, flag);
+  } else if (fmType === 'fmWindowZhq' && fmWindowZhq) {
+    return fmWindowZhq.play.call(fmWindowZhq, handlerState, flag);
+  } else if (fmType === 'fm3' && fm3) {
+    return fm3.play.call(fm3, handlerState, flag);
+  } else if (fmType === 'fmXr' && fmXr) {
+    return fmXr.play.call(fmXr, handlerState, flag);
+  } else if (fmType === 'fmTwoSs' && fmTwoSs) {
+    return fmTwoSs.play.call(fmTwoSs, handlerState, flag);
+  } else if (fmType === 'fmThreeTl') {
+    return fmThreeTl?.play.call(fmThreeTl, handlerState, flag);
+  } else if (fmType === 'fmHsw3') {
+    return fmHsw3?.play.call(fmHsw3, handlerState, flag);
+  } else if (fmType === 'fmYjXr') {
+    return fmYjXr?.play.call(fmYjXr, handlerState, flag);
+  } else if (fmType === 'fmYj') {
+    return fmYj?.play.call(fmYj, handlerState, flag);
+  } else if (fmType === 'fmSp1') {
+    return fmSp1?.play(handlerState, flag);
+  }
+};
+
+// export const playWindow = (rotationParam, flag) => {
+//   if (fmType === 'fmWindow' && fmWindow) {
+//     return fmWindow.playWindow.call(fmWindow, rotationParam, flag);
+//   } else if (fmType === 'fmWindowHjg' && fmWindowHjg) {
+//     return fmWindowHjg.playWindow.call(fmWindowHjg, rotationParam, flag);
+//   }
+// };
+
+export function computePlay(data, maxarea, isFirst = false) {
+  if (!model) return;
+  // 前门后窗  rearPresentValue1
+  // 前门前窗 frontPresentValue1
+  // 后门后窗 rearPresentValue2
+  // 后门前窗 frontPresentValue2
+  // data['frontArea'] = 70;
+  // data['rearArea'] = 40;
+
+  if (
+    (fmType === 'fmWindowHjg' || fmType === 'fmWindowZhq') &&
+    (data.rearPresentValue1 || data.frontPresentValue1 || data.rearPresentValue2 || data.frontPresentValue2)
+  ) {
+    maxarea = 90;
+    rotationParam.frontLeftDeg0 = (90 / maxarea) * Number(isFirst ? 0 : data.frontPresentValue1);
+    rotationParam.frontRightDeg0 = (90 / maxarea) * Number(isFirst ? 0 : data.rearPresentValue1);
+    rotationParam.frontLeftDeg1 = (90 / maxarea) * Number(data.frontPresentValue1) || 0;
+    rotationParam.frontRightDeg1 = (90 / maxarea) * Number(data.rearPresentValue1) || 0;
+    rotationParam.backLeftDeg0 = (90 / maxarea) * Number(isFirst ? 0 : data.frontPresentValue2);
+    rotationParam.backRightDeg0 = (90 / maxarea) * Number(isFirst ? 0 : data.rearPresentValue2);
+    rotationParam.backLeftDeg1 = (90 / maxarea) * Number(data.frontPresentValue2) || 0;
+    rotationParam.backRightDeg1 = (90 / maxarea) * Number(data.rearPresentValue2) || 0;
+
+    rotationParam.backLeftDeg1 = 90;
+    if (fmType === 'fmWindowHjg') {
+      fmWindowHjg.playWindow(rotationParam, 1);
+      fmWindowHjg.playWindow(rotationParam, 2);
+      fmWindowHjg.playWindow(rotationParam, 3);
+      fmWindowHjg.playWindow(rotationParam, 4);
+    } else {
+      fmWindowZhq.playWindow(rotationParam, 1);
+      fmWindowZhq.playWindow(rotationParam, 2);
+      fmWindowZhq.playWindow(rotationParam, 3);
+      fmWindowZhq.playWindow(rotationParam, 4);
+    }
+  } else if (fmType === 'fmWindow' && data.frontPresentValue1 && data.frontPresentValue2 && data.rearPresentValue1 && data.rearPresentValue2) {
+    maxarea = 90;
+    rotationParam.frontLeftDeg0 = (90 / maxarea) * Number(isFirst ? 0 : data.frontPresentValue1);
+    rotationParam.frontRightDeg0 = (90 / maxarea) * Number(isFirst ? 0 : data.frontPresentValue2);
+    rotationParam.frontLeftDeg1 = (90 / maxarea) * Number(data.frontPresentValue1) || 0;
+    rotationParam.frontRightDeg1 = (90 / maxarea) * Number(data.frontPresentValue2) || 0;
+    rotationParam.backLeftDeg0 = (90 / maxarea) * Number(isFirst ? 0 : data.rearPresentValue1);
+    rotationParam.backRightDeg0 = (90 / maxarea) * Number(isFirst ? 0 : data.rearPresentValue2);
+    rotationParam.backLeftDeg1 = (90 / maxarea) * Number(data.rearPresentValue1) || 0;
+    rotationParam.backRightDeg1 = (90 / maxarea) * Number(data.rearPresentValue2) || 0;
+    fmWindow.playWindow(rotationParam, 1);
+    fmWindow.playWindow(rotationParam, 2);
+    fmWindow.playWindow(rotationParam, 3);
+    fmWindow.playWindow(rotationParam, 4);
+  }
+}
+
+// 切换风门类型
+export const setModelType = (type) => {
+  if (!model) return Promise.reject('model is null');
+  fmType = type;
+  return new Promise((resolve) => {
+    // 暂停风门1动画
+    if (fmType === 'fm1' && fm1 && fm1.group) {
+      if (fm1.clipActionArr.frontDoor && fm1.clipActionArr.backDoor) {
+        fm1.clipActionArr.frontDoor.reset();
+        fm1.clipActionArr.frontDoor.time = 0.5;
+        fm1.clipActionArr.backDoor.reset();
+        fm1.clipActionArr.backDoor.time = 0.5;
+        fm1.clipActionArr.frontDoor.stop();
+        fm1.clipActionArr.backDoor.stop();
+      }
+
+      if (fm1.frontDamperOpenMesh) fm1.frontDamperOpenMesh.visible = false;
+      if (fm1.frontDamperClosedMesh) fm1.frontDamperClosedMesh.visible = true;
+      if (fm1.backDamperOpenMesh) fm1.backDamperOpenMesh.visible = false;
+      if (fm1.backDamperClosedMesh) fm1.backDamperClosedMesh.visible = true;
+      model.scene.remove(group);
+      model.startAnimation = fm1.render.bind(fm1);
+      group = fm1.group;
+      group.rotation.y = 0;
+      const oldCameraPosition = { x: -1000, y: 100, z: 500 };
+      setTimeout(async () => {
+        resolve(null);
+        model.scene.add(fm1.group);
+        await animateCamera(
+          oldCameraPosition,
+          { x: 0, y: 0, z: 0 },
+          { x: 50.99, y: 69.32, z: 93.61 },
+          { x: -10.04, y: -14.38, z: -31.4 },
+          model,
+          0.8
+        );
+      }, 300);
+    } else if (fmType === 'fm2' && fm2 && fm2.group) {
+      if (fm2.clipActionArr.frontDoor && fm2.clipActionArr.backDoor) {
+        fm2.clipActionArr.frontDoor.reset();
+        fm2.clipActionArr.frontDoor.time = 0.5;
+        fm2.clipActionArr.backDoor.reset();
+        fm2.clipActionArr.backDoor.time = 0.5;
+
+        fm2.clipActionArr.centerDoor.reset();
+        fm2.clipActionArr.centerDoor.time = 0.5;
+
+        fm2.clipActionArr.frontDoor.stop();
+        fm2.clipActionArr.backDoor.stop();
+        fm2.clipActionArr.centerDoor.stop();
+      }
+
+      // 显示单道风窗
+      model.startAnimation = fm2.render.bind(fm2);
+      model.scene.remove(group);
+      group = fm2.group;
+      const oldCameraPosition = { x: -761, y: 569, z: 871 };
+      setTimeout(async () => {
+        resolve(null);
+        model.scene.add(fm2.group);
+        const position = { x: -2.28, y: -0.91, z: -5.68 };
+
+        await animateCamera(
+          oldCameraPosition,
+          { x: -2.27, y: -0.91, z: -5.67 },
+          { x: 66.257, y: 57.539, z: 94.313 },
+          { x: position.x, y: position.y, z: position.z },
+          model,
+          0.6
+        );
+      }, 300);
+    } else if (fmType === 'fmWindow' && fmWindow && fmWindow.group) {
+      if (fmWindow.clipActionArr.frontDoor && fmWindow.clipActionArr.backDoor) {
+        fmWindow.clipActionArr.frontDoor.reset();
+        fmWindow.clipActionArr.frontDoor.time = 0.5;
+        fmWindow.clipActionArr.backDoor.reset();
+        fmWindow.clipActionArr.backDoor.time = 0.5;
+
+        fmWindow.clipActionArr.frontDoor.stop();
+        fmWindow.clipActionArr.backDoor.stop();
+      }
+
+      model.startAnimation = fmWindow.render.bind(fmWindow);
+      model.scene.remove(group);
+      group = fmWindow.group;
+      const oldCameraPosition = { x: -761, y: 569, z: 871 };
+      setTimeout(async () => {
+        resolve(null);
+        model.scene.add(fmWindow.group);
+        const position = { x: -2.28, y: -0.91, z: -5.68 };
+
+        await animateCamera(
+          oldCameraPosition,
+          { x: -2.27, y: -0.91, z: -5.67 },
+          { x: 66.257, y: 57.539, z: 94.313 },
+          { x: position.x, y: position.y, z: position.z },
+          model,
+          0.6
+        );
+      }, 300);
+    } else if (fmType === 'fmWindowHjg' && fmWindowHjg && fmWindowHjg.group) {
+      if (fmWindowHjg.clipActionArr.frontDoor && fmWindowHjg.clipActionArr.backDoor) {
+        fmWindowHjg.clipActionArr.frontDoor.reset();
+        fmWindowHjg.clipActionArr.frontDoor.time = 0.5;
+        fmWindowHjg.clipActionArr.backDoor.reset();
+        fmWindowHjg.clipActionArr.backDoor.time = 0.5;
+
+        fmWindowHjg.clipActionArr.frontDoor.stop();
+        fmWindowHjg.clipActionArr.backDoor.stop();
+      }
+
+      model.startAnimation = fmWindowHjg.render.bind(fmWindowHjg);
+      model.scene.remove(group);
+      group = fmWindowHjg.group;
+      const oldCameraPosition = { x: -761, y: 569, z: 871 };
+      setTimeout(async () => {
+        resolve(null);
+        model.scene.add(fmWindowHjg.group);
+        const position = { x: -2.28, y: -0.91, z: -5.68 };
+
+        await animateCamera(
+          oldCameraPosition,
+          { x: -2.27, y: -0.91, z: -5.67 },
+          { x: 66.257, y: 57.539, z: 94.313 },
+          { x: position.x, y: position.y, z: position.z },
+          model,
+          0.6
+        );
+      }, 300);
+    } else if (fmType === 'fmWindowZhq' && fmWindowZhq && fmWindowZhq.group) {
+      if (fmWindowZhq.clipActionArr.frontDoor && fmWindowZhq.clipActionArr.backDoor) {
+        fmWindowZhq.clipActionArr.frontDoor.reset();
+        fmWindowZhq.clipActionArr.frontDoor.time = 0.5;
+        fmWindowZhq.clipActionArr.backDoor.reset();
+        fmWindowZhq.clipActionArr.backDoor.time = 0.5;
+
+        fmWindowZhq.clipActionArr.frontDoor.stop();
+        fmWindowZhq.clipActionArr.backDoor.stop();
+      }
+
+      model.startAnimation = fmWindowZhq.render.bind(fmWindowZhq);
+      model.scene.remove(group);
+      group = fmWindowZhq.group;
+      const oldCameraPosition = { x: -761, y: 569, z: 871 };
+      setTimeout(async () => {
+        resolve(null);
+        model.scene.add(fmWindowZhq.group);
+        const position = { x: -2.28, y: -0.91, z: -5.68 };
+
+        await animateCamera(
+          oldCameraPosition,
+          { x: -2.27, y: -0.91, z: -5.67 },
+          { x: 66.257, y: 57.539, z: 94.313 },
+          { x: position.x, y: position.y, z: position.z },
+          model,
+          0.6
+        );
+      }, 300);
+    } else if (fmType === 'fmThreeTl' && fmThreeTl && fmThreeTl.group) {
+      if (fmThreeTl.clipActionArr.frontDoor && fmThreeTl.clipActionArr.backDoor && fmThreeTl.clipActionArr.centerDoor) {
+        fmThreeTl.clipActionArr.frontDoor.reset();
+        fmThreeTl.clipActionArr.frontDoor.time = 0.5;
+        fmThreeTl.clipActionArr.backDoor.reset();
+        fmThreeTl.clipActionArr.backDoor.time = 0.5;
+        fmThreeTl.clipActionArr.centerDoor.reset();
+        fmThreeTl.clipActionArr.centerDoor.time = 0.5;
+
+        fmThreeTl.clipActionArr.frontDoor.stop();
+        fmThreeTl.clipActionArr.backDoor.stop();
+        fmThreeTl.clipActionArr.centerDoor.stop();
+      }
+      if (fmThreeTl.frontDamperOpenMesh) fmThreeTl.frontDamperOpenMesh.visible = false;
+      if (fmThreeTl.frontDamperClosedMesh) fmThreeTl.frontDamperClosedMesh.visible = true;
+      if (fmThreeTl.centerDamperOpenMesh) fmThreeTl.centerDamperOpenMesh.visible = false;
+      if (fmThreeTl.centerDamperClosedMesh) fmThreeTl.centerDamperClosedMesh.visible = true;
+      if (fmThreeTl.backDamperOpenMesh) fmThreeTl.backDamperOpenMesh.visible = false;
+      if (fmThreeTl.backDamperClosedMesh) fmThreeTl.backDamperClosedMesh.visible = true;
+
+      // 显示单道风窗
+      model.startAnimation = fmThreeTl.render.bind(fmThreeTl);
+      model.scene.remove(group);
+      group = fmThreeTl.group;
+      const oldCameraPosition = { x: -761, y: 569, z: 871 };
+      setTimeout(async () => {
+        resolve(null);
+        model.scene.add(fmThreeTl.group);
+        const position = { x: 31.873075535732386, y: -3.501715880262631, z: -15.490295891616942 };
+
+        await animateCamera(
+          oldCameraPosition,
+          { x: -2.27, y: -0.91, z: -5.67 },
+          { x: 88.60102093060523, y: 53.89462381404774, z: 109.90762232602137 },
+          { x: position.x, y: position.y, z: position.z },
+          model,
+          0.6
+        );
+      }, 300);
+    } else if (fmType === 'fm3' && fm3 && fm3.group) {
+      if (fm3.clipActionArr.frontDoor && fm3.clipActionArr.backDoor) {
+        fm3.clipActionArr.frontDoor.reset();
+        fm3.clipActionArr.frontDoor.time = 0.5;
+        fm3.clipActionArr.backDoor.reset();
+        fm3.clipActionArr.backDoor.time = 0.5;
+        fm3.clipActionArr.frontDoor.stop();
+        fm3.clipActionArr.backDoor.stop();
+      }
+
+      if (fm3.frontDamperOpenMesh) fm3.frontDamperOpenMesh.visible = false;
+      if (fm3.frontDamperClosedMesh) fm3.frontDamperClosedMesh.visible = true;
+      if (fm3.backDamperOpenMesh) fm3.backDamperOpenMesh.visible = false;
+      if (fm3.backDamperClosedMesh) fm3.backDamperClosedMesh.visible = true;
+
+      model.scene.remove(group);
+      model.startAnimation = fm3.render.bind(fm3);
+      group = fm3.group;
+      group.rotation.y = 0;
+      const oldCameraPosition = { x: -1000, y: 100, z: 500 };
+      setTimeout(async () => {
+        resolve(null);
+        model.scene.add(fm3.group);
+        await animateCamera(
+          oldCameraPosition,
+          { x: 0, y: 0, z: 0 },
+          { x: 50.99, y: 69.32, z: 93.61 },
+          { x: -10.04, y: -14.38, z: -31.4 },
+          model,
+          0.8
+        );
+      }, 300);
+    } else if (fmType === 'fmXr' && fmXr && fmXr.group) {
+      if (fmXr.clipActionArr.frontDoor && fmXr.clipActionArr.backDoor) {
+        fmXr.clipActionArr.frontDoor.reset();
+        fmXr.clipActionArr.frontDoor.time = 0.5;
+        fmXr.clipActionArr.backDoor.reset();
+        fmXr.clipActionArr.backDoor.time = 0.5;
+        fmXr.clipActionArr.frontDoor.stop();
+        fmXr.clipActionArr.backDoor.stop();
+      }
+
+      if (fmXr.frontDamperOpenMesh) fmXr.frontDamperOpenMesh.visible = false;
+      if (fmXr.frontDamperClosedMesh) fmXr.frontDamperClosedMesh.visible = true;
+      if (fmXr.backDamperOpenMesh) fmXr.backDamperOpenMesh.visible = false;
+      if (fmXr.backDamperClosedMesh) fmXr.backDamperClosedMesh.visible = true;
+
+      model.startAnimation = fmXr.render.bind(fmXr);
+      model.scene.remove(group);
+      group = fmXr.group;
+      group.rotation.y = 0;
+
+      const oldCameraPosition = { x: -1000, y: 100, z: 500 };
+      setTimeout(async () => {
+        resolve(null);
+        model.scene.add(fmXr.group);
+        await animateCamera(
+          oldCameraPosition,
+          { x: 0, y: 0, z: 0 },
+          { x: 50.99, y: 69.32, z: 93.61 },
+          { x: -10.04, y: -14.38, z: -31.4 },
+          model,
+          0.8
+        );
+      }, 300);
+    } else if (fmType === 'fmTwoSs' && fmTwoSs && fmTwoSs.group) {
+      if (fmTwoSs.clipActionArr.frontDoor && fmTwoSs.clipActionArr.backDoor) {
+        fmTwoSs.clipActionArr.frontDoor.reset();
+        fmTwoSs.clipActionArr.frontDoor.time = 0.5;
+        fmTwoSs.clipActionArr.backDoor.reset();
+        fmTwoSs.clipActionArr.backDoor.time = 0.5;
+        fmTwoSs.clipActionArr.frontDoor.stop();
+        fmTwoSs.clipActionArr.backDoor.stop();
+      }
+
+      if (fmTwoSs.frontDamperOpenMesh) fmTwoSs.frontDamperOpenMesh.visible = false;
+      if (fmTwoSs.frontDamperClosedMesh) fmTwoSs.frontDamperClosedMesh.visible = true;
+      if (fmTwoSs.backDamperOpenMesh) fmTwoSs.backDamperOpenMesh.visible = false;
+      if (fmTwoSs.backDamperClosedMesh) fmTwoSs.backDamperClosedMesh.visible = true;
+
+      model.startAnimation = fmTwoSs.render.bind(fmTwoSs);
+      model.scene.remove(group);
+      group = fmTwoSs.group;
+      group.rotation.y = 0;
+
+      const oldCameraPosition = { x: -1000, y: 100, z: 500 };
+      setTimeout(async () => {
+        resolve(null);
+        model.scene.add(fmTwoSs.group);
+        await animateCamera(
+          oldCameraPosition,
+          { x: 0, y: 0, z: 0 },
+          { x: 50.99, y: 69.32, z: 93.61 },
+          { x: -10.04, y: -14.38, z: -31.4 },
+          model,
+          0.8
+        );
+      }, 300);
+    } else if (fmType === 'fmHsw3' && fmHsw3 && fmHsw3.group) {
+      if (fmHsw3.clipActionArr.frontDoor && fmHsw3.clipActionArr.backDoor) {
+        fmHsw3.clipActionArr.frontDoor.reset();
+        fmHsw3.clipActionArr.frontDoor.time = 0.5;
+        fmHsw3.clipActionArr.backDoor.reset();
+        fmHsw3.clipActionArr.backDoor.time = 0.5;
+        fmHsw3.clipActionArr.frontDoor.stop();
+        fmHsw3.clipActionArr.backDoor.stop();
+      }
+
+      if (fmHsw3.frontDamperOpenMesh) fmHsw3.frontDamperOpenMesh.visible = false;
+      if (fmHsw3.frontDamperClosedMesh) fmHsw3.frontDamperClosedMesh.visible = true;
+      if (fmHsw3.backDamperOpenMesh) fmHsw3.backDamperOpenMesh.visible = false;
+      if (fmHsw3.backDamperClosedMesh) fmHsw3.backDamperClosedMesh.visible = true;
+
+      model.startAnimation = fmHsw3.render.bind(fmHsw3);
+      model.scene.remove(group);
+      group = fmHsw3.group;
+      group.rotation.y = 0;
+
+      const oldCameraPosition = { x: -1000, y: 100, z: 500 };
+      setTimeout(async () => {
+        resolve(null);
+        model.scene.add(fmHsw3.group);
+        await animateCamera(
+          oldCameraPosition,
+          { x: 0, y: 0, z: 0 },
+          { x: 50.99, y: 69.32, z: 93.61 },
+          { x: -10.04, y: -14.38, z: -31.4 },
+          model,
+          0.8
+        );
+      }, 300);
+    } else if (fmType === 'fmYjXr' && fmYjXr && fmYjXr.group) {
+      if (fmYjXr.clipActionArr.frontDoor && fmYjXr.clipActionArr.backDoor) {
+        fmYjXr.clipActionArr.frontDoor.reset();
+        fmYjXr.clipActionArr.frontDoor.time = 0.5;
+        fmYjXr.clipActionArr.backDoor.reset();
+        fmYjXr.clipActionArr.backDoor.time = 0.5;
+        fmYjXr.clipActionArr.frontDoor.stop();
+        fmYjXr.clipActionArr.backDoor.stop();
+      }
+
+      if (fmYjXr.frontDamperOpenMesh) fmYjXr.frontDamperOpenMesh.visible = false;
+      if (fmYjXr.frontDamperClosedMesh) fmYjXr.frontDamperClosedMesh.visible = true;
+      if (fmYjXr.backDamperOpenMesh) fmYjXr.backDamperOpenMesh.visible = false;
+      if (fmYjXr.backDamperClosedMesh) fmYjXr.backDamperClosedMesh.visible = true;
+
+      model.startAnimation = fmYjXr.render.bind(fmYjXr);
+      model.scene.remove(group);
+      group = fmYjXr.group;
+      group.rotation.y = 0;
+
+      const oldCameraPosition = { x: -1000, y: 100, z: 500 };
+      setTimeout(async () => {
+        resolve(null);
+        model.scene.add(fmYjXr.group);
+        await animateCamera(
+          oldCameraPosition,
+          { x: 0, y: 0, z: 0 },
+          { x: 50.99, y: 69.32, z: 93.61 },
+          { x: -10.04, y: -14.38, z: -31.4 },
+          model,
+          0.8
+        );
+      }, 300);
+    } else if (fmType === 'fmYj' && fmYj && fmYj.group) {
+      if (fmYj.clipActionArr.frontDoor && fmYj.clipActionArr.backDoor) {
+        fmYj.clipActionArr.frontDoor.reset();
+        fmYj.clipActionArr.frontDoor.time = 0.5;
+        fmYj.clipActionArr.backDoor.reset();
+        fmYj.clipActionArr.backDoor.time = 0.5;
+        fmYj.clipActionArr.frontDoor.stop();
+        fmYj.clipActionArr.backDoor.stop();
+      }
+
+      if (fmYj.frontDamperOpenMesh) fmYj.frontDamperOpenMesh.visible = false;
+      if (fmYj.frontDamperClosedMesh) fmYj.frontDamperClosedMesh.visible = true;
+      if (fmYj.backDamperOpenMesh) fmYj.backDamperOpenMesh.visible = false;
+      if (fmYj.backDamperClosedMesh) fmYj.backDamperClosedMesh.visible = true;
+
+      model.startAnimation = fmYj.render.bind(fmYj);
+      model.scene.remove(group);
+      group = fmYj.group;
+      group.rotation.y = 0;
+
+      const oldCameraPosition = { x: -1000, y: 100, z: 500 };
+      setTimeout(async () => {
+        resolve(null);
+        model.scene.add(fmYj.group);
+        await animateCamera(
+          oldCameraPosition,
+          { x: 0, y: 0, z: 0 },
+          { x: 50.99, y: 69.32, z: 93.61 },
+          { x: -10.04, y: -14.38, z: -31.4 },
+          model,
+          0.8
+        );
+      }, 300);
+    } else if (fmType === 'fmSp1' && fmSp1 && fmSp1.group) {
+      if (fmSp1.clipActionArr.frontDoor) {
+        fmSp1.clipActionArr.frontDoor.reset();
+        fmSp1.clipActionArr.frontDoor.time = 0.5;
+        fmSp1.clipActionArr.frontDoor.stop();
+      }
+
+      if (fmSp1.frontDamperOpenMesh) fmSp1.frontDamperOpenMesh.visible = false;
+      if (fmSp1.frontDamperClosedMesh) fmSp1.frontDamperClosedMesh.visible = true;
+
+      model.startAnimation = fmSp1.render.bind(fmSp1);
+      model.scene.remove(group);
+      group = fmSp1.group;
+      group.rotation.y = 0;
+
+      const oldCameraPosition = { x: -1000, y: 100, z: 500 };
+      setTimeout(async () => {
+        resolve(null);
+        model.scene.add(fmSp1.group);
+        await animateCamera(
+          oldCameraPosition,
+          { x: 0, y: 0, z: 0 },
+          { x: 50.99, y: 69.32, z: 93.61 },
+          { x: -10.04, y: -14.38, z: -31.4 },
+          model,
+          0.8
+        );
+      }, 300);
+    }
+  });
+};
+
+export const initCameraCanvas = async (playerVal1) => {
+  if (!model) return;
+  if (fmType === 'fm1' && fm1) {
+    return await fm1.initCamera.call(fm1, playerVal1);
+  } else if (fmType === 'fm2' && fm2) {
+    return fm2.initCamera.call(fm2, playerVal1);
+  } else if (fmType === 'fm3' && fm3) {
+    return fm3.initCamera.call(fm3, playerVal1);
+  } else if (fmType === 'fmXr' && fmXr) {
+    return fmXr.initCamera.call(fmXr, playerVal1);
+  }
+};
+
+const loadModel = (code): Promise<any> => {
+  if (code === 'Fm1') return import('./gate.threejs.yy').then((r) => r.default);
+  if (code === 'Fm3') return import('./gate.threejs.qd').then((r) => r.default);
+  if (code === 'FmXR') return import('./gate.threejs.xr').then((r) => r.default);
+  if (code === 'Fm2') return import('./gate.threejs.three').then((r) => r.default);
+  if (code === 'FmTwoSs') return import('./gate.threejs.two.ss').then((r) => r.default);
+  if (code === 'FmThreeTl') return import('./gate.threejs.three.tl').then((r) => r.default);
+  if (code === 'FmDc') return import('./gate.threejs.window').then((r) => r.default);
+  if (code === 'FmDcHJG') return import('./gate.threejs.window.hjg').then((r) => r.default);
+  if (code === 'FmDcZHQ') return import('./gate.threejs.window.zhq').then((r) => r.default);
+  if (code === 'FmHsw3') return import('./gate.threejs.three.hsw').then((r) => r.default);
+  if (code === 'FmYjXr') return import('./gate.threejs.two.yj').then((r) => r.default); // 姚街行人
+  if (code === 'FmYj') return import('./gate.threejs.yj').then((r) => r.default); // 姚街
+  if (code === 'FmSp1') return import('./gate.threejs.one.sp').then((r) => r.default);
+  return import('./gate.threejs.yy').then((r) => r.default);
+};
+
+export const mountedThree = (playerDom) => {
+  // const { sysOrgCode } = useGlobSetting();
+  // const sysOrgCode = 'gsgszdek';
+  return new Promise(async (resolve) => {
+    model = new UseThree('#damper3D', '', '#deviceDetail');
+    model.setEnvMap('test1.hdr');
+    model.renderer.toneMappingExposure = 0.9;
+    model.camera.position.set(100, 0, 1000);
+    const dictCodes = getDictItemsByCode('gateStyle');
+    if (dictCodes && dictCodes.length > 0) {
+      for (let i = 0; i < dictCodes.length; i++) {
+        const dict = dictCodes[i];
+        switch (dict.value) {
+          case 'fmXr':
+            const FmXR = await loadModel('FmXR');
+            fmXr = new FmXR(model);
+            fmXr.mountedThree(playerDom);
+            break;
+          case 'fmYy':
+            const Fm1 = await loadModel('Fm1');
+            fm1 = new Fm1(model);
+            fm1.mountedThree(playerDom);
+            break;
+          case 'gate_qd':
+            const Fm3 = await loadModel('Fm3');
+            fm3 = new Fm3(model);
+            await fm3.mountedThree();
+            break;
+          case 'fmSs':
+            const FmTwoSs = await loadModel('FmTwoSs');
+            fmTwoSs = new FmTwoSs(model);
+            await fmTwoSs.mountedThree();
+            break;
+          case 'fmtl3':
+            const FmThreeTl = await loadModel('FmThreeTl');
+            fmThreeTl = new FmThreeTl(model);
+            await fmThreeTl.mountedThree();
+            break;
+          case 'fmSs3':
+            const Fm2 = await loadModel('Fm2');
+            fm2 = new Fm2(model);
+            await fm2.mountedThree();
+            break;
+          case 'fm_fc_hjg':
+            const FmDcHJG = await loadModel('FmDcHJG');
+            fmWindowHjg = new FmDcHJG(model);
+            await fmWindowHjg.mountedThree();
+            break;
+          case 'fm_fc':
+            const FmDc = await loadModel('FmDc');
+            fmWindow = new FmDc(model);
+            await fmWindow.mountedThree();
+            break;
+          case 'fm_fc_zhq': //fmWindowZhq
+            const FmDcZHQ = await loadModel('FmDcZHQ');
+            fmWindowZhq = new FmDcZHQ(model);
+            await fmWindowZhq.mountedThree();
+            break;
+          case 'fmHsw3':
+            const FmHsw3 = await loadModel('FmHsw3');
+            fmHsw3 = new FmHsw3(model);
+            await fmHsw3.mountedThree();
+            break;
+          case 'fmYjXr':
+            const FmYjXr = await loadModel('FmYjXr');
+            fmYjXr = new FmYjXr(model);
+            await fmYjXr.mountedThree();
+            break;
+          case 'fmYj':
+            const FmYj = await loadModel('FmYj');
+            fmYj = new FmYj(model);
+            await fmYj.mountedThree();
+            break;
+          case 'fmSp1':
+            const FmSp1 = await loadModel('FmSp1');
+            fmSp1 = new FmSp1(model);
+            await fmSp1.mountedThree();
+            break;
+        }
+      }
+      resolve(null);
+    } else {
+      const Fm3 = await loadModel('Fm3');
+      fm3 = new Fm3(model);
+      fm3.mountedThree(playerDom);
+      const FmTwoSs = await loadModel('FmTwoSs');
+      fmTwoSs = new FmTwoSs(model);
+      fmTwoSs.mountedThree(playerDom);
+      const Fm2 = await loadModel('Fm2');
+      fm2 = new Fm2(model);
+      fm2.mountedThree(playerDom);
+      // 三道门
+      const FmThreeTl = await loadModel('FmThreeTl');
+      fmThreeTl = new FmThreeTl(model);
+      if (fmThreeTl) fmThreeTl.mountedThree(playerDom);
+      const FmXR = await loadModel('FmXR');
+      fmXr = new FmXR(model);
+      fmXr.mountedThree(playerDom);
+      // 液压风门
+      const Fm1 = await loadModel('Fm1');
+      fm1 = new Fm1(model);
+      fm1.mountedThree(playerDom);
+      resolve(null);
+    }
+
+    model.animate();
+    // startAnimation();
+  });
+};
+
+export const destroy = () => {
+  if (model) {
+    model.isRender = false;
+    if (fm1) fm1.destroy();
+    if (fm2) fm2.destroy();
+    if (fm3) fm3.destroy();
+    if (fmXr) fmXr.destroy();
+    if (fmTwoSs) fmTwoSs.destroy();
+    if (fmWindowHjg) fmWindowHjg.destroy();
+    if (fmWindowZhq) fmWindowZhq.destroy();
+    if (fmWindow) fmWindow.destroy();
+    if (fmThreeTl) fmThreeTl.destroy();
+    if (fmHsw3) fmHsw3.destroy();
+    if (fmYjXr) fmYjXr.destroy();
+    if (fmYj) fmYj.destroy();
+    if (fmSp1) fmSp1.destroy();
+    fm1 = null;
+    fm2 = null;
+    fm3 = null;
+    fmXr = null;
+    fmWindow = null;
+    fmWindowHjg = null;
+    fmWindowZhq = null;
+    fmThreeTl = null;
+    fmTwoSs = null;
+    fmHsw3 = null;
+    fmYjXr = null;
+    fmYj = null;
+    fmSp1 = null;
+    group = null;
+    model.mixers = [];
+    model.destroy();
+  }
+  model = null;
+};

+ 109 - 0
src/views/vent/monitorManager/airDoor/components/Modal.vue

@@ -0,0 +1,109 @@
+<template>
+  <a-modal ref="modalRef"   width="850px" :visible="props.visible" :wrap-style="{ overflow: 'hidden' }" @ok="handleOk"
+    :mask-closable="maskClosable" :centered="props.centered" :footer="props.footer" @cancel="handleCancel"
+    :confirm-loading="props.confirmLoading" :destroyOnClose="props.destroyOnClose">
+    <slot></slot>
+
+    <template #title>
+      <div ref="modalTitleRef" style="width: 100%; cursor: move">{{ props.title }}</div>
+    </template>
+
+    <template #modalRender="{ originVNode }">
+      <div :style="transformStyle">
+        <component :is="originVNode" />
+      </div>
+    </template>
+  </a-modal>
+</template>
+<script lang="ts" setup>
+import { computed, CSSProperties, ref, watch, watchEffect } from "vue";
+import { useDraggable } from "@vueuse/core";
+
+// 定义Props接口,描述组件的属性
+interface Props {
+  title?: string, // 对话框的标题,默认为"提示"
+  footer?: null, // 对话框的页脚,默认为null
+  visible: boolean, // 对话框是否可见
+  confirmLoading?: boolean, // 确认按钮是否处于加载状态,默认为false
+  destroyOnClose?: boolean, // 对话框关闭时是否销毁组件,默认为false
+  maskClosable?: boolean, // 点击遮罩层是否可关闭对话框,默认为false
+  centered?: boolean, // 对话框是否居中显示,默认为false
+}
+
+// 设置props的默认值
+const props = withDefaults(defineProps<Props>(), { title: '摄像头信息', visible: false, destroyOnClose: false, maskClosable: true })
+
+// 创建ref引用modalTitleRef,用于引用组件中的标题元素
+const modalTitleRef = ref<HTMLElement | null | any>(null);
+
+// 使用useDraggable钩子,获取拖拽相关属性
+const { x, y, isDragging } = useDraggable(modalTitleRef);
+
+// 创建emit函数,用于触发自定义事件
+const emit = defineEmits(['ok', 'update:visible', 'cancel'])
+
+// 处理ok事件的函数
+const handleOk = (e: MouseEvent) => {
+  emit('ok')
+};
+
+// 处理cancel事件的函数
+const handleCancel = () => {
+  emit('update:visible', false)
+  emit('cancel')
+}
+
+// 创建各种响应式数据
+const startX = ref<number>(0); // 记录起始点的 x 坐标
+const startY = ref<number>(0); // 记录起始点的 y 坐标
+const startedDrag = ref(false); // 标志位,表示是否开始拖拽,默认为 false
+const transformX = ref(0); // x 偏移量
+const transformY = ref(0); // y 偏移量
+const preTransformX = ref(0); // 拖拽前的 x 偏移量
+const preTransformY = ref(0); // 拖拽前的 y 偏移量
+const dragRect = ref({ left: 0, right: 0, top: 0, bottom: 0 }); // 可拖拽的边界
+
+// 监听x和y的变化
+watch([x, y], () => { // 监听鼠标移动事件的函数
+  if (!startedDrag.value) { // 如果尚未开始拖拽
+    startX.value = x.value; // 记录起始点的 x 坐标
+    startY.value = y.value; // 记录起始点的 y 坐标
+    const bodyRect = document.body.getBoundingClientRect(); // 获取页面 body 元素的边界信息
+    const titleRect = modalTitleRef.value.getBoundingClientRect(); // 获取 modalTitle 元素的边界信息
+    dragRect.value.right = bodyRect.width - titleRect.width; // 计算可拖拽的最大右边界
+    dragRect.value.bottom = bodyRect.height - titleRect.height; // 计算可拖拽的最大下边界
+    preTransformX.value = transformX.value; // 记录拖拽前的 x 偏移量
+    preTransformY.value = transformY.value; // 记录拖拽前的 y 偏移量
+  }
+  startedDrag.value = true; // 设置已开始拖拽标志
+});
+
+// 监听isDragging的变化
+watch(isDragging, () => { // 监听 isDragging 变量的改变
+  if (!isDragging) { // 如果 isDragging 变为 false
+    startedDrag.value = false; // 将 startedDrag.value 设置为 false,表示拖拽操作结束
+  }
+});
+
+// 使用watchEffect监听响应式数据的变化
+watchEffect(() => { // 响应 startedDrag.value 的变化
+  if (startedDrag.value) { // 如果 startedDrag.value 为 true,表示正在进行拖拽操作
+    transformX.value = // 计算 x 偏移量
+      preTransformX.value +
+      Math.min(Math.max(dragRect.value.left, x.value), dragRect.value.right) -
+      startX.value;
+    transformY.value = // 计算 y 偏移量
+      preTransformY.value +
+      Math.min(Math.max(dragRect.value.top, y.value), dragRect.value.bottom) -
+      startY.value;
+  }
+});
+
+// 计算transformStyle,用于动态设置对话框的位置
+const transformStyle = computed<CSSProperties>(() => {
+  return {
+    transform: `translate(${transformX.value}px, ${transformY.value}px)`,
+  };
+});
+
+</script>

+ 287 - 0
src/views/vent/monitorManager/airDoor/components/cameraModal.vue

@@ -0,0 +1,287 @@
+<template>
+  <div class="camera-modal">
+    <div v-for="(item, index) in addrList" :key="index" class="player-box">
+      <div class="player-name">{{ item.name }}</div>
+      <div style="padding-top: 3px">
+        <template v-if="item.addr.startsWith('rtsp://')">
+          <video :id="`video${index}`" muted autoplay></video>
+          <div class="click-box" @dblclick="goFullScreen(`video${index}`)"></div>
+        </template>
+        <template v-else>
+          <div :id="'player' + index"></div>
+        </template>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { onMounted, onUnmounted, ref, reactive, computed } from 'vue';
+import { useRouter } from 'vue-router';
+import Player, { I18N } from 'xgplayer';
+import ZH from 'xgplayer/es/lang/zh-cn';
+import HlsPlugin from 'xgplayer-hls';
+import FlvPlugin from 'xgplayer-flv';
+import 'xgplayer/dist/index.min.css';
+import { cameraAddr } from '../airdoor.api';
+let props = defineProps({
+  cameraData: {
+    type: Object,
+    default: () => {
+      return {}
+    }
+  }
+})
+
+const playerList = ref([]);
+let addrList = ref<{ name: string; addr: string; cameraRate: number; devicekind: string }[]>([]);
+const webRtcServerList = <any[]>[];
+
+async function getVideoAddrs() {
+  console.log(props.cameraData, 'camera---')
+  clearCamera();
+  playerList.value = [];
+
+
+  const cameraList = <{ name: string; addr: string; cameraRate: number; devicekind: string }[]>[];
+  const cameras = props.cameraData.cameras;
+  for (let i = 0; i < cameras.length; i++) {
+    const item = cameras[i];
+    if (item['devicekind'] === 'toHKRtsp' || item['devicekind'] === 'toHKHLs' || item['devicekind'] === 'HLL' || item['devicekind'] === 'YZG_URL') {
+      // 从海康平台接口获取视频流
+      const videoType = item['devicekind'] === 'toHKRtsp' ? 'rtsp' : '';
+      const devicekindType = item['devicekind'] === 'YZG_URL' ? 'YZG_URL' : ''
+      try {
+        const data = await cameraAddr({ devicekind: devicekindType, cameraCode: item['addr'], videoType });
+        if (data && data['url']) {
+          cameraList.push({ name: item['name'], addr: data['url'], cameraRate: item['cameraRate'], devicekind: item['devicekind'] });
+        }
+
+      } catch (error) { }
+    } else {
+      if (item['addr'].includes('0.0.0.0')) {
+        item['addr'] = item['addr'].replace('0.0.0.0', window.location.hostname);
+      }
+      cameraList.push({ name: item['name'], addr: item['addr'], cameraRate: item['cameraRate'], devicekind: item['devicekind'] });
+    }
+  }
+  addrList.value = cameraList;
+  console.log(addrList.value, ' addrList.value-------------');
+
+}
+
+function getVideo() {
+  const ip = VUE_APP_URL.webRtcUrl;
+  for (let i = 0; i < addrList.value.length; i++) {
+    const item = addrList.value[i];
+    if (item.addr.startsWith('rtsp://')) {
+      const dom = document.getElementById('video' + i) as HTMLVideoElement;
+      dom.muted = true;
+      dom.volume = 0;
+      const webRtcServer = new window['WebRtcStreamer'](dom, location.protocol + ip);
+      webRtcServerList.push(webRtcServer);
+      webRtcServer.connect(item.addr);
+    } else {
+      setNoRtspVideo('player' + i, item.addr, item.cameraRate, item.devicekind);
+    }
+  }
+}
+function clearCamera() {
+  const num = webRtcServerList.length;
+  for (let i = 0; i < num; i++) {
+    if (webRtcServerList[i]) {
+      webRtcServerList[i].disconnect();
+      webRtcServerList[i] = null;
+    }
+  }
+  for (let i = 0; i < playerList.value.length; i++) {
+    const player = playerList.value[i];
+    if (player.destroy) player.destroy();
+  }
+  playerList.value = [];
+}
+
+function setNoRtspVideo(id, videoAddr, cameraRate, devicekind) {
+  const fileExtension = videoAddr.split('.').pop();
+  if (fileExtension === 'flv' || devicekind == 'flv') {
+    const player = new Player({
+      lang: 'zh',
+      id: id,
+      url: videoAddr,
+      width: 589,
+      height: 330,
+      poster: '/src/assets/images/vent/noSinge.png',
+      plugins: [FlvPlugin],
+      fluid: true,
+      autoplay: true,
+      isLive: true,
+      playsinline: true,
+      screenShot: true,
+      whitelist: [''],
+      ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
+      closeVideoClick: true,
+      customConfig: {
+        isClickPlayBack: false,
+      },
+      defaultPlaybackRate: cameraRate || 1,
+      controls: false,
+      flv: {
+        retryCount: 3, // 重试 3 次,默认值
+        retryDelay: 1000, // 每次重试间隔 1 秒,默认值
+        loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
+        fetchOptions: {
+          // 该参数会透传给 fetch,默认值为 undefined
+          mode: 'cors',
+        },
+        targetLatency: 10, // 直播目标延迟,默认 10 秒
+        maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
+        disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
+        maxJumpDistance: 10,
+      },
+    });
+    playerList.value.push(player);
+  }
+  if (fileExtension === 'm3u8' || devicekind == 'm3u8') {
+    let player;
+    if (document.createElement('video').canPlayType('application/vnd.apple.mpegurl')) {
+      // 原生支持 hls 播放
+      player = new Player({
+        lang: 'zh',
+        id: id,
+        url: videoAddr,
+        width: 376,
+        height: 210,
+        isLive: true,
+        autoplay: true,
+        autoplayMuted: true,
+        cors: true,
+        ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
+        poster: '/src/assets/images/vent/noSinge.png',
+        defaultPlaybackRate: cameraRate || 1,
+        controls: false,
+        hls: {
+          retryCount: 3, // 重试 3 次,默认值
+          retryDelay: 1000, // 每次重试间隔 1 秒,默认值
+          loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
+          fetchOptions: {
+            // 该参数会透传给 fetch,默认值为 undefined
+            mode: 'cors',
+          },
+          targetLatency: 10, // 直播目标延迟,默认 10 秒
+          maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
+          disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
+          maxJumpDistance: 10,
+        },
+      });
+    } else if (HlsPlugin.isSupported()) {
+      // 第一步
+      player = new Player({
+        lang: 'zh',
+        id: id,
+        url: videoAddr,
+        width: 376,
+        height: 210,
+        isLive: true,
+        autoplay: true,
+        autoplayMuted: true,
+        plugins: [HlsPlugin], // 第二步
+        poster: '/src/assets/images/vent/noSinge.png',
+        ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
+        defaultPlaybackRate: cameraRate || 1,
+        controls: false,
+        hls: {
+          retryCount: 3, // 重试 3 次,默认值
+          retryDelay: 1000, // 每次重试间隔 1 秒,默认值
+          loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
+          fetchOptions: {
+            // 该参数会透传给 fetch,默认值为 undefined
+            mode: 'cors',
+          },
+          targetLatency: 10, // 直播目标延迟,默认 10 秒
+          maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
+          disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
+          maxJumpDistance: 10,
+        },
+      });
+    }
+    playerList.value.push(player);
+  }
+}
+
+function goFullScreen(domId) {
+  const videoDom = document.getElementById(domId) as HTMLVideoElement;
+  if (videoDom.requestFullscreen) {
+    videoDom.requestFullscreen();
+    videoDom.play();
+  } else if (videoDom.mozRequestFullscreen) {
+    videoDom.mozRequestFullscreen();
+    videoDom.play();
+  } else if (videoDom.webkitRequestFullscreen) {
+    videoDom.webkitRequestFullscreen();
+    videoDom.play();
+  } else if (videoDom.msRequestFullscreen) {
+    videoDom.msRequestFullscreen();
+    videoDom.play();
+  }
+}
+
+onMounted(async () => {
+  await getVideoAddrs();
+  getVideo();
+});
+
+onUnmounted(() => {
+  clearCamera();
+});
+</script>
+
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+
+@{theme-deepblue} {
+  .camera-modal {
+    --image-camera_bg: url('/@/assets/images/themify/deepblue/vent/camera_bg.png');
+  }
+}
+
+.camera-modal {
+  --image-camera_bg: url('/@/assets/images/vent/camera_bg.png');
+  display: flex;
+  justify-content: space-between;
+  flex-wrap: wrap;
+
+  .player-box {
+    width: 400px;
+    height: 235px;
+    padding: 10px 12px;
+    background: var(--image-camera_bg);
+    background-size: 100% 100%;
+    position: relative;
+    margin: 10px;
+
+    .player-name {
+      font-size: 14px;
+      position: absolute;
+      top: 12px;
+      right: 12px;
+      color: #fff;
+      background-color: hsla(0, 0%, 50%, 0.5);
+      border-radius: 2px;
+      padding: 1px 5px;
+      max-width: 120px;
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+      z-index: 999;
+    }
+
+    .click-box {
+      position: absolute;
+      width: 100%;
+      height: 100%;
+      top: 0;
+      left: 0;
+    }
+  }
+}
+</style>

+ 312 - 0
src/views/vent/monitorManager/airDoor/components/door-content-r.vue

@@ -0,0 +1,312 @@
+<template>
+  <div class="door-content-r">
+    <div class="content-r-btn">
+      <a-button preIcon="ant-design:pause-circle-outlined" type="primary" @click="handlerOpenOrClose">同步开启</a-button>
+      <a-button preIcon="ant-design:play-circle-outlined" type="primary" style="margin: 0px 10px"
+        @click="handlerOpenOrClose">同步关闭</a-button>
+      <a-button preIcon="ant-design:clock-circle-outlined" type="primary" @click="handlerTimeSet">定时设置</a-button>
+    </div>
+    <div class="content-r-container">
+      <div class="content-r-box" v-for="(item, index) in infoData" :key="index">
+        <div class="box-title">
+          <div class="title-text">{{ item.strinstallpos }}</div>
+          <div class="title-detail" @click="handlerDetail(item.deviceID)">详情</div>
+        </div>
+        <div class="box-content">
+          <!-- 二三维信息 -->
+          <component ref="modelRef" :loading="loading" :is="modelComponent" />
+        </div>
+        <img src="@/assets/images/camera.png" alt="" @click="handlerCamera(item, index)" />
+      </div>
+    </div>
+    <!-- 同步开启/关闭弹窗 -->
+    <syncModal :visible="visible" @handleCancel="handleCancel"></syncModal>
+    <!-- 定时设置弹窗 -->
+    <timeSetModal :visibleTime="visibleTime" @handleCancelTime="handleCancelTime"></timeSetModal>
+    <!-- 摄像头信息 -->
+    <Modal :visible="modalVisible" :destroyOnClose="true" @cancel="handleCancelCamera">
+      <CameraModal :cameraData="cameraData"></CameraModal>
+    </Modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref, inject, watch } from 'vue';
+import syncModal from './syncModal.vue';
+import timeSetModal from './timeSetModal.vue';
+import CameraModal from './cameraModal.vue';
+import Modal from './Modal.vue';
+import { useRouter } from 'vue-router';
+import { useGlobSetting } from '/@/hooks/setting';
+import { chartsColumns, getModelComponent } from '../airdoor.data';
+import { computePlay, play } from '../../gateMonitor/gate.threejs';
+let props = defineProps({
+  infoData: {
+    type: Array,
+    default: () => {
+      return [];
+    },
+  },
+});
+
+const { sysOrgCode } = useGlobSetting();
+const globalConfig = inject<any>('globalConfig');
+let router = useRouter();
+//同步开启/关闭弹窗-控制显示与隐藏
+let visible = ref(false);
+//设置定时弹窗-控制显示与影藏
+let visibleTime = ref(false);
+//摄像头-控制显示与隐藏
+let modalVisible = ref(false);
+let cameraData = reactive({})
+//模型加载状态
+const loading = ref(false);
+/** 模型对应的组件,根据实际情况分为二维三维 */
+const modelComponent = getModelComponent(globalConfig.is2DModel, sysOrgCode);
+const modelRef = ref();
+
+/** 开关门动画调用 */
+let isFrontOpenRunning = false; //开关门动作是否在进行
+// let isFrontCloseRunning = false; //开关门动作是否在进行
+let isRearOpenRunning = false; //开关门动作是否在进行
+// let isRearCloseRunning = false; //开关门动作是否在进行
+let isMidOpenRunning = false; //中间门动作是否在进行
+// let isMidCloseRunning = false; //中间门动作是否在进行
+// 0 关闭 1 正在打开 2 打开 3正在关闭
+let frontDeviceState = 0; //记录设备状态,为了与下一次监测数据做比较
+let rearDeviceState = 0; //记录设备状态,为了与下一次监测数据做比较
+let midDeviceState = 0; //记录设备状态,为了与下一次监测数据做比较
+const frontDoorIsOpen = ref(false); //前门是否开启
+const backDoorIsOpen = ref(false); //后门是否开启
+const midDoorIsOpen = ref(false); //中间门是否开启
+
+
+//同步开启/关闭
+function handlerOpenOrClose() {
+  visible.value = true;
+}
+function handleCancel(param) {
+  visible.value = param;
+}
+
+//定时设置
+function handlerTimeSet() {
+  visibleTime.value = true;
+}
+function handleCancelTime(param) {
+  visibleTime.value = param;
+}
+//详情跳转
+function handlerDetail(id) {
+  router.push(`/monitorChannel/monitor-gate?id=${id}&deviceType=gate`);
+}
+//摄像头切换
+function handlerCamera(item, index) {
+  modalVisible.value = true;
+  cameraData = Object.assign({}, item)
+}
+function handleCancelCamera(param) {
+  modalVisible.value = param;
+}
+
+function playWindowAnimation(data, maxarea = 90, isFirst = false) {
+  computePlay(data, maxarea, isFirst);
+}
+function monitorAnimation(selectData) {
+  console.log(selectData, 'selectD---')
+  const timeScale = 0.005;
+  // 带风窗 风窗动画
+  if (selectData['gateStyle'] && selectData['gateStyle'].includes('fm_fc')) playWindowAnimation(selectData);
+
+  if (selectData.frontGateOpen == '1' && selectData.frontGateClose == '0' && !isFrontOpenRunning) {
+    isFrontOpenRunning = true;
+    if (frontDeviceState != 1) {
+      // import.meta.env.VITE_GLOB_IS_SIMULATE ? play(1, timeScale) : play(1);
+      play(1, timeScale);
+      frontDeviceState = 1;
+      frontDoorIsOpen.value = false;
+      backDoorIsOpen.value = true;
+    }
+  }
+
+  if (selectData.frontGateOpen == '0' && selectData.frontGateClose == '0' && !isFrontOpenRunning) {
+    isFrontOpenRunning = true;
+    if (frontDeviceState != 1) {
+      // import.meta.env.VITE_GLOB_IS_SIMULATE ? play(1, timeScale) : play(1);
+      play(1, timeScale);
+      frontDeviceState = 1;
+      frontDoorIsOpen.value = false;
+      backDoorIsOpen.value = true;
+    }
+  }
+
+  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.rearGateOpen == '1' && selectData.rearGateClose == '0' && !isRearOpenRunning) {
+    isRearOpenRunning = true;
+
+    if (rearDeviceState != 1) {
+      // import.meta.env.VITE_GLOB_IS_SIMULATE ? play(3, timeScale) : play(3);
+      play(3, timeScale);
+      rearDeviceState = 1;
+      backDoorIsOpen.value = false;
+      frontDoorIsOpen.value = true;
+    }
+  }
+  if (selectData.rearGateOpen == '0' && selectData.rearGateClose == '0' && !isRearOpenRunning) {
+    isRearOpenRunning = true;
+
+    if (rearDeviceState != 1) {
+      // import.meta.env.VITE_GLOB_IS_SIMULATE ? play(3, timeScale) : play(3);
+      play(3, timeScale);
+      rearDeviceState = 1;
+      backDoorIsOpen.value = false;
+      frontDoorIsOpen.value = true;
+    }
+  }
+
+  if (selectData.rearGateClose == '1' && selectData.rearGateOpen == '0' && isRearOpenRunning) {
+    isRearOpenRunning = false;
+    if (rearDeviceState != 0) {
+      // import.meta.env.VITE_GLOB_IS_SIMULATE ? play(4, timeScale) : play(4);
+      play(4, timeScale);
+      rearDeviceState = 0;
+      backDoorIsOpen.value = false;
+    }
+  }
+
+  if (selectData.midGateOpen == '1' && selectData.midGateClose == '0' && !isMidOpenRunning) {
+    isMidOpenRunning = true;
+
+    if (midDeviceState != 1) {
+      // import.meta.env.VITE_GLOB_IS_SIMULATE ? play(3, timeScale) : play(3);
+      play(8, timeScale);
+      midDeviceState = 1;
+      backDoorIsOpen.value = false;
+      frontDoorIsOpen.value = true;
+    }
+  }
+
+  if (selectData.midGateOpen == '0' && selectData.midGateClose == '0' && !isMidOpenRunning) {
+    isMidOpenRunning = true;
+
+    if (midDeviceState != 1) {
+      // import.meta.env.VITE_GLOB_IS_SIMULATE ? play(3, timeScale) : play(3);
+      play(8, timeScale);
+      midDeviceState = 1;
+      backDoorIsOpen.value = false;
+      frontDoorIsOpen.value = true;
+    }
+  }
+
+  if (selectData.midGateClose == '1' && selectData.midGateOpen == '0' && isMidOpenRunning) {
+    isMidOpenRunning = false;
+    if (midDeviceState != 0) {
+      // import.meta.env.VITE_GLOB_IS_SIMULATE ? play(4, timeScale) : play(4);
+      play(9, timeScale);
+      midDeviceState = 0;
+      backDoorIsOpen.value = false;
+    }
+  }
+
+  modelRef.value?.animate?.(selectData.frontGateOpen == '1', selectData.midGateOpen == '1', selectData.rearGateOpen == '1');
+}
+
+watch(() => props.infoData, (newV, oldV) => {
+  console.log(newV, 'new---')
+  if (newV.length) {
+    newV.forEach((el: any) => {
+      el = Object.assign(el, el.readData)
+      monitorAnimation(el);
+    })
+  }
+})
+</script>
+
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+
+@{theme-deepblue} {
+  .door-content-r {
+    --image-camera_bg: url('/@/assets/images/themify/deepblue/vent/camera_bg.png');
+  }
+}
+
+.door-content-r {
+  --image-camera_bg: url('/@/assets/images/vent/camera_bg.png');
+  width: 100%;
+  height: 100%;
+
+  .content-r-btn {
+    width: 100%;
+    height: 40px;
+    display: flex;
+    // align-items: center;
+  }
+
+  .content-r-container {
+    width: 100%;
+    height: calc(100% - 40px);
+    display: flex;
+    justify-content: flex-start;
+    flex-wrap: wrap;
+
+    .content-r-box {
+      position: relative;
+      width: 514px;
+      height: 300px;
+      margin: 10px;
+      background: var(--image-camera_bg);
+      background-size: 100% 100%;
+
+      .box-title {
+        position: absolute;
+        left: 50%;
+        top: 12px;
+        transform: translate(-50%, 0);
+        width: 95%;
+        height: 28px;
+        line-height: 28px;
+        text-align: center;
+        color: #fff;
+        background-color: rgba(51, 211, 255, 0.2);
+      }
+
+      .title-detail {
+        position: absolute;
+        right: 12px;
+        top: 0px;
+        color: rgba(60, 242, 255);
+        font-size: 12px;
+        cursor: pointer;
+      }
+
+      .box-content {
+        position: absolute;
+        left: 50%;
+        top: 40px;
+        transform: translate(-50%, 0);
+        width: 95%;
+        height: calc(100% - 40px);
+      }
+
+      img {
+        position: absolute;
+        right: 20px;
+        bottom: 15px;
+        width: 20px;
+        height: 20px;
+        cursor: pointer;
+      }
+    }
+  }
+}
+</style>

+ 62 - 0
src/views/vent/monitorManager/airDoor/components/door-menu-l.vue

@@ -0,0 +1,62 @@
+<template>
+  <div class="door-status-list">
+    <div class="list-content" v-for="(item, index) in menuDatas" :key="index">
+      <div class="item-label">{{ item.strinstallpos }}</div>
+      <div :class="item.statusC == '正常' ? 'item-status' : 'item-status-warn'">{{ item.statusC }}</div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref,watch } from 'vue'
+
+let props=defineProps({
+  menuData:{
+    type:Array,
+    default:()=>{
+      return []
+    }
+  }
+})
+
+let menuDatas=ref<any[]>([])
+
+watch(()=>props.menuData,(newV,oldV)=>{
+  console.log(newV,'menuData')
+  if(newV.length){
+    menuDatas.value=newV.map((el:any)=>{
+      return {
+        ...el,
+        statusC:el.readData.midGateOpen ? el.readData.frontGateOpen	=='1' && el.readData.midGateOpen=='1' && el.readData.rearGateOpen	=='1' ? '正常' : '异常' : el.readData.frontGateOpen=='1' && el.readData.rearGateOpen=='1' ? '正常' : '异常'
+      }
+    })
+  }
+})
+
+</script>
+
+<style lang="less" scoped>
+.door-status-list {
+  position: relative;
+  width: 100%;
+  height: 100%;
+
+  .list-content {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    width: 100%;
+    height: 38px;
+    color: #fff;
+    margin-bottom: 10px;
+
+    .item-status {
+      color: #16e65c;
+    }
+
+    .item-status-warn {
+      color: #f00808;
+    }
+  }
+}
+</style>

+ 26 - 0
src/views/vent/monitorManager/airDoor/components/entryThree.vue

@@ -0,0 +1,26 @@
+<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="deviceDetail" class="device-detail">
+      <div id="deviceCard" class="device-card" style="z-index: -1; position: absolute">
+        <div class="title">KJ-980-F矿用本安型监控分站</div>
+        <div class="detail-box">
+          <div class="left-box"></div>
+          <div class="right-box">
+            <div><span class="detail-title">规格型号:</span> <span>KJ-980-F</span></div>
+            <div
+              ><span class="detail-title">技术参数:</span>
+              <span
+                >380V,电机功率22kW,50Hz,B级绝缘,额定电流42.2A,效率90.5%,能效等级3,接法角型,2940r/min,轴承6311/CM 6211/CM,功率因数0.89</span
+              >
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div id="damper3D" style="width: 100%; height: 100%; position: absolute; overflow: hidden"></div>
+  </div>
+</template>
+<script lang="ts" setup>
+  defineProps<{ loading: boolean }>();
+</script>

File diff ditekan karena terlalu besar
+ 588 - 0
src/views/vent/monitorManager/airDoor/components/gateDualSVG.vue


+ 48 - 0
src/views/vent/monitorManager/airDoor/components/syncModal.vue

@@ -0,0 +1,48 @@
+<template>
+  <div class="sync-modal">
+    <a-modal v-model:visible="Visible" width="450px" :title="Title" centered destroyOnClose  @ok="handleOk" @cancel="$emit('handleCancel',false)">
+      <p>您正在执行 “开启/关闭” 全部风门的操作,请输入密码执行。</p>
+      <a-form :model="formState" name="basic" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }" autocomplete="off">
+        <a-form-item label="输入密码">
+          <a-input style="width:220px" v-model:value="formState.passWord" />
+        </a-form-item>
+      
+      </a-form>
+    </a-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, watchEffect } from 'vue'
+
+let props = defineProps({
+  visible: {
+    type: Boolean,
+    default: false
+  },
+  Title: {
+    type: String,
+    default: '操作确认'
+  }
+})
+
+let Visible = ref(false)
+let formState = reactive({
+  passWord: ''
+})
+//确定
+function handleOk(){
+
+}
+
+
+watchEffect(() => {
+  Visible.value = props.visible
+})
+</script>
+
+<style lang="less" scoped>
+p{
+  margin: 20px 40px;
+}
+</style>

+ 179 - 0
src/views/vent/monitorManager/airDoor/components/timeSetModal.vue

@@ -0,0 +1,179 @@
+<template>
+  <div class="time-set">
+    <a-modal v-model:visible="Visible" width="550px" :title="Title" centered destroyOnClose @ok="handleOk"
+      @cancel="$emit('handleCancelTime', false)">
+
+      <a-form :model="formState" name="basic" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }" autocomplete="off">
+        <a-form-item label="开启时间">
+          <a-select ref="select" v-model:value="formState.hourS" style="width:130px" placeholder="请选择...">
+            <a-select-option v-for="(item, index) in hourOption" :key="index" :value="item.value">{{ item.label
+            }}</a-select-option>
+          </a-select>
+          <div class="unit">时</div>
+          <a-select ref="select" v-model:value="formState.minuteS" style="width:130px" placeholder="请选择...">
+            <a-select-option v-for="(item, index) in minuteOption" :key="index" :value="item.value">{{ item.label
+            }}</a-select-option>
+          </a-select>
+          <div class="unit">分</div>
+        </a-form-item>
+        <a-form-item label="关闭时间">
+          <a-select ref="select" v-model:value="formState.hourE" style="width:130px" placeholder="请选择...">
+            <a-select-option v-for="(item, index) in hourOption" :key="index" :value="item.value">{{ item.label
+            }}</a-select-option>
+          </a-select>
+          <div class="unit">时</div>
+          <a-select ref="select" v-model:value="formState.minuteE" style="width:130px" placeholder="请选择...">
+            <a-select-option v-for="(item, index) in minuteOption" :key="index" :value="item.value">{{ item.label
+            }}</a-select-option>
+          </a-select>
+          <div class="unit">分</div>
+        </a-form-item>
+        <a-form-item label="启动定时">
+          <a-switch v-model:checked="formState.checked" />
+        </a-form-item>
+        <a-form-item label="输入密码">
+          <a-input v-model:value="formState.passWord" placeholder="请输入" style="width: 240px" />
+        </a-form-item>
+      </a-form>
+    </a-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, watchEffect } from 'vue'
+
+let props = defineProps({
+  visibleTime: {
+    type: Boolean,
+    default: false
+  },
+  Title: {
+    type: String,
+    default: '设定定时'
+  }
+})
+
+let Visible = ref(false)
+let formState = reactive({
+  hourS: '',
+  minuteS: '',
+  hourE: '',
+  minuteE: '',
+  checked: true,
+  passWord: ''
+})
+let hourOption = ref<any[]>([
+  { label: '00', value: 0 },
+  { label: '01', value: 1 },
+  { label: '02', value: 2 },
+  { label: '03', value: 3 },
+  { label: '04', value: 4 },
+  { label: '05', value: 5 },
+  { label: '06', value: 6 },
+  { label: '07', value: 7 },
+  { label: '08', value: 8 },
+  { label: '09', value: 9 },
+  { label: '10', value: 10 },
+  { label: '11', value: 11 },
+  { label: '12', value: 12 },
+  { label: '13', value: 13 },
+  { label: '14', value: 14 },
+  { label: '15', value: 15 },
+  { label: '16', value: 16 },
+  { label: '17', value: 17 },
+  { label: '18', value: 18 },
+  { label: '19', value: 19 },
+  { label: '20', value: 20 },
+  { label: '21', value: 21 },
+  { label: '22', value: 22 },
+  { label: '23', value: 23 },
+])
+let minuteOption = ref<any[]>([
+  { label: '00', value: 0 },
+  { label: '01', value: 1 },
+  { label: '02', value: 2 },
+  { label: '03', value: 3 },
+  { label: '04', value: 4 },
+  { label: '05', value: 5 },
+  { label: '06', value: 6 },
+  { label: '07', value: 7 },
+  { label: '08', value: 8 },
+  { label: '09', value: 9 },
+  { label: '10', value: 10 },
+  { label: '11', value: 11 },
+  { label: '12', value: 12 },
+  { label: '13', value: 13 },
+  { label: '14', value: 14 },
+  { label: '15', value: 15 },
+  { label: '16', value: 16 },
+  { label: '17', value: 17 },
+  { label: '18', value: 18 },
+  { label: '19', value: 19 },
+  { label: '20', value: 20 },
+  { label: '21', value: 21 },
+  { label: '22', value: 22 },
+  { label: '23', value: 23 },
+  { label: '24', value: 24 },
+  { label: '25', value: 25 },
+  { label: '26', value: 26 },
+  { label: '27', value: 27 },
+  { label: '28', value: 28 },
+  { label: '29', value: 29 },
+  { label: '30', value: 30 },
+  { label: '31', value: 31 },
+  { label: '32', value: 32 },
+  { label: '33', value: 33 },
+  { label: '34', value: 34 },
+  { label: '35', value: 35 },
+  { label: '36', value: 36 },
+  { label: '37', value: 37 },
+  { label: '38', value: 38 },
+  { label: '39', value: 39 },
+  { label: '40', value: 40 },
+  { label: '41', value: 41 },
+  { label: '42', value: 42 },
+  { label: '43', value: 43 },
+  { label: '44', value: 44 },
+  { label: '45', value: 45 },
+  { label: '46', value: 46 },
+  { label: '47', value: 47 },
+  { label: '48', value: 48 },
+  { label: '49', value: 49 },
+  { label: '50', value: 50 },
+  { label: '51', value: 51 },
+  { label: '52', value: 52 },
+  { label: '53', value: 53 },
+  { label: '54', value: 54 },
+  { label: '55', value: 55 },
+  { label: '56', value: 56 },
+  { label: '57', value: 57 },
+  { label: '58', value: 58 },
+  { label: '59', value: 59 },
+
+])
+
+//确定
+function handleOk() {
+
+}
+
+watchEffect(() => {
+  Visible.value = props.visibleTime
+})
+</script>
+
+<style lang="less" scoped>
+.zxm-form {
+  margin: 20px;
+}
+
+.unit {
+  color: #fff;
+  margin: 0px 10px;
+}
+
+::v-deep .zxm-form-item-control-input-content {
+  display: flex;
+  align-items: center;
+}
+</style>

+ 72 - 0
src/views/vent/monitorManager/airDoor/index.vue

@@ -0,0 +1,72 @@
+<template>
+  <div class="air-door">
+    <customHeader>风门集中同控</customHeader>
+    <div class="main-container">
+      <div class="container-left">
+        <doorMenuL :menuData="menuData"></doorMenuL>
+      </div>
+      <div class="container-right">
+        <doorContentR :infoData="menuData"></doorContentR>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted } from 'vue'
+import customHeader from '/@/components/vent/customHeader.vue';
+import doorMenuL from './components/door-menu-l.vue'
+import doorContentR from './components/door-content-r.vue'
+import { getDevice } from './airdoor.api'
+
+let menuData = ref<any[]>([])
+
+//左侧数据
+async function getMenuList() {
+  let res = await getDevice({ devicetype: "gate", pagetype: "normal" })
+  console.log(res, 'menuList')
+  menuData.value = res.msgTxt[0].datalist || []
+}
+
+onMounted(() => {
+  getMenuList()
+})
+
+</script>
+
+<style lang="less" scoped>
+.air-door {
+  position: relative;
+  width: 100%;
+  height: 100%;
+
+  .main-container {
+    display: flex;
+    width: 100%;
+    margin-top: 70px;
+    height: calc(100% - 70px);
+    padding: 10px;
+    box-sizing: border-box;
+  }
+
+  .container-left {
+    width: 260px;
+    height: 100%;
+    padding: 10px 15px;
+    margin-right: 10px;
+    border: 1px solid #99e8ff66;
+    background: #27546e1a;
+    overflow-y: auto;
+    box-shadow: 0px 0px 20px 7px rgba(145, 233, 254, 0.7) inset;
+    -moz-box-shadow: 0px 0px 20px 7px rgba(145, 233, 254, 0.7) inset;
+    -webkit-box-shadow: 0px 0px 50px 1px rgb(149 235 255 / 5%) inset;
+  }
+
+  .container-right {
+    width: calc(100% - 275px);
+    height: 100%;
+    // padding: 10px;
+    // box-sizing: border-box;
+  }
+}
+</style>

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini