Просмотр исходного кода

1. 神东公司端地图样式重新修改
2. 解决网络结算页面跳转bug

hongrunxia 9 месяцев назад
Родитель
Сommit
5f73766c0c

BIN
public/texture/earth3.png


BIN
src/assets/images/company/home-dz.png


BIN
src/assets/images/company/home-map.png


BIN
src/assets/images/company/home-map1.png


BIN
src/assets/images/company/home/point1.png


BIN
src/assets/images/company/home/point2.png


BIN
src/assets/images/company/home/point3.png


BIN
src/assets/images/company/home/point4.png


+ 7 - 5
src/views/vent/home/clique/components/3Dmap/3dMap.ts

@@ -150,7 +150,7 @@ class earthtMap {
     const spotLight = new THREE.SpotLight(0xffffff, 2);
     spotLight.position.set(0.62, 10.0, 2.78);
     spotLight.castShadow = true;
-    this.scene?.add(spotLight);
+    // this.scene?.add(spotLight);
     // 聚光光源辅助线
     // const spotLightHelper = new THREE.SpotLightHelper(spotLight);
     // this.scene?.add(spotLightHelper);
@@ -164,7 +164,7 @@ class earthtMap {
     // 点光源 - 蓝色照地球
     const pointLightMap = new THREE.PointLight('#4161ff', 1.4, 20);
     pointLightMap.position.set(-0.18, -1.19, 1.5);
-    this.scene?.add(pointLightMap);
+    // this.scene?.add(pointLightMap);
     // const spotLightHelperMap = new THREE.PointLightHelper(pointLightMap);
     // this.scene?.add(spotLightHelperMap);
   }
@@ -313,13 +313,15 @@ class earthtMap {
             bevelSegments: 0,
             bevelThickness: 0.1,
           };
-
+          const texture = new THREE.TextureLoader().load('/texture/earth3.png');
           const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
           const material = new THREE.MeshPhongMaterial({
-            color: '#3962DE',
+            // color: '#3962DE',
+            // color: '#222222',
             transparent: true,
-            opacity: 0.4,
+            opacity: 0.8,
             side: THREE.FrontSide,
+            map: texture,
             // depthTest: true,
           });
           let material1 = new THREE.MeshBasicMaterial({

+ 124 - 32
src/views/vent/home/clique/components/icon-light.vue

@@ -3,7 +3,9 @@
     <template v-for="(item, index) in pointList" :key="index">
       <div class="icon-point" @click="openModal(item.code, item.label, item.leftV, item.topV)" :style="{ left: item.leftV, top: item.topV }">
         <img :src="item.imgSrc" alt="" />
-        <span :style="{ color: item.textColor }">{{ item.label }}</span>
+        <span :class="{ 'icon-text-c': item.align == 'center', 'icon-text-l': item.align == 'left', 'icon-text-r': item.align == 'right' }">{{
+          item.label
+        }}</span>
       </div>
     </template>
   </div>
@@ -16,41 +18,99 @@
   const emit = defineEmits(['showDetail']);
   const globalConfig = inject('globalConfig');
 
-  const unselectBgPath = getAssetURL('company/home/unselect-bg.png');
-  const selectBgPath = getAssetURL('company/home/select-bg.png');
+  const warningLevel1 = getAssetURL('company/home/point1.png'); // 低风险
+  const warningLevel2 = getAssetURL('company/home/point2.png'); // 中风险
+  const warningLevel3 = getAssetURL('company/home/point3.png'); // 高风险
+  const warningLevel4 = getAssetURL('company/home/point4.png'); // 报警
   let pointList = ref<any[]>(
     globalConfig.History_Type == 'vent'
       ? [
-          { code: 'liuTa', imgSrc: unselectBgPath, label: 'lt', leftV: '327px', topV: '40px', textColor: '#fff' },
-          { code: 'cunCaoErTa', imgSrc: unselectBgPath, label: 'cce', leftV: '291px', topV: '69px', textColor: '#fff' },
-          { code: 'buErTaiTa', imgSrc: selectBgPath, label: 'bet', leftV: '286px', topV: '97px', textColor: 'rgba(255, 231, 83,.9)' },
-          { code: 'wuLunTa', imgSrc: unselectBgPath, label: 'wlml', leftV: '327px', topV: '115px', textColor: '#fff' },
-          { code: 'cunCaoTa', imgSrc: selectBgPath, label: 'cc', leftV: '346px', topV: '132px', textColor: 'rgba(255, 231, 83,.9)' },
-          { code: 'shiKanTa', imgSrc: unselectBgPath, label: 'skt', leftV: '373px', topV: '149px', textColor: '#fff' },
-          { code: 'buLieTa', imgSrc: selectBgPath, label: 'blt', leftV: '408px', topV: '184px', textColor: 'rgba(255, 231, 83,.9)' },
-          { code: 'haLaGouTa', imgSrc: unselectBgPath, label: 'hlg', leftV: '445px', topV: '214px', textColor: '#fff' },
-          { code: 'shangWanTa', imgSrc: selectBgPath, label: 'sw', leftV: '439px', topV: '244px', textColor: 'rgba(255, 231, 83,.9)' },
-          { code: 'huoJiTuTa', imgSrc: unselectBgPath, label: 'hjt', leftV: '398px', topV: '265px', textColor: '#fff' },
-          { code: 'daLiuTa', imgSrc: unselectBgPath, label: 'dlt', leftV: '492px', topV: '260px', textColor: '#fff' },
-          { code: 'jinJieTa', imgSrc: selectBgPath, label: 'jj', leftV: '565px', topV: '413px', textColor: 'rgba(255, 231, 83,.9)' },
-          { code: 'yuJiaTa', imgSrc: unselectBgPath, label: 'yjl', leftV: '669px', topV: '308px', textColor: '#fff' },
-          { code: 'baoDeTa', imgSrc: selectBgPath, label: 'bd', leftV: '862px', topV: '340px', textColor: 'rgba(255, 231, 83,.9)' },
+          { code: 'liuTa', imgSrc: warningLevel1, label: 'lt', leftV: '327px', topV: '40px', textColor: '#fff', align: 'left' },
+          { code: 'cunCaoErTa', imgSrc: warningLevel1, label: 'cce', leftV: '291px', topV: '69px', textColor: '#fff', align: 'left' },
+          { code: 'buErTaiTa', imgSrc: warningLevel1, label: 'bet', leftV: '286px', topV: '97px', textColor: 'rgba(255, 231, 83,.9)', align: 'left' },
+          { code: 'wuLunTa', imgSrc: warningLevel1, label: 'wlml', leftV: '327px', topV: '115px', textColor: '#fff', align: 'left' },
+          { code: 'cunCaoTa', imgSrc: warningLevel1, label: 'cc', leftV: '346px', topV: '132px', textColor: 'rgba(255, 231, 83,.9)', align: 'left' },
+          { code: 'shiKanTa', imgSrc: warningLevel1, label: 'skt', leftV: '373px', topV: '149px', textColor: '#fff', align: 'left' },
+          { code: 'buLieTa', imgSrc: warningLevel1, label: 'blt', leftV: '408px', topV: '184px', textColor: 'rgba(255, 231, 83,.9)', align: 'left' },
+          { code: 'haLaGouTa', imgSrc: warningLevel1, label: 'hlg', leftV: '445px', topV: '214px', textColor: '#fff', align: 'left' },
+          {
+            code: 'shangWanTa',
+            imgSrc: warningLevel1,
+            label: 'sw',
+            leftV: '439px',
+            topV: '244px',
+            textColor: 'rgba(255, 231, 83,.9)',
+            align: 'left',
+          },
+          { code: 'huoJiTuTa', imgSrc: warningLevel1, label: 'hjt', leftV: '398px', topV: '265px', textColor: '#fff', align: 'left' },
+          { code: 'daLiuTa', imgSrc: warningLevel1, label: 'dlt', leftV: '492px', topV: '260px', textColor: '#fff', align: 'left' },
+          { code: 'jinJieTa', imgSrc: warningLevel1, label: 'jj', leftV: '565px', topV: '413px', textColor: 'rgba(255, 231, 83,.9)', align: 'left' },
+          { code: 'yuJiaTa', imgSrc: warningLevel1, label: 'yjl', leftV: '669px', topV: '308px', textColor: '#fff', align: 'left' },
+          { code: 'baoDeTa', imgSrc: warningLevel1, label: 'bd', leftV: '862px', topV: '340px', textColor: 'rgba(255, 231, 83,.9)', align: 'left' },
         ]
       : [
-          { code: 'liuTa', imgSrc: unselectBgPath, label: '柳塔矿', leftV: '327px', topV: '40px', textColor: '#fff' },
-          { code: 'cunCaoErTa', imgSrc: unselectBgPath, label: '寸草塔二矿', leftV: '291px', topV: '69px', textColor: '#fff' },
-          { code: 'buErTaiTa', imgSrc: selectBgPath, label: '布尔台矿', leftV: '286px', topV: '97px', textColor: 'rgba(255, 231, 83,.9)' },
-          { code: 'wuLunTa', imgSrc: unselectBgPath, label: '乌兰木伦矿', leftV: '327px', topV: '115px', textColor: '#fff' },
-          { code: 'cunCaoTa', imgSrc: selectBgPath, label: '寸草塔矿', leftV: '346px', topV: '132px', textColor: 'rgba(255, 231, 83,.9)' },
-          { code: 'shiKanTa', imgSrc: unselectBgPath, label: '石圪台矿', leftV: '373px', topV: '149px', textColor: '#fff' },
-          { code: 'buLieTa', imgSrc: selectBgPath, label: '补连塔矿', leftV: '408px', topV: '184px', textColor: 'rgba(255, 231, 83,.9)' },
-          { code: 'haLaGouTa', imgSrc: unselectBgPath, label: '哈拉沟矿', leftV: '445px', topV: '214px', textColor: '#fff' },
-          { code: 'shangWanTa', imgSrc: selectBgPath, label: '上湾矿', leftV: '439px', topV: '244px', textColor: 'rgba(255, 231, 83,.9)' },
-          { code: 'huoJiTuTa', imgSrc: unselectBgPath, label: '活鸡兔井', leftV: '398px', topV: '265px', textColor: '#fff' },
-          { code: 'daLiuTa', imgSrc: unselectBgPath, label: '大柳塔矿', leftV: '492px', topV: '260px', textColor: '#fff' },
-          { code: 'jinJieTa', imgSrc: selectBgPath, label: '锦界矿', leftV: '565px', topV: '413px', textColor: 'rgba(255, 231, 83,.9)' },
-          { code: 'yuJiaTa', imgSrc: unselectBgPath, label: '榆家梁矿', leftV: '669px', topV: '308px', textColor: '#fff' },
-          { code: 'baoDeTa', imgSrc: selectBgPath, label: '保德矿', leftV: '862px', topV: '340px', textColor: 'rgba(255, 231, 83,.9)' },
+          { code: 'liuTa', imgSrc: warningLevel4, label: '柳塔矿', leftV: '375px', topV: '135px', textColor: '#fff', align: 'right' },
+          { code: 'cunCaoErTa', imgSrc: warningLevel1, label: '寸草塔二矿', leftV: '269px', topV: '116px', textColor: '#fff', align: 'left' },
+          {
+            code: 'buErTaiTa',
+            imgSrc: warningLevel1,
+            label: '布尔台矿',
+            leftV: '329px',
+            topV: '177px',
+            textColor: 'rgba(255, 231, 83,.9)',
+            align: 'left',
+          },
+          { code: 'wuLunTa', imgSrc: warningLevel3, label: '乌兰木伦矿', leftV: '432px', topV: '178px', textColor: '#fff', align: 'right' },
+          {
+            code: 'cunCaoTa',
+            imgSrc: warningLevel1,
+            label: '寸草塔矿',
+            leftV: '396px',
+            topV: '224px',
+            textColor: 'rgba(255, 231, 83,.9)',
+            align: 'left',
+          },
+          { code: 'shiKanTa', imgSrc: warningLevel1, label: '石圪台矿', leftV: '470px', topV: '236px', textColor: '#fff', align: 'right' },
+          {
+            code: 'buLieTa',
+            imgSrc: warningLevel1,
+            label: '补连塔矿',
+            leftV: '432px',
+            topV: '282px',
+            textColor: 'rgba(255, 231, 83,.9)',
+            align: 'left',
+          },
+          { code: 'haLaGouTa', imgSrc: warningLevel2, label: '哈拉沟矿', leftV: '511px', topV: '302px', textColor: '#fff', align: 'right' },
+          {
+            code: 'shangWanTa',
+            imgSrc: warningLevel1,
+            label: '上湾矿',
+            leftV: '381px',
+            topV: '334px',
+            textColor: 'rgba(255, 231, 83,.9)',
+            align: 'left',
+          },
+          // { code: 'huoJiTuTa', imgSrc: warningLevel1, label: '活鸡兔井', leftV: '398px', topV: '265px', textColor: '#fff', align: 'left' },
+          { code: 'daLiuTa', imgSrc: warningLevel1, label: '大柳塔矿', leftV: '588px', topV: '338px', textColor: '#fff', align: 'right' },
+          {
+            code: 'jinJieTa',
+            imgSrc: warningLevel1,
+            label: '锦界矿',
+            leftV: '575px',
+            topV: '460px',
+            textColor: 'rgba(255, 231, 83,.9)',
+            align: 'center',
+          },
+          { code: 'yuJiaTa', imgSrc: warningLevel1, label: '榆家梁矿', leftV: '699px', topV: '307px', textColor: '#fff', align: 'center' },
+          {
+            code: 'baoDeTa',
+            imgSrc: warningLevel4,
+            label: '保德矿',
+            leftV: '892px',
+            topV: '200px',
+            textColor: 'rgba(255, 231, 83,.9)',
+            align: 'center',
+          },
         ]
   );
   function openModal(code, label, leftV, topV) {
@@ -78,7 +138,39 @@
       span {
         font-size: 12px;
         padding: 0px 5px;
-        background-color: rgba(12, 13, 13);
+        color: #ffffff;
+        // background-color: rgba(12, 13, 13);
+      }
+      .icon-text-l {
+        position: absolute;
+        font-size: 12px;
+        padding: 0px 5px;
+        // background-color: rgba(12, 13, 13);
+        display: block;
+        width: 100px;
+        text-align: center;
+        // top: -20px;
+        left: -75px;
+      }
+      .icon-text-r {
+        position: absolute;
+        font-size: 12px;
+        padding: 0px 5px;
+        display: block;
+        width: 100px;
+        text-align: center;
+        // top: -20px;
+        left: 12px;
+      }
+      .icon-text-c {
+        position: absolute;
+        font-size: 12px;
+        padding: 0px 5px;
+        display: block;
+        width: 60px;
+        text-align: center;
+        top: -20px;
+        left: -12px;
       }
     }
   }

+ 20 - 12
src/views/vent/home/clique/index.vue

@@ -224,7 +224,8 @@
           flex-direction: column;
           justify-content: space-between;
           align-items: center;
-
+          position: relative;
+          z-index: 1;
           .area-card {
             width: 100%;
             height: calc(60% - 15px);
@@ -242,25 +243,31 @@
         }
 
         .center-area {
-          width: calc(54% - 30px);
+          width: 1000px;
           height: 100%;
           position: relative;
-
+          z-index: 0;
           .center-bg {
             position: absolute;
-            bottom: 269px;
+            bottom: 240px;
             left: 50%;
             transform: translate(-50%, 0);
-            height: calc(100% - 325px);
-            width: 100%;
+            width: 1128px;
+            height: 630px;
             background: url('../../../../assets/images/company/home-dz.png') no-repeat center;
-            background-position: 50% 90%;
-
+            background-size: contain;
+            background-position: 50% 50px;
+            z-index: 0;
             .bg-map {
-              width: 100%;
-              height: 100%;
+              width: 1086px;
+              height: 610px;
+              left: 30px;
+              top: 60px;
               background: url('../../../../assets/images/company/home-map.png') no-repeat center;
-              background-size: 100% 100%;
+              background-size: contain;
+              // background-position: 0px 70px;
+              position: relative;
+              z-index: 0;
             }
           }
 
@@ -294,7 +301,8 @@
           flex-direction: column;
           justify-content: space-between;
           align-items: center;
-
+          position: relative;
+          z-index: 1;
           // pointer-events: auto;
           .area-card {
             width: 100%;

+ 0 - 1
src/views/vent/monitorManager/deviceMonitor/components/network/index.vue

@@ -415,7 +415,6 @@ function setTable(code) {
 }
 
 watch(() => props.pageResult, (newData) => {
-  
   analyzePageResult(newData)
 })
 

+ 314 - 0
src/views/vent/monitorManager/windowMonitorBet/dandaoFc.threejs.ts

@@ -0,0 +1,314 @@
+import * as THREE from 'three';
+import { getTextCanvas, renderVideo } from '/@/utils/threejs/util';
+import gsap from 'gsap';
+
+class singleWindow {
+  model;
+  modelName = 'ddFc';
+  group: THREE.Object3D = new THREE.Object3D();
+  animationTimer;
+  isLRAnimation = true;
+  direction = 1;
+  windowsActionArr = {
+    frontWindow: [],
+  };
+  player1;
+  player2;
+  playerStartClickTime1 = new Date().getTime();
+  constructor(model) {
+    this.model = model;
+    // this.group.name = 'ddFc';
+  }
+  // // 重置摄像头
+  // const resetCamera = () => {
+  //   this.model.camera.position.set(30.328, 58.993, 148.315);
+  //   this.model.camera.rotation.set(-27.88, 14.35, 7.47);
+  //   this.model.orbitControls?.update();
+  //   this.model.camera.updateProjectionMatrix();
+  // };
+
+  // 设置模型位置
+  setModalPosition() {
+    this.group?.scale.set(22, 22, 22);
+    this.group?.position.set(-35, 25, 15);
+  }
+
+  addMonitorText(selectData) {
+    if (!this.group) {
+      return;
+    }
+    const textArr = [
+      {
+        text: `远程定量调节自动风窗`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 110,
+        y: 90,
+      },
+      {
+        text: `${selectData.OpenDegree ? '开度值(%)' : selectData.forntArea ? '过风面积(㎡)' : '过风面积(㎡)'}:`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 5,
+        y: 145,
+      },
+      {
+        text: selectData.OpenDegree
+          ? Number(`${selectData.OpenDegree}`).toFixed(2)
+          : selectData.forntArea
+          ? Number(`${selectData.forntArea}`).toFixed(2)
+          : '-',
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 330,
+        y: 145,
+      },
+      {
+        text: `${selectData.frontRearDP ? '风窗压差(Pa)' : selectData.windSpeed ? '风速(m/s)' : '通信状态'}:`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 5,
+        y: 200,
+      },
+      {
+        text: `${
+          selectData.frontRearDP
+            ? selectData.frontRearDP
+            : selectData.windSpeed
+            ? selectData.windSpeed
+            : selectData.netStatus == '0'
+            ? '断开'
+            : '连接'
+        }`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 330,
+        y: 200,
+      },
+      {
+        text: `${selectData.fWindowM3 ? '过风量(m³/min)' : '风窗道数'}: `,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 5,
+        y: 256,
+      },
+      {
+        text: `${selectData.fWindowM3 ? selectData.fWindowM3 : selectData.nwindownum}`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 330,
+        y: 256,
+      },
+      {
+        text: History_Type['type'] == 'remote' ? `国能神东煤炭集团监制` : '煤炭科学技术研究院有限公司研制',
+        font: 'normal 28px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: History_Type['type'] == 'remote' ? 120 : 60,
+        y: 302,
+      },
+    ];
+
+    getTextCanvas(726, 546, textArr, '').then((canvas: HTMLCanvasElement) => {
+      const textMap = new THREE.CanvasTexture(canvas); // 关键一步
+      const textMaterial = new THREE.MeshBasicMaterial({
+        // 关于材质并未讲解 实操即可熟悉                 这里是漫反射类似纸张的材质,对应的就有高光类似金属的材质.
+        map: textMap, // 设置纹理贴图
+        transparent: true,
+        side: THREE.DoubleSide, // 这里是双面渲染的意思
+      });
+      textMap.dispose();
+      textMaterial.blending = THREE.CustomBlending;
+      const monitorPlane = this.group?.getObjectByName('monitorText');
+      if (monitorPlane) {
+        monitorPlane.material = textMaterial;
+      } else {
+        const planeGeometry = new THREE.PlaneGeometry(570, 346); // 平面3维几何体PlaneGeometry
+        const planeMesh = new THREE.Mesh(planeGeometry, textMaterial);
+        planeMesh.name = 'monitorText';
+        planeMesh.scale.set(0.0025, 0.003, 0.002);
+        planeMesh.position.set(3.71, -0.042, -0.23);
+        this.group?.add(planeMesh);
+      }
+    });
+  }
+
+  /* 提取风门序列帧,初始化前后门动画 */
+  initAnimation() {
+    const meshArr01: THREE.Object3D[] = [];
+    if (this.group) {
+      this.group.getObjectByName('ddFc')?.children.forEach((obj) => {
+        if (obj.type === 'Mesh' && ((obj.name && obj.name.startsWith('shanye')) || obj.name.startsWith('FCshanye'))) {
+          obj.rotateOnAxis(new THREE.Vector3(0, 1, 0), 0);
+          meshArr01.push(obj);
+        }
+      });
+    }
+    this.windowsActionArr.frontWindow = meshArr01;
+  }
+
+  play(rotationParam, flag) {
+    if (this.windowsActionArr.frontWindow.length <= 0) {
+      return;
+    }
+    if (flag === 1) {
+      // 前风窗动画
+      this.windowsActionArr.frontWindow.forEach((mesh: THREE.Mesh) => {
+        gsap.to(mesh.rotation, {
+          y: THREE.MathUtils.degToRad(rotationParam.frontDeg1),
+          duration: (1 / 9) * Math.abs(rotationParam.frontDeg1 - mesh.rotation.y),
+          overwrite: true,
+        });
+      });
+    } else if (flag === 0) {
+      ([...this.windowsActionArr.frontWindow] as THREE.Mesh[]).forEach((mesh) => {
+        gsap.to(mesh.rotation, {
+          y: 0,
+          overwrite: true,
+        });
+      });
+    }
+  }
+
+  /* 点击风窗,风窗全屏 */
+  mousedownModel(intersects: THREE.Intersection<THREE.Object3D<THREE.Event>>[]) {
+    this.isLRAnimation = false;
+    if (this.animationTimer) {
+      clearTimeout(this.animationTimer);
+      this.animationTimer = null;
+    }
+    // 判断是否点击到视频
+    intersects.find((intersect) => {
+      const mesh = intersect.object;
+      if (mesh.name === 'player1') {
+        if (new Date().getTime() - this.playerStartClickTime1 < 400) {
+          // 双击,视频放大
+          if (this.player1) {
+            this.player1.requestFullscreen();
+          }
+        }
+        this.playerStartClickTime1 = new Date().getTime();
+        return true;
+      }
+      return false;
+    });
+  }
+
+  mouseUpModel() {
+    // 10s后开始摆动
+    if (!this.animationTimer && !this.isLRAnimation) {
+      this.animationTimer = setTimeout(() => {
+        this.isLRAnimation = true;
+      }, 10000);
+    }
+  }
+
+  /* 风门动画 */
+  render() {
+    if (!this.model) {
+      return;
+    }
+    if (this.isLRAnimation && this.group) {
+      // 左右摇摆动画
+      if (Math.abs(this.group.rotation.y) >= 0.2) {
+        this.direction = -this.direction;
+        this.group.rotation.y += 0.00002 * 30 * this.direction;
+      } else {
+        this.group.rotation.y += 0.00002 * 30 * this.direction;
+      }
+    }
+  }
+
+  async initCamera(dom1) {
+    const videoPlayer1 = dom1;
+    let monitorPlane: THREE.Mesh | null = null;
+
+    const textArr = [
+      {
+        text: `无信号输入`,
+        font: 'normal 40px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 170,
+        y: 40,
+      },
+    ];
+
+    const canvas = await getTextCanvas(320, 180, textArr, null);
+    if (canvas) {
+      const textMap = new THREE.CanvasTexture(canvas); // 关键一步
+      const textMaterial = new THREE.MeshBasicMaterial({
+        map: textMap, // 设置纹理贴图
+        transparent: true,
+        side: THREE.DoubleSide, // 这里是双面渲染的意思
+      });
+      textMaterial.blending = THREE.CustomBlending;
+      monitorPlane = this.group?.getObjectByName('noPlayer');
+      if (monitorPlane) {
+        monitorPlane.material = textMaterial;
+      } else {
+        const planeGeometry = new THREE.PlaneGeometry(100, 100); // 平面3维几何体PlaneGeometry
+        monitorPlane = new THREE.Mesh(planeGeometry, textMaterial);
+        textMaterial.dispose();
+        planeGeometry.dispose();
+      }
+    }
+
+    const videoPlayer = this.group.getObjectByName('player1');
+    if (videoPlayer) {
+      this.model.clearMesh(videoPlayer);
+      this.group.remove(videoPlayer);
+    }
+    const noPlayer1 = this.group.getObjectByName('noPlayer1');
+    if (noPlayer1) {
+      this.model.clearMesh(noPlayer1);
+      this.group.remove(noPlayer1);
+    }
+
+    if (!videoPlayer1 && videoPlayer1 === null) {
+      if (monitorPlane && !this.group.getObjectByName('noPlayer1')) {
+        const planeMesh = monitorPlane.clone();
+        planeMesh.name = 'noPlayer1';
+        planeMesh.scale.set(0.011, 0.0053, 0.012);
+        planeMesh.position.set(-4.3, 0.13, -0.23);
+        this.group?.add(planeMesh.clone());
+      }
+    } else if (videoPlayer1) {
+      try {
+        const mesh = renderVideo(this.group, videoPlayer1, 'player1');
+        if (mesh) {
+          mesh?.scale.set(0.0382, 0.028, 0.022);
+          mesh?.position.set(-3.008, 0.148, -0.22);
+          this.group.add(mesh);
+        }
+      } catch (error) {
+        console.log('视频信号异常');
+      }
+    }
+  }
+
+  mountedThree(playerDom) {
+    return new Promise((resolve) => {
+      this.model.setGLTFModel(['ddFc'], this.group).then(() => {
+        this.setModalPosition();
+        this.initAnimation();
+        resolve(null);
+      });
+    });
+  }
+
+  destroy() {
+    this.model.clearGroup(this.group);
+    this.windowsActionArr.frontWindow = undefined;
+    this.model = null;
+    this.group = null;
+  }
+}
+export default singleWindow;

+ 350 - 0
src/views/vent/monitorManager/windowMonitorBet/shuangdaoFc.threejs.ts

@@ -0,0 +1,350 @@
+import * as THREE from 'three';
+import { getTextCanvas, renderVideo } from '/@/utils/threejs/util';
+import gsap from 'gsap';
+
+class doubleWindow {
+  model;
+  modelName = 'sdFc';
+  group: THREE.Object3D = new THREE.Object3D();
+  animationTimer;
+  isLRAnimation = true;
+  direction = 1;
+  windowsActionArr = {
+    frontWindow: <THREE.Mesh[]>[],
+    backWindow: <THREE.Mesh[]>[],
+  };
+  playerStartClickTime1 = new Date().getTime();
+  playerStartClickTime2 = new Date().getTime();
+
+  constructor(model) {
+    this.model = model;
+    // this.group.name = this.modelName;
+  }
+
+  // 设置模型位置
+  setModalPosition() {
+    // this.group.getObjectByName(this.modelName)?.scale.set(9, 9, 9);
+    this.group?.scale.set(22, 22, 22);
+    this.group?.position.set(-15, 25, 15);
+  }
+
+  addMonitorText(selectData) {
+    if (!this.group) {
+      return;
+    }
+    const textArr = [
+      {
+        text: `远程定量调节自动风窗`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 95,
+        y: 97,
+      },
+      {
+        text: `${selectData.OpenDegree ? '开度值(%)' : selectData.forntArea ? '过风面积(㎡)' : '过风面积(㎡)'}:`,
+        font: 'normal 28px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 5,
+        y: 150,
+      },
+      {
+        text: selectData.OpenDegree
+          ? Number(`${selectData.OpenDegree}`).toFixed(2)
+          : selectData.forntArea
+          ? Number(`${selectData.forntArea}`).toFixed(2)
+          : '-',
+        font: 'normal 28px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 330,
+        y: 150,
+      },
+      {
+        text: `${selectData.frontRearDP ? '风窗压差(Pa)' : selectData.windSpeed ? '风速' : '通信状态'}:`,
+        font: 'normal 28px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 5,
+        y: 210,
+      },
+      {
+        text: `${
+          selectData.frontRearDP
+            ? selectData.frontRearDP
+            : selectData.windSpeed
+            ? selectData.windSpeed
+            : selectData.netStatus == '0'
+            ? '断开'
+            : '连接'
+        }`,
+        font: 'normal 28px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 325,
+        y: 210,
+      },
+      {
+        text: `${selectData.fWindowM3 ? '过风量(m³/min)' : '风窗道数'}: `,
+        font: 'normal 28px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 5,
+        y: 266,
+      },
+      {
+        text: `${selectData.fWindowM3 ? selectData.fWindowM3 : selectData.nwindownum}`,
+        font: 'normal 28px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 330,
+        y: 266,
+      },
+
+      {
+        text: History_Type['type'] == 'remote' ? `国能神东煤炭集团监制` : '煤炭科学技术研究院有限公司研制',
+        font: 'normal 28px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: History_Type['type'] == 'remote' ? 120 : 60,
+        y: 322,
+      },
+    ];
+
+    getTextCanvas(570, 346, textArr, '').then((canvas: HTMLCanvasElement) => {
+      const textMap = new THREE.CanvasTexture(canvas); // 关键一步
+      const textMaterial = new THREE.MeshBasicMaterial({
+        // 关于材质并未讲解 实操即可熟悉                 这里是漫反射类似纸张的材质,对应的就有高光类似金属的材质.
+        map: textMap, // 设置纹理贴图
+        transparent: true,
+        side: THREE.DoubleSide, // 这里是双面渲染的意思
+      });
+      textMaterial.blending = THREE.CustomBlending;
+      const monitorPlane = this.group?.getObjectByName('monitorText');
+      if (monitorPlane) {
+        monitorPlane.material = textMaterial;
+      } else {
+        const planeGeometry = new THREE.PlaneGeometry(570, 346); // 平面3维几何体PlaneGeometry
+        const planeMesh = new THREE.Mesh(planeGeometry, textMaterial);
+        planeMesh.name = 'monitorText';
+        planeMesh.scale.set(0.0018, 0.0018, 0.0018);
+        planeMesh.position.set(4.04, 0.178, -0.2);
+        this.group?.add(planeMesh);
+      }
+      textMap.dispose();
+    });
+  }
+
+  /* 风门动画 */
+  render() {
+    if (!this.model) {
+      return;
+    }
+    if (this.isLRAnimation && this.group) {
+      // 左右摇摆动画
+      if (Math.abs(this.group.rotation.y) >= 0.2) {
+        this.direction = -this.direction;
+        this.group.rotation.y += 0.00002 * 30 * this.direction;
+      } else {
+        this.group.rotation.y += 0.00002 * 30 * this.direction;
+      }
+    }
+  }
+
+  /* 提取风门序列帧,初始化前后门动画 */
+  initAnimation() {
+    const meshArr01: THREE.Object3D[] = [];
+    const meshArr02: THREE.Object3D[] = [];
+    const windowGroup = new THREE.Group();
+    windowGroup.name = 'hiddenGroup';
+    this.group.getObjectByName('sdFc')?.children.forEach((obj) => {
+      if (obj.type === 'Mesh' && obj.name && (obj.name.startsWith('shanye') || obj.name.startsWith('FCshanye'))) {
+        if (obj.name.startsWith('FCshanye')) {
+          obj.rotateOnAxis(new THREE.Vector3(0, 1, 0), 0);
+          meshArr01.push(obj);
+        } else if (obj.name.startsWith('shanye')) {
+          obj.rotateOnAxis(new THREE.Vector3(0, 1, 0), 0);
+          meshArr02.push(obj);
+        }
+      }
+    });
+    this.windowsActionArr.frontWindow = meshArr01;
+    this.windowsActionArr.backWindow = meshArr02;
+    this.group?.add(windowGroup);
+  }
+
+  /* 点击风窗,风窗全屏 */
+  mousedownModel(intersects: THREE.Intersection<THREE.Object3D<THREE.Event>>[]) {
+    this.isLRAnimation = false;
+    if (this.animationTimer) {
+      clearTimeout(this.animationTimer);
+      this.animationTimer = null;
+    }
+    // 判断是否点击到视频
+    intersects.find((intersect) => {
+      const mesh = intersect.object;
+      return false;
+    });
+  }
+
+  mouseUpModel() {
+    // 10s后开始摆动
+    if (!this.animationTimer && !this.isLRAnimation) {
+      this.animationTimer = setTimeout(() => {
+        this.isLRAnimation = true;
+      }, 10000);
+    }
+  }
+
+  play(rotationParam, flag) {
+    if (this.windowsActionArr.frontWindow.length <= 0 || this.windowsActionArr.backWindow.length <= 0) {
+      return;
+    }
+    if (flag === 1) {
+      // 前风窗动画
+      this.windowsActionArr.frontWindow.forEach((mesh) => {
+        gsap.to(mesh.rotation, {
+          y: THREE.MathUtils.degToRad(rotationParam.frontDeg1),
+          duration: (1 / 9) * Math.abs(rotationParam.frontDeg1 - mesh.rotation.y),
+          overwrite: true,
+        });
+      });
+    } else if (flag === 2) {
+      // 后风窗动画
+      this.windowsActionArr.backWindow.forEach((mesh) => {
+        gsap.to(mesh.rotation, {
+          y: THREE.MathUtils.degToRad(rotationParam.backDeg1),
+          duration: (1 / 9) * Math.abs(rotationParam.backDeg1 - mesh.rotation.y),
+          overwrite: true,
+        });
+      });
+    } else if (flag === 0) {
+      ([...this.windowsActionArr.frontWindow, ...this.windowsActionArr.backWindow] as THREE.Mesh[]).forEach((mesh) => {
+        gsap.to(mesh.rotation, {
+          y: 0,
+          overwrite: true,
+        });
+      });
+    }
+  }
+
+  async initCamera(dom1?, dom2?) {
+    const videoPlayer1 = dom1;
+    const videoPlayer2 = dom2;
+    let monitorPlane: THREE.Mesh | null = null;
+    if (!videoPlayer1 || !videoPlayer2) {
+      const textArr = [
+        {
+          text: `无信号输入`,
+          font: 'normal 40px Arial',
+          color: '#009900',
+          strokeStyle: '#002200',
+          x: 170,
+          y: 40,
+        },
+      ];
+      const canvas = await getTextCanvas(320, 180, '', 'noSinge.png');
+      let textMaterial: THREE.MeshBasicMaterial | null = null;
+      if (canvas) {
+        const textMap = new THREE.CanvasTexture(canvas); // 关键一步
+        textMaterial = new THREE.MeshBasicMaterial({
+          map: textMap, // 设置纹理贴图
+          transparent: true,
+          side: THREE.DoubleSide, // 这里是双面渲染的意思
+        });
+        textMaterial.blending = THREE.CustomBlending;
+
+        const planeGeometry = new THREE.PlaneGeometry(100, 100); // 平面3维几何体PlaneGeometry
+        monitorPlane = new THREE.Mesh(planeGeometry, textMaterial);
+
+        textMaterial.dispose();
+        planeGeometry.dispose();
+        textMap.dispose();
+      }
+    }
+    const player1 = this.group.getObjectByName('player1');
+    if (player1) {
+      this.model.clearMesh(player1);
+      this.group.remove(player1);
+    }
+    const noPlayer1 = this.group.getObjectByName('noPlayer1');
+    if (noPlayer1) {
+      this.model.clearMesh(noPlayer1);
+      this.group.remove(noPlayer1);
+    }
+    if (!videoPlayer1 && videoPlayer1 === null) {
+      if (monitorPlane && !this.group.getObjectByName('noPlayer1')) {
+        const planeMesh = monitorPlane.clone();
+        planeMesh.name = 'noPlayer1';
+        planeMesh.scale.set(0.011, 0.0053, 0.012);
+        planeMesh.position.set(-4.3, 0.13, -0.23);
+        this.group?.add(planeMesh.clone());
+      }
+    } else if (videoPlayer1) {
+      try {
+        const mesh = renderVideo(this.group, videoPlayer1, 'player1');
+        if (mesh) {
+          mesh?.scale.set(-0.034, 0.024, 1);
+          mesh?.position.set(-3.332, 0.11, -0.23);
+          mesh.rotation.y = -Math.PI;
+          this.group.add(mesh);
+        }
+      } catch (error) {
+        console.log('视频信号异常');
+      }
+    }
+    const player2 = this.group.getObjectByName('player2');
+    if (player2) {
+      this.model.clearMesh(player2);
+      this.group.remove(player2);
+    }
+    const noPlayer2 = this.group.getObjectByName('noPlayer2');
+    if (noPlayer2) {
+      this.model.clearMesh(noPlayer2);
+      this.group.remove(noPlayer2);
+    }
+    if (!videoPlayer2 && videoPlayer2 === null) {
+      if (monitorPlane && !this.group.getObjectByName('noPlayer2')) {
+        const planeMesh = monitorPlane.clone();
+        planeMesh.name = 'noPlayer2';
+        planeMesh.scale.set(0.0085, 0.0056, 0.012);
+        planeMesh.position.set(4.29, 0.02, -0.41);
+        this.group?.add(planeMesh.clone());
+      }
+    } else if (videoPlayer2) {
+      try {
+        const mesh = renderVideo(this.group, videoPlayer2, 'player2');
+        if (mesh) {
+          mesh?.scale.set(-0.034, 0.024, 1);
+          mesh?.position.set(-3.332, 0.11, -0.23);
+          mesh.rotation.y = -Math.PI;
+          this.group.add(mesh);
+        }
+      } catch (error) {
+        console.log('视频信号异常');
+      }
+    }
+  }
+
+  mountedThree(playerDom) {
+    return new Promise((resolve) => {
+      this.model.setGLTFModel(['sdFc'], this.group).then(() => {
+        this.setModalPosition();
+        this.initAnimation();
+        resolve(null);
+        // this.initCamera(playerDom);
+      });
+    });
+  }
+
+  destroy() {
+    this.model.clearGroup(this.group);
+    this.windowsActionArr.frontWindow = undefined;
+    this.windowsActionArr.backWindow = undefined;
+    this.model = null;
+    this.group = null;
+  }
+}
+
+export default doubleWindow;