Browse Source

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

bobo04052021@163.com 3 days ago
parent
commit
50cf70e5e5
64 changed files with 3092 additions and 784 deletions
  1. BIN
      src/assets/images/dataCenter/infoCenter/icon1.png
  2. BIN
      src/assets/images/dataCenter/infoCenter/icon2.png
  3. BIN
      src/assets/images/dataCenter/infoCenter/icon3.png
  4. BIN
      src/assets/images/dataCenter/infoCenter/icon4.png
  5. BIN
      src/assets/images/dataCenter/infoCenter/icon5.png
  6. BIN
      src/assets/images/dataCenter/infoCenter/info-border1.png
  7. BIN
      src/assets/images/dataCenter/infoCenter/info-border2.png
  8. BIN
      src/assets/images/dataCenter/infoCenter/info-border3.png
  9. BIN
      src/assets/images/dataCenter/infoCenter/info-border4.png
  10. BIN
      src/assets/images/dataCenter/infoCenter/info-title.png
  11. BIN
      src/assets/images/dataCenter/infoCenter/rank-1.png
  12. BIN
      src/assets/images/dataCenter/infoCenter/rank-2.png
  13. BIN
      src/assets/images/dataCenter/infoCenter/rank-3.png
  14. BIN
      src/assets/images/dataCenter/infoCenter/rank-4.png
  15. BIN
      src/assets/images/dataCenter/infoCenter/rank-bg.png
  16. BIN
      src/assets/images/dataCenter/infoCenter/split-line.png
  17. BIN
      src/assets/images/dataCenter/infoCenter/view-icon.png
  18. BIN
      src/assets/images/themify/green/vent/border/box-bottom.png
  19. BIN
      src/assets/images/themify/green/vent/border/box-top.png
  20. BIN
      src/assets/images/themify/green/vent/sheet-bg.png
  21. BIN
      src/assets/images/themify/green/vent/sheet-header.png
  22. BIN
      src/assets/images/themify/green/vent/vent-header1.png
  23. 6 0
      src/components/vent/customHeader.vue
  24. 12 0
      src/components/vent/ventBox1.vue
  25. 2 0
      src/design/theme.less
  26. 68 0
      src/design/themify/green.less
  27. 1 0
      src/enums/appEnum.ts
  28. 45 27
      src/hooks/vent/useSvgAnimation.ts
  29. 4 0
      src/layouts/default/header/components/user-dropdown/ThemeSelect.vue
  30. 2 0
      src/views/vent/comment/threejs/ArrowFlow.ts
  31. 10 7
      src/views/vent/comment/threejs/SmokePartical.ts
  32. 71 0
      src/views/vent/dataCenter/infoCenter/components/infoBox.vue
  33. 270 0
      src/views/vent/dataCenter/infoCenter/index.vue
  34. 228 0
      src/views/vent/dataCenter/infoCenter/infoCenter.data.ts
  35. 1 1
      src/views/vent/home/clique/components/3Dmap/3dMap.ts
  36. 10 0
      src/views/vent/home/configurable/components/ModuleCommon.vue
  37. 20 10
      src/views/vent/home/configurable/components/ModuleCommonDual.vue
  38. 5 1
      src/views/vent/home/configurable/dust.vue
  39. 5 0
      src/views/vent/home/configurable/fire.vue
  40. 8 7
      src/views/vent/home/configurable/ventSDG.vue
  41. 15 116
      src/views/vent/monitorManager/comment/GroupMonitorTable.vue
  42. 6 1
      src/views/vent/monitorManager/deviceMonitor/staticSheets/commonSheet.vue
  43. 6 1
      src/views/vent/monitorManager/deviceMonitor/staticSheets/dustSheet.vue
  44. 6 1
      src/views/vent/monitorManager/deviceMonitor/staticSheets/fireSheet.vue
  45. 6 1
      src/views/vent/monitorManager/deviceMonitor/staticSheets/gasSheet.vue
  46. 6 1
      src/views/vent/monitorManager/deviceMonitor/staticSheets/ventilateSheet.vue
  47. 400 0
      src/views/vent/monitorManager/fanLocalMonitor/fanLocal.threejs.single.ts
  48. 56 37
      src/views/vent/monitorManager/fanLocalMonitor/fanLocal.threejs.ts
  49. 16 7
      src/views/vent/monitorManager/fanLocalMonitor/index.vue
  50. 174 0
      src/views/vent/monitorManager/fireDoorMonitor/fireDoor.threejs.ssl.ts
  51. 60 13
      src/views/vent/monitorManager/fireDoorMonitor/fireDoor.threejs.ts
  52. 296 296
      src/views/vent/monitorManager/fireDoorMonitor/index.vue
  53. 12 12
      src/views/vent/monitorManager/gateMonitor/gate.threejs.ts
  54. 249 0
      src/views/vent/monitorManager/mainFanMonitor/components/entryThree.vue
  55. 699 0
      src/views/vent/monitorManager/mainFanMonitor/components/mainFanSVG.vue
  56. 20 236
      src/views/vent/monitorManager/mainFanMonitor/index.vue
  57. 25 2
      src/views/vent/monitorManager/mainFanMonitor/main.data.ts
  58. 45 1
      src/views/vent/monitorManager/mainFanMonitor/main.threejs.ts
  59. 199 0
      src/views/vent/monitorManager/mainFanMonitor/mainWind.lidt.threejs.ts
  60. 11 4
      src/views/vent/monitorManager/windowMonitor/components/windowDualSVG.vue
  61. 3 1
      src/views/vent/monitorManager/windowMonitor/components/windowSVG.vue
  62. 1 1
      src/views/vent/monitorManager/windrectMonitor/index.vue
  63. 9 0
      src/views/vent/sys/setting/index.vue
  64. 4 0
      src/views/vent/sys/setting/setting.data.ts

BIN
src/assets/images/dataCenter/infoCenter/icon1.png


BIN
src/assets/images/dataCenter/infoCenter/icon2.png


BIN
src/assets/images/dataCenter/infoCenter/icon3.png


BIN
src/assets/images/dataCenter/infoCenter/icon4.png


BIN
src/assets/images/dataCenter/infoCenter/icon5.png


BIN
src/assets/images/dataCenter/infoCenter/info-border1.png


BIN
src/assets/images/dataCenter/infoCenter/info-border2.png


BIN
src/assets/images/dataCenter/infoCenter/info-border3.png


BIN
src/assets/images/dataCenter/infoCenter/info-border4.png


BIN
src/assets/images/dataCenter/infoCenter/info-title.png


BIN
src/assets/images/dataCenter/infoCenter/rank-1.png


BIN
src/assets/images/dataCenter/infoCenter/rank-2.png


BIN
src/assets/images/dataCenter/infoCenter/rank-3.png


BIN
src/assets/images/dataCenter/infoCenter/rank-4.png


BIN
src/assets/images/dataCenter/infoCenter/rank-bg.png


BIN
src/assets/images/dataCenter/infoCenter/split-line.png


BIN
src/assets/images/dataCenter/infoCenter/view-icon.png


BIN
src/assets/images/themify/green/vent/border/box-bottom.png


BIN
src/assets/images/themify/green/vent/border/box-top.png


BIN
src/assets/images/themify/green/vent/sheet-bg.png


BIN
src/assets/images/themify/green/vent/sheet-header.png


BIN
src/assets/images/themify/green/vent/vent-header1.png


+ 6 - 0
src/components/vent/customHeader.vue

@@ -85,6 +85,12 @@
   @import '/@/design/theme.less';
   @ventSpace: zxm;
 
