Forráskód Böngészése

[Mod 0000] 主风机添加风流流动箭头

hongrunxia 4 napja
szülő
commit
e6c791ea1d

+ 11 - 8
src/components/Table/src/hooks/useTableFooter.ts

@@ -21,17 +21,20 @@ export function useTableFooter(
   });
 
   // 是否有展开行
-  const hasExpandedRow = computed(() => Object.keys(slots).includes('expandedRowRender'))
+  const hasExpandedRow = computed(() => Object.keys(slots).includes('expandedRowRender'));
 
   const getFooterProps = computed((): Recordable | undefined => {
     const { summaryFunc, showSummary, summaryData, bordered } = unref(propsRef);
-    return showSummary && !unref(getIsEmptyData) ? () => h(TableFooter, {
-      bordered,
-      summaryFunc,
-      summaryData,
-      scroll: unref(scrollRef),
-      hasExpandedRow: hasExpandedRow.value
-    }) : undefined;
+    return showSummary && !unref(getIsEmptyData)
+      ? () =>
+          h(TableFooter, {
+            bordered,
+            summaryFunc,
+            summaryData,
+            scroll: unref(scrollRef),
+            hasExpandedRow: hasExpandedRow.value,
+          })
+      : undefined;
   });
 
   watchEffect(() => {

+ 0 - 1
src/components/Table/src/hooks/useTableScroll.ts

@@ -87,7 +87,6 @@ export function useTableScroll(
 
     bodyEl!.style.height = 'unset';
 
-
     await nextTick();
     //Add a delay to get the correct bottomIncludeBody paginationHeight footerHeight headerHeight
 

+ 107 - 25
src/views/vent/gas/gasZk/index.vue

@@ -3,11 +3,13 @@
     <customHeader>瓦斯钻孔实时监测</customHeader>
     <div class="content">
       <div class="left-box">
-        <div class="video">
-          <video controls autoplay muted loop fluent style="height: 100%; width: 100%; padding: 30px 20px">
-            <source :src="`${baseApiUrl}/sys/common/static/gif/output_video.mp4`" type="video/mp4" />
-          </video>
-        </div>
+        <a-spin :spinning="loadding" size="large" tip="正在加载视频,请稍等。。。">
+          <div class="video-box" ref="video">
+            <!-- <video controls autoplay muted loop fluent style="height: 100%; width: 100%; padding: 30px 20px">
+              <source :src="`${baseApiUrl}/sys/common/static/gif/output_video.mp4`" type="video/mp4" />
+            </video> -->
+          </div>
+        </a-spin>
       </div>
       <div class="right-box">
         <div class="count">
@@ -34,12 +36,18 @@
   import { getVideoPlayTime, getGasZkStatus } from './gasZk.api';
   import customHeader from '/@/components/vent/customHeader.vue';
   import { useGlobSetting } from '/@/hooks/setting';
+  import Player, { I18N } from 'xgplayer';
+  import ZH from 'xgplayer/es/lang/zh-cn';
+  import Mp4Plugin from 'xgplayer-mp4';
+
   const globSetting = useGlobSetting();
   const baseApiUrl = globSetting.domainUrl;
   const videoPlayTime = ref(0); //视频开始播放时间
   const curStatus = ref(''); //当前状态
   const usedCount = ref(''); //钻孔计数
   const videoUrl = ref('');
+  const video = ref();
+  const loadding = ref(true);
   let timer: IntervalHandle; //定时器
   //时间轮询
   const startPolling = () => {
@@ -70,20 +78,70 @@
     }
   };
 
+  const initPlayer = () => {
+    const player = new Player({
+      url: `${baseApiUrl}/sys/common/static/gif/output_video.mp4`,
+      el: video.value,
+      lang: 'zh',
+      width: '1120',
+      height: '740',
+      poster: '/src/assets/images/vent/noSinge.png',
+      plugins: [Mp4Plugin],
+      // marginControls: false,
+      autoplay: true,
+      loop: true,
+      // fluid: false,
+      isLive: true,
+      playsinline: false,
+      screenShot: false,
+      // closeVideoClick: true,
+      // closeFocusVideoFocus: true,
+      // closePauseVideoFocus: true,
+      // closePlayVideoFocus: true,
+      // topBarAutoHide: false,
+      customConfig: {
+        isClickPlayBack: false,
+      },
+      controls: false,
+      // ignores: ['time', 'start', 'definition', 'error', 'fullscreen', 'i18n', 'loading', 'mobile', 'pc', 'play', 'poster', 'progress', 'replay', 'volume', 'loading', 'pc', 'fullscreen', 'error', 'definition'],
+      mp4plugin: {
+        maxBufferLength: 30,
+        // minBufferLength: 10,
+      },
+    });
+    player.on('play', async () => {
+      try {
+        loadding.value = false;
+        videoPlayTime.value = await getVideoPlayTime();
+        // 第一次请求
+        const res = await getGasZkStatus({
+          playTime: videoPlayTime.value,
+        });
+        usedCount.value = res.usedCount;
+        curStatus.value = res.curStatus;
+        // 启动轮询
+        startPolling();
+      } catch (error) {
+        console.error('初始化失败', error);
+      }
+    });
+  };
+
   onMounted(async () => {
-    try {
-      videoPlayTime.value = await getVideoPlayTime();
-      // 第一次请求
-      const res = await getGasZkStatus({
-        playTime: videoPlayTime.value,
-      });
-      usedCount.value = res.usedCount;
-      curStatus.value = res.curStatus;
-      // 启动轮询
-      startPolling();
-    } catch (error) {
-      console.error('初始化失败', error);
-    }
+    initPlayer();
+    // try {
+    //   videoPlayTime.value = await getVideoPlayTime();
+    //   // 第一次请求
+    //   const res = await getGasZkStatus({
+    //     playTime: videoPlayTime.value,
+    //   });
+    //   usedCount.value = res.usedCount;
+    //   curStatus.value = res.curStatus;
+    //   // 启动轮询
+    //   startPolling();
+    // } catch (error) {
+    //   console.error('初始化失败', error);
+    // }
   });
 
   // 组件卸载时清理
@@ -105,10 +163,9 @@
     --image-border: url('/@/assets/images/fire/border.png');
     position: relative;
     width: calc(100% - 20px);
-    height: calc(100% - 90px);
+    height: calc(100% - 160px);
     position: relative;
     margin: 30px 10px 10px 10px;
-
     .content {
       position: relative;
       width: 100%;
@@ -116,15 +173,40 @@
       display: flex;
       justify-content: space-between;
       align-items: center;
-      margin-top: 80px;
       .left-box {
-        flex: 3;
-        height: 100%;
+        width: 1200px;
+        height: 760px;
         padding: 10px;
         background: var(--image-border) no-repeat center;
         background-size: 100% 100%;
-        .video {
-          height: 100%;
+        position: relative;
+        .video-box {
+          width: 100%;
+          height: 740px !important;
+          padding-top: 0 !important;
+          display: flex;
+          align-items: center;
+          video {
+            height: 740px !important;
+          }
+          :deep(.xgplayer-loading) {
+            display: none;
+          }
+          :deep(.xgplayer-enter) {
+            display: none;
+          }
+          :deep(.xgplayer-start) {
+            display: none;
+          }
+          :deep(.xgplayer-error) {
+            display: none;
+          }
+          :deep(.xgplayer-replay) {
+            display: none;
+          }
+          :deep(.xg-mini-layer) {
+            display: none;
+          }
         }
       }
       .right-box {

+ 104 - 22
src/views/vent/monitorManager/deviceCameraMonitor/index.vue

@@ -25,10 +25,11 @@
             <div class="button-box" @click="playAnimation('后窗1面积设置', 'frontSetValue3')">后窗1面积</div>
             <div class="button-box" @click="playAnimation('后窗2面积设置', 'frontSetValue4')">后窗2面积</div>
           </div>
-
+          <div v-if="hasPermission('window:ldkz')" class="button-box" @click="playAnimation('风窗自主调控', 'ldkz')">风窗自主调控</div>
+          <div v-if="hasPermission('window:sameSet')" class="button-box" @click="playAnimation('风窗面积设置', 'sameSetValue')">设定风窗面积</div>
           <div class="row" v-if="Number(selectData.nwindownum) == 1">
-            <div v-if="hasPermission('window:AreaControl')" class="button-box" @click="setArea(1)">设定风窗面积</div>
-            <div v-if="hasPermission('window:showAngle')" class="button-box" @click="setAngle(1)">设定风窗角度</div>
+            <div v-if="hasPermission('window:AreaControl')" class="button-box" @click="playAnimation('设定风窗面积', 'frontSetValue')">设定风窗面积</div>
+            <div v-if="hasPermission('window:showAngle')" class="button-box" @click="playAnimation('设定风窗角度', 'frontSetValue')">设定风窗角度</div>
           </div>
         </template>
       </div>
@@ -215,11 +216,18 @@
       @handle-ok="handleOK"
       @handle-cancel="handleCancel"
     />
+    <SupplyAir
+      :data="currentData"
+      :targetVolume="targetVolume"
+      :modal-is-show="showTargetModal"
+      :modalType="modalType"
+      @handle-cancel="() => (showTargetModal = false)"
+    />
   </div>
 </template>
 
 <script setup lang="ts">
-import { ref, onMounted, onUnmounted, ComponentOptions, shallowRef, reactive, defineProps, watch, nextTick,unref } from 'vue';
+import { ref, onMounted, onUnmounted, ComponentOptions, shallowRef, reactive, defineProps, watch, toRaw, nextTick,unref, inject } from 'vue';
 import { list, getDeviceList, getDepartmentInfo } from './device.api';
 import AlarmHistoryTable from '../comment/AlarmHistoryTable.vue';
 import HistoryTable from '../comment/HistoryTable.vue';
@@ -240,12 +248,12 @@ import {
 } from './device.data';
 import { getDictItemsByCode } from '/@/utils/dict';
 import BarAndLine from '/@/components/chart/BarAndLine.vue';
-import { useMethods } from '/@/hooks/system/useMethods';
-import { useGo } from '/@/hooks/web/usePage';
 import { useGlobSetting } from '/@/hooks/setting';
 import { useCamera } from '/@/hooks/system/useCamera';
 import { usePermission } from '/@/hooks/web/usePermission';
 import { deviceControlApi } from '/@/api/vent/index';
+import SupplyAir from '../windowMonitor/components/supplyAir.vue';
+import { updateWindowAutoAdjustStatus } from '../windowMonitor/window.api';
 
 type DeviceType = { deviceType: string; deviceName: string; datalist: any[] };
 const glob = useGlobSetting();
@@ -268,6 +276,23 @@ const echatsOption = {
     feature: {},
   },
 };
+const initData = {
+  deviceID: '',
+  deviceType: '',
+  strname: '',
+  dataDh: '-', //压差
+  dataDtestq: '-', //测试风量
+  sourcePressure: '-', //气源压力
+  dataDequivalarea: '-',
+  netStatus: '0', //通信状态
+  fault: '气源压力超限',
+  forntArea: '0',
+  rearArea: '0',
+  frontRearDifference: '-',
+  rearPresentValue: '-',
+  maxarea: 0,
+  nwindownum: 0,
+};
 const { hasPermission } = usePermission();
 const { getCamera, removeCamera, getPlayer } = useCamera();
 const playerRef = ref()
@@ -289,16 +314,20 @@ const deviceActive = ref('');
 const activeKey = ref('1'); // tab key
 const dataSource = shallowRef([]); // 实时监测数据
 const selectData = ref({})
+const currentData = ref(initData);
 const majorPathEchartsData = ref({}); // 关键路线echarts数据
 const surfaceEchartsData = ref<any[]>(); // 工作面历史记录,echarts数据
 const activeID = ref(''); // 打开详情modal时监测的设备id
 const deviceType = ref(''); // 监测设备类型
 const selectRowIndex = ref(-1)
 const systemID = ref(''); // 系统监测时,系统id
-const cameraAddrs = ref([])
+const cameraAddrs = ref([]);
+const targetVolume = ref(0);
+const showTargetModal = ref(false);
 const scroll = reactive({
   y: 180,
 });
+const globalConfig = inject('globalConfig');
 let departmentInfo: Null | Object = null;
 function tabChange(activeKeyVal) {
   activeKey.value = activeKeyVal;
@@ -313,7 +342,8 @@ function getMonitor(flag?) {
     if (Object.prototype.toString.call(timer) === '[object Null]') {
       timer = setTimeout(
         async () => {
-          await getDataSource();
+          const data = await getDataSource();
+          currentData.value = data
           if (timer) {
             getMonitor();
           }
@@ -334,6 +364,7 @@ async function getDataSource() {
         const readData = data.readData;
         return Object.assign(data, readData);
       });
+      
       if (item.type != 'sys') {
         if (item.type === 'majorpath') {
           deviceArr.unshift({ deviceType: item.type, deviceName: item['typeName'], datalist: item['datalist'][0]['paths'] });
@@ -482,6 +513,8 @@ async function getDataSource() {
       }
     }
   }
+  const data: any = toRaw(dataSource.value[selectRowIndex.value]); //maxarea
+  return data;
 }
 
 
@@ -521,6 +554,7 @@ async function getSelectRow(selectRow, index) {
   if (!selectRow) return;
   selectRowIndex.value = index;
   selectData.value = selectRow;
+  
   cameraAddrs.value = await getCamera(selectRow.deviceID, playerRef.value, null, true);
   if(cameraAddrs.value && cameraAddrs.value.length > 0){
     nextTick(async() => {
@@ -553,29 +587,77 @@ const playAnimation = (title, flag) => {
   modalTitle.value = title;
   modalIsShow.value = true;
 };
-const handleOK = (passWord, handlerState, value) => {
+const handleOK = async(passWord, handlerState, value) => {
   if (!passWord && !globalConfig?.simulatedPassword) {
     message.warning('请输入密码!');
     return;
   }
-  let data = {
-    deviceid: selectData.value.deviceID,
-    devicetype: selectData.value.deviceType,
-    paramcode: handlerState,
-    password: passWord || globalConfig?.simulatedPassword,
-    value: value ? value : null,
-  };
-  deviceControlApi(data)
-    .then((res) => {
+  
+  if (handlerState == 'sameSetValue') {
+    const data1 = {
+      deviceid: selectData.value.deviceID,
+      devicetype: selectData.value.deviceType,
+      paramcode: 'frontSetValue',
+      password: passWord || globalConfig?.simulatedPassword,
+      value: value ? value : null,
+    };
+    const data2 = {
+      deviceid: selectData.value.deviceID,
+      devicetype: selectData.value.deviceType,
+      paramcode: 'rearSetValue',
+      password: passWord || globalConfig?.simulatedPassword,
+      value: value ? value : null,
+    };
+    const res1 = await deviceControlApi(data1);
+    const res2 = await deviceControlApi(data2);
+    if (res1.success && res2.success) {
+      message.success('指令已下发成功!');
+    } else {
+      message.error(res1.message);
+    }
+    handleCancel();
+  }else if(handlerState == 'ldkz') {
+    const params = {
+      windowId: selectData.value.deviceID,
+      auto: 1,
+      fengliangF: value,
+    };
+    updateWindowAutoAdjustStatus(params).then((res) => {
       if (res.success) {
-        message.success('指令已下发成功!');
+        if (globalConfig.History_Type == 'remote') {
+          message.success('指令已下发至生产管控平台成功!');
+        } else {
+          message.success('指令已下发成功!');
+        }
+        handleCancel();
+        targetVolume.value = value;
+        showTargetModal.value = true;
       } else {
         message.error(res.message);
       }
-    })
-    .finally(() => {
-      handleCancel();
     });
+    return;
+  } else{
+    let data = {
+      deviceid: selectData.value.deviceID,
+      devicetype: selectData.value.deviceType,
+      paramcode: handlerState,
+      password: passWord || globalConfig?.simulatedPassword,
+      value: value ? value : null,
+    };
+    deviceControlApi(data)
+      .then((res) => {
+        if (res.success) {
+          message.success('指令已下发成功!');
+        } else {
+          message.error(res.message);
+        }
+      })
+      .finally(() => {
+        handleCancel();
+      });
+  }
+ 
 };
 
 const handleCancel = () => {

+ 67 - 3
src/views/vent/monitorManager/mainFanMonitor/mainWind.threejs.ts

@@ -1,7 +1,7 @@
 import * as THREE from 'three';
 import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
 import Smoke from '/@/views/vent/comment/threejs/Smoke';
-import { renderVideo } from '/@/utils/threejs/util';
+import { PathPointList, PathGeometry } from 'three.path';
 import gsap from 'gsap';
 
 class mainWindRect {
@@ -44,7 +44,12 @@ class mainWindRect {
   fbmAnimationClip: THREE.AnimationClip | null = null;
   fbmMixers: THREE.AnimationMixer | null = null;
   fbmOpenAction: THREE.AnimationAction | null = null;
-
+  clock = new THREE.Clock();
+  material;
+  airTexture;
+  offset = 0;
+  direction = 0; // -1 代表反向,1代表正向
+  arrowMesh;
   constructor(model, playerVal1) {
     this.model = model;
     this.player1 = playerVal1;
@@ -149,6 +154,10 @@ class mainWindRect {
       return;
     }
     if (this.fbmMixers) this.fbmMixers?.update(1 / 25);
+    if (this.airTexture) {
+      this.airTexture.offset.x = this.offset;
+      this.offset -= this.clock.getDelta() * 2;
+    }
   }
 
   /* 点击风窗,风窗全屏 */
@@ -230,6 +239,8 @@ class mainWindRect {
   }
 
   async setSmokeDirection(deviceType, smokeDirection) {
+    let smoke;
+    const pathPoints: THREE.Vector3[] = [];
     const windowPositivePath = [
       {
         path0: new THREE.Vector3(4.441, 20.267, 3.614),
@@ -322,7 +333,24 @@ class mainWindRect {
         spreadDirection: 0, // 1是由小变大,-1是由大变小
       },
     ];
-    let smoke;
+    const getPathPoint = () => {
+      this.arrowMesh = this.group?.getObjectByName('arrow');
+      if (this.arrowMesh) return;
+      pathPoints.push(new THREE.Vector3(16.441, 1.485, 2.614), new THREE.Vector3(35.583, 1.485, 2.614));
+      const pathPointList = new PathPointList();
+      const up = new THREE.Vector3(0, 0, 1);
+      pathPointList.set(pathPoints, 0, 0, up, false);
+      const geometry = new PathGeometry(pathPoints.length, false);
+      geometry.update(pathPointList, {
+        width: 2,
+        arrow: false,
+      });
+
+      this.arrowMesh = new THREE.Mesh(geometry, this.material);
+      this.arrowMesh.name = 'arrow';
+      this.group?.add(this.arrowMesh);
+    };
+
     if (deviceType === 'front') {
       smoke = this.frontSmoke;
     } else if (deviceType === 'back') {
@@ -331,17 +359,39 @@ class mainWindRect {
     switch (smokeDirection) {
       case 'tubPositivePath': // 风筒正
         smoke.setPath(tubPositivePath);
+        if (this.direction !== 1) {
+          this.direction = 1;
+          this.airTexture.repeat.x = 1;
+        }
         break;
       case 'tubInversePath': // 风筒反
         smoke.setPath(tubInversePath);
+        if (this.direction !== -1) {
+          this.direction = -1;
+          this.airTexture.repeat.x = -1;
+        }
         break;
       case 'windowPositivePath': // 风窗正
         smoke.setPath(windowPositivePath);
+        if (this.direction !== 1) {
+          this.direction = 1;
+          this.airTexture.repeat.x = 1;
+        }
         break;
       case 'windowInversePath': // 风窗反
         smoke.setPath(windowInversePath);
+        if (this.direction !== -1) {
+          this.direction = -1;
+          this.airTexture.repeat.x = -1;
+        }
         break;
     }
+    getPathPoint();
+    if (deviceType === 'front') {
+      if (this.arrowMesh && this.arrowMesh.position.z !== 2.31) this.arrowMesh.position.set(-2.51, 5.51, 13.25);
+    } else {
+      if (this.arrowMesh && this.arrowMesh.position.z !== -12.99) this.arrowMesh.position.set(-2.2, 5.51, -2.8);
+    }
   }
 
   /* 播放气流动画 */
@@ -774,6 +824,20 @@ class mainWindRect {
         resolve(null);
         this.initWindow();
         this.initFbmAnimation();
+
+        const loader = new THREE.TextureLoader();
+        this.airTexture = loader.load('/model/img/air.png');
+        this.airTexture.wrapS = THREE.RepeatWrapping;
+        this.airTexture.repeat.set(1, 1.2);
+        this.airTexture.offset.y = 0;
+        this.airTexture.matrix.scale(0.5, 0.5);
+        this.airTexture.needsUpdate = true;
+        this.material = new THREE.MeshBasicMaterial({
+          map: this.airTexture,
+          transparent: true,
+          side: THREE.FrontSide,
+        });
+        this.clock.start();
       });
     });
   }

+ 69 - 4
src/views/vent/monitorManager/mainFanMonitor/mainWind.xj.threejs.ts

@@ -1,7 +1,7 @@
 import * as THREE from 'three';
 import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
 import Smoke from '/@/views/vent/comment/threejs/Smoke';
-import { renderVideo } from '/@/utils/threejs/util';
+import { PathPointList, PathGeometry } from 'three.path';
 import gsap from 'gsap';
 
 class mainXjWindRect {
@@ -44,7 +44,12 @@ class mainXjWindRect {
   fbmAnimationClip: THREE.AnimationClip | null = null;
   fbmMixers: THREE.AnimationMixer | null = null;
   fbmOpenAction: THREE.AnimationAction | null = null;
-
+  clock = new THREE.Clock();
+  material;
+  airTexture;
+  offset = 0;
+  direction = 0; // -1 代表反向,1代表正向
+  arrowMesh;
   constructor(model, playerVal1) {
     this.model = model;
     this.player1 = playerVal1;
@@ -145,6 +150,10 @@ class mainXjWindRect {
       return;
     }
     if (this.fbmMixers) this.fbmMixers?.update(1 / 25);
+    if (this.airTexture) {
+      this.airTexture.offset.x = this.offset;
+      this.offset -= this.clock.getDelta() * 2;
+    }
   }
 
   /* 点击风窗,风窗全屏 */
@@ -195,6 +204,7 @@ class mainXjWindRect {
     } else {
       smoke = this.backSmoke;
     }
+
     if (!smoke.frameId) {
       await this.lookMotor(deviceType, 'open', duration);
       // await this.openOrCloseValve(deviceType, 'open', duration);
@@ -227,6 +237,8 @@ class mainXjWindRect {
   }
 
   async setSmokeDirection(deviceType, smokeDirection) {
+    let smoke;
+    const pathPoints: THREE.Vector3[] = [];
     const windowPositivePath = [
       {
         path0: new THREE.Vector3(4.441, 20.267, 3.614),
@@ -250,7 +262,7 @@ class mainXjWindRect {
         path0: new THREE.Vector3(42.741, 5.364, 3.614),
         path1: new THREE.Vector3(44.741, 17.267, 3.614),
         isSpread: true,
-        spreadDirection: 1, // 1是由小变大(出),-1是由大变小(进)
+        spreadDirection: 1, // 1是由小变大(出),-1是由大变小(进)0
       },
     ];
     const windowInversePath = [
@@ -319,7 +331,24 @@ class mainXjWindRect {
         spreadDirection: 0, // 1是由小变大,-1是由大变小
       },
     ];
-    let smoke;
+    const getPathPoint = () => {
+      this.arrowMesh = this.group?.getObjectByName('arrow');
+      if (this.arrowMesh) return;
+      pathPoints.push(new THREE.Vector3(23.441, 1.485, 2.614), new THREE.Vector3(35.583, 1.485, 2.614));
+      const pathPointList = new PathPointList();
+      const up = new THREE.Vector3(0, 0, 1);
+      pathPointList.set(pathPoints, 0, 0, up, false);
+      const geometry = new PathGeometry(pathPoints.length, false);
+      geometry.update(pathPointList, {
+        width: 2,
+        arrow: false,
+      });
+
+      this.arrowMesh = new THREE.Mesh(geometry, this.material);
+      this.arrowMesh.name = 'arrow';
+      this.group?.add(this.arrowMesh);
+    };
+
     if (deviceType === 'front') {
       smoke = this.frontSmoke;
     } else if (deviceType === 'back') {
@@ -327,18 +356,40 @@ class mainXjWindRect {
     }
     switch (smokeDirection) {
       case 'tubPositivePath': // 风筒正
+        if (this.direction !== 1) {
+          this.direction = 1;
+          this.airTexture.repeat.x = 1;
+        }
         smoke.setPath(tubPositivePath);
         break;
       case 'tubInversePath': // 风筒反
+        if (this.direction !== -1) {
+          this.direction = -1;
+          this.airTexture.repeat.x = -1;
+        }
         smoke.setPath(tubInversePath);
         break;
       case 'windowPositivePath': // 风窗正
+        if (this.direction !== 1) {
+          this.direction = 1;
+          this.airTexture.repeat.x = 1;
+        }
         smoke.setPath(windowPositivePath);
         break;
       case 'windowInversePath': // 风窗反
+        if (this.direction !== -1) {
+          this.direction = -1;
+          this.airTexture.repeat.x = -1;
+        }
         smoke.setPath(windowInversePath);
         break;
     }
+    getPathPoint();
+    if (deviceType === 'front') {
+      if (this.arrowMesh && this.arrowMesh.position.z !== 2.31) this.arrowMesh.position.set(3.18, 6.36, 2.31);
+    } else {
+      if (this.arrowMesh && this.arrowMesh.position.z !== -12.99) this.arrowMesh.position.set(3.18, 6.36, -12.99);
+    }
   }
 
   /* 播放气流动画 */
@@ -710,6 +761,20 @@ class mainXjWindRect {
         this.airJin2 = airJinGroup.getObjectByName('pasted__Jin_2') as THREE.Mesh;
         this.airChu1 = airChuGroup.getObjectByName('pasted__Chu_1') as THREE.Mesh;
         this.airChu2 = airChuGroup.getObjectByName('pasted__Chu_2') as THREE.Mesh;
+        const loader = new THREE.TextureLoader();
+
+        this.airTexture = loader.load('/model/img/air.png');
+        this.airTexture.wrapS = THREE.RepeatWrapping;
+        this.airTexture.repeat.set(1, 1.2);
+        this.airTexture.offset.y = 0;
+        this.airTexture.matrix.scale(0.6, 0.6);
+        this.airTexture.needsUpdate = true;
+        this.material = new THREE.MeshBasicMaterial({
+          map: this.airTexture,
+          transparent: true,
+          side: THREE.FrontSide,
+        });
+        this.clock.start();
       });
     });
   }

+ 6 - 3
src/views/vent/monitorManager/safetyMonitor/AlarmHistoryTable.vue

@@ -93,8 +93,11 @@
     },
     async (newVal) => {
       if (!newVal) return;
-
-      const column = getTableHeaderColumns(newVal + '_history');
+      let column;
+      column = getTableHeaderColumns('safetymonitor_warning');
+      if (!column || column.length < 1) {
+        column = getTableHeaderColumns(newVal + '_history');
+      }
       if (column && column.length < 1) {
         const arr = newVal.split('_');
         const columnKey = arr.reduce((prev, cur, index) => {
@@ -139,7 +142,7 @@
     tableProps: {
       api: safetyList,
       columns: props.columnsType ? columns : (props.columns as any[]),
-      canResize: true,
+      canResize: false,
       showTableSetting: false,
       showActionColumn: false,
       bordered: false,

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

@@ -5,7 +5,7 @@
         <ExclamationCircleFilled style="color: #ffb700; font-size: 30px" />
         <div class="warning-text">您是否要进行{{ title }}操作?</div>
       </div>
-      <template v-if="type == '1' || type == '2'">
+      <template v-if="type == '1' || type == '2' || type.startsWith('sameSetValue')">
         <div class="vent-flex-row input-box">
           <div class="label">{{ title.includes('角度') ? '风窗角度:' : '风窗面积:' }}</div>
           <a-input-number size="small" placeholder="0" :min="0" v-model:value="data" />
@@ -13,13 +13,13 @@
       </template>
       <template v-if="type == '7'">
         <div class="vent-flex-row input-box">
-          <div class="label">窗目标风量(m³/min):</div>
+          <div class="label">窗目标风量(m³/min):</div>
           <a-input-number size="small" placeholder="0" :min="0" v-model:value="data" />
         </div>
       </template>
       <template v-if="type == '8'">
         <div class="vent-flex-row input-box">
-          <div class="label">窗目标风量(m³/min):</div>
+          <div class="label">窗目标风量(m³/min):</div>
           <a-input-number size="small" placeholder="0" :min="0" v-model:value="data" />
         </div>
       </template>

+ 2 - 2
src/views/vent/monitorManager/windowMonitor/index.vue

@@ -525,14 +525,14 @@
         devicetype: selectData.value.deviceType,
         paramcode: 'frontSetValue',
         password: passWord || globalConfig?.simulatedPassword,
-        value: null,
+        value: value ? value : null,
       };
       const data2 = {
         deviceid: selectData.value.deviceID,
         devicetype: selectData.value.deviceType,
         paramcode: 'rearSetValue',
         password: passWord || globalConfig?.simulatedPassword,
-        value: null,
+        value: value ? value : null,
       };
       const res1 = await deviceControlApi(data1);
       const res2 = await deviceControlApi(data2);

+ 37 - 30
src/views/vent/monitorManager/workFaceMonitor/components/workFaceHandleHistory.vue

@@ -1,41 +1,48 @@
 <template>
   <div class="handle-history">
-    <HandlerHistoryTable v-if="refresh" columns-type="operator_history" :device-type="type" :sys-id="deviceId"
-      designScope="caimei_history" :scroll="{ y: 650 }"/>
+    <HandlerHistoryTable
+      v-if="refresh"
+      columns-type="operator_history"
+      :device-type="type"
+      :sys-id="deviceId"
+      designScope="caimei_history"
+      :scroll="{ y: 650 }"
+    />
   </div>
 </template>
 <script setup lang="ts">
-import { watch, ref, nextTick } from 'vue'
-import HandlerHistoryTable from '../../comment/WorkFaceHandlerHistoryTable.vue';
+  import { watch, ref, nextTick } from 'vue';
+  import HandlerHistoryTable from '../../comment/WorkFaceHandlerHistoryTable.vue';
 
-const props = defineProps({
-  deviceType: {
-    type: String,
-    required: true,
-  },
-  deviceId: {
-    type: String,
-    required: true,
-  }
-})
-
-const type = ref(props.deviceType)
+  const props = defineProps({
+    deviceType: {
+      type: String,
+      required: true,
+    },
+    deviceId: {
+      type: String,
+      required: true,
+    },
+  });
 
-const refresh = ref(true)
-
-watch(() => props.deviceType, (newVal) => {
-  type.value = newVal
-  refresh.value = false
-  nextTick(() => {
-    refresh.value = true
-  })
-})
+  const type = ref(props.deviceType);
 
+  const refresh = ref(true);
 
+  watch(
+    () => props.deviceType,
+    (newVal) => {
+      type.value = newVal;
+      refresh.value = false;
+      nextTick(() => {
+        refresh.value = true;
+      });
+    }
+  );
 </script>
 <style lang="less" scoped>
-.handle-history {
-  width: 100%;
-  pointer-events: auto;
-}
-</style>
+  .handle-history {
+    width: 100%;
+    pointer-events: auto;
+  }
+</style>

+ 3 - 9
src/views/vent/monitorManager/workFaceMonitor/components/workFaceHistory.vue

@@ -1,18 +1,12 @@
 <template>
   <div class="history-box">
-    <HistoryTable
-      :columns-type="`${deviceType}`"
-      :device-type="deviceType"
-      :sysId="deviceId"
-      designScope="pressurefan_history"
-      :scroll="{ y: 650 }"
-    />
+    <HistoryTable :device-code="`${deviceType}`" :dict-code="`${deviceType}_dict`" :scroll="{ y: 650 }" />
   </div>
 </template>
 <script setup lang="ts">
   import { ref, defineProps } from 'vue';
-  import HistoryTable from '../../comment/HistoryTable.vue';
-
+  // import HistoryTable from '../../comment/HistoryTable.vue';
+  import HistoryTable from '/@/views/vent/comment/history/HistoryTable.vue';
   const props = defineProps({
     deviceType: {
       type: String,