Преглед изворни кода

[Mod 0000] 优化皮带模型预警弹框特效,添加双击事件

hongrunxia пре 3 дана
родитељ
комит
aa06091ecc

+ 72 - 17
src/views/vent/home/configurable/belt/threejs/MonitorPanel.vue

@@ -1,14 +1,14 @@
 <!-- MonitorPanel.vue 完整版(支持销毁) -->
 <template>
   <!-- 外层动画包裹层 -->
-  <div class="panel-wrapper">
-    <div class="monitor-panel" ref="panelRef" :class="{ alarm: monitorData.alarmLevel > 102 }">
+  <div class="panel-wrapper" v-if="monitorData">
+    <div class="monitor-panel" ref="panelRef" :class="getStatusClass()">
       <div class="panel-title">{{ monitorData.positionName || 'XXXXX位置' }}</div>
       <div class="panel-grid">
         <div class="panel-item">
           <span class="item-label">▸ 微震测声</span>
           <span class="item-value" :class="getStatusClass('microseism')">
-            {{ monitorData.microseism ?? '-' }}
+            {{ monitorData.microseism != null ? (monitorData.microseism == 0 ? '无震动' : '有震动') : '-' }}
           </span>
         </div>
         <div class="panel-item">
@@ -26,7 +26,7 @@
         <div class="panel-item">
           <span class="item-label">▸ 火焰</span>
           <span class="item-value" :class="getStatusClass('flame')">
-            {{ monitorData.flame ?? '-' }}
+            {{ monitorData.flame != null ? (monitorData.flame == 0 ? '无火' : '有火') : '-' }}
           </span>
         </div>
         <!-- <div class="panel-item item-full">
@@ -50,7 +50,7 @@
         <div class="panel-item">
           <span class="item-label">▸ 烟雾</span>
           <span class="item-value" :class="getStatusClass('smoke')">
-            {{ monitorData.smoke ?? '-' }}
+            {{ monitorData.smoke != null ? (monitorData.smoke == 0 ? '无烟' : '有烟') : '-' }}
           </span>
         </div>
         <div class="panel-item">
@@ -126,6 +126,7 @@
   const panelRef = ref<HTMLElement | null>(null);
   const cssObject = ref<CSS3DObject | null>(null);
   const monitorData = ref<SensorData>(props.sensorData);
+  const threshold = ref<AlarmThreshold>(props.threshold);
 
   // 默认阈值
   const defaultThreshold: Required<AlarmThreshold> = {
@@ -147,11 +148,19 @@
   }));
 
   // 判断状态:normal/warn/alarm
-  const getStatusClass = (key: keyof AlarmThreshold) => {
-    const value = props.sensorData[key];
-    if (value == null) return 'normal';
-    const threshold = currentThreshold.value[key]!;
-    if (threshold > 102) return 'alarm';
+  const getStatusClass = (key?: keyof AlarmThreshold) => {
+    debugger;
+    if (key) {
+      const value = props.sensorData[key];
+      if (value == null) return 'normal';
+      const threshold = currentThreshold.value[key]!;
+      if (threshold >= 102) return `alarm alarm_${threshold}`;
+    } else {
+      // const threshold = monitorData.value.alarmLevel;
+      const threshold = monitorData.value.alarmLevel;
+      if (threshold >= 102) return `alarm alarm_${threshold}`;
+    }
+
     return 'normal';
   };
 
@@ -173,8 +182,9 @@
     }
   };
 