+  @{theme-green} {
+    .vent-custom-header {
+      --image-vent-header1: url('/@/assets/images/themify/green/vent/vent-header1.png');
+      --image-select-bg: url('/@/assets/images/themify/deepblue/vent/home/select-bg.png');
+    }
+  }
   @{theme-deepblue} {
     .vent-custom-header {
       --image-vent-header1: url('/@/assets/images/themify/deepblue/vent/vent-header1.png');

+ 12 - 0
src/components/vent/ventBox1.vue

@@ -23,6 +23,18 @@
 
 <style lang="less" scoped>
   @import '/@/design/theme.less';
+  @{theme-green} {
+    .vent-box1-bg {
+      --image-box1-top: url(/@/assets/images/themify/green/vent/border/box-top.png);
+      --image-box1-bottom: url('/@/assets/images/themify/green/vent/border/box-bottom.png');
+      // --container-color: #1e2932;
+      --container-image: linear-gradient(#3df6ff00, #308972, #3df6ff00);
+    }
+    // .box1-center {
+    //   height: calc(100% - 37px);
+    //   clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 10px 100%, 0 calc(100% - 10px));
+    // }
+  }
 
   @{theme-deepblue} {
     .vent-box1-bg {

+ 2 - 0
src/design/theme.less

@@ -2,6 +2,7 @@
 @import './themify/light.less';
 @import './themify/default.less';
 @import './themify/deepblue.less';
+@import './themify/green.less';
 
 @ventSpace: zxm;
 
@@ -138,3 +139,4 @@ html[data-theme='light'] {
 @theme-light: ~"html[data-theme='light']";
 @theme-default: ~'html';
 @theme-deepblue: ~"html[data-theme='deepblue']";
+@theme-green: ~"html[data-theme='green']";

+ 68 - 0
src/design/themify/green.less

@@ -0,0 +1,68 @@
+html[data-theme='green'] {
+  --vent-primary-color: #0a84ff;
+
+  --vent-header-bg-color: linear-gradient(#0a84ff99, #0a84ff22);
+
+  --vent-btn-primary-hover-color: #2986e9;
+  --vent-btn-primary-focus-color: #4cc4b5;
+  --vent-btn-primary-color: #257180;
+  --vent-btn-primary-border-color: #3f506a;
+
+  --vent-table-thead: #172943;
+  --vent-table-thead-border: #4cc4b5;
+  --vent-table-hover: #2c4650;
+  --vent-table-no-hover: #223038;
+  --vent-table-action-link: #86b9a7;
+
+  --vent-modal-title: #0a80fa;
+  --vent-modal-border: #308972;
+  --vent-modal-bg: #181b24;
+  --vent-modal-box-shadow: #316b92;
+  --vent-modal-bg2: linear-gradient(#2986e955, #0963c155);
+
+  --vent-tabs-bg: linear-gradient(#0091aa33, #2081ff11);
+  --vent-tabs-table-thead: #3d9dd433;
+  // --vent-tabs-action-link: #28f3f3;
+  // --vent-tabs-bg: linear-gradient(#28385155, #27334722);
+  // --vent-tabs-table-thead: #172943;
+  --vent-tabs-action-link: #0a80fa;
+
+  --vent-form-item-border: #308972;
+
+  --vent-text-base: #fff;
+  --vent-base-color: #181b24;
+  --vent-base-border: #3f506a;
+  --vent-base-light-bg: #8691a3;
+  --vent-base-light-bg-opcity: #8691a355;
+  --vent-transparent: #1e2932;
+  --vent-font-color: #ffffff;
+  --vent-font-action-link: #0a80fa;
+
+  --vent-configurable-bg: #02132c;
+  --vent-configurable-module-bg: #0d2037;
+  --vent-configurable-original-module-bg: #0d203711;
+  --vent-configurable-module-border-bd: linear-gradient(#3b4c65 0%, #3b4c65 60%, #000723);
+  --vent-configurable-dropdown: linear-gradient(to bottom, #293645, #3b4550);
+  --vent-configurable-home-bg-img: linear-gradient(to top, #39a3ff00, #0091ff99);
+  --vent-configurable-home-timeline: linear-gradient(to top, #39a3ff00, #0091ff99, #39a3ff00);
+  --vent-configurable-home-light-border: #fbfdff;
+
+  --vent-gas-list-item-bg-img: linear-gradient(to right, #213248, #21324800);
+  --vent-gas-tab-bg: #10427a;
+  --vent-gas-tab-bg-actived: #166ab5;
+  --vent-gas-tab-border: #525c68;
+  --vent-gas-primary-text: #ededed;
+  --vent-gas-primary-bg: #244d84;
+  --vent-gas-primary-trasparent-bg: #0091ff12;
+
+  --vent-device-manager-box-border: #fff;
+  --vent-device-manager-box-bg: #aaaaaa11;
+  --vent-device-manager-control-btn: linear-gradient(#1572d5, #0963c1);
+  --vent-device-manager-control-btn-hover: linear-gradient(#1572d555, #0963c155);
+
+  --vent-warn-tab-bg: #0f376c;
+  --vent-warn-tab-border: #107eec;
+  --vent-warn-tab-bg-actived: #0963c1;
+
+
+}

+ 1 - 0
src/enums/appEnum.ts

@@ -24,6 +24,7 @@ export enum ThemeEnum {
   LIGHT = 'light',
   VENT1 = 'vent1',
   DEEPBLUE = 'deepblue',
+  GREEN = 'green',
 }
 
 export enum SettingButtonPositionEnum {

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

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

+ 4 - 0
src/layouts/default/header/components/user-dropdown/ThemeSelect.vue

@@ -45,6 +45,10 @@
       value: ThemeEnum.DEEPBLUE,
       label: '深蓝',
     },
+    {
+      value: ThemeEnum.GREEN,
+      label: '绿色',
+    },
   ];
 
   const { getDarkMode, setDarkMode } = useRootSetting();

+ 2 - 0
src/views/vent/comment/threejs/ArrowFlow.ts

@@ -23,6 +23,7 @@ export default class ArrowFlow extends THREE.MeshBasicMaterial {
       offsetX = 0,
       /** 0-1 */
       offsetY = 1,
+      rotation = 0,
     } = {}
   ) {
     const t = new THREE.TextureLoader().load(texturePath);
@@ -30,6 +31,7 @@ export default class ArrowFlow extends THREE.MeshBasicMaterial {
     t.wrapT = THREE.RepeatWrapping;
     t.repeat = new THREE.Vector2(repeatX, repeatY);
     t.offset = new THREE.Vector2(offsetX, offsetY);
+    t.rotation = rotation;
     super({ map: t, transparent: true });
     this.texture = t;
     this.repeat = t.repeat;

+ 10 - 7
src/views/vent/comment/threejs/SmokePartical.ts

@@ -70,26 +70,29 @@ export default class SmokePartical {
               (Math.random() * 2 - 1) * 3 + obj.path1.z
             );
           } else {
+            const len = obj.spreadRang ? obj.spreadRang : 3;
             vec = new THREE.Vector3(
-              (Math.random() * 2 - 1) * 3 + obj.path1.x,
-              (Math.random() * 2 - 1) * 3 + obj.path1.y,
-              (Math.random() * 2 - 1) * 3 + obj.path1.z
+              (Math.random() * 2 - 1) * len + obj.path1.x,
+              (Math.random() * 2 - 1) * len + obj.path1.y,
+              (Math.random() * 2 - 1) * len + obj.path1.z
             );
           }
           obj.path1.copy(vec);
         } else if (obj.spreadDirection == -1) {
           let vec;
           if (Math.abs(obj.path0.y - obj.path1.y) > 3) {
+            const len = obj.spreadRang ? obj.spreadRang : 8;
             vec = new THREE.Vector3(
               (Math.random() * 2 - 1) * 3 + obj.path0.x,
-              Math.random() * 8 + obj.path0.y,
+              Math.random() * len + obj.path0.y,
               (Math.random() * 2 - 1) * 3 + obj.path0.z
             );
           } else {
+            const len = obj.spreadRang ? obj.spreadRang : 3;
             vec = new THREE.Vector3(
-              (Math.random() * 2 - 1) * 3 + obj.path0.x,
-              (Math.random() * 2 - 1) * 3 + obj.path0.y,
-              (Math.random() * 2 - 1) * 3 + obj.path0.z
+              (Math.random() * 2 - 1) * len + obj.path0.x,
+              (Math.random() * 2 - 1) * len + obj.path0.y,
+              (Math.random() * 2 - 1) * len + obj.path0.z
             );
           }
           obj.path0.copy(vec);

+ 71 - 0
src/views/vent/dataCenter/infoCenter/components/infoBox.vue

@@ -0,0 +1,71 @@
+<template>
+  <div class="info-box-bg">
+    <div class="box-top">
+      <div class="title">
+        <slot name="title"></slot>
+      </div>
+    </div>
+    <div class="box-center">
+      <div class="box-container">
+        <slot name="container"></slot>
+      </div>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup></script>
+
+<style lang="less" scoped>
+  @font-face {
+    font-family: 'douyuFont';
+    src: url('@/assets/font/douyuFont.otf');
+  }
+  @import '/@/design/theme.less';
+  .info-box-bg {
+    --image-border1: url('/@/assets/images/dataCenter/infoCenter/info-border1.png');
+    --image-border2: url('/@/assets/images/dataCenter/infoCenter/info-border2.png');
+    --image-border3: url('/@/assets/images/dataCenter/infoCenter/info-border3.png');
+    --image-border4: url('/@/assets/images/dataCenter/infoCenter/info-border4.png');
+    --image-title: url('/@/assets/images/dataCenter/infoCenter/info-title.png');
+
+    --container-color: #00213236;
+    --container-image: linear-gradient(#3df6ff00, #3df6ff, #3df6ff00);
+    width: 100%;
+    height: 100%;
+    min-height: 80px;
+    position: relative;
+    background: var(--image-border4) no-repeat;
+    background-size: 100% 100%;
+    overflow: hidden;
+    .box-top {
+      width: 220px;
+      height: 35px;
+
+      .title {
+        width: 100%;
+        height: 35px;
+        display: flex;
+        background-image: var(--image-title);
+        background-size: 100% 100%;
+        justify-content: center;
+        align-items: center;
+        color: #66ffff;
+        font-family: 'douyuFont';
+        padding-top: 5px;
+      }
+    }
+    .box-center {
+      width: calc(100% + 0.5px);
+      height: calc(100% - 50px);
+      position: relative;
+
+      .box-container {
+        width: calc(100% - 2px);
+        height: 100%;
+        min-height: 50px;
+        padding: 10px 25px;
+        color: #fff;
+        overflow-y: scroll;
+      }
+    }
+  }
+</style>

+ 270 - 0
src/views/vent/dataCenter/infoCenter/index.vue

@@ -0,0 +1,270 @@
+<template>
+  <div class="company-home">
+    <customHeader>{{ mainTitle }}</customHeader>
+    <div class="company-content">
+      <div class="content-item item-1">
+        <infoBox class="infoBox1">
+          <template #title> 系统数据量 </template>
+          <template #container>
+            <div class="content-wrapper wrapper-1 grid">
+              <div class="data-item">
+                <div class="item-icon icon1"></div>
+                <div>
+                  <div class="label">接入系统数量</div>
+                  <div class="value">262 </div>
+                </div>
+              </div>
+              <div class="data-item">
+                <div class="item-icon icon2"></div>
+                <div>
+                  <div class="label">接入点位数量</div>
+                  <div class="value status-normal">14521</div>
+                </div>
+              </div>
+              <div class="data-item">
+                <div class="item-icon icon3"></div>
+                <div>
+                  <div class="label">数据存储量</div>
+                  <div class="value status-normal">14521</div>
+                </div>
+              </div>
+              <div class="data-item">
+                <div class="item-icon icon4"></div>
+                <div>
+                  <div class="label">消息总数量(条)</div>
+                  <div class="value status-normal">14521</div>
+                </div>
+              </div>
+              <div class="data-item">
+                <div class="item-icon icon5"></div>
+                <div>
+                  <div class="label">共享接口数量</div>
+                  <div class="value status-normal">14521</div>
+                </div>
+              </div>
+            </div>
+          </template>
+        </infoBox>
+      </div>
+      <div class="content-item item-2 grid">
+        <infoBox class="infoBox2">
+          <template #title> 每日采集数据量 </template>
+          <template #container>
+            <div class="content-wrapper">
+              <CustomChart :chart-config="dailyNumOption" :chart-data="dailyNumData" height="250px" />
+            </div>
+          </template>
+        </infoBox>
+        <infoBox class="infoBox3">
+          <template #title> 系统数据量排名 </template>
+          <template #container>
+            <div class="content-wrapper">
+              <a-table size="small" :dataSource="sysData" :columns="sysDataColumn" :pagination="false" />
+            </div>
+          </template>
+        </infoBox>
+      </div>
+      <div class="content-item item-3">
+        <infoBox class="infoBox4">
+          <template #title> 系统接入情况 </template>
+          <template #container>
+            <div class="content-wrapper">
+              <a-table size="small" :dataSource="accessStatusData" :columns="accessStatusColumn" :pagination="false">
+                <template #action="{ record }">
+                  <div class="option-cont">
+                    <div class="view-icon"></div>
+                    <a class="table-action-link" @click="viewData(record)">查看数据</a>
+                  </div>
+                </template>
+              </a-table>
+            </div>
+          </template>
+        </infoBox>
+      </div>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { ref } from 'vue';
+  import customHeader from '/@/components/vent/customHeader.vue';
+  import infoBox from './components/infoBox.vue';
+  import { sysDataColumn, sysData, accessStatusColumn, accessStatusData, dailyNumOption, dailyNumData } from './infoCenter.data';
+  import CustomChart from '@/views/vent/home/configurable/components/detail/CustomChart.vue';
+  let mainTitle = ref('智能通风数据中心');
+
+  function viewData(record) {
+    console.log(record);
+  }
+</script>
+<style lang="less" scoped>
+  @font-face {
+    font-family: 'douyuFont';
+    src: url('@/assets/font/douyuFont.otf');
+  }
+
+  .company-home {
+    width: 100%;
+    height: 100%;
+    position: relative;
+
+    :deep(.vent-home-header) {
+      height: 50px;
+      background: url('@/assets/images/vent/home/modal-top.png') no-repeat center;
+      background-size: 100% 100%;
+    }
+
+    .company-content {
+      position: absolute;
+      left: 0;
+      width: 100%;
+      height: calc(100% - 50px);
+      padding: 20px 20px 10px 20px;
+      --image-border1: url('/@/assets/images/dataCenter/infoCenter/info-border1.png');
+      --image-border2: url('/@/assets/images/dataCenter/infoCenter/info-border2.png');
+      --image-border3: url('/@/assets/images/dataCenter/infoCenter/info-border3.png');
+      --image-border4: url('/@/assets/images/dataCenter/infoCenter/info-border4.png');
+      --image-split-line: url('/@/assets/images/dataCenter/infoCenter/split-line.png');
+      --image-rank: url('/@/assets/images/dataCenter/infoCenter/rank-bg.png');
+      .content-item {
+        width: 100%;
+        overflow: hidden;
+        padding-bottom: 20px;
+      }
+      .item-1 {
+        height: 20%;
+      }
+      .item-2 {
+        height: 40%;
+        grid-template-columns: 6fr 4fr;
+        gap: 20px;
+        :deep(table) {
+          border-collapse: separate !important;
+          border-spacing: 0 10px !important;
+          .zxm-table-thead {
+            height: 35px;
+            background-color: unset !important;
+            .zxm-table-cell {
+              color: #66ffff !important;
+            }
+          }
+          .zxm-table-tbody {
+            background-color: unset !important;
+          }
+          .zxm-table-cell {
+            border: none !important;
+            background: none !important;
+            padding: 0px;
+          }
+          tr {
+            background: var(--image-rank) no-repeat !important;
+            background-size: 100% 100% !important;
+          }
+        }
+      }
+      .item-3 {
+        height: 40%;
+        padding-bottom: 0;
+        :deep(table) {
+          border-collapse: collapse !important;
+          // border-spacing: 0 10px !important;
+          .zxm-table-thead {
+            height: 35px;
+            background-color: #0b2542 !important;
+            border: 1px solid #1f7eb5;
+            .zxm-table-cell {
+              border: none !important;
+              color: #37e0eb !important;
+            }
+          }
+          .zxm-table-tbody {
+            .zxm-table-row:nth-child(odd) {
+              background-color: #0e3455;
+            }
+
+            /* 偶数行背景色 */
+            .zxm-table-row:nth-child(even) {
+              background-color: #114268 !important;
+            }
+            .zxm-table-cell {
+              border: none !important;
+              background: none !important;
+            }
+          }
+        }
+      }
+      .infoBox1 {
+        background-color: var(--image-border1) no-repeat;
+      }
+      .infoBox2 {
+        background-color: var(--image-border2) no-repeat;
+      }
+      .infoBox3 {
+        background-color: var(--image-border3) no-repeat;
+      }
+      .infoBox4 {
+        background-color: var(--image-border4) no-repeat;
+      }
+    }
+    .wrapper-1 {
+      grid-template-columns: repeat(5, 1fr);
+      .data-item {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        position: relative;
+        &::after {
+          position: absolute;
+          right: 1px;
+          top: 5px;
+          display: block;
+          width: 2px;
+          height: 100%;
+          background: var(--image-split-line);
+          content: '';
+        }
+        &:last-child::after {
+          display: none;
+        }
+        .value {
+          font-family: 'douyuFont';
+          color: #66ffff;
+          margin-top: 20px;
+        }
+        .item-icon {
+          width: 80px;
+          height: 80px;
+          background-size: 100% 100%;
+          margin-right: 15px;
+        }
+        .icon1 {
+          background-image: url('/@/assets/images/dataCenter/infoCenter/icon1.png');
+        }
+        .icon2 {
+          background-image: url('/@/assets/images/dataCenter/infoCenter/icon2.png');
+        }
+        .icon3 {
+          background-image: url('/@/assets/images/dataCenter/infoCenter/icon3.png');
+        }
+        .icon4 {
+          background-image: url('/@/assets/images/dataCenter/infoCenter/icon4.png');
+        }
+        .icon5 {
+          background-image: url('/@/assets/images/dataCenter/infoCenter/icon5.png');
+        }
+      }
+    }
+    .option-cont {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      .view-icon {
+        width: 20px;
+        height: 20px;
+        background-image: url('/@/assets/images/dataCenter/infoCenter/view-icon.png');
+        background-repeat: no-repeat;
+        background-size: contain;
+        background-position: center;
+      }
+    }
+  }
+</style>

+ 228 - 0
src/views/vent/dataCenter/infoCenter/infoCenter.data.ts

@@ -0,0 +1,228 @@
+import { BasicColumn } from '/@/components/Table';
+import { ModuleDataChart } from '/@/views/vent/deviceManager/configurationTable/types';
+
+import { h } from 'vue'; // 引入vue的h函数用于创建VNode
+
+// 系统数据排名
+export const sysDataColumn: BasicColumn[] = [
+  {
+    title: '',
+    align: 'center',
+    width: 60,
+    // 修正customRender的类型和返回值
+    customRender: ({ index }: { index: number }) => {
+      // 确保index是数字类型,避免算术运算错误
+      const numIndex = Number(index);
+      let rankImg = '';
+      if (numIndex === 0) {
+        rankImg = '/src/assets/images/dataCenter/infoCenter/rank-1.png';
+      } else if (numIndex === 1) {
+        rankImg = '/src/assets/images/dataCenter/infoCenter/rank-2.png';
+      } else if (numIndex === 2) {
+        rankImg = '/src/assets/images/dataCenter/infoCenter/rank-3.png';
+      } else {
+        rankImg = '/src/assets/images/dataCenter/infoCenter/rank-4.png';
+      }
+      return h(
+        'div',
+        {
+          style: {
+            width: '80px',
+            height: '35px',
+            backgroundImage: `url(${rankImg})`,
+            backgroundSize: '100% 100%',
+            backgroundRepeat: 'no-repeat',
+            position: 'relative',
+            margin: '0 15px',
+          },
+        },
+        [
+          // 排名文字
+          h(
+            'span',
+            {
+              style: {
+                position: 'absolute',
+                top: '50%',
+                left: '50%',
+                transform: 'translate(-50%, -50%)', 
+                color: '#fff',
+                fontSize: '14px',
+                fontWeight: 'bold',
+              },
+            },
+            `NO.${index + 1}`
+          ), // 显示NO.1、NO.2等
+        ]
+      );
+    },
+  },
+  {
+    title: '系统名称',
+    dataIndex: 'sysName',
+    align: 'center',
+  },
+  {
+    title: '数量',
+    dataIndex: 'sysNum',
+    align: 'center',
+  },
+];
+// 系统接入情况数据
+export const sysData = [
+  {
+    sysName: 'wscc',
+    sysNum: 32,
+  },
+  {
+    sysName: 'wscc',
+    sysNum: 33,
+  },
+  {
+    sysName: 'wscc',
+    sysNum: 34,
+  },
+  {
+    sysName: 'wscc',
+    sysNum: 35,
+  },
+  {
+    sysName: 'wscc',
+    sysNum: 35,
+  },
+  {
+    sysName: 'wscc',
+    sysNum: 35,
+  },
+];
+
+//系统接入情况
+export const accessStatusColumn: BasicColumn[] = [
+  {
+    title: '序号',
+    align: 'center',
+    customRender: ({ index }: { index: number }) => `${index + 1}`,
+  },
+  {
+    title: '系统名称',
+    dataIndex: 'sysName',
+    align: 'center',
+  },
+
+  {
+    title: '系统接入时间',
+    dataIndex: 'accessTime',
+    align: 'center',
+  },
+  {
+    title: '点位数量',
+    dataIndex: 'pointNum',
+    align: 'center',
+  },
+  {
+    title: '运行状态',
+    dataIndex: 'runStatus',
+    align: 'center',
+  },
+  {
+    title: '数据更新时间',
+    dataIndex: 'dataUpdateTime',
+    align: 'center',
+  },
+  {
+    title: '操作',
+    dataIndex: 'action',
+    width: 200,
+    align: 'center',
+    slots: { customRender: 'action' },
+  },
+];
+// 系统接入情况数据
+export const accessStatusData = [
+  {
+    sysName: '1',
+    accessTime: '2025.10.01',
+    pointNum: 32,
+    runStatus: '1',
+    dataUpdateTime: '2025.10.01',
+  },
+  {
+    sysName: '2',
+    accessTime: '2025.10.02',
+    pointNum: 32,
+    runStatus: '2',
+    dataUpdateTime: '2025.10.02',
+  },
+  {
+    sysName: '3',
+    accessTime: '2025.10.02',
+    pointNum: 32,
+    runStatus: '3',
+    dataUpdateTime: '2025.10.02',
+  },
+  {
+    sysName: '3',
+    accessTime: '2025.10.02',
+    pointNum: 32,
+    runStatus: '3',
+    dataUpdateTime: '2025.10.02',
+  },
+];
+// 每日采集数据表格属性
+export const dailyNumOption: ModuleDataChart = {
+  type: 'bar',
+  readFrom: '',
+  legend: { show: false },
+  xAxis: [{ show: true }],
+  yAxis: [{ show: true, name: '', position: 'left' }],
+  series: [{ readFrom: 'history', xprop: 'time', yprop: 'val', label: '' }],
+};
+// 每日采集数据
+export const dailyNumData = {
+  history: [
+    {
+      time: '2025-10-13',
+      val: '113',
+    },
+    {
+      time: '2025-10-14',
+      val: '153',
+    },
+    {
+      time: '2025-10-15',
+      val: '163',
+    },
+    {
+      time: '2025-10-16',
+      val: '193',
+    },
+    {
+      time: '2025-10-17',
+      val: '203',
+    },
+    {
+      time: '2025-10-18',
+      val: '203',
+    },
+    {
+      time: '2025-10-19',
+      val: '223',
+    },
+    {
+      time: '2025-10-20',
+      val: '183',
+    },
+    {
+      time: '2025-10-21',
+      val: '153',
+    },
+    {
+      time: '2025-10-22',
+      val: '183',
+    },
+    {
+      time: '2025-10-23',
+      val: '153',
+    },
+  ],
+};

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

@@ -642,7 +642,7 @@ class earthtMap {
     const geometry = new THREE.PlaneGeometry(15.0, 15.0);
     const texture = new THREE.TextureLoader().load('/texture/earth.jpg');
     const bumpTexture = new THREE.TextureLoader().load('/texture/earth.jpg');
-    texture.encoding = THREE.sRGBEncoding;
+    texture.encoding = THREE.colorSpace;
     const material = new THREE.MeshPhongMaterial({
       map: texture, // 贴图
       bumpMap: bumpTexture,

+ 10 - 0
src/views/vent/home/configurable/components/ModuleCommon.vue

@@ -91,6 +91,16 @@ function redirectTo() {
   padding: 0 !important;
   width: 100% !important;
 }
+@{theme-green} {
+  .module-common-longer {
+    :deep(.box1-top) {
+      --image-box1-top: url('/@/assets/images/themify/green/vent/border/box-top.png');
+    }
+    :deep(.box1-bottom) {
+      --image-box1-bottom:  url('/@/assets/images/themify/green/vent/border/box-bottom.png');
+    }
+  }
+}
 @{theme-deepblue} {
   .module-common-longer {
     :deep(.box1-top) {

+ 20 - 10
src/views/vent/home/configurable/components/ModuleCommonDual.vue

@@ -149,16 +149,26 @@
       background: linear-gradient(to top, #2bafc6 0%, rgba(44, 255, 221, 0.1) 50%, rgba(44, 255, 221, 0) 90%);
     }
   }
-  // @{theme-deepblue} {
-  //   .module-common-dual-longer {
-  //     :deep(.box1-top) {
-  //       --image-box1-top: url('/@/assets/images/themify/deepblue/vent/border/box2-top-long.png');
-  //     }
-  //     :deep(.box1-bottom) {
-  //       --image-box1-bottom: none;
-  //     }
-  //   }
-  // }
+  @{theme-green} {
+    .module-common-dual-longer {
+      :deep(.box1-top) {
+        --image-box1-top: url('/@/assets/images/themify/green/vent/border/box-top.png');
+      }
+      :deep(.box1-bottom) {
+        --image-box1-bottom: url('/@/assets/images/themify/green/vent/border/box-bottom.png');
+      }
+    }
+  }
+  @{theme-deepblue} {
+    .module-common-dual-longer {
+      :deep(.box1-top) {
+        --image-box1-top: url('/@/assets/images/themify/deepblue/vent/border/box2-top-long.png');
+      }
+      :deep(.box1-bottom) {
+        --image-box1-bottom: none;
+      }
+    }
+  }
   .module-common-dual-longer {
     :deep(.box1-top) {
       --image-box1-top: url('/@/assets/images/vent/box-top-bg.png');

+ 5 - 1
src/views/vent/home/configurable/dust.vue

@@ -109,7 +109,11 @@
     font-family: 'douyuFont';
     src: url('../../../../assets/font/douyuFont.otf');
   }
-
+  @{theme-green} {
+    .company-home {
+      --image-modal-top: url('@/assets/images/themify/green/vent/vent-header1.png');
+    }
+  }
   @{theme-deepblue} {
     .company-home {
       --image-modal-top: url('@/assets/images/themify/deepblue/vent/home/modal-top.png');

+ 5 - 0
src/views/vent/home/configurable/fire.vue

@@ -110,6 +110,11 @@
     src: url('../../../../assets/font/douyuFont.otf');
   }
 
+  @{theme-green} {
+    .company-home {
+      --image-modal-top: url('@/assets/images/themify/green/vent/vent-header1.png');
+    }
+  }
   @{theme-deepblue} {
     .company-home {
       --image-modal-top: url('@/assets/images/themify/deepblue/vent/home/modal-top.png');

+ 8 - 7
src/views/vent/home/configurable/ventSDG.vue

@@ -21,7 +21,7 @@
     </div> -->
     </div>
     <!-- 如果是有 deviceType、type 等 query,认为是详情页,不需要展示普通模块,只需要模型 -->
-    <CustomHeader v-if="!route.query.embed">三道沟煤矿均压综采面智能监测与动态调控</CustomHeader>
+    <CustomHeader v-if="!route.query.embed">{{ mainTitle }}</CustomHeader>
     <!-- <a class="ant-dropdown-link module-dropdown" @click.prevent="showBar = !showBar">
         全矿井通风检测
         <CaretDownOutlined />
@@ -104,14 +104,14 @@
   import { list } from './configurable.api';
   import { useRoute } from 'vue-router';
   import { useGlobSetting } from '/@/hooks/setting';
-  import { testConfigSDG } from './configurable.data.wz';
+  // import { testConfigSDG } from './configurable.data.wz';
 
-  const { sysDataType = 'monitor', title = '三道沟煤矿均压综采面智能监测与动态调控', sysOrgCode } = useGlobSetting();
+  const { /* sysDataType = 'monitor', */ title = '三道沟煤矿均压综采面智能监测与动态调控', sysOrgCode } = useGlobSetting();
   const { configs, isOriginal, isCommon, fetchConfigs } = useInitConfigs();
-  const { enhancedConfigs, hiddenList, data, updateData, updateEnhancedConfigs } = useInitPage(title);
+  const { enhancedConfigs, hiddenList, data, updateData, updateEnhancedConfigs, mainTitle } = useInitPage(title);
   const route = useRoute();
   // const router = useRouter();
-  const isDataRealTime = ref(sysDataType === 'monitor');
+  // const isDataRealTime = ref(sysDataType === 'monitor');
   // const showBar = ref(true);
   const loading = ref(false);
   const showModules = ref(true);
@@ -123,8 +123,9 @@
   // }
 
   function refresh() {
-    fetchConfigs(isDataRealTime.value ? 'vent_realtime' : 'vent').then(() => {
-      configs.value = testConfigSDG;
+    // fetchConfigs(isDataRealTime.value ? 'vent_realtime' : 'vent').then(() => {
+    fetchConfigs('vent').then(() => {
+      // configs.value = testConfigSDG;
       updateEnhancedConfigs(configs.value);
 
       // 测风装置	windrect

+ 15 - 116
src/views/vent/monitorManager/comment/GroupMonitorTable.vue

@@ -101,105 +101,6 @@
     }
   };
 
-  /** 定义table Columns */
-  // function setColumns(columnsType) {
-  //   const isCheckColumn = {
-  //     title: '',
-  //     dataIndex: 'isCheck',
-  //     width: 40,
-  //     align: 'center',
-  //     customCell: (_, index) => {
-
-  //       if (index % 3 == 0) {
-  //         return { rowSpan: 3 };
-  //       } else {
-  //         return { rowSpan: 0 };
-  //       }
-  //     },
-  //   };
-  //   const indexColumn = {
-  //     title: '序号',
-  //     dataIndex: 'key',
-  //     width: 80,
-  //     align: 'center',
-  //     customCell: (_, index) => {
-  //       if (index % 3 == 0) {
-  //         return { rowSpan: 3 };
-  //       } else {
-  //         return { rowSpan: 0 };
-  //       }
-  //     },
-  //     customRender: function ({ index }) {
-  //       return index / 3 + 1;
-  //     },
-  //   };
-  //   const runDevice = {
-  //     title: '风机',
-  //     dataIndex: 'runDevice',
-  //     width: 80,
-  //     align: 'center',
-  //   };
-
-  //   columns.value = getTableHeaderColumns(columnsType);
-  //   console.log('风机columns------------------>', columnsType);
-  //   if (columns.value && columns.value.length < 1) {
-  //     columns.value = getTableHeaderColumns(columnsType.split('_')[0] + '_monitor');
-  //   }
-
-  //   const strinstallpos = columns.value.find((item) => {
-  //     return item.dataIndex === 'strinstallpos' || item.dataIndex === 'strname';
-  //   });
-  //   if (strinstallpos) {
-  //     strinstallpos.customCell = (_, index) => {
-  //       if (index % 3 == 0) {
-  //         return { rowSpan: 3 };
-  //       } else {
-  //         return { rowSpan: 0 };
-  //       }
-  //     };
-  //   }
-  //   columns.value.forEach((item) => {
-  //     if (item.dataIndex === 'strinstallpos' || item.dataIndex === 'strname' || item.dataIndex.endsWith('_merge')) {
-  //       item.customCell = (_, index) => {
-  //         if (index % 3 == 0) {
-  //           return { rowSpan: 3 };
-  //         } else {
-  //           return { rowSpan: 0 };
-  //         }
-  //       };
-  //     }
-  //   });
-
-  //   columns.value.splice(1, 0, runDevice);
-  //   if (props.isShowSelect) {
-  //     columns.value = [isCheckColumn, indexColumn, ...columns.value];
-  //   } else {
-  //     columns.value = [indexColumn, ...columns.value];
-  //   }
-
-  //   if (props.isAction) {
-  //     columns.value = [
-  //       ...columns.value,
-  //       {
-  //         title: '操作',
-  //         dataIndex: 'operation',
-  //         width: 120,
-  //         align: 'center',
-  //         slots: { customRender: 'operation' },
-  //         customCell: (_, index) => {
-  //           if (index % 3 == 0) {
-  //             return { rowSpan: 3 };
-  //           } else {
-  //             return { rowSpan: 0 };
-  //           }
-  //         },
-  //       },
-  //     ];
-  //   }
-  //   // columns.value = [...columns.value, ...columns.value]
-  //   return columns;
-  // }
-
   function setColumns(columnsType) {
     const isCheckColumn = {
       title: '',
@@ -250,7 +151,6 @@
     });
     if (strinstallpos) {
       strinstallpos.customCell = (_, index) => {
-        debugger;
         const columnNum = _.modalTyoe ? (_.modalTyoe.endsWith('_3') ? 3 : _.modalTyoe.endsWith('_1') ? 1 : 2) : 2;
         if (_.rowIndex % columnNum == 0) {
           return { rowSpan: columnNum };
@@ -330,7 +230,10 @@
       const list: unknown[] = [];
       newVal.forEach((item, index) => {
         const data: any = toRaw(item);
-        const modalTyoe = data.modalTyoe; ///
+        if (data.deviceType.startsWith('fanlocal') && data.install_kind) {
+          data['modalTyoe'] = data.install_kind;
+        }
+        const modalTyoe = data.modalTyoe;
         const resultData1 = {};
         const resultData2 = {};
         const resultData3 = {};
@@ -391,34 +294,30 @@
           }
         });
         resultData1['deviceID'] = resultData2['deviceID'] = data['deviceID'];
-        debugger;
         if (props.columnsType.startsWith('fanlocal') && sysOrgCode !== 'zmhjhzmy') {
           resultData1['runDevice'] = '主机';
           resultData2['runDevice'] = '备机';
-          resultData1['rowIndex'] = 0;
-          resultData2['rowIndex'] = 1;
-          resultData1['index'] = index;
-          resultData2['index'] = index;
         } else {
           resultData1['runDevice'] = '1#风机';
           resultData2['runDevice'] = '2#风机';
           resultData3['runDevice'] = '3#风机';
-          resultData1['modalTyoe'] = modalTyoe;
-          resultData2['modalTyoe'] = modalTyoe;
-          resultData3['modalTyoe'] = modalTyoe;
+        }
+        resultData1['modalTyoe'] = modalTyoe;
+        resultData2['modalTyoe'] = modalTyoe;
+        resultData3['modalTyoe'] = modalTyoe;
 
-          resultData1['rowIndex'] = 0;
-          resultData2['rowIndex'] = 1;
-          resultData3['rowIndex'] = 2;
+        resultData1['rowIndex'] = 0;
+        resultData2['rowIndex'] = 1;
+        resultData3['rowIndex'] = 2;
 
-          resultData1['index'] = index;
-          resultData2['index'] = index;
-          resultData3['index'] = index;
-        }
+        resultData1['index'] = index;
+        resultData2['index'] = index;
+        resultData3['index'] = index;
         if (modalTyoe && modalTyoe.endsWith('_3')) {
           list.push(resultData1, resultData2, resultData3);
         } else if (modalTyoe && modalTyoe.endsWith('_1')) {
           list.push(resultData1);
+          // list.push(resultData1, resultData2);
         } else {
           list.push(resultData1, resultData2);
         }

+ 6 - 1
src/views/vent/monitorManager/deviceMonitor/staticSheets/commonSheet.vue

@@ -295,7 +295,12 @@
 </script>
 <style lang="less" scoped>
   @import url('/@/design/theme.less');
-
+  @{theme-green} {
+    .sheet-bg {
+      --sheet-bg: url(/@/assets/images/themify/green/vent/sheet-bg.png);
+      --sheet-header: url(/@/assets/images/themify/green/vent/sheet-header.png);
+    }
+  }
   @{theme-deepblue} {
     .sheet-bg {
       --sheet-bg: url(/@/assets/images/themify/deepblue/vent/sheet-bg.png);

+ 6 - 1
src/views/vent/monitorManager/deviceMonitor/staticSheets/dustSheet.vue

@@ -103,7 +103,12 @@
 </script>
 <style lang="less" scoped>
   @import url('/@/design/theme.less');
-
+  @{theme-green} {
+    .sheet-bg {
+      --sheet-bg: url(/@/assets/images/themify/green/vent/sheet-bg.png);
+      --sheet-header: url(/@/assets/images/themify/green/vent/sheet-header.png);
+    }
+  }
   @{theme-deepblue} {
     .sheet-bg {
       --sheet-bg: url(/@/assets/images/themify/deepblue/vent/sheet-bg.png);

+ 6 - 1
src/views/vent/monitorManager/deviceMonitor/staticSheets/fireSheet.vue

@@ -146,7 +146,12 @@
 </script>
 <style lang="less" scoped>
   @import url('/@/design/theme.less');
-
+  @{theme-green} {
+    .sheet-bg {
+      --sheet-bg: url(/@/assets/images/themify/green/vent/sheet-bg.png);
+      --sheet-header: url(/@/assets/images/themify/green/vent/sheet-header.png);
+    }
+  }
   @{theme-deepblue} {
     .sheet-bg {
       --sheet-bg: url(/@/assets/images/themify/deepblue/vent/sheet-bg.png);

+ 6 - 1
src/views/vent/monitorManager/deviceMonitor/staticSheets/gasSheet.vue

@@ -151,7 +151,12 @@
 </script>
 <style lang="less" scoped>
   @import url('/@/design/theme.less');
-
+  @{theme-green} {
+    .sheet-bg {
+      --sheet-bg: url(/@/assets/images/themify/green/vent/sheet-bg.png);
+      --sheet-header: url(/@/assets/images/themify/green/vent/sheet-header.png);
+    }
+  }
   @{theme-deepblue} {
     .sheet-bg {
       --sheet-bg: url(/@/assets/images/themify/deepblue/vent/sheet-bg.png);

+ 6 - 1
src/views/vent/monitorManager/deviceMonitor/staticSheets/ventilateSheet.vue

@@ -263,7 +263,12 @@
 </script>
 <style lang="less" scoped>
   @import url('/@/design/theme.less');
-
+  @{theme-green} {
+    .sheet-bg {
+      --sheet-bg: url(/@/assets/images/themify/green/vent/sheet-bg.png);
+      --sheet-header: url(/@/assets/images/themify/green/vent/sheet-header.png);
+    }
+  }
   @{theme-deepblue} {
     .sheet-bg {
       --sheet-bg: url(/@/assets/images/themify/deepblue/vent/sheet-bg.png);

+ 400 - 0
src/views/vent/monitorManager/fanLocalMonitor/fanLocal.threejs.single.ts

@@ -0,0 +1,400 @@
+import * as THREE from 'three';
+import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer';
+import { getTextCanvas, setModalCenter } from '/@/utils/threejs/util';
+import Smoke from '/@/views/vent/comment/threejs/Smoke';
+import gsap from 'gsap';
+
+// 本模型的上下文对象,用于实现本模型的特定功能,代码参考了旧有的 fanLocal.three.ts
+class ModelContext {
+  model;
+  modelName = 'jbfj-single';
+  group: THREE.Object3D | null = null;
+  fanType?: string;
+  topSmoke?: Smoke;
+  downSmoke?: Smoke;
+  returnSmoke?: Smoke;
+  topLife?: number;
+  downLife?: number;
+
+  constructor(model) {
+    this.model = model;
+  }
+
+  addLight() {
+    // optional implementation
+  }
+
+  mountedThree() {
+    return new Promise((resolve) => {
+      this.model.setGLTFModel([this.modelName]).then(async (gltf) => {
+        this.group = gltf[0];
+        if (this.group) {
+          setModalCenter(this.group);
+          this.addLight();
+          this.initFly();
+          this.setModalPosition();
+          resolve(null);
+        }
+      });
+    });
+  }
+
+  destroy() {
+    if (this.model) {
+      this.model.isRender = false;
+      this.clearFly();
+      this.topSmoke = undefined;
+      this.downSmoke = undefined;
+      this.returnSmoke = undefined;
+      // @ts-ignore
+      this.group = undefined;
+      this.model.destroy();
+    }
+  }
+
+  async initFly() {
+    const topCurve = [
+      {
+        path0: new THREE.Vector3(7.698, 0.398, 0.19),
+        path1: new THREE.Vector3(-0.65, 0.398, 0.19),
+        isSpread: true,
+        spreadDirection: 0, //
+      },
+      {
+        path0: new THREE.Vector3(-0.65, 0.398, 0.19),
+        path1: new THREE.Vector3(-7.599, 0.398, 0.19),
+        isSpread: true,
+        spreadRang: 1,
+        spreadDirection: 1, //
+      },
+    ];
+
+    if (!this.topSmoke) {
+      this.topSmoke = new Smoke('/model/img/texture-smoke.png', '#ffffff', 2, 0.35, 3.7, 100);
+      this.topSmoke.setPath(topCurve);
+      await this.topSmoke.setPoints();
+      this.group?.add(this.topSmoke.points);
+    }
+  }
+
+  playSmoke(selectData) {
+    // debugger;
+    if (selectData['Fan1StartStatus'] == '1') {
+      // 主风机打开
+      this.setSmokeFrequency('top', 40);
+      this.runFly('top', 'open');
+    } else {
+      // 备风机关闭
+      this.runFly('top', 'close');
+    }
+
+    if (selectData['Fan1StartStatus'] != '1') {
+      this.runFly('all', 'close');
+    }
+  }
+
+  runFly(deviceType, state) {
+    if (state === 'open') {
+      if (deviceType === 'top') {
+        if (this.downSmoke && this.downSmoke.frameId) {
+          this.downSmoke.stopSmoke();
+        }
+        if (this.topSmoke && !this.topSmoke.frameId) {
+          this.topSmoke.startSmoke();
+        }
+      }
+    } else {
+      if (deviceType === 'top') {
+        if (this.topSmoke && this.topSmoke.frameId) {
+          this.topSmoke.stopSmoke();
+        }
+      }
+    }
+    if (deviceType === 'all' && state === 'close') {
+      this.returnSmoke?.stopSmoke();
+    }
+  }
+
+  setSmokeFrequency(deviceType, frequency) {
+    const life = (frequency - 30) * 25;
+    let duration = 0;
+    let smoke;
+
+    if (deviceType === 'top') {
+      if (this.topLife == life) {
+        return;
+      }
+      this.topLife = life;
+      smoke = this.topSmoke;
+      duration = (Math.abs(life - smoke.life) / 500) * 25;
+    }
+    if (smoke) {
+      gsap.to(smoke, {
+        life: life,
+        duration: duration,
+        ease: 'easeInCubic',
+        overwrite: true,
+      });
+    }
+  }
+
+  addText(selectData) {
+    if (!this.group) {
+      return;
+    }
+    // @ts-ignore
+    const screenDownText = VENT_PARAM['modalText']
+      ? // @ts-ignore
+        VENT_PARAM['modalText']
+      : // @ts-ignore
+      History_Type['type'] == 'remote'
+      ? `国能神东煤炭集团监制`
+      : '煤炭科学技术研究院有限公司研制';
+
+    const screenDownTextX = 80 - (screenDownText.length - 10) * 6;
+    const textArr = [
+      {
+        text: `智能局部通风机监测与控制系统`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 20,
+        y: 108,
+      },
+      {
+        text: `供风距离(m):`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 0,
+        y: 152,
+      },
+      {
+        text: `${
+          selectData.airSupplyDistence_merge
+            ? selectData.airSupplyDistence_merge
+            : selectData.fchimenylength
+            ? selectData.fchimenylength
+            : selectData.airSupplyDistence_merge
+            ? selectData.airSupplyDistence_merge
+            : '-'
+        }`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 228,
+        y: 152,
+      },
+      {
+        text: `风筒直径(mm): `,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 0,
+        y: 200,
+      },
+      {
+        text: ` ${selectData.fchimenydiamlimit ? selectData.fchimenydiamlimit : selectData.ductDiameter_merge ? selectData.ductDiameter_merge : '-'}`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 220,
+        y: 200,
+      },
+      {
+        text: `故障诊断:`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 0,
+        y: 245,
+      },
+      {
+        text: `${selectData.warnLevel_str ? selectData.warnLevel_str : '-'}`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 220,
+        y: 245,
+      },
+      {
+        text: `型号功率:`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 0,
+        y: 285,
+      },
+      {
+        text: `${selectData.model_Power_merge ? selectData.model_Power_merge : '-'}`,
+        font: 'normal 26px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 220,
+        y: 285,
+      },
+      {
+        text: screenDownText,
+        font: 'normal 28px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: screenDownTextX,
+        y: 325,
+      },
+    ];
+    getTextCanvas(526, 346, textArr, '').then((canvas: HTMLCanvasElement) => {
+      const textMap = new THREE.CanvasTexture(canvas); // 关键一步
+      const textMaterial = new THREE.MeshBasicMaterial({
+        // 关于材质并未讲解 实操即可熟悉                 这里是漫反射类似纸张的材质,对应的就有高光类似金属的材质.
+        map: textMap, // 设置纹理贴图
+        transparent: true,
+        side: THREE.FrontSide, // 这里是双面渲染的意思
+      });
+      textMaterial.blending = THREE.CustomBlending;
+      const monitorPlane = this.group?.getObjectByName('monitorText');
+      if (monitorPlane) {
+        // @ts-ignore-next-line
+        monitorPlane.material = textMaterial;
+      } else {
+        const planeGeometry = new THREE.PlaneGeometry(526, 346); // 平面3维几何体PlaneGeometry
+        const planeMesh = new THREE.Mesh(planeGeometry, textMaterial);
+        planeMesh.name = 'monitorText';
+        planeMesh.scale.set(0.0135, 0.0135, 0.0135);
+        planeMesh.rotation.y = -Math.PI / 2;
+        planeMesh.position.set(-84.79, 0.82, 17.0);
+        this.group?.add(planeMesh);
+      }
+    });
+  }
+
+  addCssText() {
+    if (!this.group) return;
+
+    if (!this.group.getObjectByName('text1')) {
+      const element = document.getElementById('inputBox') as HTMLElement;
+      if (element) {
+        const fanLocalCSS3D = new CSS3DObject(element);
+        fanLocalCSS3D.name = 'text1';
+        fanLocalCSS3D.scale.set(0.04, 0.04, 0.04);
+        fanLocalCSS3D.rotation.y = -Math.PI / 2;
+        fanLocalCSS3D.position.set(-85.68, 5.97, -3.39);
+        this.group.add(fanLocalCSS3D);
+      }
+    }
+    if (!this.group.getObjectByName('text2')) {
+      const element = document.getElementById('outBox') as HTMLElement;
+      if (element) {
+        const fanLocalCSS3D = new CSS3DObject(element);
+        fanLocalCSS3D.name = 'text2';
+        fanLocalCSS3D.scale.set(0.15, 0.15, 0.15);
+        fanLocalCSS3D.rotation.y = -Math.PI / 2;
+        fanLocalCSS3D.position.set(85.62, 17.65, 7.71);
+        this.group.add(fanLocalCSS3D);
+      }
+    }
+    if (!this.group.getObjectByName('text3')) {
+      const element = document.getElementById('returnBox') as HTMLElement;
+      if (element) {
+        const fanLocalCSS3D = new CSS3DObject(element);
+        fanLocalCSS3D.name = 'text3';
+        fanLocalCSS3D.scale.set(0.07, 0.07, 0.07);
+        fanLocalCSS3D.rotation.y = -Math.PI / 2;
+        fanLocalCSS3D.position.set(-25.97, 9.3, -15.09);
+        this.group.add(fanLocalCSS3D);
+      }
+    }
+    if (!this.group.getObjectByName('text4')) {
+      const element = document.getElementById('gateBox') as HTMLElement;
+      if (element) {
+        // element.innerHTML = '';
+        const fanLocalCSS3D = new CSS3DObject(element);
+        fanLocalCSS3D.name = 'text4';
+        fanLocalCSS3D.scale.set(0.04, 0.04, 0.04);
+        fanLocalCSS3D.rotation.y = -Math.PI / 2;
+        fanLocalCSS3D.position.set(-73.13, 8.44, -23.52);
+        this.group.add(fanLocalCSS3D);
+      }
+    }
+    if (!this.group.getObjectByName('text5')) {
+      const element = document.getElementById('windownBox') as HTMLElement;
+      if (element) {
+        // element.innerHTML = '';
+        const fanLocalCSS3D = new CSS3DObject(element);
+        fanLocalCSS3D.name = 'text5';
+        fanLocalCSS3D.scale.set(0.07, 0.07, 0.07);
+        fanLocalCSS3D.rotation.y = -Math.PI / 2;
+        fanLocalCSS3D.position.set(-28.44, 9.78, -40.42);
+        this.group.add(fanLocalCSS3D);
+      }
+    }
+    if (!this.group.getObjectByName('text7')) {
+      const element = document.getElementById('inputBox0') as HTMLElement;
+      if (element) {
+        const fanLocalCSS3D = new CSS3DObject(element);
+        fanLocalCSS3D.name = 'text7';
+        fanLocalCSS3D.scale.set(0.04, 0.04, 0.04);
+        fanLocalCSS3D.rotation.y = -Math.PI / 2;
+        fanLocalCSS3D.position.set(-84.23, 4.97, -18.92);
+        this.group.add(fanLocalCSS3D);
+      }
+    }
+    if (!this.group.getObjectByName('text6')) {
+      const element = document.getElementById('inputBox1') as HTMLElement;
+      if (element) {
+        const fanLocalCSS3D = new CSS3DObject(element);
+        fanLocalCSS3D.name = 'text6';
+        fanLocalCSS3D.scale.set(0.04, 0.04, 0.04);
+        fanLocalCSS3D.rotation.y = -Math.PI / 2;
+        fanLocalCSS3D.position.set(-84.47, 6.56, -19.47);
+        this.group.add(fanLocalCSS3D);
+      }
+    }
+    if (!this.group.getObjectByName('text8')) {
+      const element = document.getElementById('gasBox3') as HTMLElement;
+      if (element) {
+        const fanLocalCSS3D = new CSS3DObject(element);
+        fanLocalCSS3D.name = 'text8';
+        fanLocalCSS3D.scale.set(0.03, 0.03, 0.03);
+        fanLocalCSS3D.rotation.y = -Math.PI / 2;
+        fanLocalCSS3D.position.set(-90.04, 6, 5);
+        this.group.add(fanLocalCSS3D);
+      }
+    }
+    if (!this.group.getObjectByName('text9')) {
+      const element = document.getElementById('gasBox2') as HTMLElement;
+      if (element) {
+        const fanLocalCSS3D = new CSS3DObject(element);
+        fanLocalCSS3D.name = 'text9';
+        fanLocalCSS3D.scale.set(0.07, 0.07, 0.07);
+        fanLocalCSS3D.rotation.y = -Math.PI / 2;
+        fanLocalCSS3D.position.set(-8, 7.46, -35.28);
+        this.group.add(fanLocalCSS3D);
+      }
+    }
+    if (!this.group.getObjectByName('text10')) {
+      const element = document.getElementById('gasBox1') as HTMLElement;
+      if (element) {
+        const fanLocalCSS3D = new CSS3DObject(element);
+        fanLocalCSS3D.name = 'text10';
+        fanLocalCSS3D.scale.set(0.1, 0.1, 0.1);
+        fanLocalCSS3D.rotation.y = -Math.PI / 2;
+        fanLocalCSS3D.position.set(80, 9, -43);
+        this.group.add(fanLocalCSS3D);
+      }
+    }
+  }
+
+  clearFly() {
+    if (this.topSmoke) this.topSmoke.clearSmoke();
+    if (this.downSmoke) this.downSmoke.clearSmoke();
+    if (this.returnSmoke) this.returnSmoke.clearSmoke();
+  }
+
+  // 设置模型位置
+  setModalPosition() {
+    if (!this.group) return;
+    this.group.scale.set(13, 13, 13);
+    this.group.position.set(0, -10, -50);
+  }
+}
+export default ModelContext;

+ 56 - 37
src/views/vent/monitorManager/fanLocalMonitor/fanLocal.threejs.ts

@@ -1,10 +1,9 @@
 import * as THREE from 'three';
 import UseThree from '../../../../utils/threejs/useThree';
-import FanLocal from './fanLocal.threejs.base';
-import FanLocalTwo from './fanLocal.threejs.Two';
-import FanLocalDual from './fanLocalDual.threejs.base';
+
 import { animateCamera } from '/@/utils/threejs/util';
 import useEvent from '../../../../utils/threejs/useEvent';
+import { getDictItemsByCode } from '/@/utils/dict';
 
 /** 模型总控制器 */
 let model: UseThree;
@@ -66,7 +65,7 @@ function initEventListender() {
 // }
 
 /** 设置模型类型并切换,不同的类型通常对应不同的具体模型,在模型总控制器下的具体模型会根据传入的参数彼此交互、切换 */
-export function setModelType(modelType: 'fanLocal' | 'fanLocalDual' | string, subModelType: string, data?: any) {
+export function setModelType(modelType: 'fanLocal' | 'fanLocalDual' | 'fanLocalSingle' | string, subModelType: string, data?: any) {
   return new Promise((resolve, reject) => {
     if (!model) return reject('模型控制器未初始化');
     // 判断是否是同一个/类模型
@@ -86,7 +85,7 @@ export function setModelType(modelType: 'fanLocal' | 'fanLocalDual' | string, su
 
       if (modelType === type) {
         group = context?.group as THREE.Object3D;
-        context.setModelType(subModelType, data);
+        if (context.setModelType) context.setModelType(subModelType, data);
 
         // 还没添加到控制器的添加进去
         if (!model.scene?.getObjectByName(group.name) && group) {
@@ -107,10 +106,8 @@ export function setModelType(modelType: 'fanLocal' | 'fanLocalDual' | string, su
           animateCamera(oldCameraPosition, { x: 0, y: 0, z: 0 }, { x: -1.85, y: 13.58, z: 37.39 }, { x: -1.83, y: 2.58, z: -0.75 }, model, 0.8);
         }
         if (type == 'fanLocalDual') {
-          // if (!isUpdate) {
           const oldCameraPosition = { x: -693, y: 474, z: 398 };
           animateCamera(oldCameraPosition, { x: 0, y: 0, z: 0 }, { x: 14.83, y: 16.9, z: 36.46 }, { x: 0, y: 0, z: 0 }, model, 0.8);
-          // }
         }
 
         resolve(null);
@@ -131,29 +128,55 @@ export function mountedThree(sceneSelctor: string, cssSelectors: string[]) {
       model.renderer.sortObjects = true;
     }
 
-    const model1 = new FanLocal(model);
-    await model1.mountedThree();
-    modelContextList.push({
-      type: 'fanLocal',
-      context: model1,
-    });
-
-    const model2 = new FanLocalDual(model);
-    await model2.mountedThree();
-    // 暂时先不加双行
-    modelContextList.push({
-      type: 'fanLocalDual',
-      context: model2,
-    });
-
-    const model3 = new FanLocalTwo(model);
-    const flag = await model3.mountedThree();
-
-    if (flag)
-      modelContextList.push({
-        type: 'fanLocalTwo',
-        context: model3,
-      });
+    // 这里根据字典判断
+    const dictCodes = getDictItemsByCode('fanlocal_install_kind');
+    if (dictCodes && dictCodes.length > 0) {
+      for (let i = 0; i < dictCodes.length; i++) {
+        const dict = dictCodes[i];
+        switch (dict.value) {
+          case 'single':
+            const FanLocal = await import('./fanLocal.threejs.base');
+            const model1 = new FanLocal.default(model);
+            await model1.mountedThree();
+            modelContextList.push({
+              type: 'fanLocal',
+              context: model1,
+            });
+            break;
+          case 'dual_inner':
+          case 'dual_outer':
+            if (modelContextList.find((item) => item.type == 'fanLocalDual')) continue;
+            const FanLocalDual = await import('./fanLocalDual.threejs.base');
+            const model2 = new FanLocalDual.default(model);
+            await model2.mountedThree();
+            // 暂时先不加双行
+            modelContextList.push({
+              type: 'fanLocalDual',
+              context: model2,
+            });
+            break;
+          case 'fanLocalTwo':
+            const FanLocalTwo = await import('./fanLocal.threejs.Two');
+            const model3 = new FanLocalTwo.default(model);
+            const flag = await model3.mountedThree();
+            if (flag)
+              modelContextList.push({
+                type: 'fanLocalTwo',
+                context: model3,
+              });
+            break;
+          case 'fanlocal_1':
+            const FanLocalSingle = await import('./fanLocal.threejs.single');
+            const model4 = new FanLocalSingle.default(model);
+            await model4.mountedThree();
+            modelContextList.push({
+              type: 'fanLocalSingle',
+              context: model4,
+            });
+            break;
+        }
+      }
+    }
 
     initEventListender();
     setCamera();
@@ -182,12 +205,8 @@ export function addCssText() {
   if (modelContextList[2] && modelContextList[2].context && modelContextList[2].context['addCssText']) modelContextList[2].context['addCssText']();
 }
 export function playSmoke(d) {
-  if (modelContextList[0]) {
-    // @ts-ignore
-    modelContextList[0].context?.playSmoke(d);
-  }
-  if (modelContextList[2]) {
-    // @ts-ignore
-    modelContextList[2].context?.playSmoke(d);
+  for (let i = 0; i < modelContextList.length; i++) {
+    const item = modelContextList[i];
+    if (item.context && item.context.playSmoke) item.context.playSmoke(d);
   }
 }

+ 16 - 7
src/views/vent/monitorManager/fanLocalMonitor/index.vue

@@ -271,7 +271,7 @@
               fanTitles[0]
             }}</div>
             <div
-              v-if="selectData && ((selectData['modalTyoe'] && !selectData['modalTyoe'].endsWith('_1')) || !selectData['modalTyoe'])"
+              v-if="selectData && ((selectData['install_kind'] && !selectData['install_kind'].endsWith('_1')) || !selectData['install_kind'])"
               class="tab-item"
               :class="{ 'tab-item-active-r': warningMonitorRowIndex === 1 }"
               @click="selectDevice('warningMonitorRowIndex', 1)"
@@ -342,7 +342,7 @@
                 fanTitles[0]
               }}</div>
               <div
-                v-if="selectData && ((selectData['modalTyoe'] && !selectData['modalTyoe'].endsWith('_1')) || !selectData['modalTyoe'])"
+                v-if="selectData && ((selectData['install_kind'] && !selectData['install_kind'].endsWith('_1')) || !selectData['install_kind'])"
                 class="tab-item"
                 :class="{ 'tab-item-active-r': dataMonitorRowIndex == 1 }"
                 @click="selectDevice('dataMonitorRowIndex', 1)"
@@ -609,7 +609,10 @@
             <a-radio value="stop">停止</a-radio>
           </a-radio-group>
         </div>
-        <div class="startSmoke-select">
+        <div
+          v-if="selectData && ((selectData['install_kind'] && !selectData['install_kind'].endsWith('_1')) || !selectData['install_kind'])"
+          class="startSmoke-select"
+        >
           <div class="label">备机:</div>
           <a-radio-group v-model:value="mainWindIsShow2" @change="changeMotor" name="localWind2">
             <a-radio value="open">开启</a-radio>
@@ -625,7 +628,10 @@
           <div :class="{ 'button-box': true, 'button-disable': false }" @click="handleOk('Fan1Stop')">停止</div>
           <div :class="{ 'button-box': true, 'button-disable': false }" @click="handleOk('Fan1Reset')">复位</div>
         </div>
-        <div class="startSmoke-select">
+        <div
+          v-if="selectData && ((selectData['install_kind'] && !selectData['install_kind'].endsWith('_1')) || !selectData['install_kind'])"
+          class="startSmoke-select"
+        >
           <div class="label">备机:</div>
           <div :class="{ 'button-box': true, 'button-disable': false }" @click="handleOk('Fan2Open')">开启</div>
           <div :class="{ 'button-box': true, 'button-disable': false }" @click="handleOk('Fan2Stop')">停止</div>
@@ -638,7 +644,10 @@
           <div class="label">主机:</div>
           <div :class="{ 'button-box': true, 'button-disable': false }" @click="handleOk('Fan1remote')">远程/就地切换</div>
         </div>
-        <div class="startSmoke-select">
+        <div
+          v-if="selectData && ((selectData['install_kind'] && !selectData['install_kind'].endsWith('_1')) || !selectData['install_kind'])"
+          class="startSmoke-select"
+        >
           <div class="label">备机:</div>
           <div :class="{ 'button-box': true, 'button-disable': false }" @click="handleOk('Fan2remote')">远程/就地切换</div>
         </div>
@@ -1332,16 +1341,16 @@
         if (data['install_kind']) {
           const keymap = {
             fanLocalTwo: ['fanLocalTwo', modalType.value],
-            single: ['fanLocal', modalType.value],
+            single: ['fanLocal', modalType.value], //单巷(默认两风筒)
             dual_inner: ['fanLocalDual', 'inner'],
             dual_outer: ['fanLocalDual', 'outer'],
+            fanlocal_1: ['fanLocalSingle', modalType.value], //单巷单风筒
           };
           mainModelType.value = keymap[data['install_kind']][0];
           modalType.value = keymap[data['install_kind']][1];
         } else {
           // 为了兼容没有添加 install_kind 的旧的单巷
           mainModelType.value = 'fanLocal';
-          // mainModelType.value = 'fanLocalTwo';
         }
       }
       setModelType(mainModelType.value, modalType.value, fanDualArray.value);

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

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

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

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

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

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

+ 12 - 12
src/views/vent/monitorManager/gateMonitor/gate.threejs.ts

@@ -1,18 +1,18 @@
 import * as THREE from 'three';
 import UseThree from '../../../../utils/threejs/useThree';
-import Fm1 from './gate.threejs.yy';
-import Fm3 from './gate.threejs.qd';
+// 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 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';

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

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

File diff suppressed because it is too large
+ 699 - 0
src/views/vent/monitorManager/mainFanMonitor/components/mainFanSVG.vue


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

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

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

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

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

@@ -5,6 +5,7 @@ import mainWindRect from './mainWind.threejs';
 import mainXjWindRect from './mainWind.xj.threejs';
 import mainLjWindRect from './mainWind.lj.threejs';
 import mainWindLj3 from './mainWind.li3.threejs';
+import mainWindLjDt from './mainWind.lidt.threejs';
 import useEvent from '../../../../utils/threejs/useEvent';
 import { getDictItemsByCode } from '/@/utils/dict';
 
@@ -20,6 +21,7 @@ let model: UseThree | undefined, //
   mainXjWindObj: mainXjWindRect | undefined,
   mainLjWindObj: mainLjWindRect | undefined,
   mainLj3WindObj: mainWindLj3 | undefined,
+  mainFanLjDtObj: mainWindLjDt | undefined,
   modalType = 'mainWindRect',
   explosionVentClose = -1,
   explosionVentOpen = -1;
@@ -78,6 +80,8 @@ const mouseEvent = (event) => {
         mainXjWindObj?.mousedownModel.call(mainXjWindObj, intersects);
       } else if (modalType === 'mainWindRect3' && mainLj3WindObj) {
         mainLj3WindObj?.mousedownModel.call(mainLj3WindObj, intersects);
+      } else if (modalType === 'mainLjDtWindRect' && mainFanLjDtObj) {
+        mainFanLjDtObj?.mousedownModel.call(mainFanLjDtObj, intersects);
       }
     });
   }
@@ -104,6 +108,8 @@ export const addText = () => {
     return mainLjWindObj.addCssText.call(mainLjWindObj);
   } else if (modalType === 'mainWindRect3' && mainLj3WindObj) {
     return mainLj3WindObj.addCssText.call(mainLj3WindObj);
+  } else if (modalType === 'mainLjDtWindRect' && mainFanLjDtObj) {
+    return mainFanLjDtObj.addCssText.call(mainFanLjDtObj);
   }
 };
 
@@ -140,6 +146,8 @@ export const play = (controlType, deviceType, frequencyVal?, state?, smokeDirect
     return mainLjWindObj.playSmoke.call(mainLjWindObj, controlType, deviceType, frequencyVal, state, smokeDirection);
   } else if (modalType === 'mainWindRect3' && mainLj3WindObj) {
     return mainLj3WindObj.playSmoke.call(mainLj3WindObj, controlType, deviceType, frequencyVal, state, smokeDirection);
+  } else if (modalType === 'mainLjDtWindRect' && mainFanLjDtObj) {
+    return mainFanLjDtObj.playSmoke.call(mainFanLjDtObj, controlType, deviceType, frequencyVal, state, smokeDirection);
   }
 };
 
@@ -155,6 +163,8 @@ export const playAnimate1 = async (selectData, duration?) => {
     mainObj = mainLjWindObj;
   } else if (modalType === 'mainWindRect3') {
     mainObj = mainLj3WindObj;
+  } else if (modalType === 'mainLjDtWindRect') {
+    mainObj = mainFanLjDtObj;
   }
   if (selectData && mainObj) {
     if (selectData.Fan1WindowOpen !== undefined) {
@@ -237,7 +247,7 @@ export const playAnimate = async (selectData, duration?) => {
   // if (Number(selectData.Fan2FreqHz) < 0) selectData.Fan2FreqHz = Math.abs(Number(selectData.Fan2FreqHz));
   if (!mainWindObj) return;
 
-  let mainObj: mainWindRect | mainXjWindRect | mainWindLj3 | undefined;
+  let mainObj: mainWindRect | mainXjWindRect | mainWindLj3 | mainWindLjDt | undefined;
 
   if (modalType === 'mainWindRect') {
     mainObj = mainWindObj;
@@ -247,6 +257,8 @@ export const playAnimate = async (selectData, duration?) => {
     mainObj = mainLjWindObj;
   } else if (modalType === 'mainWindRect3') {
     mainObj = mainLj3WindObj;
+  } else if (modalType === 'mainLjDtWindRect') {
+    mainObj = mainFanLjDtObj;
   }
   if (selectData && mainObj) {
     if (selectData['Fan1FreqHz'] == undefined || selectData['Fan1FreqHz'] == null || selectData['Fan1FreqHz'] == '') selectData['Fan1FreqHz'] = 50;
@@ -331,6 +343,8 @@ export const playAnimate = async (selectData, duration?) => {
         // 主风机停止
         mainObj.closeDevice('back');
       }
+    } else if (modalType === 'mainLjDtWindRect') {
+      (mainObj as mainWindLjDt).playSmoke(selectData.Fan1StartStatus == 1, selectData.Fan2FreqForwardRun == 1, selectData.Fan2FreqReverseRun == 1);
     } else {
       mainObj.resetSmokeParam('front', selectData.Fan2FreqHz, duration);
       mainObj.resetSmokeParam('back', selectData.Fan1FreqHz, duration);
@@ -456,6 +470,7 @@ export const setModelType = (type) => {
     mainWindObj?.stopSmoke();
     mainXjWindObj?.stopSmoke();
     mainLj3WindObj?.stopSmoke();
+    mainFanLjDtObj?.stopSmoke();
     mainLjWindObj?.stopSmoke();
     if (group) model?.scene?.remove(group);
     if (modalType === 'mainWindRect' && mainWindObj && mainWindObj.group) {
@@ -529,6 +544,23 @@ export const setModelType = (type) => {
         );
         if (group) model?.scene?.add(group);
       }, 300);
+    } else if (modalType === 'mainLjDtWindRect' && mainFanLjDtObj && mainFanLjDtObj.group) {
+      (<UseThree>model).startAnimation = mainFanLjDtObj.render.bind(mainFanLjDtObj);
+      group = mainFanLjDtObj.group;
+      setTimeout(async () => {
+        resolve(null);
+        const position = new THREE.Vector3(2.815, -7.014, -5.985);
+        const oldCameraPosition = { x: -332.39, y: 283.47, z: 438.61 };
+        await animateCamera(
+          oldCameraPosition,
+          { x: -3.41, y: -29.01, z: 8.84 },
+          { x: 5.128, y: 72.363, z: 93.655 },
+          { x: position.x, y: position.y, z: position.z },
+          model,
+          0.8
+        );
+        if (group) model?.scene?.add(group);
+      }, 300);
     }
   });
 };
@@ -536,6 +568,7 @@ export const setModelType = (type) => {
 export const mountedThree = (playerVal1) => {
   return new Promise(async (resolve) => {
     model = new UseThree('#main3D', '#main3DCSS');
+    if (!model || !model.renderer || !model.camera) return;
     model.setEnvMap('test1.hdr');
     model.renderer.toneMappingExposure = 1.0;
     if (model.renderer) {
@@ -575,6 +608,11 @@ export const mountedThree = (playerVal1) => {
               mainLj3WindObj = new mainWindLj3(model, playerVal1);
               await mainLj3WindObj.mountedThree();
               break;
+            case 'lijing_1':
+              modalType = 'mainLjDtWindRect';
+              mainFanLjDtObj = new mainWindLjDt(model);
+              await mainFanLjDtObj.mountedThree();
+              break;
           }
         }
       } else {
@@ -608,6 +646,12 @@ export const mountedThree = (playerVal1) => {
         if (mainLj3WindObj.airJin2) mainLj3WindObj.airJin2.visible = false;
         if (mainLj3WindObj.airChu1) mainLj3WindObj.airChu1.visible = false;
       }
+      if (mainFanLjDtObj) {
+        // if (mainFanLjDtObj.airChu2) mainFanLjDtObj.airChu2.visible = false;
+        // if (mainFanLjDtObj.airJin1) mainFanLjDtObj.airJin1.visible = false;
+        // if (mainFanLjDtObj.airJin2) mainFanLjDtObj.airJin2.visible = false;
+        // if (mainFanLjDtObj.airChu1) mainFanLjDtObj.airChu1.visible = false;
+      }
     });
     startAnimation();
   });

+ 199 - 0
src/views/vent/monitorManager/mainFanMonitor/mainWind.lidt.threejs.ts

@@ -0,0 +1,199 @@
+import * as THREE from 'three';
+import ArrowFlow from '/@/views/vent/comment/threejs/ArrowFlow';
+import Smoke from '/@/views/vent/comment/threejs/Smoke';
+
+// import * as dat from 'dat.gui';
+// const gui = new dat.GUI();
+// gui.domElement.style = 'position:absolute;top:100px;left:10px;z-index:99999999999999';
+
+class mainWindLjDt {
+  model;
+  modelName = 'main';
+  group: THREE.Group | null = null; // 主通风机场景
+
+  /** 进气箭头,这里指风机吸入气体 */
+  airIn: ArrowFlow | null = null;
+  /** 出气箭头,这里指风机排出气体 */
+  airOut: ArrowFlow | null = null;
+  /** 烟雾粒子,配合风机吸入气体时使用 */
+  smokeIn: Smoke | null = null;
+  /** 烟雾粒子,配合风机排出气体时使用 */
+  smokeOut: Smoke | null = null;
+
+  constructor(model) {
+    this.model = model;
+  }
+  // 添加 cssObject
+  addCssText() {
+    if (!this.group) return;
+  }
+
+  clearCssText() {}
+
+  /* 更新动画 */
+  render() {
+    if (!this.model) return;
+  }
+
+  /* 点击风窗,风窗全屏 */
+  mousedownModel() {}
+
+  mouseUpModel() {}
+
+  /**
+   * 播放气流动画
+   * @param FanStart // 风机启动
+   * @param FanForward // 风机正转排风
+   * @param FanReverse // 风机反转吸风
+   */
+  async playSmoke(FanStart, FanForward, FanReverse) {
+    if (!this.model) return;
+
+    if (!FanStart) {
+      // 风机停机
+      this.stopSmoke();
+    } else if (FanForward && !FanReverse) {
+      // 确认风机正转
+      this.airOut?.showElement();
+      if (!this.smokeOut?.frameId) {
+        // 如果没在播放再call这个方法
+        this.smokeOut?.startSmoke();
+      }
+    } else if (FanReverse && !FanForward) {
+      // 确认风机反转
+      this.airIn?.showElement();
+      if (!this.smokeIn?.frameId) {
+        // 如果没在播放再call这个方法
+        this.smokeIn?.startSmoke();
+      }
+    } else {
+      // 默认风机正转
+      this.airOut?.showElement();
+      if (!this.smokeOut?.frameId) {
+        // 如果没在播放再call这个方法
+        this.smokeOut?.startSmoke();
+      }
+    }
+  }
+  stopSmoke() {
+    this.airIn?.hideElement();
+    this.airOut?.hideElement();
+
+    this.smokeIn?.stopSmoke();
+    this.smokeOut?.stopSmoke();
+  }
+
+  /* 初始化 进气出气箭头、烟雾元素  */
+  initAnimationElements() {
+    const arrows = [
+      {
+        texturePath: '/model/img/greenArrow.png',
+        id: 'airIn',
+        offsetY: 0,
+        repeatX: 5,
+        rotation: Math.PI,
+        points: [new THREE.Vector3(21.07, 10.441, -5.816), new THREE.Vector3(8.693, 10.441, -5.816)],
+      },
+      {
+        texturePath: '/model/img/greenArrow.png',
+        id: 'airOut',
+        offsetY: 0,
+        repeatX: 5,
+        rotation: 0,
+        points: [new THREE.Vector3(8.693, 10.441, -5.816), new THREE.Vector3(21.07, 10.441, -5.816)],
+      },
+    ];
+
+    arrows.forEach(({ points, id, texturePath, repeatX, rotation }) => {
+      // 初始化箭头,偏移设置为0.25可以让贴图在管道上方
+      const arrow = new ArrowFlow(texturePath as any, {
+        repeatX,
+        repeatY: 1,
+        rotation,
+      });
+      // 这里开启动画后隐藏元素可以让后续动画控制仅调用 show/hideElement 即可
+      arrow.startAnimation();
+      arrow.hideElement();
+      const width = Math.abs(points[1].x - points[0].x);
+
+      // 添加几何,几何为一个二维简单几何
+      const geometry = new THREE.PlaneGeometry(width, 2);
+      const mesh = new THREE.Mesh(geometry, arrow);
+      mesh.position.set(8.693 + width / 2, 10.441, -5.816);
+      this[id] = arrow;
+      this.group?.add(mesh);
+    });
+
+    this.smokeOut = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.4, 1.8, 100);
+    this.smokeOut.setPath([
+      // {
+      //   path0: new THREE.Vector3(26.956, 7.138, -5.816),
+      //   path1: new THREE.Vector3(26.956, 17.016, -5.816),
+      //   isSpread: false,
+      //   spreadDirection: 0,
+      // },
+      {
+        path0: new THREE.Vector3(26.956, 10.016, -5.816),
+        path1: new THREE.Vector3(26.956, 26.913, -5.816),
+        isSpread: true,
+        spreadDirection: 1, // 1是由小变大(出),-1是由大变小(进)
+      },
+    ]);
+    this.smokeIn = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.4, 1.8, 100);
+    this.smokeIn.setPath([
+      // {
+      //   path0: new THREE.Vector3(26.956, 7.138, -5.816),
+      //   path1: new THREE.Vector3(26.956, 17.016, -5.816),
+      //   isSpread: false,
+      //   spreadDirection: 0,
+      // },
+      {
+        path0: new THREE.Vector3(26.956, 26.913, -5.816),
+        path1: new THREE.Vector3(26.956, 10.016, -5.816),
+        isSpread: true,
+        spreadDirection: -1, // 1是由小变大(出),-1是由大变小(进)
+      },
+    ]);
+
+    this.smokeIn?.setPoints().then(() => {
+      this.group?.add(this.smokeIn?.points);
+    });
+    this.smokeOut?.setPoints().then(() => {
+      this.group?.add(this.smokeOut?.points);
+    });
+  }
+
+  mountedThree() {
+    return new Promise(async (resolve) => {
+      this.model.setGLTFModel(['mainFanLjDt'], this.group).then(async (gltf) => {
+        this.group = gltf[0];
+
+        this.group?.position.set(4, 20.6, 22);
+        this.group?.scale.set(1.2, 1.2, 1.2);
+        await this.initAnimationElements();
+        resolve(null);
+      });
+    });
+  }
+
+  destroy() {
+    this.model = undefined;
+    this.group = null;
+  }
+
+  resetSmokeParam() {}
+
+  openDevice() {}
+
+  closeDevice() {}
+
+  lookMotor() {}
+
+  openOrCloseValve() {}
+
+  startGearAnimation() {}
+
+  setSmokeDirection() {}
+}
+
+export default mainWindLjDt;

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

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

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

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

+ 1 - 1
src/views/vent/monitorManager/windrectMonitor/index.vue

@@ -351,7 +351,7 @@ async function getMonitor(flag?: boolean) {
           console.error('Error', error);
         } finally {
           timer = null;
-          getMonitor(flag);
+          getMonitor();
         }
       },
       flag ? 0 : 1000

+ 9 - 0
src/views/vent/sys/setting/index.vue

@@ -57,6 +57,15 @@
   });
 </script>
 <style lang="less" scoped>
+  @import '/@/design/theme.less';
+  // 绿色主题特化的变量
+  @{theme-green} {
+    .setting-box {
+      border: 1px solid #3c5c64;
+      box-shadow: 0 0 20px #44b4ff33 inset;
+      background-color: #ffffff11;
+    }
+  }
   .setting-box {
     margin: 10px 8px;
     height: calc(100% - 72px);

+ 4 - 0
src/views/vent/sys/setting/setting.data.ts

@@ -95,6 +95,10 @@ export const formSchema: FormSchema[] = [
           label: '深蓝(开发中)',
           value: ThemeEnum.DEEPBLUE,
         },
+        {
+          label: '绿色(开发中)',
+          value: ThemeEnum.GREEN,
+        },
       ],
     },
   },

Some files were not shown because too many files changed in this diff