-  const updateMonitorData = (data: SensorData) => {
-    monitorData.value = data;
+  const updateMonitorData = (data: { sensorData: SensorData; threshold: AlarmThreshold }) => {
+    monitorData.value = data.sensorData;
+    threshold.value = data.threshold;
   };
 
   onMounted(() => {
@@ -258,12 +268,47 @@
 
   /* 报警状态整体变红 */
   .monitor-panel.alarm {
-    /* border-color: #ff4d4f; */
-    box-shadow: inset 0 0 60px rgb(206, 0, 0.7); /* 内发光效果 */
-    animation: panelPulse 0.8s infinite alternate;
+    border-color: #ff4d4f;
+    /* box-shadow: inset 0 0 60px rgb(206, 0, 0.7); 内发光效果
+    animation: panelPulse 0.8s infinite alternate; */
+  }
+  .monitor-panel.alarm_102 {
+    /* color: #ffdb3d; */
+    box-shadow: inset 0 0 60px rgb(206, 202, 0); /* 内发光效果 */
+    animation: panelPulse_102 0.8s infinite alternate;
+  }
+  @keyframes panelPulse_102 {
+    0% {
+      border-color: #ffed4d55;
+      box-shadow: inset 0 0 60px rgba(206, 175, 0, 0.5); /* 内发光效果 */
+    }
+    100% {
+      border-color: #bb9f0088;
+      box-shadow: inset 0 0 60px rgb(206, 175, 0); /* 内发光效果 */
+    }
+  }
+
+  .monitor-panel.alarm_103 {
+    /* color: #ff5e00; */
+    box-shadow: inset 0 0 60px rgb(206, 82, 0); /* 内发光效果 */
+    animation: panelPulse_103 0.8s infinite alternate;
+  }
+  @keyframes panelPulse_103 {
+    0% {
+      border-color: #ffb24d55;
+      box-shadow: inset 0 0 60px rgba(206, 100, 0, 0.5); /* 内发光效果 */
+    }
+    100% {
+      border-color: #bb450088;
+      box-shadow: inset 0 0 60px rgb(206, 82, 0); /* 内发光效果 */
+    }
   }
 
-  @keyframes panelPulse {
+  .monitor-panel.alarm_104 {
+    box-shadow: inset 0 0 60px rgb(206, 0, 0.7); /* 内发光效果 */
+    animation: panelPulse_104 0.8s infinite alternate;
+  }
+  @keyframes panelPulse_104 {
     0% {
       border-color: #ff4d4f55;
       box-shadow: inset 0 0 60px rgb(206, 0, 0, 0.5); /* 内发光效果 */
@@ -345,11 +390,21 @@
 
   /* 报警闪烁 */
   .item-value.alarm {
-    color: #ff4d4f !important;
+    /* color: #ff4d4f !important; */
     text-shadow: 0 0 12px rgba(255, 77, 79, 0.6);
     animation: valueBlink 0.8s infinite alternate;
   }
 
+  .alarm_102 {
+    color: #ffdb3d;
+  }
+  .alarm_103 {
+    color: #ff5e00;
+  }
+  .alarm_104 {
+    color: #ff2424;
+  }
+
   @keyframes valueBlink {
     0% {
       opacity: 0.6;

+ 152 - 31
src/views/vent/home/configurable/belt/threejs/belt.threejs.ts

@@ -1,22 +1,23 @@
 import * as THREE from 'three';
 import { PathPointList, PathGeometry } from 'three.path';
 import gsap from 'gsap';
-import { ref, watch } from 'vue';
+import { ref, watch, Ref } from 'vue';
 import { modelMouseHandler } from '/@/utils/threejs/useEvent';
 import { panelManager } from './PanelManager';
-import { Ref } from 'vue';
 import { defHttp } from '/@/utils/http/axios';
 import { animateCamera } from '/@/utils/threejs/util';
+import doubleWindow from '/@/views/vent/monitorManager/windowMonitorBet/shuangdaoFc.threejs';
 
 const gateList = ref([]);
 const panelApp = [];
-let mouseoverEvent, mouseUpEvent;
+let mouseoverEvent, mouseUpEvent, doubleEvent;
 const normalColor = new THREE.Color(0xff0000);
-const warningColor = new THREE.Color(0xfc5f2e);
+let warningColor = new THREE.Color(0xfc5f2e);
 const color = new THREE.Color(0x00ff00);
 let clickSelecteObject;
 const modalData = ref(null);
 const warningPartitionIndex = ref('-1');
+const partitionRefMap = new Map();
 
 export async function modalAnimate(modal, modalMonitorData: Ref<any, any>) {
   // const data = modalMonitorData.value;
@@ -40,6 +41,9 @@ export async function modalAnimate(modal, modalMonitorData: Ref<any, any>) {
   // const len = Object.keys(res?.device).length;
   const { partitionList, blinkAnimationList } = drawPartition(beltModal, 100, 2000);
 
+  modal.orbitControls?.update();
+  modal.camera?.updateMatrixWorld();
+
   watch(
     modalMonitorData,
     async (data) => {
@@ -53,9 +57,16 @@ export async function modalAnimate(modal, modalMonitorData: Ref<any, any>) {
       if (Object.keys(alarminfo).length > 0) {
         for (const key in alarminfo) {
           if (!Object.hasOwn(alarminfo, key)) continue;
-          if (alarminfo[key] > 102) {
+          if (Number(alarminfo[key]) >= 102) {
             if (modalData.value && modalData.value[key]) modalData.value[key]['alarmLevel'] = alarminfo[key];
             warningPartitionIndex.value = key;
+            if (alarminfo[key] == '102') {
+              warningColor = new THREE.Color('#ffdb3d');
+            } else if (alarminfo[key] == '103') {
+              warningColor = new THREE.Color('#ff5e00');
+            } else if (alarminfo[key] == '104') {
+              warningColor = new THREE.Color('#ff2424');
+            }
             setPartitionAnimate();
             break;
           }
@@ -63,6 +74,11 @@ export async function modalAnimate(modal, modalMonitorData: Ref<any, any>) {
       }
       // 根据监测数据进行风门模型动画控制(前提条件:模型上的风门与实际风门设备建立了映射关系)
       handleGateAnimate(gateList.value);
+      // if (warningPartitionIndex.value == '-1') {
+      //   warningColor = new THREE.Color('#ffdb3d');
+      //   warningPartitionIndex.value = '12';
+      //   setPartitionAnimate();
+      // }
     },
     { immediate: true }
   );
@@ -224,10 +240,10 @@ export async function modalAnimate(modal, modalMonitorData: Ref<any, any>) {
       const solidBox = partition.getObjectByName(partition.name + '_solid') as THREE.Mesh;
       const line = partition.getObjectByName(partition.name + '_line') as THREE.LineSegments;
       if (solidBox && line) {
-        if (Number(warningPartitionIndex.value) == index + 1) {
-          if (!solidBox.material.color.equals(warningColor)) solidBox.material.color.setHex(0xfc5f2e);
+        if (partition.name == `partition${warningPartitionIndex.value}`) {
+          if (!solidBox.material.color.equals(warningColor)) solidBox.material.color.setHex(warningColor.getHex());
           if (solidBox.material.opacity !== 0.3) solidBox.material.opacity = 0.3;
-          if (!line.material.color.equals(warningColor)) line.material.color.setHex(0xfc5f2e);
+          if (!line.material.color.equals(warningColor)) line.material.color.setHex(warningColor.getHex());
           if (line.material.opacity !== 0.8) line.material.opacity = 0.8;
           warningPartition.push(partition);
         } else {
@@ -242,11 +258,12 @@ export async function modalAnimate(modal, modalMonitorData: Ref<any, any>) {
     // 进行动画检测
     const warningOpacity = 0.9;
     const normalOpacity = 0.05;
+
     for (let i = 0; i < blinkAnimationList.length; i++) {
-      if (Number(warningPartitionIndex.value) == i + 1) {
+      const partition = partitionList[i];
+      if (partition.name == `partition${warningPartitionIndex.value}`) {
         // 开始动画
         if (!blinkAnimationList[i]) {
-          const partition = partitionList[i];
           const solidBox = partition.getObjectByName(partition.name + '_solid') as THREE.Mesh;
 
           blinkAnimationList[i] = gsap.to(solidBox.material, {
@@ -265,25 +282,8 @@ export async function modalAnimate(modal, modalMonitorData: Ref<any, any>) {
             },
           });
 
-          const pos = createMonitorPanel3D(modal, partition);
-
-          // 这里进行重新定位
-          console.log(pos);
-          gsap.fromTo(
-            modal.camera.position,
-            { x: modal.camera.position.x, y: modal.camera.position.y, z: modal.camera.position.z },
-            {
-              x: pos.x,
-              y: modal.camera.position.y,
-              z: pos.y,
-              duration: 1,
-              onUpdate: () => {
-                modal.orbitControls.target.set(modal.camera.position.x, pos.z, -modal.camera.position.z);
-                modal.orbitControls.update();
-                modal.renderer?.render(modal.scene, modal.camera);
-              },
-            }
-          );
+          createMonitorPanel3D(modal, partition);
+          partitionAnimate(partition, modal);
         }
       } else {
         // 停止动画
@@ -338,7 +338,16 @@ function createMonitorPanel3D(modal, partition) {
   const partitionIndex = partition.name.split('_')[0];
   const index = Number(partitionIndex.split('partition')[1]) + 1;
   const data = modalData.value[index + ''];
-  if (data) {
+  // if (partitionRefMap.has(partition.name)) {
+  //   partitionRefMap.set(partition.name, ref(data));
+  // } else {
+  //   partitionRefMap.get(partition.name).value = data;
+  // }
+  if (data && !panelManager.getPanel(partition.name)) {
+    // if (partition.name == `partition${warningPartitionIndex.value}`) {
+    //   data['alarmLevel'] = 102;
+    // }
+
     panelManager.createPanel(modal.scene, {
       instanceId: partition.name,
       sensorData: {
@@ -375,7 +384,30 @@ function updateMonitorPanel3D() {
       const partitionIndex = id.split('_')[0];
       const index = Number(partitionIndex.split('partition')[1]) + 1;
       const data = modalData.value[index + ''];
-      instance.vm.updateMonitorData(data);
+      const res = {
+        sensorData: {
+          positionName: `分区#${index}`,
+          temperature: data['温度传感器'] ? data['温度传感器'][0]?.value : null,
+          smoke: data['烟雾传感器'] ? data['烟雾传感器'][0]?.value : null,
+          co: data['CO传感器'] ? data['CO传感器'][0]?.value : null,
+          microseism: data['微震测声传感器'] ? data['微震测声传感器'][0]?.value : null,
+          fiberTemp: data['光纤测温'] ? data['光纤测温'][0]?.value : null,
+          flame: data['火焰传感器'] ? data['火焰传感器'][0]?.value : null,
+          hcl: data['HCl传感器'] ? data['HCl传感器'][0]?.value : null,
+          alarmLevel: data['alarmLevel'] ? data['alarmLevel'] : null,
+        },
+        threshold: {
+          temperature: data['温度传感器'] ? data['温度传感器'][0]?.alarmLevel : null,
+          smoke: data['烟雾传感器'] ? data['烟雾传感器'][0]?.alarmLevel : null,
+          co: data['CO传感器'] ? data['CO传感器'][0]?.alarmLevel : null,
+          microseism: data['微震测声传感器'] ? data['微震测声传感器'][0]?.alarmLevel : null,
+          fiberTemp: data['光纤测温'] ? data['光纤测温'][0]?.alarmLevel : null,
+          flame: data['火焰传感器'] ? data['火焰传感器'][0]?.alarmLevel : null,
+          hcl: data['HCl传感器'] ? data['HCl传感器'][0]?.alarmLevel : null,
+        },
+      };
+
+      instance.vm.updateMonitorData(res);
     }
   });
 }
@@ -396,6 +428,17 @@ function initMouseEvent(modal, beltModal, partitionList) {
       partitionEvent(intersects, partitionList, modal, event);
     });
   };
+
+  doubleEvent = (event) => {
+    modelMouseHandler(modal, penlin, event, (intersects) => {
+      const intersect0 = intersects[0];
+      if (intersect0 && intersect0.object.name.includes('partition')) {
+        partitionAnimate(intersect0.object, modal);
+      }
+    });
+  };
+
+  modal.canvasContainer?.addEventListener('dblclick', doubleEvent);
   modal.canvasContainer?.addEventListener('mousemove', mouseoverEvent);
   modal.canvasContainer?.addEventListener('mouseup', mouseoverEvent);
 }
@@ -892,3 +935,81 @@ export function destroy(modal, gateList) {
   gateList.value = null;
   modal.canvasContainer?.removeEventListener('mousemove', mouseoverEvent);
 }
+
+function partitionAnimate(intersect0, modal) {
+  const box = new THREE.Box3();
+  box.setFromObject(intersect0);
+  const point = box.getCenter(new THREE.Vector3());
+
+  const target0 = <THREE.Vector3>modal.orbitControls.target.clone();
+  const oldCameraPosition = <THREE.Vector3>modal.camera.position.clone();
+
+  const nor = point.clone().sub(oldCameraPosition.clone()).normalize();
+  const d = 28;
+
+  const animateObj = {
+    x1: target0.x, // 相机x
+    y1: target0.y, // 相机y
+    z1: target0.z, // 相机z
+    x2: oldCameraPosition.x, // 相机x
+    y2: oldCameraPosition.y, // 相机y
+    z2: oldCameraPosition.z, // 相机z
+  };
+  let newAnimateObj;
+  console.log('point---->', point);
+  if (oldCameraPosition.z > target0.z) {
+    newAnimateObj = {
+      x1: point.x,
+      y1: point.y - 10,
+      z1: point.z,
+      x2: point.x,
+      // y2: Math.abs(nor.y) * d + point.y,
+      // z2: Math.abs(nor.z) * d + point.z,
+      y2: 25.5 + point.y,
+      z2: 10.5 + point.z,
+    };
+    console.log('Math.abs(nor.z) * d', { x: point.x, y: Math.abs(nor.y) * d, z: Math.abs(nor.z) * d });
+  } else {
+    newAnimateObj = {
+      x1: point.x,
+      y1: point.y - 10,
+      z1: point.z,
+      x2: point.x,
+      // y2: Math.abs(nor.y) * d + point.y,
+      y2: 25.5 + point.y,
+      // z2: -Math.abs(nor.z) * d + point.z,
+      z2: -10.5 + point.z,
+    };
+    console.log('-Math.abs(nor.z) * d', { x: point.x, y: Math.abs(nor.y) * d, z: -Math.abs(nor.z) * d });
+  }
+
+  modal.orbitControls.renderEnabled = false;
+  modal.orbitControls.enabled = false;
+  gsap.fromTo(
+    animateObj,
+    {
+      x1: target0.x, // 相机x
+      y1: target0.y, // 相机y
+      z1: target0.z, // 相机z
+      x2: oldCameraPosition.x, // 相机x
+      y2: oldCameraPosition.y, // 相机y
+      z2: oldCameraPosition.z, // 相机z
+    },
+    {
+      ...newAnimateObj,
+      duration: 1,
+      ease: 'easeInCirc',
+      onUpdate: function (object) {
+        modal.orbitControls.target.set(object.x1, object.y1, object.z1);
+        modal.camera.position.set(object.x2, object.y2, object.z2);
+      },
+      onUpdateParams: [animateObj],
+      onComplete: function () {
+        modal.orbitControls.renderEnabled = true;
+        modal.orbitControls.enabled = true;
+        modal.orbitControls?.update();
+        modal.camera?.updateMatrixWorld();
+      },
+    }
+  );
+}