Parcourir la source

Merge branch 'master' of http://39.97.59.228:8013/hrx/mky-vent-base

lxh il y a 5 mois
Parent
commit
5b00d36b24
81 fichiers modifiés avec 4812 ajouts et 2846 suppressions
  1. 3 0
      src/assets/images/home-container/configurable/close.svg
  2. BIN
      src/assets/images/home-container/configurable/confirmBj.png
  3. BIN
      src/assets/images/home-container/configurable/confirmTitle.png
  4. 6 0
      src/assets/images/home-container/configurable/hidePassword.svg
  5. BIN
      src/assets/images/home-container/configurable/input-error.png
  6. BIN
      src/assets/images/home-container/configurable/input-success.png
  7. 3 0
      src/assets/images/home-container/configurable/showPassword.svg
  8. BIN
      src/assets/images/home-warn/2-2.png
  9. BIN
      src/assets/images/vent/home/2.5D.png
  10. BIN
      src/assets/images/vent/home/3D.png
  11. BIN
      src/assets/images/vent/sider/border-bottom.png
  12. BIN
      src/assets/images/vent/sider/border-lbot.png
  13. BIN
      src/assets/images/vent/sider/border-left.png
  14. BIN
      src/assets/images/vent/sider/border-ltop.png
  15. BIN
      src/assets/images/vent/sider/border-rbot.png
  16. BIN
      src/assets/images/vent/sider/border-right.png
  17. BIN
      src/assets/images/vent/sider/border-rtop.png
  18. BIN
      src/assets/images/vent/sider/border.png
  19. BIN
      src/assets/images/vent/sider/buttonBot-active.png
  20. BIN
      src/assets/images/vent/sider/buttonBot.png
  21. BIN
      src/assets/images/vent/sider/buttonCont-hover.png
  22. BIN
      src/assets/images/vent/sider/buttonCont.png
  23. BIN
      src/assets/images/vent/sider/buttonTitle-hover.png
  24. BIN
      src/assets/images/vent/sider/buttonTitle.png
  25. 30 0
      src/assets/images/vent/sider/icon-inner.svg
  26. BIN
      src/assets/images/vent/sider/icon-outer.png
  27. BIN
      src/assets/images/vent/sider/menu-bg.png
  28. BIN
      src/assets/images/vent/sider/svg-arrow.png
  29. 14 0
      src/assets/images/vent/sider/svg-close.svg
  30. 392 409
      src/components/vent/BasicMonitoring.vue
  31. 2 5
      src/components/vent/micro/createSubApp.vue
  32. 77 0
      src/components/vent/siderBorderBg.vue
  33. 515 0
      src/layouts/default/sider/bottomSideder3.vue
  34. 4 1
      src/layouts/default/sider/index.vue
  35. 0 1
      src/main.ts
  36. 11 3
      src/utils/http/axios/index.ts
  37. 4 4
      src/views/monitor/quartz/quartz.api.ts
  38. 1 1
      src/views/vent/dataCenter/deviceCenter/history/history.api.ts
  39. 200 174
      src/views/vent/deviceManager/comment/DeviceModal.vue
  40. 344 0
      src/views/vent/gas/components/modal/confirmModal1.vue
  41. 190 184
      src/views/vent/home/configurable/blue/components/ModuleFireNew.vue
  42. 221 221
      src/views/vent/home/configurable/blue/components/NewNavFire.vue
  43. 20 20
      src/views/vent/home/configurable/blue/components/center-area-warn.vue
  44. 1 2
      src/views/vent/home/configurable/blue/components/detail/MiniBoard-Warn.vue
  45. 1 1
      src/views/vent/home/configurable/blue/components/moduleBottom-warn.vue
  46. 79 79
      src/views/vent/home/configurable/blue/components/moduleLeft-warn.vue
  47. 19 19
      src/views/vent/home/configurable/blue/components/originalNew/moduleTopFire.vue
  48. 9 7
      src/views/vent/home/configurable/blue/components/yj_gasWarn.vue
  49. 40 14
      src/views/vent/home/configurable/blue/components/yj_ventWarn.vue
  50. 152 152
      src/views/vent/home/configurable/blue/fireNew.vue
  51. 14 18
      src/views/vent/home/configurable/blue/warnMonitor.vue
  52. 426 411
      src/views/vent/home/configurable/components/detail/ComplexList-FireNew.vue
  53. 4 2
      src/views/vent/home/configurable/components/detail/CustomList-fire.vue
  54. 1 1
      src/views/vent/home/configurable/components/detail/CustomTable-fire.vue
  55. 123 116
      src/views/vent/home/configurable/components/detail/MiniBoard-FireNew.vue
  56. 33 27
      src/views/vent/home/configurable/components/preset/nitrogenBtnList.vue
  57. 2 2
      src/views/vent/home/configurable/components/three3D.vue
  58. 80 118
      src/views/vent/home/configurable/configurable.api.ts
  59. 9 9
      src/views/vent/home/configurable/configurable.data.New.ts
  60. 2 2
      src/views/vent/home/configurable/configurable.data.ts
  61. 3 7
      src/views/vent/home/configurable/green/components/dz-card.vue
  62. 8 8
      src/views/vent/home/configurable/green/components/dz-list.vue
  63. 23 11
      src/views/vent/home/configurable/vent182.vue
  64. 318 318
      src/views/vent/monitorManager/comment/components/DeviceBaseInfo.vue
  65. 2 2
      src/views/vent/monitorManager/compressor/nitrogen.data.ts
  66. 4 3
      src/views/vent/monitorManager/deviceMonitor/components/device/index.vue
  67. 1 0
      src/views/vent/monitorManager/deviceMonitor/index.vue
  68. 1 1
      src/views/vent/monitorManager/fanLocalMonitor/components/conditionAssistance.vue
  69. 4 4
      src/views/vent/monitorManager/fanLocalMonitor/components/entryThree.vue
  70. 15 3
      src/views/vent/monitorManager/fanLocalMonitor/fanLocal.data.ts
  71. 4 4
      src/views/vent/monitorManager/fanLocalMonitor/fanLocalDual.threejs.base.ts
  72. 126 78
      src/views/vent/monitorManager/fanLocalMonitor/index.vue
  73. 117 25
      src/views/vent/monitorManager/mainFanMonitor/components/entryThree.vue
  74. 17 57
      src/views/vent/monitorManager/mainFanMonitor/index.vue
  75. 253 5
      src/views/vent/monitorManager/mainFanMonitor/main.data.ts
  76. 707 59
      src/views/vent/monitorManager/nitrogenMonitor/index.vue
  77. 43 16
      src/views/vent/monitorManager/nitrogenMonitor/model-tip.vue
  78. 5 238
      src/views/vent/monitorManager/sprayMonitor/index.vue
  79. 19 2
      src/views/vent/monitorManager/sprayMonitor/spray.three.ts
  80. 108 0
      src/views/vent/monitorManager/sprayMonitor/spray.threejs.pensha.ts
  81. 2 2
      src/views/vent/monitorManager/sprayMonitor/tashan.vue

+ 3 - 0
src/assets/images/home-container/configurable/close.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="37.6" height="37.6" viewBox="0 0 37.6 37.6">
+  <path id="关闭" d="M18.8,37.6A18.8,18.8,0,1,1,37.6,18.8,18.8,18.8,0,0,1,18.8,37.6Zm0-34.914c-8.405,0-16.114,7.709-16.114,16.114S10.394,34.914,18.8,34.914,34.914,27.2,34.914,18.8,27.2,2.686,18.8,2.686Zm8.425,24.673a1.789,1.789,0,0,1-2.532,0l-5.7-5.7-5.7,5.7a1.79,1.79,0,1,1-2.533-2.532l5.7-5.7-5.7-5.7A1.79,1.79,0,1,1,13.3,10.9L19,16.6l5.7-5.7a1.79,1.79,0,1,1,2.532,2.532l-5.7,5.7,5.7,5.7a1.79,1.79,0,0,1,0,2.532Z" fill="#36c7ff" opacity="0.8"/>
+</svg>

BIN
src/assets/images/home-container/configurable/confirmBj.png


BIN
src/assets/images/home-container/configurable/confirmTitle.png


+ 6 - 0
src/assets/images/home-container/configurable/hidePassword.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="21.727" height="19.204" viewBox="0 0 21.727 19.204">
+  <g id="密码不可见" opacity="0.7">
+    <path id="路径_57174" data-name="路径 57174" d="M35.3,73.6a15.31,15.31,0,0,0-3.128-4.515L30.9,70.358a13.324,13.324,0,0,1,2.712,3.89c-2.073,4.291-5.015,6.329-9.037,6.329a9.646,9.646,0,0,1-3.335-.558l-1.373,1.373a11.075,11.075,0,0,0,4.708.979q7.183,0,10.719-7.482a1.5,1.5,0,0,0,0-1.284Zm-1.585-7.989-1.06-1.064a.2.2,0,0,0-.282,0l-2.726,2.723a10.978,10.978,0,0,0-5.065-1.15q-7.183,0-10.719,7.481a1.5,1.5,0,0,0,0,1.284,14.979,14.979,0,0,0,3.4,4.768L14.626,82.3a.2.2,0,0,0,0,.281L15.69,83.64a.2.2,0,0,0,.282,0L33.715,65.9a.2.2,0,0,0,0-.282ZM15.545,74.247c2.075-4.291,5.017-6.329,9.036-6.329a9.451,9.451,0,0,1,3.716.708l-1.752,1.752a4.386,4.386,0,0,0-5.933,5.933L18.533,78.39a12.95,12.95,0,0,1-2.989-4.142Zm6.147,0a2.794,2.794,0,0,1,3.642-2.658L21.823,75.1a2.791,2.791,0,0,1-.132-.852Z" transform="translate(-13.718 -64.494)" fill="#fff"/>
+    <path id="路径_57175" data-name="路径 57175" d="M436.926,444.374c-.086,0-.171,0-.256-.011l-1.316,1.316a4.387,4.387,0,0,0,5.667-5.666l-1.317,1.316a2.786,2.786,0,0,1-2.778,3.045Z" transform="translate(-426.162 -431.826)" fill="#fff"/>
+  </g>
+</svg>

BIN
src/assets/images/home-container/configurable/input-error.png


BIN
src/assets/images/home-container/configurable/input-success.png


+ 3 - 0
src/assets/images/home-container/configurable/showPassword.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="22.75" height="16.822" viewBox="0 0 22.75 16.822">
+  <path id="密码可见" d="M72.117,135.911c-.944,4.5-5.449,7.911-10.865,7.911s-9.918-3.411-10.863-7.911c.943-4.5,5.449-7.911,10.864-7.911S71.172,131.411,72.117,135.911Zm-5.227,4.426a7.866,7.866,0,0,0,3.177-4.426c-.942-3.6-4.6-6.146-8.813-6.146s-7.871,2.549-8.813,6.146c.939,3.6,4.6,6.152,8.813,6.153A9.877,9.877,0,0,0,66.89,140.337Zm-8.831-1.629a3.617,3.617,0,0,1,0-5.594,4.985,4.985,0,0,1,6.39,0,3.617,3.617,0,0,1,0,5.594A4.985,4.985,0,0,1,58.058,138.708Zm4.97-1.243a2.01,2.01,0,0,0,0-3.108,2.77,2.77,0,0,0-3.551,0,2.01,2.01,0,0,0,0,3.108A2.77,2.77,0,0,0,63.029,137.465Z" transform="translate(-49.879 -127.5)" fill="#fff" stroke="rgba(0,0,0,0)" stroke-miterlimit="10" stroke-width="1" opacity="0.7"/>
+</svg>

BIN
src/assets/images/home-warn/2-2.png


BIN
src/assets/images/vent/home/2.5D.png


BIN
src/assets/images/vent/home/3D.png


BIN
src/assets/images/vent/sider/border-bottom.png


BIN
src/assets/images/vent/sider/border-lbot.png


BIN
src/assets/images/vent/sider/border-left.png


BIN
src/assets/images/vent/sider/border-ltop.png


BIN
src/assets/images/vent/sider/border-rbot.png


BIN
src/assets/images/vent/sider/border-right.png


BIN
src/assets/images/vent/sider/border-rtop.png


BIN
src/assets/images/vent/sider/border.png


BIN
src/assets/images/vent/sider/buttonBot-active.png


BIN
src/assets/images/vent/sider/buttonBot.png


BIN
src/assets/images/vent/sider/buttonCont-hover.png


BIN
src/assets/images/vent/sider/buttonCont.png


BIN
src/assets/images/vent/sider/buttonTitle-hover.png


BIN
src/assets/images/vent/sider/buttonTitle.png


+ 30 - 0
src/assets/images/vent/sider/icon-inner.svg

@@ -0,0 +1,30 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="45.37" height="45.369" viewBox="0 0 45.37 45.369">
+  <defs>
+    <linearGradient id="linear-gradient" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#fff"/>
+      <stop offset="1" stop-color="#3df6ff"/>
+    </linearGradient>
+    <filter id="路径_57740" x="0" y="0" width="45.37" height="45.369" filterUnits="userSpaceOnUse">
+      <feOffset input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="1" result="blur"/>
+      <feFlood flood-color="#2cb6ff"/>
+      <feComposite operator="in" in2="blur"/>
+      <feComposite in="SourceGraphic"/>
+    </filter>
+    <filter id="路径_57741" x="21.344" y="22.768" width="22.602" height="22.602" filterUnits="userSpaceOnUse">
+      <feOffset input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="1" result="blur-2"/>
+      <feFlood flood-color="#36c7ff"/>
+      <feComposite operator="in" in2="blur-2"/>
+      <feComposite in="SourceGraphic"/>
+    </filter>
+  </defs>
+  <g id="组_16453" data-name="组 16453" transform="translate(3 3)">
+    <g transform="matrix(1, 0, 0, 1, -3, -3)" filter="url(#路径_57740)">
+      <path id="路径_57740-2" data-name="路径 57740" d="M35.463,23.464h7.116a4.758,4.758,0,0,1,4.743,4.743V40.066H35.463a4.758,4.758,0,0,1-4.743-4.747V28.2A4.757,4.757,0,0,1,35.463,23.464Zm0,21.345H47.322V56.668a4.758,4.758,0,0,1-4.743,4.743H35.463a4.761,4.761,0,0,1-4.743-4.747V49.547a4.757,4.757,0,0,1,4.743-4.739Zm0,0,24.9-1.189-8.3-8.535a4.3,4.3,0,0,1-1.423-3.32,4.672,4.672,0,0,1,1.423-3.32l4.981-4.981a4.583,4.583,0,0,1,6.639,0l4.981,4.981a4.583,4.583,0,0,1,0,6.639Zm0-19.21a2.165,2.165,0,0,0-1.662.712L53.727,30.1a2.3,2.3,0,0,0,0,3.324l6.639,6.639L67,33.427A2.3,2.3,0,0,0,67,30.1l-4.981-4.981a2.15,2.15,0,0,0-1.658-.712Zm0,0" transform="translate(-27.72 -19.04)" fill="url(#linear-gradient)"/>
+    </g>
+    <g transform="matrix(1, 0, 0, 1, -3, -3)" filter="url(#路径_57741)">
+      <path id="路径_57741-2" data-name="路径 57741" d="M562.074,588.8h11.859a4.758,4.758,0,0,1,4.743,4.743v7.116a4.758,4.758,0,0,1-4.743,4.743h-7.116a4.758,4.758,0,0,1-4.743-4.743Zm0,0" transform="translate(-537.73 -563.03)" fill="url(#linear-gradient)"/>
+    </g>
+  </g>
+</svg>

BIN
src/assets/images/vent/sider/icon-outer.png


BIN
src/assets/images/vent/sider/menu-bg.png


BIN
src/assets/images/vent/sider/svg-arrow.png


+ 14 - 0
src/assets/images/vent/sider/svg-close.svg

@@ -0,0 +1,14 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="23.83" height="23.83" viewBox="0 0 23.83 23.83">
+  <defs>
+    <filter id="路径_57742" x="0" y="0" width="23.83" height="23.83" filterUnits="userSpaceOnUse">
+      <feOffset input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="1" result="blur"/>
+      <feFlood flood-color="#3df6ff"/>
+      <feComposite operator="in" in2="blur"/>
+      <feComposite in="SourceGraphic"/>
+    </filter>
+  </defs>
+  <g transform="matrix(1, 0, 0, 1, 0, 0)" filter="url(#路径_57742)">
+    <path id="路径_57742-2" data-name="路径 57742" d="M168.085,164.9l5.459-5.459a.993.993,0,0,0,0-1.364l-1.82-1.82a.993.993,0,0,0-1.364,0l-5.459,5.456-5.459-5.459a.993.993,0,0,0-1.364,0l-1.82,1.82a.993.993,0,0,0,0,1.364l5.456,5.461-5.459,5.459a.993.993,0,0,0,0,1.364l1.82,1.82a.993.993,0,0,0,1.364,0l5.461-5.459,5.459,5.459a.993.993,0,0,0,1.364,0l1.82-1.82a.993.993,0,0,0,0-1.364Z" transform="translate(-152.98 -152.98)" fill="#3df6ff"/>
+  </g>
+</svg>

+ 392 - 409
src/components/vent/BasicMonitoring.vue

@@ -9,13 +9,13 @@
     {{ mainTitle }}
   </customHeader>
   <!-- 默认插槽,一般用来放模型 -->
-  <slot :monitor-data="monitorData"></slot>
+  <slot :monitor-data="monitorData" :monitor-active="monitorActive"></slot>
   <div class="scene-box">
     <div class="center-container">
       <!-- 监测模块 -->
-      <template v-if="activeKey == 'monitor'">
-        <slot name="opration" v-if="monitorType !== 'nitrogenMonitor'">
-          <div class="button-wrapper">
+      <template v-if="monitorActive">
+        <slot name="opration">
+          <div v-if="mainConfig.operations.length" class="button-wrapper">
             <template v-for="item in mainConfig.operations" :key="item.key">
               <template v-if="hasPermission(item.permission)">
                 <div :class="{ 'button-box': true, 'button-box-disable': item.disabled }" @click="handleOperate(item)">{{ item.label }} </div>
@@ -49,39 +49,32 @@
           </div>
         </div>
         <div class="history-container">
-          <slot name="history" :device-type="deviceType" :device-id="optionValue">
-            <HistoryTable
-              v-if="activeKey == 'monitor_history'"
-              class="vent-margin-t-20"
-              :columns-type="deviceType"
-              :device-type="deviceType"
-              :sysId="optionValue"
-              :scroll="{ y: 650 }"
-              v-bind="monitorHistoryConfig"
-            />
-          </slot>
-          <slot name="handler" :device-type="deviceType" :device-id="optionValue">
-            <HandlerHistoryTable
-              v-if="activeKey == 'handler_history'"
-              class="vent-margin-t-20"
-              columns-type="operator_history"
-              :deviceType="deviceType"
-              v-bind="handlerHistoryConfig"
-            />
-          </slot>
-          <slot name="alarm" :device-type="deviceType" :device-id="optionValue">
-            <AlarmHistoryTable
-              v-if="activeKey == 'faultRecord'"
-              columns-type="alarm"
-              :device-type="deviceType"
-              :sys-id="optionValue"
-              v-bind="alarmHistoryConfig"
-            />
-          </slot>
+          <template v-if="activeKey === 'monitor_history'">
+            <slot name="history" :device-type="deviceType" :device-id="optionValue">
+              <HistoryTable
+                class="vent-margin-t-20"
+                :columns-type="deviceType"
+                :device-type="deviceType"
+                :sysId="optionValue"
+                :scroll="{ y: 650 }"
+                v-bind="monitorHistoryConfig"
+              />
+            </slot>
+          </template>
+          <template v-if="activeKey === 'handler_history'">
+            <slot name="handler" :device-type="deviceType" :device-id="optionValue">
+              <HandlerHistoryTable class="vent-margin-t-20" columns-type="operator_history" :deviceType="deviceType" v-bind="handlerHistoryConfig" />
+            </slot>
+          </template>
+          <template v-if="activeKey === 'faultRecord'">
+            <slot name="alarm" :device-type="deviceType" :device-id="optionValue">
+              <AlarmHistoryTable columns-type="alarm" :device-type="deviceType" :sys-id="optionValue" v-bind="alarmHistoryConfig" />
+            </slot>
+          </template>
         </div>
       </div>
     </div>
-    <BottomMenu @change="changeActive" />
+    <BottomMenu @change="activeKey = $event" />
   </div>
   <PasswordModal
     :modal-is-show="passwordModalShown"
@@ -92,425 +85,415 @@
 </template>
 
 <script setup lang="ts">
-import customHeader from '/@/components/vent/customHeader.vue';
-import { ref, onMounted, onUnmounted } from 'vue';
-import { getDevice, sysList } from '/@/views/vent/monitorManager/comment/comment.api';
-import BottomMenu from '/@/views/vent/comment/components/bottomMenu.vue';
-import ModuleCommon from '/@/views/vent/home/configurable/components/ModuleCommon.vue';
-import HistoryTable from '/@/views/vent/monitorManager/comment/HistoryTable.vue';
-import HandlerHistoryTable from '/@/views/vent/monitorManager/comment/HandlerHistoryTable.vue';
-import AlarmHistoryTable from '/@/views/vent/monitorManager/comment/AlarmHistoryTable.vue';
-import { useRouter } from 'vue-router';
-import { Config } from '/@/views/vent/deviceManager/configurationTable/types';
-import { message, Modal } from 'ant-design-vue';
-import _ from 'lodash';
-import PasswordModal from '/@/views/vent/monitorManager/comment/components/PasswordModal.vue';
-import { usePermission } from '/@/hooks/web/usePermission';
-import { deviceControlApi } from '/@/api/vent';
-
-type DeviceType = { deviceType: string; deviceName: string; datalist: any[] };
-type Operation = {
-  label: string;
-  key: string;
-  value: string;
-  disabled?: boolean;
-  showPassword?: boolean;
-  permission?: string;
-  onOk?: (key: string, value: string, pwd?: string) => Promise<void>;
-};
-
-const props = withDefaults(
-  defineProps<{
-    mainTitle: string;
-    /** 是否显示历史数据上方的设备类型选择器 */
-    showDeviceList?: boolean;
-    /** 请求场景数据传入的类型字符 */
-    strtype: string;
-    /** 请求场景数据传入的页面类型字符 */
-    pagetype?: string;
-    /** 获取各表格配置时依赖的设备类型 */
-    monitorType?: string;
-    /** 监测页面类型*/
-    // deviceType: string;
-    /** 主要模块配置 */
-    mainConfig: {
-      operations: Operation[];
-      configs: Config[];
-      /** 获取该场景所含设备及其监测信息的API */
-      monitorApi?: (params: { deviceType: string; deviceId: number | string }) => Promise<any>;
-      /** 定时获取监测信息的配置,单位为毫秒,不传入即默认,传0即停用 */
-      timer?: number;
-    };
-    /** 历史数据配置 */
-    monitorHistoryConfig: {
-      /** 请求历史数据时传入的类型字符 */
-      columnsType?: string;
-      /** 如果默认的设备类型不适用,可以传递固定的类型 */
-      deviceType?: string;
-      /** 仅展示已绑定设备,选择是则从系统中获取sysId下已绑定设备。仅能查询到已绑定设备的历史数据 */
-      onlyBounedDevices?: boolean;
-      /** 显示历史数据曲线图 */
-      showHistoryCurve?: boolean;
-    };
-    /** 操作历史配置 */
-    handlerHistoryConfig: {
-      /** 请求操作历史时传入的类型字符 */
-      columnsType?: string;
-      /** 如果默认的设备类型不适用,可以传递固定的类型 */
-      deviceType?: string;
-      /** 获取操作历史的API,可以不提供以使用默认的请求 */
-      deviceListApi?: (params: any) => Promise<any[]>;
-    };
-    /** 报警历史配置 */
-    alarmHistoryConfig: {
-      /** 请求报警历史时传入的类型字符 */
-      columnsType?: string;
-      /** 如果默认的设备类型不适用,可以传递固定的类型 */
-      deviceType?: string;
-      /** 获取报警历史的API,可以不提供以使用默认的请求 */
-      list?: (params: any) => Promise<any[]>;
-      /** 获取设备以供报警历史过滤的API,可以不提供以使用默认的请求 */
-      deviceListApi?: (params: any) => Promise<any[]>;
-    };
-  }>(),
-  {
-    mainConfig: () => ({
-      operations: [],
-      configs: [],
-    }),
-    monitorHistoryConfig: () => ({}),
-    handlerHistoryConfig: () => ({}),
-    alarmHistoryConfig: () => ({}),
-    pagetype: 'normal',
-    showDeviceList: true,
-  }
-);
-
-const { currentRoute } = useRouter();
-const { hasPermission } = usePermission();
+  import customHeader from '/@/components/vent/customHeader.vue';
+  import { ref, onMounted, computed } from 'vue';
+  import { getDevice, sysList } from '/@/views/vent/monitorManager/comment/comment.api';
+  import BottomMenu from '/@/views/vent/comment/components/bottomMenu.vue';
+  import ModuleCommon from '/@/views/vent/home/configurable/components/ModuleCommon.vue';
+  import HistoryTable from '/@/views/vent/monitorManager/comment/HistoryTable.vue';
+  import HandlerHistoryTable from '/@/views/vent/monitorManager/comment/HandlerHistoryTable.vue';
+  import AlarmHistoryTable from '/@/views/vent/monitorManager/comment/AlarmHistoryTable.vue';
+  import { useRouter } from 'vue-router';
+  import { Config } from '/@/views/vent/deviceManager/configurationTable/types';
+  import { message, Modal } from 'ant-design-vue';
+  import _ from 'lodash';
+  import PasswordModal from '/@/views/vent/monitorManager/comment/components/PasswordModal.vue';
+  import { usePermission } from '/@/hooks/web/usePermission';
+  import { deviceControlApi } from '/@/api/vent';
+  import { useIntervalFn } from '@vueuse/shared';
+
+  type DeviceType = { deviceType: string; deviceName: string; datalist: any[] };
+  type Operation = {
+    label: string;
+    key: string;
+    value: string;
+    disabled?: boolean;
+    showPassword?: boolean;
+    permission?: string;
+    onOk?: (key: string, value: string, pwd?: string) => Promise<void>;
+  };
+
+  const props = withDefaults(
+    defineProps<{
+      mainTitle: string;
+      /** 是否显示历史数据上方的设备类型选择器 */
+      showDeviceList?: boolean;
+      /** 请求场景数据传入的类型字符 */
+      strtype: string;
+      /** 请求场景数据传入的页面类型字符 */
+      pagetype?: string;
+      /** 主要模块配置 */
+      mainConfig: {
+        /** 监测上方操作栏配置 */
+        operations: Operation[];
+        /** 监测可配置模块配置 */
+        configs: Config[];
+        /** 获取该场景所含设备及其监测信息的API */
+        monitorApi?: (params: { deviceType: string; deviceId: number | string }) => Promise<any>;
+        /** 定时获取监测信息的配置,单位为毫秒,不传入即默认,传0即停用 */
+        timer?: number;
+      };
+      /** 历史数据配置 */
+      monitorHistoryConfig: {
+        /** 请求历史数据时传入的类型字符 */
+        columnsType?: string;
+        /** 如果默认的设备类型不适用,可以传递固定的类型 */
+        deviceType?: string;
+        /** 仅展示已绑定设备,选择是则从系统中获取sysId下已绑定设备。仅能查询到已绑定设备的历史数据 */
+        onlyBounedDevices?: boolean;
+        /** 显示历史数据曲线图 */
+        showHistoryCurve?: boolean;
+      };
+      /** 操作历史配置 */
+      handlerHistoryConfig: {
+        /** 请求操作历史时传入的类型字符 */
+        columnsType?: string;
+        /** 如果默认的设备类型不适用,可以传递固定的类型 */
+        deviceType?: string;
+        /** 获取操作历史的API,可以不提供以使用默认的请求 */
+        deviceListApi?: (params: any) => Promise<any[]>;
+      };
+      /** 报警历史配置 */
+      alarmHistoryConfig: {
+        /** 请求报警历史时传入的类型字符 */
+        columnsType?: string;
+        /** 如果默认的设备类型不适用,可以传递固定的类型 */
+        deviceType?: string;
+        /** 获取报警历史的API,可以不提供以使用默认的请求 */
+        list?: (params: any) => Promise<any[]>;
+        /** 获取设备以供报警历史过滤的API,可以不提供以使用默认的请求 */
+        deviceListApi?: (params: any) => Promise<any[]>;
+      };
+    }>(),
+    {
+      mainConfig: () => ({
+        operations: [],
+        configs: [],
+      }),
+      monitorHistoryConfig: () => ({}),
+      handlerHistoryConfig: () => ({}),
+      alarmHistoryConfig: () => ({}),
+      pagetype: 'normal',
+      showDeviceList: true,
+    }
+  );
 
-const activeKey = ref('monitor');
+  const { currentRoute } = useRouter();
+  const { hasPermission } = usePermission();
 
-function changeActive(activeValue) {
-  activeKey.value = activeValue;
-}
+  const activeKey = ref('monitor');
+  const monitorActive = computed(() => activeKey.value === 'monitor');
 
-/** 场景选项 */
-const options = ref([]);
-/** 已选择了的场景的id */
-const optionValue = ref('');
+  /** 场景选项 */
+  const options = ref([]);
+  /** 已选择了的场景的id */
+  const optionValue = ref('');
 
-/** 获取左上角场景选择框数据的方法,如果此时初始场景未赋值则选择首项做初始化 */
-async function getSysDataSource() {
-  const res = await sysList({ strtype: props.strtype, pagetype: props.pagetype }).catch(() => {
-    message.error('获取场景数据时发生了错误');
-  });
-  // 初始时选择第一条数据
-  options.value = res.records || [];
-  if (!optionValue.value) {
-    changeSelectRow(options.value[0]['id']);
+  /** 获取左上角场景选择框数据的方法,如果此时初始场景未赋值则选择首项做初始化 */
+  async function getSysDataSource() {
+    const res = await sysList({ strtype: props.strtype, pagetype: props.pagetype }).catch(() => {
+      message.error('获取场景数据时发生了错误');
+    });
+    options.value = res.records || [];
+    // 初始时选择第一条数据
+    if (!optionValue.value) {
+      changeSelectRow(options.value[0]['id']);
+    }
   }
-}
 
-// 切换检测数据
-function changeSelectRow(deviceID) {
-  optionValue.value = deviceID;
-  if (props.monitorType !== 'nitrogenMonitor') {
+  // 切换检测数据
+  function changeSelectRow(deviceID) {
+    optionValue.value = deviceID;
     getDeviceList();
   }
-}
-
-/** 当前场景所关联设备 */
-const deviceList = ref<DeviceType[]>([]);
-const deviceActive = ref('');
-const deviceType = ref('');
-
-/** 选择设备 */
-function deviceChange(index) {
-  deviceType.value = deviceList.value[index]?.deviceType || '';
-  deviceActive.value = deviceList.value[index]?.deviceType || '';
-}
-
-/** 查询当前场景所关联设备列表 */
-async function getDeviceList() {
-  const { msgTxt = [] } = await getDevice({ devicetype: 'sys', systemID: optionValue.value }).catch(() => {
-    message.error('获取已绑定设备时发生了错误');
-  });
 
-  deviceList.value = msgTxt.reduce((arr, item) => {
-    const data = item.datalist.map((data: any) => {
-      return Object.assign(data, data.readData);
+  /** 当前场景所关联设备 */
+  const deviceList = ref<DeviceType[]>([]);
+  const deviceActive = ref('');
+  const deviceType = ref('');
+
+  /** 选择设备 */
+  function deviceChange(index) {
+    deviceType.value = deviceList.value[index]?.deviceType || '';
+    deviceActive.value = deviceList.value[index]?.deviceType || '';
+  }
+
+  /** 查询当前场景所关联设备列表 */
+  async function getDeviceList() {
+    const { msgTxt = [] } = await getDevice({ devicetype: 'sys', systemID: optionValue.value }).catch(() => {
+      message.error('获取已绑定设备时发生了错误');
     });
-    // sys代表场景本身,应该过滤掉去处理该场景下的关联设备
-    if (item.type != 'sys') {
+
+    // 排除场景本身后并处理设备数据
+    deviceList.value = msgTxt.reduce((arr, item) => {
+      // 合并readData
+      const data = item.datalist.map((data: any) => {
+        return Object.assign(data, data.readData);
+      });
+      // sys代表场景本身,应该过滤掉去处理该场景下的关联设备
+      if (item.type === 'sys') return arr;
+
       arr.unshift({
         deviceType: item.type,
-        deviceName: item.typeName || item.datalist[0].typeName,
+        deviceName: item.typeName || item.datalist[0]?.typeName,
         datalist: data,
       });
+      return arr;
+    }, []);
+
+    if (!deviceActive.value) {
+      deviceChange(0);
     }
-    return arr;
-  }, []);
-  if (!deviceActive.value) {
-    deviceChange(0);
   }
-}
-
-let timer: NodeJS.Timeout;
-/** 场景的监测数据 */
-const monitorData = ref<any>({});
-/** 获取本场景下所绑定的设备,将监测数据赋值 */
-async function getMonitor() {
-  if (props.mainConfig.monitorApi) {
-    monitorData.value = await props.mainConfig
-      .monitorApi({
-        deviceType: deviceType.value,
-        deviceId: optionValue.value,
-      })
-      .catch(() => {
+
+  /** 场景的监测数据 */
+  const monitorData = ref<any>({});
+  /** 获取本场景下所绑定的设备,将监测数据赋值 */
+  async function getMonitor() {
+    if (!monitorActive.value) return;
+
+    if (props.mainConfig.monitorApi) {
+      monitorData.value = await props.mainConfig
+        .monitorApi({
+          deviceType: deviceType.value,
+          deviceId: optionValue.value,
+        })
+        .catch(() => {
+          message.error('获取已绑定设备时发生了错误');
+        });
+    } else {
+      const { msgTxt = [] } = await getDevice({ devicetype: 'sys', systemID: optionValue.value }).catch(() => {
         message.error('获取已绑定设备时发生了错误');
       });
-    console.log(monitorData.value, '123123123123');
-  } else if (optionValue.value) {
-    const { msgTxt = [] } = await getDevice({ devicetype: 'sys', systemID: optionValue.value }).catch(() => {
-      message.error('获取已绑定设备时发生了错误');
-    });
-    const temp = {};
-    msgTxt.forEach((item) => {
-      _.set(temp, item.type, item.datalist);
-    });
-    monitorData.value = temp;
-    console.log(monitorData.value, '111111');
+      monitorData.value = msgTxt.reduce((obj, item) => {
+        _.set(obj, item.type, item.datalist);
+        return obj;
+      }, {});
+    }
   }
-}
-
-/** 密码提示框是否显示 */
-const passwordModalShown = ref(false);
-/** 下发操作时的目标配置 */
-const operatingTarget = ref<Operation>();
-
-/** 操作按钮点击后根据配置弹出确认框,初始化数据 */
-function handleOperate(item: Operation) {
-  if (item.disabled) return;
-  operatingTarget.value = item;
-
-  if (item.showPassword) {
-    passwordModalShown.value = true;
-  } else {
-    Modal.confirm({
-      title: '操作确认',
-      content: `确定要进行${operatingTarget.value.label}操作吗?`,
-      iconType: 'info',
-      onOk: () => handlePasswordOK(),
-    });
+
+  /** 密码提示框是否显示 */
+  const passwordModalShown = ref(false);
+  /** 下发操作时的目标配置 */
+  const operatingTarget = ref<Operation>();
+
+  /** 操作按钮点击后根据配置弹出确认框,初始化数据 */
+  function handleOperate(item: Operation) {
+    if (item.disabled) return;
+    operatingTarget.value = item;
+
+    if (item.showPassword) {
+      passwordModalShown.value = true;
+    } else {
+      Modal.confirm({
+        title: '操作确认',
+        content: `确定要进行${operatingTarget.value.label}操作吗?`,
+        iconType: 'info',
+        onOk: () => handlePasswordOK(),
+      });
+    }
   }
-}
 
-/** 密码输入后确认的回调 */
-function handlePasswordOK(pwd?: string) {
-  if (!operatingTarget.value) return message.error('操作目标不存在');
-  const { onOk = deviceControl, key, value } = operatingTarget.value;
+  /** 密码输入后确认的回调 */
+  function handlePasswordOK(pwd?: string) {
+    if (!operatingTarget.value) return message.error('操作目标不存在');
+    const { onOk = deviceControl, key, value } = operatingTarget.value;
 
-  return onOk(key, value, pwd)
-    .then(() => {
-      passwordModalShown.value = false;
-    })
-    .finally(() => {
-      operatingTarget.value = undefined;
-    });
-}
-
-function deviceControl(key: string, value: string, pwd?: string) {
-  return deviceControlApi({
-    deviceid: optionValue.value,
-    devicetype: deviceType.value,
-    password: pwd,
-    paramcode: key,
-    value: value,
-  })
-    .then((r) => {
-      if (!r.success) throw r.message || '操作失败';
-      message.success('指令下发成功');
-    })
-    .catch((e) => {
-      message.error(typeof e === 'string' ? e : '指令下发失败');
-    });
-}
-
-onMounted(async () => {
-  if (currentRoute.value && currentRoute.value['query'] && currentRoute.value['query']['id']) {
-    optionValue.value = currentRoute.value['query']['id'] as string;
+    return onOk(key, value, pwd)
+      .then(() => {
+        passwordModalShown.value = false;
+      })
+      .finally(() => {
+        operatingTarget.value = undefined;
+      });
   }
-  await getSysDataSource();
-  if (props.mainConfig.timer !== 0) {
-    timer = setInterval(() => {
-      getMonitor();
-    }, props.mainConfig.timer || 5000);
-  } else {
-    getMonitor();
+
+  /** 设备操作指令下发 */
+  function deviceControl(key: string, value: string, pwd?: string) {
+    return deviceControlApi({
+      deviceid: optionValue.value,
+      devicetype: deviceType.value,
+      password: pwd,
+      paramcode: key,
+      value: value,
+    })
+      .then((r) => {
+        if (!r.success) throw r.message || '操作失败';
+        message.success('指令下发成功');
+      })
+      .catch((e) => {
+        message.error(typeof e === 'string' ? e : '指令下发失败');
+      });
   }
-});
 
-onUnmounted(() => {
-  clearInterval(timer);
-});
+  onMounted(async () => {
+    if (currentRoute.value && currentRoute.value['query'] && currentRoute.value['query']['id']) {
+      optionValue.value = currentRoute.value['query']['id'] as string;
+    }
+    await getSysDataSource();
+    if (props.mainConfig.timer !== 0) {
+      useIntervalFn(() => getMonitor(), props.mainConfig.timer || 5000, { immediateCallback: true });
+    } else {
+      getMonitor();
+    }
+  });
 </script>
 <style lang="less" scoped>
-@import '/@/design/vent/modal.less';
-@ventSpace: zxm;
-
-.scene-box {
-  --image-tab-group-bg: url('/@/assets/images/vent/tab-group-bg.png');
-  margin-top: 30px;
-  pointer-events: none;
-  .history-group {
-    margin-top: 80px;
-    padding: 0 10px;
-    .history-container {
-      pointer-events: auto;
-      background: #6195af1a;
-      // width: 100%;
-      border: 1px solid #00fffd22;
-      padding: 10px 0;
-      box-shadow: 0 0 20px #44b4ff33 inset;
-    }
-  }
-  .device-button-group {
-    // margin: 0 20px;
-    padding: 0 10px;
-    display: flex;
-    pointer-events: auto;
-    position: relative;
-    &::after {
-      position: absolute;
-      content: '';
-      width: 100%;
-      height: 2px;
-      top: 30px;
-      left: -1px;
-      border-bottom: 1px solid #0efcff;
+  @import '/@/design/vent/modal.less';
+  @ventSpace: zxm;
+
+  .scene-box {
+    --image-tab-group-bg: url('/@/assets/images/vent/tab-group-bg.png');
+    margin-top: 30px;
+    pointer-events: none;
+    .history-group {
+      margin-top: 80px;
+      padding: 0 10px;
+      .history-container {
+        pointer-events: auto;
+        background: #6195af1a;
+        // width: 100%;
+        border: 1px solid #00fffd22;
+        padding: 10px 0;
+        box-shadow: 0 0 20px #44b4ff33 inset;
+      }
     }
-    .device-button {
-      padding: 4px 15px;
-      position: relative;
+    .device-button-group {
+      // margin: 0 20px;
+      padding: 0 10px;
       display: flex;
-      justify-content: center;
-      align-items: center;
-      font-size: 14px;
-
-      color: #fff;
-      cursor: pointer;
-      margin: 0 3px;
-
-      &::before {
-        content: '';
+      pointer-events: auto;
+      position: relative;
+      &::after {
         position: absolute;
-        top: 0;
-        right: 0;
-        bottom: 0;
-        left: 0;
-        border: 1px solid #6176af;
-        transform: skewX(-38deg);
-        background-color: rgba(0, 77, 103, 85%);
-        z-index: -1;
+        content: '';
+        width: 100%;
+        height: 2px;
+        top: 30px;
+        left: -1px;
+        border-bottom: 1px solid #0efcff;
       }
-    }
-    .device-active {
-      // color: #0efcff;
-      &::before {
-        border-color: #0efcff;
-        box-shadow: 1px 1px 3px 1px #0efcff inset;
+      .device-button {
+        padding: 4px 15px;
+        position: relative;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        font-size: 14px;
+
+        color: #fff;
+        cursor: pointer;
+        margin: 0 3px;
+
+        &::before {
+          content: '';
+          position: absolute;
+          top: 0;
+          right: 0;
+          bottom: 0;
+          left: 0;
+          border: 1px solid #6176af;
+          transform: skewX(-38deg);
+          background-color: rgba(0, 77, 103, 85%);
+          z-index: -1;
+        }
+      }
+      .device-active {
+        // color: #0efcff;
+        &::before {
+          border-color: #0efcff;
+          box-shadow: 1px 1px 3px 1px #0efcff inset;
+        }
       }
     }
   }
-}
-.center-container {
-  width: 100%;
-  height: calc(100% - 150px);
-}
-
-.input-box {
-  display: flex;
-  align-items: center;
-  padding-left: 10px;
-
-  .input-title {
-    color: #73e8fe;
-    width: auto;
+  .center-container {
+    width: 100%;
+    height: calc(100% - 150px);
   }
 
-  .@{ventSpace}-input-number {
-    border-color: #ffffff88;
-  }
+  .input-box {
+    display: flex;
+    align-items: center;
+    padding-left: 10px;
 
-  margin-right: 10px;
-}
-
-.button-wrapper {
-  position: relative;
-  top: 30px;
-  left: 500px;
-  display: flex;
-  justify-content: flex-start;
-  align-items: center;
-  margin-top: 10px;
-  width: 1165px !important;
-  height: 75px;
-  background: var(--image-tab-group-bg) no-repeat;
-  background-image: 100% 100%;
-  padding: 0 30px;
-  z-index: 10;
+    .input-title {
+      color: #73e8fe;
+      width: auto;
+    }
 
-  .button-box {
-    margin: 0 10px;
-  }
-}
-
-.button-box {
-  position: relative;
-  padding: 5px;
-  border-radius: 5px;
-  width: auto;
-  height: 34px;
-  border: 1px solid var(--vent-base-border);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  color: var(--vent-font-color);
-  padding: 0 10px;
-  cursor: pointer;
-  pointer-events: all;
-
-  &:hover {
-    background: var(--vent-device-manager-control-btn-hover);
+    .@{ventSpace}-input-number {
+      border-color: #ffffff88;
+    }
+
+    margin-right: 10px;
   }
 
-  &::before {
-    width: calc(100% - 6px);
-    height: 26px;
-    content: '';
-    position: absolute;
-    top: 3px;
-    right: 0;
-    left: 3px;
-    bottom: 0;
-    z-index: -1;
-    border-radius: inherit;
-    /*important*/
-    background: var(--vent-device-manager-control-btn);
+  .button-wrapper {
+    position: relative;
+    top: 30px;
+    left: 500px;
+    display: flex;
+    justify-content: flex-start;
+    align-items: center;
+    margin-top: 10px;
+    width: 1165px !important;
+    height: 75px;
+    background: var(--image-tab-group-bg) no-repeat;
+    background-image: 100% 100%;
+    padding: 0 30px;
+    z-index: 10;
+
+    .button-box {
+      margin: 0 10px;
+    }
   }
-}
 
-.button-box-disable {
-  cursor: not-allowed;
-  border: 1px solid var(--vent-base-border);
+  .button-box {
+    position: relative;
+    padding: 5px;
+    border-radius: 5px;
+    width: auto;
+    height: 34px;
+    border: 1px solid var(--vent-base-border);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: var(--vent-font-color);
+    padding: 0 10px;
+    cursor: pointer;
+    pointer-events: all;
+
+    &:hover {
+      background: var(--vent-device-manager-control-btn-hover);
+    }
 
-  &:hover {
-    background: none;
+    &::before {
+      width: calc(100% - 6px);
+      height: 26px;
+      content: '';
+      position: absolute;
+      top: 3px;
+      right: 0;
+      left: 3px;
+      bottom: 0;
+      z-index: -1;
+      border-radius: inherit;
+      /*important*/
+      background: var(--vent-device-manager-control-btn);
+    }
   }
 
-  &:before {
-    background: linear-gradient(#5897c299, #4a92a899);
+  .button-box-disable {
+    cursor: not-allowed;
+    border: 1px solid var(--vent-base-border);
+
+    &:hover {
+      background: none;
+    }
+
+    &:before {
+      background: linear-gradient(#5897c299, #4a92a899);
+    }
   }
-}
 </style>

+ 2 - 5
src/components/vent/micro/createSubApp.vue

@@ -45,12 +45,9 @@
       // 这里判断
       if (newPath !== oldPath) {
         await resetComponent();
-      } else if (newQuery !== oldQuery) {
+      } else if (newQuery !== oldQuery && (Object.keys(oldQuery).length == 0 || Object.keys(newQuery).length == 0)) {
         const actions = getActions();
-        const appType = route.path.split('/')[1];
-        activeApps[appType].mountPromise.then(() => {
-          actions.setGlobalState({ currentRouter: route });
-        });
+        actions.setGlobalState({ currentRouter: route });
       }
     }
   );

+ 77 - 0
src/components/vent/siderBorderBg.vue

@@ -0,0 +1,77 @@
+<template>
+  <div class="sider-border-bg">
+    <!-- 边框效果 -->
+    <div class="border_corner border_corner_left_top"></div>
+    <div class="border_corner border_corner_right_top"></div>
+    <div class="border_corner border_corner_left_bottom"></div>
+    <div class="border_corner border_corner_right_bottom"></div>
+    <div class="border_corner_left"></div>
+    <div class="border_corner_right"></div>
+    <div class="main-container">
+      <!-- 这里写内容 -->
+      <slot></slot>
+    </div>
+  </div>
+</template>
+<script>
+  import { defineComponent } from 'vue';
+  export default defineComponent({
+    name: 'SiderBorderBg',
+    setup() {},
+  });
+</script>
+
+<style lang="less" scoped>
+  .sider-border-bg {
+    position: relative;
+    .main-container {
+    }
+    .border_corner {
+      position: absolute;
+      width: 265px;
+      height: 130px;
+    }
+    .border_corner_left_top {
+      background: url(/@/assets/images//vent/sider/border-ltop.png) no-repeat;
+      background-size: 100% 100%;
+      top: -28px;
+      left: -28px;
+    }
+    .border_corner_right_top {
+      background: url(/@/assets/images//vent/sider/border-rtop.png) no-repeat;
+      background-size: 100% 100%;
+      top: -28px;
+      right: -28px;
+    }
+    .border_corner_left_bottom {
+      background: url(/@/assets/images//vent/sider/border-lbot.png) no-repeat;
+      background-size: 100% 100%;
+      bottom: -28px;
+      left: -28px;
+    }
+    .border_corner_right_bottom {
+      background: url(/@/assets/images//vent/sider/border-rbot.png) no-repeat;
+      background-size: 100% 100%;
+      bottom: -28px;
+      right: -28px;
+    }
+    .border_corner_left {
+      position: absolute;
+      width: 12px;
+      height: calc(100% - 222px);
+      background: url(/src/assets/images/vent/sider/border-left.png) no-repeat;
+      background-size: 100% 100%;
+      top: 110px;
+      left: -18px;
+    }
+    .border_corner_right {
+      position: absolute;
+      width: 12px;
+      height: calc(100% - 222px);
+      background: url(/src/assets/images/vent/sider/border-right.png) no-repeat;
+      background-size: 100% 100%;
+      top: 110px;
+      right: -18px;
+    }
+  }
+</style>

+ 515 - 0
src/layouts/default/sider/bottomSideder3.vue

@@ -0,0 +1,515 @@
+<template>
+  <div v-if="isShowMenu == -1" class="bottom-side" :class="{ 'bottom-size-show': isShowMenu }" @click="closeMenu">
+    <SiderBorderBg>
+      <div class="close-btn" @click.stop="closeMenu"></div>
+      <div class="menu-panel" @click.stop>
+        <!-- 关键修改1:panel-header 从flex默认的justify-content:flex-start(已满足),但内层wrapper改为inline-flex -->
+        <div class="panel-header">
+          <template v-for="(menu, index) in currentParentRoute.children" :key="index">
+            <div class="col-header-wrapper" v-if="!menu.hideMenu">
+              <div class="col-header">
+                <div class="col-header-inner">{{ menu.name }}</div>
+              </div>
+              <div class="col-arrow"></div>
+            </div>
+          </template>
+        </div>
+
+        <!-- 关键修改2:panel-body 改为inline-flex,取消flex:1均分,添加align-items:flex-start -->
+        <div class="panel-body">
+          <template v-for="(menu, index) in currentParentRoute.children" :key="index">
+            <div class="menu-col" v-if="!menu.hideMenu">
+              <div class="menu-col-bg">
+                <template v-for="(childMenu, childIndex) in menu.children" :key="childIndex">
+                  <template v-if="!childMenu.hideMenu">
+                    <div v-if="childMenu['ver'] == 1" class="menu-item" @click.stop="handleMenuClick(childMenu)">{{ childMenu.name }}</div>
+                    <div v-else class="menu-item menu-item-disabled" @click.stop="handleMenuClick(childMenu)">{{ childMenu.name }}</div>
+                  </template>
+                </template>
+              </div>
+            </div>
+          </template>
+        </div>
+
+        <div class="panel-footer">
+          <div class="tab-group">
+            <template v-for="(programMenu, key) in menuModules" :key="key">
+              <div
+                v-if="!programMenu.title.startsWith('首页')"
+                class="tab-btn"
+                :class="{ 'tab-btn-active': currentParentRoute == programMenu }"
+                @click.stop="selectMenu($event, programMenu)"
+                >{{ programMenu.title }}</div
+              >
+            </template>
+          </div>
+        </div>
+      </div>
+    </SiderBorderBg>
+  </div>
+  <div v-else-if="isShowMenu == 0" class="menu-show-icon">
+    <div class="icon" @click="openMenu"></div>
+  </div>
+</template>
+
+<script lang="ts">
+  import { defineComponent, nextTick, onMounted, ref, unref } from 'vue';
+  import type { Menu } from '/@/router/types';
+  import { SvgIcon } from '/@/components/Icon';
+  import { getMenus } from '/@/router/menus';
+  import { useGo } from '/@/hooks/web/usePage';
+  import { useRouter } from 'vue-router';
+  import { getActions } from '/@/qiankun/state';
+  import { PageEnum } from '/@/enums/pageEnum';
+  import { useGlobSetting } from '/@/hooks/setting';
+  import { unmountMicroApps } from '/@/qiankun';
+  import { useUserStoreWithOut } from '/@/store/modules/user';
+  import { useAppStore } from '/@/store/modules/app';
+  import { router } from '/@/router';
+  import SiderBorderBg from '/@/components/vent/siderBorderBg.vue';
+
+  export default defineComponent({
+    name: 'BottomSider',
+    components: { SvgIcon, SiderBorderBg },
+    setup() {
+      let menuModules = ref<Menu[]>([]);
+      const actions = getActions();
+      const currentParentRoute = ref<Menu>();
+      const { currentRoute } = useRouter();
+      const route = unref(currentRoute);
+      const go = useGo();
+      const glob = useGlobSetting();
+      const isShowMenu = ref(route.path.startsWith('/cad-viewer') ? 1 : 0);
+      const userStore = useUserStoreWithOut();
+      const themeIcon = ref('styleTwo');
+
+      function selectMenu(e: Event, programMenu) {
+        e.stopPropagation();
+        currentParentRoute.value = programMenu;
+      }
+
+      async function handleMenuClick(path: Menu) {
+        if (path.path == currentRoute.value.fullPath) {
+          return;
+        }
+        if (path.path.includes('sw/monitor-fanmain')) {
+          var url = window.open('_blank') as Window;
+          url.location = 'https://swkhmi.shendong.vip:9043/#SW_PW_NORTH';
+          return;
+        }
+        if (path.path.includes('sw/forcFan')) {
+          var url = window.open('_blank') as Window;
+          url.location = 'https://swkhmi.shendong.vip:9043/#SW_CA';
+          return;
+        }
+        if (path.path.includes('outlink/index')) {
+          var url = window.open('_blank') as Window;
+          url.location = path['frameSrc'];
+          return;
+        }
+        go(path.path);
+        isShowMenu.value = 0;
+      }
+
+      function geHome() {
+        if (userStore.getUserInfo.homePath) {
+          go(userStore.getUserInfo.homePath);
+        } else if (currentRoute.value.path.startsWith('/micro-need-air')) {
+          window.history.pushState({}, '', glob.homePath || PageEnum.BASE_HOME);
+        } else {
+          go(glob.homePath || PageEnum.BASE_HOME);
+        }
+      }
+      function closeMenu(e?: Event) {
+        e?.stopPropagation();
+        isShowMenu.value = 0;
+        document.removeEventListener('click', closeMenu);
+      }
+
+      function openMenu(e: Event) {
+        e.stopPropagation();
+        isShowMenu.value = -1;
+        document.addEventListener('click', closeMenu);
+      }
+
+      onMounted(async () => {
+        menuModules.value = await getMenus();
+        const index = menuModules.value.findIndex((menu) => menu.children && menu.children.length > 0);
+        currentParentRoute.value = menuModules.value[index];
+      });
+      return {
+        themeIcon,
+        menuModules,
+        isShowMenu,
+        handleMenuClick,
+        openMenu,
+        closeMenu,
+        selectMenu,
+        go,
+        geHome,
+        currentParentRoute,
+      };
+    },
+  });
+</script>
+
+<style lang="less" scoped>
+  @panel-bg: #091624;
+  @panel-border: #1a7aaa;
+  @accent: #00c8ff;
+  @accent-dim: #1a8aaa;
+  @header-bg: linear-gradient(180deg, #1b4e7a 0%, #0d3055 100%);
+  @header-border: #2a7aaa;
+  @item-bg: #071828;
+  @item-border: #155a80;
+  @item-text: #b0dcf0;
+  @tab-active-bg: #00b8d9;
+  @tab-active-text: #001f30;
+  @white: #ffffff;
+
+  @keyframes menuFadeIn {
+    from {
+      opacity: 0;
+    }
+    to {
+      opacity: 1;
+    }
+  }
+
+  @keyframes panelScaleIn {
+    from {
+      opacity: 0;
+      transform: scale(0.96);
+    }
+    to {
+      opacity: 1;
+      transform: scale(1);
+    }
+  }
+
+  .bottom-side {
+    position: fixed;
+    inset: 0;
+    z-index: 99999;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background: rgba(0, 8, 22, 0.7);
+    backdrop-filter: blur(3px);
+    color: #fff;
+    animation: panelScaleIn 0.3s ease forwards;
+
+    .menu-panel {
+      position: relative;
+      width: 92vw;
+      max-width: 1440px;
+      background: @panel-bg;
+      display: flex;
+      flex-direction: column;
+      height: 70%;
+      max-height: 70%;
+      overflow: hidden;
+      background: url(/@/assets/images//vent/sider/border.png) no-repeat;
+      background-size: 100% 100%;
+    }
+
+    .side-bar {
+      position: absolute;
+      top: 12%;
+      bottom: 12%;
+      width: 3px;
+      background: linear-gradient(180deg, transparent, @accent 25%, @accent 75%, transparent);
+      z-index: 10;
+      box-shadow: 0 0 6px @accent;
+
+      &.side-bar-left {
+        left: -2px;
+      }
+      &.side-bar-right {
+        right: -2px;
+      }
+    }
+
+    .close-btn {
+      position: absolute;
+      top: -18px;
+      right: 0px;
+      width: 22px;
+      height: 22px;
+      cursor: pointer;
+      background: url(/@/assets/images//vent/sider/svg-close.svg) no-repeat;
+      background-size: 100% 100%;
+    }
+
+    .panel-header {
+      display: flex;
+      align-items: center;
+      justify-content: flex-start;
+      padding: 0 24px;
+      flex-wrap: nowrap;
+      overflow-x: auto;
+      overflow-y: hidden;
+      position: relative;
+
+      .col-header-wrapper {
+        display: inline-flex;
+        align-items: center;
+        min-width: 100px;
+        width: 175px;
+        position: relative;
+        justify-content: center;
+      }
+
+      .col-header {
+        min-width: 100px;
+        width: 100%;
+        padding: 24px 12px 12px 12px;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        margin: 0;
+        .col-header-inner {
+          width: 100%;
+          max-width: 135px;
+          height: 60px;
+          background: url(/@/assets/images//vent/sider/buttonTitle.png) no-repeat;
+          background-size: 100% 100%;
+          text-align: center;
+          font-size: 13px;
+          font-weight: 500;
+          letter-spacing: 1px;
+          white-space: nowrap;
+        }
+      }
+
+      .col-arrow {
+        width: 20px;
+        height: 12px;
+        position: absolute;
+        right: -10px;
+        top: 57%;
+        transform: translateY(-50%);
+        pointer-events: none;
+        background: url(/@/assets/images//vent/sider/svg-arrow.png) no-repeat;
+        background-size: 100% 100%;
+      }
+      .col-header-wrapper:last-of-type .col-arrow {
+        display: none;
+      }
+    }
+
+    .panel-body {
+      display: inline-flex;
+      align-items: flex-start;
+      width: 100%;
+      height: 100%;
+      overflow-y: auto;
+      overflow-x: auto;
+      padding: 14px 24px 10px;
+
+      // &::-webkit-scrollbar {
+      //   width: 2px;
+      //   height: 2px;
+      // }
+      // &::-webkit-scrollbar-track {
+      //   background: #071420;
+      // }
+      // &::-webkit-scrollbar-thumb {
+      //   background: @accent-dim;
+      //   border-radius: 2px;
+      // }
+
+      .menu-col {
+        width: 175px;
+        height: 100%;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+      }
+      .menu-col-bg {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        gap: 15px;
+        height: 500px;
+        width: 80%;
+        max-width: 175px;
+        max-height: 600px;
+        background: url(/@/assets/images//vent/sider/menu-bg.png) no-repeat;
+        background-size: 100% 100%;
+        padding: 10px;
+        overflow-y: auto;
+        overflow-x: hidden;
+
+        &::-webkit-scrollbar {
+          width: 6px !important;
+          border: 0;
+        }
+        // &::-webkit-scrollbar-track {
+        //   background: #071420;
+        // }
+        &::-webkit-scrollbar-thumb {
+          // width: 1px !important;
+          background: @accent-dim;
+        }
+      }
+
+      .menu-item {
+        width: 100%;
+        max-width: 128px;
+        min-height: 30px;
+        padding: 5px 6px;
+        text-align: center;
+        background: url(/@/assets/images//vent/sider/buttonCont.png) no-repeat;
+        background-size: 100% 100%;
+        font-size: 12px;
+        color: @item-text;
+        cursor: pointer;
+        transition: all 0.18s;
+        line-height: 1.5;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+
+        &:hover {
+          background: url(/@/assets/images//vent/sider/buttonCont-hover.png) no-repeat;
+          background-size: 100% 100%;
+        }
+      }
+
+      .menu-item-disabled {
+        opacity: 0.45;
+        cursor: default;
+        background: #111a26;
+        border-color: #2a3a4a;
+        color: #6a8a9a;
+
+        &:hover {
+          background: #111a26;
+          border-color: #2a3a4a;
+          color: #6a8a9a;
+          box-shadow: none;
+        }
+      }
+    }
+
+    .panel-footer {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      padding: 10px 20px 0 20px;
+      position: relative;
+      flex-shrink: 0;
+
+      .tab-group {
+        width: 50%;
+        height: 35px;
+        display: flex;
+        align-items: center;
+        gap: 1px;
+        background: url(/@/assets/images/vent/sider/border-bottom.png) no-repeat;
+        background-size: 100% 100%;
+        padding: 0 20px;
+      }
+
+      .tab-btn {
+        width: 120px;
+        height: 25px;
+        color: @white;
+        font-size: 13px;
+        cursor: pointer;
+        background: url(/@/assets/images/vent/sider/buttonBot.png) no-repeat;
+        background-size: 100% 100%;
+        white-space: nowrap;
+        padding: 3px 5px 0 5px;
+        margin-bottom: 10px;
+        text-align: center;
+      }
+
+      .tab-btn-active {
+        width: 120px;
+        height: 35px;
+        background: url(/@/assets/images/vent/sider/buttonBot-active.png) no-repeat;
+        background-size: 105% 100%;
+        font-weight: 500;
+        margin: 0 0 4px -5px;
+      }
+
+      .setting-group {
+        position: absolute;
+        right: 20px;
+        display: flex;
+        align-items: center;
+        gap: 10px;
+        color: @accent-dim;
+
+        .icon-style {
+          cursor: pointer;
+          transition: color 0.18s;
+
+          &:hover {
+            color: @accent;
+          }
+        }
+      }
+    }
+  }
+
+  .bottom-size-show {
+    animation: menuFadeIn 0.3s ease forwards;
+  }
+
+  .menu-show-icon {
+    position: fixed;
+    bottom: 20px;
+    left: 25px;
+    z-index: 1000000;
+
+    .icon {
+      width: 60px;
+      height: 60px;
+      position: relative;
+
+      &:before {
+        content: '';
+        display: block;
+        width: 60px;
+        height: 60px;
+        position: absolute;
+        background: url('/@/assets/images/vent/sider/icon-outer.png') no-repeat;
+        background-size: 100% 100%;
+        background-position: center;
+      }
+
+      &:after {
+        content: '';
+        display: block;
+        width: 36px;
+        height: 35px;
+        position: absolute;
+        top: 18%;
+        right: 18%;
+        background: url('/@/assets/images/vent/sider/icon-inner.svg') no-repeat;
+        background-size: 100% 100%;
+        background-position: center;
+        animation-timing-function: ease-in;
+        animation: fadenum 8s infinite;
+      }
+
+      @keyframes fadenum {
+        0% {
+          transform: rotate(0deg);
+        }
+
+        10% {
+          transform: rotate(360deg);
+        }
+
+        100% {
+          transform: rotate(360deg);
+        }
+      }
+    }
+  }
+</style>

+ 4 - 1
src/layouts/default/sider/index.vue

@@ -14,6 +14,8 @@
   <BottomSider v-else-if="getIsBottomMenu" />
   <Sider v-else /> -->
   <BottomSider v-if="!noSiderLink.includes(routePath)" />
+  <!-- 新版样式 -->
+  <!-- <bottomSider3 v-if="!noSiderLink.includes(routePath)" /> -->
   <!-- <bottomSider2 v-if="!noChatLink.includes(routePath)" /> -->
   <!-- <bottomSider2 /> -->
 </template>
@@ -24,6 +26,7 @@
   import MixSider from './MixSider.vue';
   import BottomSider from './bottomSideder.vue';
   import bottomSider2 from './bottomSider2.vue';
+  import bottomSider3 from './bottomSideder3.vue';
   import { Drawer } from 'ant-design-vue';
 
   import { useAppInject } from '/@/hooks/web/useAppInject';
@@ -33,7 +36,7 @@
   import { noSiderLink, noChatLink } from '../layout.data';
   export default defineComponent({
     name: 'SiderWrapper',
-    components: { Sider, Drawer, MixSider, BottomSider, bottomSider2 },
+    components: { Sider, Drawer, MixSider, BottomSider, bottomSider2, bottomSider3 },
     setup() {
       const route = useRoute();
       const { prefixCls } = useDesign('layout-sider-wrapper');

+ 0 - 1
src/main.ts

@@ -75,7 +75,6 @@ async function bootstrap() {
   initTHREE();
 
   app.component('GlobalConfig', GlobalConfig);
-  debugger;
   app.provide('globalConfig', {
     // 你的全局配置
     simulatedPassword: VENT_PARAM['simulatedPassword'],

+ 11 - 3
src/utils/http/axios/index.ts

@@ -54,11 +54,11 @@ const transform: AxiosTransform = {
       // throw new Error(t('sys.api.apiRequestFailed'));
     }
     //  这里 code,result,message为 后台统一的字段,需要在 types.ts内修改为项目自己的接口返回格式
-    if (data.result) {
+    if (data.result || data['success']) {
       //lxh
       const { code, result, message, success } = data;
       // 这里逻辑可以根据项目进行修改
-      const hasSuccess = data && Reflect.has(data, 'code') && (code === ResultEnum.SUCCESS || code == 200);
+      const hasSuccess = (data && Reflect.has(data, 'code') && (code === ResultEnum.SUCCESS || code == 200 || success == true)) || success == true;
       if (hasSuccess) {
         if (success && message && options.successMessageMode === 'success') {
           //信息成功提示
@@ -118,7 +118,15 @@ const transform: AxiosTransform = {
         const firstCode = config.url?.split('/')[1];
         const currentPort = window.location.port;
         if (currentPort === '8062' && !proxyFixList.includes(firstCode)) {
-          config.url = `/dataCenter${config.url}`;
+          const originalUrl = config.url;
+          // 判断是否以 /safety 开头
+          if (originalUrl.startsWith('/safety')) {
+            // 替换 /safety 为 /ventanaly-collect/collect
+            config.url = `/dataCenter${originalUrl.replace('/safety', '/ventanaly-collect/collect')}`;
+          } else {
+            // 不是 /safety 开头则正常拼接
+            config.url = `/dataCenter${originalUrl}`;
+          }
         } else if (!proxyFixList || !proxyFixList.includes(firstCode)) {
           config.url = `${apiUrl}${config.url}`;
         }

+ 4 - 4
src/views/monitor/quartz/quartz.api.ts

@@ -9,12 +9,13 @@ enum Api {
   pause = '/ventanaly/quartzJob/pause',
   resume = '/ventanaly/quartzJob/resume',
   delete = '/ventanaly/quartzJob/delete',
+  dataCenterDelete = '/ventanaly-collect/collect/quartzJob/delete',
   exportXlsUrl = '/ventanaly/quartzJob/exportXls',
   importExcelUrl = '/ventanaly/quartzJob/importExcel',
   execute = '/ventanaly/quartzJob/execute',
-  deleteBatch = '/ventanaly/quartzJob/deleteBatch',
+  deleteBatch = '/ventanaly-collect/collect/quartzJob/deleteBatch',
   //数据中心
-  dataCenterList = '/dataCenter/ventanaly/quartzJob/list',
+  dataCenterList = '/dataCenter/ventanaly-collect/collect/quartzJob/list',
   // dataCentersave = '/dataCenter/ventanaly/quartzJob/add',
   // dataCenteredit = '/dataCenter/ventanaly/quartzJob/edit',
 }
@@ -61,7 +62,6 @@ export const deleteQuartz = (params, handleSuccess) => {
     handleSuccess();
   });
 };
-
 /**
  * 启动
  * @param params
@@ -138,7 +138,7 @@ export const deleteDataCenterQuartz = (params, handleSuccess) => {
   return defHttp
     .delete(
       {
-        url: Api.delete,
+        url: Api.dataCenterDelete,
         data: params,
       },
       { joinParamsToUrl: true }

+ 1 - 1
src/views/vent/dataCenter/deviceCenter/history/history.api.ts

@@ -2,7 +2,7 @@ import { PaginationProps } from '/@/components/Table';
 import { defHttp } from '/@/utils/http/axios';
 
 enum Api {
-  getHistory = '/dataCenter/safety/ventanalyMonitorData/getRealHistory',
+  getHistory = '/safety/ventanalyMonitorData/getRealHistory',
   getDeviceList = '/monitor/device',
 }
 /**

+ 200 - 174
src/views/vent/deviceManager/comment/DeviceModal.vue

@@ -1,10 +1,19 @@
 <template>
-  <BasicModal v-bind="$attrs" @register="registerModal" :title="title" width="1000px" :showCancelBtn="false"
-    :showOkBtn="false" :footer="null" :destroyOnClose="true" :mask-closable="false" @cancel="closeModalFn">
+  <BasicModal
+    v-bind="$attrs"
+    @register="registerModal"
+    :title="title"
+    width="1000px"
+    :showCancelBtn="false"
+    :showOkBtn="false"
+    :footer="null"
+    :destroyOnClose="true"
+    :mask-closable="false"
+    @cancel="closeModalFn"
+  >
     <a-tabs v-if="props.showTab" v-model:activeKey="activeKey">
       <a-tab-pane key="1" tab="基本信息" force-render>
-        <FormModal :deviceType="deviceType" :record="deviceData"
-          @saveOrUpdate="(values) => emit('saveOrUpdate', values)" />
+        <FormModal :deviceType="deviceType" :record="deviceData" @save-or-update="(values) => emit('saveOrUpdate', values)" />
       </a-tab-pane>
       <!-- <a-tab-pane key="10" v-if="deviceType == 'fanlocal' || deviceType == 'fanmain'" tab="风机曲线" force-render>
         <FanLineModal :record="deviceData" />
@@ -13,16 +22,27 @@
         <DeviceReportInfo v-if="activeKey == '11'" />
       </a-tab-pane>
       <a-tab-pane key="2" tab="点表关联">
-        <PointTable :columns="pointColumns" :pointType="deviceData.strtype" :deviceId="deviceData.id" :valuetype="9"
-          @save="savePointData" @delete="deletePointById" />
+        <PointTable
+          :columns="pointColumns"
+          :pointType="deviceData.strtype"
+          :deviceId="deviceData.id"
+          :valuetype="9"
+          @save="savePointData"
+          @delete="deletePointById"
+        />
       </a-tab-pane>
       <a-tab-pane key="8" tab="虚拟点表配置">
-        <PointTable :columns="simulationColumns" :pointType="deviceData.strtype" :deviceId="deviceData.id"
-          :valuetype="4" @save="savePointData" @delete="deletePointById" />
+        <PointTable
+          :columns="simulationColumns"
+          :pointType="deviceData.strtype"
+          :deviceId="deviceData.id"
+          :valuetype="4"
+          @save="savePointData"
+          @delete="deletePointById"
+        />
       </a-tab-pane>
       <a-tab-pane key="3" tab="关联设备" v-if="deviceType == 'managesys'">
-        <WorkFacePointTable :columns="linkColumns" :deviceId="deviceData.id" @save="savePointData"
-          @delete="deletePointById" />
+        <WorkFacePointTable :columns="linkColumns" :deviceId="deviceData.id" @save="savePointData" @delete="deletePointById" />
       </a-tab-pane>
       <a-tab-pane key="12" tab="关联报表" v-if="deviceType == 'managesys'">
         <WorkFaceReportTable :columns="reportColumns" :device="deviceData" />
@@ -43,8 +63,14 @@
         </template>
       </a-tab-pane>
       <a-tab-pane key="7" tab="摄像头配置">
-        <EditRowTable :columns="cameraColumns" :list="cameraList" :params="{ deviceid: deviceData.id }"
-          @saveOrUpdate="saveCameraData" @deleteById="deleteCameraById" :isAdd="true" />
+        <EditRowTable
+          :columns="cameraColumns"
+          :list="cameraList"
+          :params="{ deviceid: deviceData.id }"
+          @save-or-update="saveCameraData"
+          @delete-by-id="deleteCameraById"
+          :isAdd="true"
+        />
       </a-tab-pane>
       <a-tab-pane key="9" :tab="'预案管理'">
         <AccidentPlanTable :deviceId="deviceData.id" />
@@ -53,8 +79,8 @@
         <LEDPlaylistTable :deviceId="deviceData.id" />
       </a-tab-pane>
 
-      <a-tab-pane key="13" v-if="deviceType == 'gate'" :tab="'故障配置管理'">
-        <FaultConfigTable  :deviceId="deviceData.id"></FaultConfigTable>
+      <a-tab-pane key="13" :tab="'故障配置管理'">
+        <FaultConfigTable :deviceId="deviceData.id" />
       </a-tab-pane>
 
       <!-- <a-tab-pane key="9" tab="预警指标修改">
@@ -63,180 +89,180 @@
         </template>
       </a-tab-pane> -->
     </a-tabs>
-    <FormModal v-else :record="record" @saveOrUpdate="(values) => emit('saveOrUpdate', values)" />
+    <FormModal v-else :record="record" @save-or-update="(values) => emit('saveOrUpdate', values)" />
   </BasicModal>
 </template>
 <script lang="ts" setup>
-import { computed, unref, inject, reactive, ref, watch } from 'vue';
-import { BasicModal, useModalInner } from '/@/components/Modal';
-import EditRowTable from '../../comment/EditRowTable.vue';
-import PointTable from './pointTabel/PointTable.vue';
-import WarningTable from './warningTabel/index.vue';
-import ManagerWarningTable from './warningTabel/index1.vue';
-import ManagerWarningDeviceTable from './warningTabel/index2.vue';
-import BackWindDeviceTable from './warningTabel/index3.vue';
-import AccidentPlanTable from './warningTabel/index4.vue';
-import LEDPlaylistTable from './warningTabel/index5.vue';
-import WorkFacePointTable from './pointTabel/WorkFacePointTable.vue';
-import WorkFaceReportTable from './pointTabel/WorkFaceReportTable.vue';
-import DeviceReportInfo from './DeviceReportInfo.vue';
-import FaultConfigTable from './faultConfigTable/index.vue'
-// import editWarnTable from './editWarnTable/index.vue'
-import FanLineModal from './FanLineModal .vue';
-import FormModal from './FormModal.vue';
-import { cloneDeep } from 'lodash-es';
-import { columns as pointColumns, workFaceColumns, simulationColumns, reportColumns } from './pointTabel/point.data';
-import { saveOrUpdate as pointSaveOrUpdate, deleteById as pointDeleteById } from './pointTabel/point.api';
-import { columns as cameraColumns } from './cameraTabel/camera.data';
+  import { computed, unref, inject, reactive, ref, watch } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import EditRowTable from '../../comment/EditRowTable.vue';
+  import PointTable from './pointTabel/PointTable.vue';
+  import WarningTable from './warningTabel/index.vue';
+  import ManagerWarningTable from './warningTabel/index1.vue';
+  import ManagerWarningDeviceTable from './warningTabel/index2.vue';
+  import BackWindDeviceTable from './warningTabel/index3.vue';
+  import AccidentPlanTable from './warningTabel/index4.vue';
+  import LEDPlaylistTable from './warningTabel/index5.vue';
+  import WorkFacePointTable from './pointTabel/WorkFacePointTable.vue';
+  import WorkFaceReportTable from './pointTabel/WorkFaceReportTable.vue';
+  import DeviceReportInfo from './DeviceReportInfo.vue';
+  import FaultConfigTable from './faultConfigTable/index.vue';
+  // import editWarnTable from './editWarnTable/index.vue'
+  import FanLineModal from './FanLineModal .vue';
+  import FormModal from './FormModal.vue';
+  import { cloneDeep } from 'lodash-es';
+  import { columns as pointColumns, workFaceColumns, simulationColumns, reportColumns } from './pointTabel/point.data';
+  import { saveOrUpdate as pointSaveOrUpdate, deleteById as pointDeleteById } from './pointTabel/point.api';
+  import { columns as cameraColumns } from './cameraTabel/camera.data';
 
-import { list as cameraList, saveOrUpdate as cameraSaveOrUpdate, deleteById as cameraDeleteById } from './cameraTabel/camera.api';
+  import { list as cameraList, saveOrUpdate as cameraSaveOrUpdate, deleteById as cameraDeleteById } from './cameraTabel/camera.api';
 
-const props = defineProps({
-  showTab: { type: Boolean, required: true },
-  // deviceType: { type: String },
-});
-// 声明Emits
-const emit = defineEmits(['saveOrUpdate', 'register', 'closeModal']);
-const isUpdate = inject('isUpdate');
-const deviceData = inject('formData') as any;
-const deviceType = inject('deviceType') as any;
-const record = reactive({});
-const activeKey = ref('1');
-const linkColumns = ref<any[]>([]);
+  const props = defineProps({
+    showTab: { type: Boolean, required: true },
+    // deviceType: { type: String },
+  });
+  // 声明Emits
+  const emit = defineEmits(['saveOrUpdate', 'register', 'closeModal']);
+  const isUpdate = inject('isUpdate');
+  const deviceData = inject('formData') as any;
+  const deviceType = inject('deviceType') as any;
+  const record = reactive({});
+  const activeKey = ref('1');
+  const linkColumns = ref<any[]>([]);
 
-//表单赋值
-const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
-  //重置表单
-  setModalProps({ confirmLoading: false });
-  Object.assign(deviceData, data.record);
-  // 判断是否是关键阻力路线
-});
+  //表单赋值
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
+    //重置表单
+    setModalProps({ confirmLoading: false });
+    Object.assign(deviceData, data.record);
+    // 判断是否是关键阻力路线
+  });
 
-//设置标题
-const title = computed(() => {
-  if (!unref(isUpdate)) {
-    if (deviceData.strname || deviceData.systemname) {
-      return `新增(${deviceData.strname || deviceData.systemname})`;
-    }
-    return `新增`;
-  } else {
-    if (deviceData['strtype'] == 'sys_majorpath') {
-      linkColumns.value = [
-        ...workFaceColumns,
-        ...[
-          {
-            title: '是否在关键通风路线上',
-            width: 100,
-            dataIndex: 'pathflag',
-            edit: true,
-            editComponent: 'Switch',
-            editValueMap: (value) => {
-              return value ? '是' : '否';
+  //设置标题
+  const title = computed(() => {
+    if (!unref(isUpdate)) {
+      if (deviceData.strname || deviceData.systemname) {
+        return `新增(${deviceData.strname || deviceData.systemname})`;
+      }
+      return `新增`;
+    } else {
+      if (deviceData['strtype'] == 'sys_majorpath') {
+        linkColumns.value = [
+          ...workFaceColumns,
+          ...[
+            {
+              title: '是否在关键通风路线上',
+              width: 100,
+              dataIndex: 'pathflag',
+              edit: true,
+              editComponent: 'Switch',
+              editValueMap: (value) => {
+                return value ? '是' : '否';
+              },
             },
-          },
-          {
-            title: '传感器类型',
-            width: 100,
-            dataIndex: 'sensorType',
-            edit: true,
-            editComponent: 'Select',
-            editComponentProps: {
-              options: [
-                {
-                  label: '多参',
-                  value: '1',
-                },
-                {
-                  label: '测风',
-                  value: '2',
-                },
-              ],
+            {
+              title: '传感器类型',
+              width: 100,
+              dataIndex: 'sensorType',
+              edit: true,
+              editComponent: 'Select',
+              editComponentProps: {
+                options: [
+                  {
+                    label: '多参',
+                    value: '1',
+                  },
+                  {
+                    label: '测风',
+                    value: '2',
+                  },
+                ],
+              },
             },
-          },
-          {
-            title: '风向',
-            width: 100,
-            dataIndex: 'winddir',
-            edit: true,
-            editComponent: 'Select',
-            editComponentProps: {
-              options: [
-                {
-                  label: '进风',
-                  value: '1',
-                },
-                {
-                  label: '用风',
-                  value: '2',
-                },
-                {
-                  label: '回风',
-                  value: '3',
-                },
-              ],
+            {
+              title: '风向',
+              width: 100,
+              dataIndex: 'winddir',
+              edit: true,
+              editComponent: 'Select',
+              editComponentProps: {
+                options: [
+                  {
+                    label: '进风',
+                    value: '1',
+                  },
+                  {
+                    label: '用风',
+                    value: '2',
+                  },
+                  {
+                    label: '回风',
+                    value: '3',
+                  },
+                ],
+              },
             },
-          },
-          {
-            title: '是否总风量',
-            width: 100,
-            dataIndex: 'windflag',
-            edit: true,
-            editComponent: 'Switch',
-            editValueMap: (value) => {
-              return value ? '是' : '否';
+            {
+              title: '是否总风量',
+              width: 100,
+              dataIndex: 'windflag',
+              edit: true,
+              editComponent: 'Switch',
+              editValueMap: (value) => {
+                return value ? '是' : '否';
+              },
             },
-          },
-          {
-            title: '路线名称',
-            width: 100,
-            dataIndex: 'des',
-            edit: true,
-            editComponent: 'Input',
-          },
-          {
-            title: ' 阻力值',
-            width: 100,
-            dataIndex: 'testDrag',
-            edit: true,
-            editComponent: 'InputNumber',
-          },
-        ],
-      ];
-    } else {
-      linkColumns.value = [...workFaceColumns];
-    }
-    if (deviceData.strname || deviceData.systemname) {
-      return `编辑(${deviceData.strname || deviceData.systemname})`;
+            {
+              title: '路线名称',
+              width: 100,
+              dataIndex: 'des',
+              edit: true,
+              editComponent: 'Input',
+            },
+            {
+              title: ' 阻力值',
+              width: 100,
+              dataIndex: 'testDrag',
+              edit: true,
+              editComponent: 'InputNumber',
+            },
+          ],
+        ];
+      } else {
+        linkColumns.value = [...workFaceColumns];
+      }
+      if (deviceData.strname || deviceData.systemname) {
+        return `编辑(${deviceData.strname || deviceData.systemname})`;
+      }
+      return `编辑`;
     }
-    return `编辑`;
-  }
-});
+  });
 
-const closeModalFn = () => {
-  activeKey.value = '1';
-  closeModal();
-  emit('closeModal');
-};
+  const closeModalFn = () => {
+    activeKey.value = '1';
+    closeModal();
+    emit('closeModal');
+  };
 
-const savePointData = (data) => {
-  const record = cloneDeep(data.editValueRefs);
-  pointSaveOrUpdate(Object.assign(record, { id: data.id, deviceId: deviceData.id }), data.id);
-};
-const saveCameraData = (data: any, reload: Function) => {
-  cameraSaveOrUpdate(Object.assign({ ...data }, { id: data.id, deviceid: deviceData.id }), data.id);
-};
-const deletePointById = (id, reload) => {
-  pointDeleteById({ id: id }, reload);
-};
-const deleteCameraById = (id, reload) => {
-  cameraDeleteById({ id: id }, reload);
-};
+  const savePointData = (data) => {
+    const record = cloneDeep(data.editValueRefs);
+    pointSaveOrUpdate(Object.assign(record, { id: data.id, deviceId: deviceData.id }), data.id);
+  };
+  const saveCameraData = (data: any, reload: Function) => {
+    cameraSaveOrUpdate(Object.assign({ ...data }, { id: data.id, deviceid: deviceData.id }), data.id);
+  };
+  const deletePointById = (id, reload) => {
+    pointDeleteById({ id: id }, reload);
+  };
+  const deleteCameraById = (id, reload) => {
+    cameraDeleteById({ id: id }, reload);
+  };
 </script>
 <style scoped lang="less">
-::v-deep .suffix {
-  height: 32px;
-  line-height: 32px;
-  margin-left: 5px;
-  color: #fff;
-}
+  ::v-deep .suffix {
+    height: 32px;
+    line-height: 32px;
+    margin-left: 5px;
+    color: #fff;
+  }
 </style>

+ 344 - 0
src/views/vent/gas/components/modal/confirmModal1.vue

@@ -0,0 +1,344 @@
+<template>
+  <Teleport to="body">
+    <Transition name="fade">
+      <div v-if="visible" class="password-dialog-overlay" @click.self="handleOverlayClick">
+        <div class="password-dialog">
+          <!-- 标题栏 -->
+          <div class="dialog-header">
+            <h3>密码校验</h3>
+          </div>
+          <div class="close-btn" @click="handleCancel">
+            <img src="@/assets/images/home-container/configurable/close.svg" alt="" />
+          </div>
+
+          <!-- 内容区域 -->
+          <div class="dialog-content">
+            <!-- 密码输入框 -->
+            <div class="input-wrapper" :class="{ error: showError }">
+              <input
+                ref="passwordInput"
+                v-model="password"
+                :type="showPassword ? 'text' : 'password'"
+                :placeholder="placeholder"
+                maxlength="20"
+                @keyup.enter="handleConfirm"
+                @input="clearError"
+              />
+              <!-- 显示/隐藏密码切换 -->
+              <button type="button" class="toggle-password" @click="togglePassword" tabindex="-1">
+                <span v-if="showPassword"> <img src="@/assets/images/home-container/configurable/showPassword.svg" alt="" /></span>
+                <span v-else> <img src="@/assets/images/home-container/configurable/hidePassword.svg" alt="" /></span>
+              </button>
+            </div>
+
+            <!-- 错误提示 -->
+            <Transition name="slide">
+              <p v-if="errorMessage" class="error-message">{{ errorMessage }}</p>
+            </Transition>
+          </div>
+
+          <!-- 按钮区域 -->
+          <div class="dialog-footer">
+            <button class="btn cancel" @click="handleCancel"><span>取消</span></button>
+            <button class="btn confirm" @click="handleConfirm"><span>确认</span></button>
+          </div>
+        </div>
+      </div>
+    </Transition>
+  </Teleport>
+</template>
+
+<script setup>
+import { ref, nextTick, watch } from 'vue';
+
+// Props
+const props = defineProps({
+  // 弹窗显示状态
+  visible: {
+    type: Boolean,
+    default: false,
+  },
+  // 提示消息
+  message: {
+    type: String,
+    default: '',
+  },
+  // 输入框占位符
+  placeholder: {
+    type: String,
+    default: '请输入密码(必填)',
+  },
+  // 点击遮罩层是否关闭
+  closeOnClickOverlay: {
+    type: Boolean,
+    default: true,
+  },
+});
+
+// Emits
+const emit = defineEmits(['update:visible', 'confirm', 'cancel', 'forgot-password', 'error']);
+
+// 状态
+const password = ref('');
+const showPassword = ref(false);
+const errorMessage = ref('');
+const showError = ref(false);
+const passwordInput = ref(null);
+
+// 监听弹窗显示,自动聚焦
+watch(
+  () => props.visible,
+  async (newVal) => {
+    if (newVal) {
+      await nextTick();
+      passwordInput.value?.focus();
+      // 重置状态
+      password.value = '';
+      errorMessage.value = '';
+      showError.value = false;
+    }
+  }
+);
+
+// 清除错误
+const clearError = () => {
+  showError.value = false;
+  errorMessage.value = '';
+};
+
+// 切换密码显示
+const togglePassword = () => {
+  showPassword.value = !showPassword.value;
+};
+
+// 处理取消
+const handleCancel = () => {
+  emit('update:visible', false);
+  emit('handleCancel');
+  resetState();
+};
+// 处理确认
+const handleConfirm = async () => {
+  if (!password.value) {
+    showError.value = true;
+    errorMessage.value = '请输入密码';
+    return;
+  }
+  showError.value = false;
+  try {
+    // 触发确认事件,返回密码
+    const result = await emit('handleConfirm', password.value);
+    // 如果确认成功(组件外部处理),关闭弹窗
+    if (result !== false) {
+      handleCancel();
+    }
+  } catch (error) {
+    // 密码错误
+    showError.value = true;
+    errorMessage.value = '密码错误';
+
+    // 清空密码输入
+    password.value = '';
+
+    // 重新聚焦
+    await nextTick();
+    passwordInput.value?.focus();
+  } finally {
+  }
+};
+// 点击遮罩层
+const handleOverlayClick = () => {
+  if (props.closeOnClickOverlay) {
+    handleCancel();
+  }
+};
+
+// 重置状态
+const resetState = () => {
+  password.value = '';
+  showPassword.value = false;
+  errorMessage.value = '';
+  showError.value = false;
+};
+
+// 暴露方法给父组件
+defineExpose({
+  resetState,
+  focus: () => passwordInput.value?.focus(),
+});
+</script>
+
+<style scoped>
+@font-face {
+  font-family: 'douyuFont';
+  src: url('../../../../assets/font/douyuFont.otf');
+}
+.password-dialog-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: rgba(0, 0, 0, 0.5);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 1000;
+}
+
+.password-dialog {
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  border-radius: 12px;
+  width: 500px; /* 固定宽度 */
+  height: 270px; /* 固定高度 */
+  background: url('/@/assets/images/home-container/configurable/confirmBj.png') no-repeat;
+  background-size: 100% 100%;
+}
+
+/* 调整 close-btn 的位置 */
+.close-btn {
+  width: 30px;
+  position: absolute;
+  top: -6px;
+  right: -23px;
+  cursor: pointer;
+}
+
+/* 调整标题位置 */
+.dialog-header {
+  padding: 20px 24px;
+  text-align: center;
+  color: #fff;
+  background: url('/src/assets/images/home-container/configurable/confirmTitle.png') no-repeat;
+  background-size: 100% 40%;
+  width: 70%;
+  margin: 30px auto 0; /* 改为 margin: 30px auto 0 */
+}
+
+.dialog-header h3 {
+  margin: 5px;
+  font-size: 18px;
+  color: #fff;
+  margin-top: -35px;
+  font-family: 'douyuFont';
+}
+.close-btn :hover {
+  cursor: pointer;
+}
+
+/* 内容区域 */
+.dialog-content {
+  padding: 24px;
+}
+
+.input-wrapper {
+  position: relative;
+  margin-bottom: 8px;
+}
+
+.input-wrapper input {
+  width: 100%;
+  padding: 12px 40px 12px 12px;
+  background: url('/@/assets/images/home-container/configurable/input-success.png') no-repeat;
+  background-size: 100% 100%;
+  border-radius: 8px;
+  border: none;
+  font-size: 16px;
+  transition: all 0.3s;
+  box-sizing: border-box;
+  color: #fff;
+  height: 53px;
+}
+
+.input-wrapper input:focus {
+  outline: none;
+}
+
+.input-wrapper.error input {
+  background: url('/@/assets/images/home-container/configurable/input-error.png') no-repeat;
+  background-size: 100% 100%;
+}
+
+.toggle-password {
+  position: absolute;
+  right: 8px;
+  top: 50%;
+  transform: translateY(-50%);
+  background: none;
+  border: none;
+  cursor: pointer;
+  padding: 4px;
+  color: #999 !important;
+  font-size: 18px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.toggle-password:hover {
+  color: #666;
+}
+
+.error-message {
+  color: #e90203;
+  font-size: 16px;
+  margin: 4px 0 0 0;
+  min-height: 18px;
+  text-align: center;
+}
+
+.forgot-password {
+  display: inline-block;
+  margin-top: 12px;
+  color: #409eff;
+  font-size: 14px;
+  text-decoration: none;
+}
+
+.forgot-password:hover {
+  text-decoration: underline;
+}
+
+/* 底部按钮 */
+/* 调整底部按钮位置 */
+.dialog-footer {
+  position: absolute;
+  bottom: 30px;
+  left: 50%;
+  transform: translateX(-50%);
+  width: auto;
+  display: flex;
+  gap: 10px;
+}
+
+.btn {
+  flex: 1;
+  cursor: pointer;
+  transition: all 0.3s;
+  border-radius: 5px;
+  height: 40px;
+}
+
+.btn.cancel {
+  width: 100px;
+  background: none;
+  color: #fff;
+  border: 2px solid #1b6e96;
+}
+
+.btn.confirm {
+  width: 100px;
+  margin-left: 10px;
+  background-color: #1da2a9;
+  border: 2px solid #2effee;
+  color: #fff;
+}
+
+.btn.confirm:disabled {
+  background-color: #a0cfff;
+  cursor: not-allowed;
+}
+</style>

+ 190 - 184
src/views/vent/home/configurable/blue/components/ModuleFireNew.vue

@@ -10,7 +10,7 @@
         <!-- 下拉框 -->
         <div class="flex-grow-1" v-if="header.show && header.selector.show">
           <a-select
-            style="width: 350px; font-size: 16px"
+            style="width: 350px; font-size: 14px; height: 28px"
             size="small"
             placeholder="请选择"
             v-model:value="selectedDeviceID"
@@ -31,196 +31,202 @@
 </template>
 
 <script setup lang="ts">
-  import ContentFireNew from './content-FireNew.vue';
-  import { defineProps, defineEmits, computed, watch } from 'vue';
-  import { useInitModule } from '../../hooks/useInit';
-  import { getFormattedText } from '../../hooks/helper';
-  import { openWindow } from '/@/utils';
-  // import { ModuleProps } from '../types';
-
-  const props = defineProps<{
-    /** 配置的详细模块信息 */
-    moduleData: any;
-    /** 配置的详细样式信息 */
-    showStyle: any;
-    /** 该模块配置中的设备标识符 */
-    deviceType: string;
-    /** api返回的数据 */
-    data: any;
-    moduleName: string;
-    visible: boolean;
-  }>();
-  const emit = defineEmits(['close', 'select']);
-
-  const { header } = props.moduleData;
-
-  const { selectedDeviceID, selectedDevice, options, init } = useInitModule(props.deviceType, props.moduleData);
-
-  const style = computed(() => {
-    const size = props.showStyle.size;
-    const position = props.showStyle.position;
-    return size + position;
-  });
-
-  // 根据配置里的定位判断应该使用哪个module组件
-  const daneClass = computed(() => {
-    const position = props.showStyle.position;
-    const size = props.showStyle.size;
-    const [_, width] = size.match(/width:([0-9]+)px/) || [];
-    if (position.includes('bottom') || parseInt(width) > 800) {
-      return 'dane-w';
-    }
-    if (position.includes('left')) {
-      return 'dane-m';
-    }
-    if (position.includes('right')) {
-      return 'dane-m';
-    }
-    // return 'dane-m';
-    return 'dane-m'; // 默认返回顶部模块
-  });
-  //切换时间选项
-  // function onChange(value, dateString) {
-  //   console.log('Selected Time: ', value);
-  //   console.log('Formatted Selected Time: ', dateString);
-  // }
-  // function onOk(val) {
-  //   console.log('onOk: ', val);
-  // }
-
-  //下拉框选项切换
-  function selectHandler(id) {
-    selectedDeviceID.value = id;
-    emit('select', selectedDevice);
-  }
-
-  function redirectTo() {
-    const { to } = props.moduleData;
-    if (!to) return;
-    openWindow(to);
-  }
-
-  watch(
-    () => props.data,
-    (d) => {
-      init(d);
-      selectedDeviceID.value = options.value[0]?.value;
-    },
-    {
-      immediate: true,
-    }
-  );
+import ContentFireNew from './content-FireNew.vue';
+import { defineProps, defineEmits, computed, watch } from 'vue';
+import { useInitModule } from '../../hooks/useInit';
+import { getFormattedText } from '../../hooks/helper';
+import { openWindow } from '/@/utils';
+// import { ModuleProps } from '../types';
+
+const props = defineProps<{
+  /** 配置的详细模块信息 */
+  moduleData: any;
+  /** 配置的详细样式信息 */
+  showStyle: any;
+  /** 该模块配置中的设备标识符 */
+  deviceType: string;
+  /** api返回的数据 */
+  data: any;
+  moduleName: string;
+  visible: boolean;
+}>();
+const emit = defineEmits(['close', 'select']);
+
+const { header } = props.moduleData;
+
+const { selectedDeviceID, selectedDevice, options, init } = useInitModule(props.deviceType, props.moduleData);
+
+const style = computed(() => {
+  const size = props.showStyle.size;
+  const position = props.showStyle.position;
+  return size + position;
+});
+
+// 根据配置里的定位判断应该使用哪个module组件
+const daneClass = computed(() => {
+  const position = props.showStyle.position;
+  const size = props.showStyle.size;
+  const [_, width] = size.match(/width:([0-9]+)px/) || [];
+  if (position.includes('bottom') || parseInt(width) > 800) {
+    return 'dane-w';
+  }
+  if (position.includes('left')) {
+    return 'dane-m';
+  }
+  if (position.includes('right')) {
+    return 'dane-m';
+  }
+  // return 'dane-m';
+  return 'dane-m'; // 默认返回顶部模块
+});
+//切换时间选项
+// function onChange(value, dateString) {
+//   console.log('Selected Time: ', value);
+//   console.log('Formatted Selected Time: ', dateString);
+// }
+// function onOk(val) {
+//   console.log('onOk: ', val);
+// }
+
+//下拉框选项切换
+function selectHandler(id) {
+  selectedDeviceID.value = id;
+  emit('select', selectedDevice);
+}
+
+function redirectTo() {
+  const { to } = props.moduleData;
+  if (!to) return;
+  openWindow(to);
+}
+
+watch(
+  () => props.data,
+  (d) => {
+    init(d);
+    selectedDeviceID.value = options.value[0]?.value;
+  },
+  {
+    immediate: true,
+  }
+);
 </script>
 
 <style scoped lang="less">
-  @import '/@/design/theme.less';
-  @font-face {
-    font-family: 'douyuFont';
-    src: url('/@/assets/font/douyuFont.otf');
-  }
-  @{theme-deepblue} {
-    .dane-bd {
-      --image-Content: url('@/assets/images/fireNew/4-3.png');
-      --image-Select: url('@/assets/images/fireNew/5.png');
-    }
-  }
-
+@import '/@/design/theme.less';
+@font-face {
+  font-family: 'douyuFont';
+  src: url('/@/assets/font/douyuFont.otf');
+}
+@{theme-deepblue} {
   .dane-bd {
-    --image-Select: url('@/assets/images/fireNew/5.png');
     --image-Content: url('@/assets/images/fireNew/4-3.png');
-    position: absolute;
-    width: 100%;
-    height: 100%;
-    z-index: 2;
-
-    .dane-title {
-      width: 94%;
-      height: 40px;
-      text-align: center;
-      padding-top: 9px;
-
-      .common-navL {
-        position: relative;
-        align-items: center;
-        color: #fff;
-        font-size: 13px;
-        font-weight: bold;
-        font-family: 'douyuFont';
-      }
-    }
-
-    .dane-content {
-      height: calc(100% - 38px);
-      box-sizing: border-box;
-      border-top: none;
-      .common-navR {
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        background: var(--image-Select) no-repeat;
-        background-size: 100% 100%;
-        width: 87%;
-        margin-left: 30px;
-      }
-    }
-  }
-  .dane-m {
-    background: var(--image-Content) no-repeat;
-    background-size: 100% 100%;
-  }
-  :deep(.zxm-select-selector) {
-    height: 22px !important;
-    border: none !important;
-    // background-color: rgb(15 64 88) !important;
-    background-color: transparent !important;
-    color: #8087a1 !important;
-  }
-
-  :deep(.zxm-select-selection-placeholder) {
-    color: #8087a1 !important;
-  }
-
-  :deep(.zxm-select-arrow) {
-    color: #8087a1 !important;
-  }
-
-  :deep(.zxm-picker) {
-    border: none !important;
-    background-color: rgb(15 64 88) !important;
-    box-shadow: none;
-    color: #a1dff8 !important;
-  }
-
-  :deep(.zxm-picker-input > input) {
-    color: #a1dff8 !important;
-    text-align: center !important;
-  }
-
-  :deep(.zxm-picker-separator) {
-    color: #a1dff8 !important;
-  }
-
-  :deep(.zxm-picker-active-bar) {
-    display: none !important;
-  }
-
-  :deep(.zxm-picker-suffix) {
-    color: #a1dff8 !important;
-  }
-
-  :deep(.zxm-switch) {
-    min-width: 48px !important;
-  }
-
-  :deep(.zxm-switch-checked) {
-    background-color: rgb(15 64 89) !important;
+    --image-Select: url('@/assets/images/fireNew/5.png');
   }
-
-  :deep(.zxm-switch-handle::before) {
-    background-color: rgb(33 179 247) !important;
+}
+
+.dane-bd {
+  --image-Select: url('@/assets/images/fireNew/5.png');
+  --image-Content: url('@/assets/images/fireNew/4-3.png');
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  z-index: 2;
+
+  .dane-title {
+    width: 94%;
+    height: 40px;
+    text-align: center;
+    padding-top: 9px;
+
+    .common-navL {
+      position: relative;
+      align-items: center;
+      color: #fff;
+      font-size: 13px;
+      font-weight: bold;
+      font-family: 'douyuFont';
+    }
   }
 
-  :deep(.zxm-select-selection-item) {
-    color: #fff !important;
+  .dane-content {
+    height: calc(100% - 38px);
+    box-sizing: border-box;
+    border-top: none;
+    .common-navR {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      background: var(--image-Select) no-repeat;
+      background-size: 100% 100%;
+      width: 87%;
+      margin-left: 30px;
+    }
   }
+}
+.dane-m {
+  background: var(--image-Content) no-repeat;
+  background-size: 100% 100%;
+}
+:deep(.zxm-select-selector) {
+  height: 22px !important;
+  border: none !important;
+  // background-color: rgb(15 64 88) !important;
+  background-color: transparent !important;
+  color: #8087a1 !important;
+}
+
+:deep(.zxm-select-selection-placeholder) {
+  color: #8087a1 !important;
+}
+
+:deep(.zxm-select-arrow) {
+  color: #8087a1 !important;
+}
+
+:deep(.zxm-picker) {
+  border: none !important;
+  background-color: rgb(15 64 88) !important;
+  box-shadow: none;
+  color: #a1dff8 !important;
+}
+
+:deep(.zxm-picker-input > input) {
+  color: #a1dff8 !important;
+  text-align: center !important;
+}
+
+:deep(.zxm-picker-separator) {
+  color: #a1dff8 !important;
+}
+
+:deep(.zxm-picker-active-bar) {
+  display: none !important;
+}
+
+:deep(.zxm-picker-suffix) {
+  color: #a1dff8 !important;
+}
+
+:deep(.zxm-switch) {
+  min-width: 48px !important;
+}
+
+:deep(.zxm-switch-checked) {
+  background-color: rgb(15 64 89) !important;
+}
+
+:deep(.zxm-switch-handle::before) {
+  background-color: rgb(33 179 247) !important;
+}
+
+:deep(.zxm-select-selection-item) {
+  color: #fff !important;
+}
+
+.zxm-select-single.zxm-select-sm:not(.zxm-select-customize-input) .zxm-select-selector::after,
+.zxm-select-single.zxm-select-sm:not(.zxm-select-customize-input) .zxm-select-selector .zxm-select-selection-item,
+.zxm-select-single.zxm-select-sm:not(.zxm-select-customize-input) .zxm-select-selector .zxm-select-selection-placeholder {
+  line-height: 27px !important;
+}
 </style>

+ 221 - 221
src/views/vent/home/configurable/blue/components/NewNavFire.vue

@@ -53,264 +53,264 @@
   </div>
 </template>
 <script lang="ts" setup>
-  import { onMounted, onUnmounted, ref, watch, computed } from 'vue';
-  import { useRouter, useRoute } from 'vue-router';
-  let props = defineProps({
-    Title: {
-      type: String,
-      default: '',
-    },
-    disabled: {
-      type: Boolean,
-      default: false,
-    },
-  });
+import { onMounted, onUnmounted, ref, watch, computed } from 'vue';
+import { useRouter, useRoute } from 'vue-router';
+let props = defineProps({
+  Title: {
+    type: String,
+    default: '',
+  },
+  disabled: {
+    type: Boolean,
+    default: false,
+  },
+});
 
-  let menuList = ref<any[]>([
-    {
-      name: '智能通风',
-      targatUrl: '/micro-vent-3dModal/configurable/ventNew/ventS/home',
-    },
-    {
-      name: '火灾监控',
-      targatUrl: '/micro-vent-3dModal/configurable/fireNew/fireS/home',
-    },
-    {
-      name: '粉尘监控',
-      targatUrl: '/micro-vent-3dModal/configurable/dustNew/dustS/home',
-    },
-    {
-      name: '预警监测',
-      targatUrl: '/warnMonitor/configurable/home',
-    },
-  ]); //一级菜单列表
-  let activeIndex = ref(0); //当前激活menu索引
-  const router = useRouter();
-  const route = useRoute();
-  let isShowMenuItem = ref(false); //是否显示menuItem下拉选项菜单
-  let menuItemActive = ref(0); //menuItem当前激活选项
-  const leftMenus = computed(() => menuList.value.slice(0, 4));
-  const rightMenus = computed(() => menuList.value.slice(4));
-  function menuClick(data) {
-    activeIndex.value = data.index;
-    isShowMenuItem.value = !isShowMenuItem.value;
-    router.push({ path: data.item.targatUrl });
-  }
-  function menuItemClick(index) {
-    menuItemActive.value = index;
-    isShowMenuItem.value = false;
-  }
-  function updateActiveState(path: string) {
-    const currentPath = route.path;
-    menuList.value.forEach((menu, index) => {
-      // 处理有直接链接的菜单项
-      if (menu.targatUrl === currentPath) {
+let menuList = ref<any[]>([
+  {
+    name: '智能通风',
+    targatUrl: '/micro-vent-3dModal/configurable/ventNew/ventS/home',
+  },
+  {
+    name: '火灾监控',
+    targatUrl: '/micro-vent-3dModal/configurable/fireNew/fireS/home',
+  },
+  {
+    name: '粉尘监控',
+    targatUrl: '/micro-vent-3dModal/configurable/dustNew/dustS/home',
+  },
+  {
+    name: '预警监测',
+    targatUrl: '/micro-vent-3dModal/configurable/warnMonitor/home',
+  },
+]); //一级菜单列表
+let activeIndex = ref(0); //当前激活menu索引
+const router = useRouter();
+const route = useRoute();
+let isShowMenuItem = ref(false); //是否显示menuItem下拉选项菜单
+let menuItemActive = ref(0); //menuItem当前激活选项
+const leftMenus = computed(() => menuList.value.slice(0, 4));
+const rightMenus = computed(() => menuList.value.slice(4));
+function menuClick(data) {
+  activeIndex.value = data.index;
+  isShowMenuItem.value = !isShowMenuItem.value;
+  router.push({ path: data.item.targatUrl });
+}
+function menuItemClick(index) {
+  menuItemActive.value = index;
+  isShowMenuItem.value = false;
+}
+function updateActiveState(path: string) {
+  const currentPath = route.path;
+  menuList.value.forEach((menu, index) => {
+    // 处理有直接链接的菜单项
+    if (menu.targatUrl === currentPath) {
+      activeIndex.value = index;
+      isShowMenuItem.value = false;
+      return;
+    }
+    // 处理有子菜单的菜单项
+    if (menu.MenuItemList) {
+      const itemIndex = menu.MenuItemList.findIndex((item) => item.targatUrl === path);
+      if (itemIndex !== -1) {
         activeIndex.value = index;
-        isShowMenuItem.value = false;
-        return;
-      }
-      // 处理有子菜单的菜单项
-      if (menu.MenuItemList) {
-        const itemIndex = menu.MenuItemList.findIndex((item) => item.targatUrl === path);
-        if (itemIndex !== -1) {
-          activeIndex.value = index;
-          menuItemActive.value = itemIndex;
-          isShowMenuItem.value = true;
-        }
+        menuItemActive.value = itemIndex;
+        isShowMenuItem.value = true;
       }
-    });
-  }
-  watch(
-    () => route.path,
-    (newPath) => {
-      updateActiveState(newPath);
-    },
-    {
-      immediate: true,
     }
-  );
-  onMounted(() => {
-    updateActiveState(route.path);
   });
+}
+watch(
+  () => route.path,
+  (newPath) => {
+    updateActiveState(newPath);
+  },
+  {
+    immediate: true,
+  }
+);
+onMounted(() => {
+  updateActiveState(route.path);
+});
 </script>
 <style lang="less" scoped>
-  @import '/@/design/theme.less';
+@import '/@/design/theme.less';
+
+@font-face {
+  font-family: 'douyuFont';
+  src: url('/@/assets/font/douyuFont.otf');
+}
 
-  @font-face {
+.New-nav {
+  position: relative;
+  width: 100%;
+  height: 100%;
+
+  .main-title {
+    width: 518px;
+    height: 100%;
+    display: flex;
+    align-items: center;
     font-family: 'douyuFont';
-    src: url('/@/assets/font/douyuFont.otf');
+    font-size: 25px;
+    position: absolute;
+    left: 50%;
+    transform: translateX(-50%);
+    width: auto;
+    padding: 0;
+    color: #fff;
   }
 
-  .New-nav {
-    position: relative;
-    width: 100%;
+  .nav-menu {
+    position: absolute;
+    top: 0;
+    left: 675px;
     height: 100%;
+    display: flex;
+    position: static; // 移除绝对定位
+    display: flex;
+    width: auto;
+    .nav-menu-left {
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      float: left;
+      .nav-menu-active {
+        position: relative;
+        cursor: pointer;
+        width: 100px;
+        height: 35px;
+        margin-top: 10px;
+        margin-right: 40px;
+        line-height: 31px;
+        text-align: center;
+        font-size: 16px;
+        background: url(/src/assets/images/fireNew/2-1.png) no-repeat;
+        background-size: 100% 100%;
+      }
 
-    .main-title {
-      width: 518px;
-      height: 100%;
+      .nav-menu-unactive {
+        position: relative;
+        cursor: pointer;
+        width: 100px;
+        height: 35px;
+        margin-top: 10px;
+        margin-right: 40px;
+        line-height: 31px;
+        text-align: center;
+        font-size: 16px;
+        background: url(/src/assets/images/fireNew/2-2.png) no-repeat;
+        background-size: 100% 100%;
+      }
+    }
+    .nav-menu-right {
       display: flex;
+      flex-direction: row;
       align-items: center;
-      font-family: 'douyuFont';
-      font-size: 25px;
-      position: absolute;
-      left: 50%;
-      transform: translateX(-50%);
-      width: auto;
-      padding: 0;
-      color: #fff;
+      float: left;
+      margin-left: 42%;
+      .nav-menu-active {
+        position: relative;
+        cursor: pointer;
+        width: 100px;
+        height: 40px;
+        margin-top: 10px;
+        margin-right: 40px;
+        line-height: 35px;
+        text-align: center;
+        font-size: 16px;
+        background: url(/src/assets/images/fireNew/2-3.png) no-repeat;
+        background-size: 100% 100%;
+      }
+
+      .nav-menu-unactive {
+        position: relative;
+        cursor: pointer;
+        width: 100px;
+        height: 40px;
+        margin-top: 10px;
+        margin-right: 40px;
+        line-height: 35px;
+        text-align: center;
+        font-size: 16px;
+        background: url(/src/assets/images/fireNew/2-4.png) no-repeat;
+        background-size: 100% 100%;
+      }
     }
 
-    .nav-menu {
+    .nav-menu-item {
       position: absolute;
-      top: 0;
-      left: 675px;
-      height: 100%;
-      display: flex;
-      position: static; // 移除绝对定位
+      top: 23px;
+      width: 130px;
       display: flex;
-      width: auto;
-      .nav-menu-left {
-        display: flex;
-        flex-direction: row;
-        align-items: center;
-        float: left;
-        .nav-menu-active {
-          position: relative;
-          cursor: pointer;
-          width: 100px;
-          height: 40px;
-          margin-top: 10px;
-          margin-right: 40px;
-          line-height: 35px;
-          text-align: center;
-          font-size: 16px;
-          background: url(/src/assets/images/fireNew/2-1.png) no-repeat;
-          background-size: 100% 100%;
-        }
+      flex-direction: column;
+      align-items: center;
+      box-sizing: border-box;
 
-        .nav-menu-unactive {
-          position: relative;
-          cursor: pointer;
-          width: 100px;
-          height: 40px;
-          margin-top: 10px;
-          margin-right: 40px;
-          line-height: 35px;
-          text-align: center;
-          font-size: 16px;
-          background: url(/src/assets/images/fireNew/2-2.png) no-repeat;
+      .nav-menu-content {
+        width: 100%;
+        height: 100%;
+        overflow-y: auto;
+        margin-top: 25%;
+        .nav-menu-List {
+          background: url('@/assets/images/vent/homeNew/menuList.png') no-repeat;
           background-size: 100% 100%;
         }
-      }
-      .nav-menu-right {
-        display: flex;
-        flex-direction: row;
-        align-items: center;
-        float: left;
-        margin-left: 42%;
-        .nav-menu-active {
-          position: relative;
-          cursor: pointer;
-          width: 100px;
-          height: 40px;
-          margin-top: 10px;
-          margin-right: 40px;
-          line-height: 35px;
-          text-align: center;
-          font-size: 16px;
-          background: url(/src/assets/images/fireNew/2-3.png) no-repeat;
+        .menu-item-active {
+          color: #ddd;
+          z-index: 999;
+          width: 100%;
+          height: 36px;
+          line-height: 36px;
+          font-size: 14px;
+          background: url('@/assets/images/fireNew/2-2.png') no-repeat;
           background-size: 100% 100%;
         }
 
-        .nav-menu-unactive {
-          position: relative;
-          cursor: pointer;
-          width: 100px;
+        .menu-item-unactive {
+          color: #ddd;
+          width: 100%;
           height: 40px;
-          margin-top: 10px;
-          margin-right: 40px;
-          line-height: 35px;
-          text-align: center;
-          font-size: 16px;
-          background: url(/src/assets/images/fireNew/2-4.png) no-repeat;
-          background-size: 100% 100%;
+          line-height: 40px;
+          font-size: 14px;
         }
       }
+    }
 
-      .nav-menu-item {
-        position: absolute;
-        top: 23px;
-        width: 130px;
-        display: flex;
-        flex-direction: column;
-        align-items: center;
-        box-sizing: border-box;
-
-        .nav-menu-content {
-          width: 100%;
-          height: 100%;
-          overflow-y: auto;
-          margin-top: 25%;
-          .nav-menu-List {
-            background: url('@/assets/images/vent/homeNew/menuList.png') no-repeat;
-            background-size: 100% 100%;
-          }
-          .menu-item-active {
-            color: #ddd;
-            z-index: 999;
-            width: 100%;
-            height: 36px;
-            line-height: 36px;
-            font-size: 14px;
-            background: url('@/assets/images/fireNew/2-2.png') no-repeat;
-            background-size: 100% 100%;
-          }
-
-          .menu-item-unactive {
-            color: #ddd;
-            width: 100%;
-            height: 40px;
-            line-height: 40px;
-            font-size: 14px;
-          }
-        }
+    @keyframes fadeIn {
+      from {
+        opacity: 0;
       }
 
-      @keyframes fadeIn {
-        from {
-          opacity: 0;
-        }
-
-        to {
-          opacity: 1;
-        }
+      to {
+        opacity: 1;
       }
+    }
 
-      /* 定义淡出动画 */
-      @keyframes fadeOut {
-        from {
-          opacity: 1;
-        }
+    /* 定义淡出动画 */
+    @keyframes fadeOut {
+      from {
+        opacity: 1;
+      }
 
-        to {
-          opacity: 0;
-        }
+      to {
+        opacity: 0;
       }
     }
+  }
 
-    .userInfo {
-      width: 120px;
-      float: right;
-      background: url(/src/assets/images/vent/homeNew/user.png) no-repeat;
-      background-size: 100% 100%;
-      position: absolute;
-      top: 14px;
-      right: 0;
-      .userName {
-        margin-left: 40px;
-        font-size: 20px;
-      }
+  .userInfo {
+    width: 120px;
+    float: right;
+    background: url(/src/assets/images/vent/homeNew/user.png) no-repeat;
+    background-size: 100% 100%;
+    position: absolute;
+    top: 14px;
+    right: 0;
+    .userName {
+      margin-left: 40px;
+      font-size: 20px;
     }
   }
+}
 </style>

+ 20 - 20
src/views/vent/home/configurable/blue/components/center-area-warn.vue

@@ -4,8 +4,8 @@
       <div class="risk-score">
         <div class="vent-icon"></div>
         <div class="item-content">
-          <div class="ite-label">瓦斯监测预警</div>
-          <div class="ite-val">低风险</div>
+          <div class="ite-label" style="margin-left: 50px">瓦斯监测预警</div>
+          <div class="ite-val" style="margin-left: 50px">低风险</div>
         </div>
         <div class="item-content">
           <div class="ite-label">矿井瓦斯等级鉴定</div>
@@ -96,10 +96,10 @@ watch(
 
   .container {
     position: relative;
-    width: 60%;
-    height: 70%;
-    margin-top: 16%;
-    margin-left: 20%;
+    width: 51%;
+    height: 65%;
+    margin-top: 20%;
+    margin-left: 23%;
     background: url(/src/assets/images/home-warn/6-1.png) no-repeat center;
     background-size: 100% 100%;
   }
@@ -119,15 +119,15 @@ watch(
     background: url(/src/assets/images/home-warn/6-2.png) no-repeat;
     background-size: 100% 100%;
     .item-content {
-      margin-left: 30px;
+      margin-left: 20px;
     }
   }
 
   .dust-warn {
     position: absolute;
     right: -26%;
-    top: 197px;
-    width: 217px;
+    top: 177px;
+    width: 183px;
     height: 64px;
     color: #fff;
     background: url(/src/assets/images/home-warn/6-3.png) no-repeat;
@@ -137,7 +137,7 @@ watch(
     position: absolute;
     right: -19%;
     bottom: 70px;
-    width: 217px;
+    width: 183px;
     height: 64px;
     color: #fff;
     background: url(/src/assets/images/home-warn/6-7.png) no-repeat;
@@ -145,9 +145,9 @@ watch(
   }
   .db-warn {
     position: absolute;
-    left: -20%;
+    left: -21%;
     bottom: 70px;
-    width: 217px;
+    width: 183px;
     height: 64px;
     color: #fff;
     background: url(/src/assets/images/home-warn/6-8.png) no-repeat;
@@ -155,9 +155,9 @@ watch(
   }
   .vent-warn {
     position: absolute;
-    left: -20%;
+    left: -22%;
     top: 64px;
-    width: 207px;
+    width: 183px;
     height: 64px;
     color: #fff;
     background: url(/src/assets/images/home-warn/6-5.png) no-repeat;
@@ -165,9 +165,9 @@ watch(
   }
   .device-warn {
     position: absolute;
-    right: -21%;
+    right: -22%;
     top: 64px;
-    width: 217px;
+    width: 183px;
     height: 64px;
     color: #fff;
     background: url(/src/assets/images/home-warn/6-6.png) no-repeat;
@@ -175,10 +175,10 @@ watch(
   }
   .fire-warn {
     position: absolute;
-    left: -7%;
-    top: 197px;
+    left: -9%;
+    top: 177px;
     transform: translate(-50%, 0);
-    width: 217px;
+    width: 183px;
     height: 64px;
     color: #fff;
     background: url(/src/assets/images/home-warn/6-4.png) no-repeat;
@@ -205,7 +205,7 @@ watch(
     position: absolute;
     display: flex;
     flex-direction: column;
-    margin-left: 100px;
+    margin-left: 88px;
     line-height: 33px;
   }
   .ite-val {

+ 1 - 2
src/views/vent/home/configurable/blue/components/detail/MiniBoard-Warn.vue

@@ -332,8 +332,7 @@ defineEmits(['click']);
   display: flex;
   flex-direction: column;
   align-items: center;
-  width: 121px;
-  height: 100%;
+  height: 80%;
   background: url('@/assets/images/home-warn/3-1.png') no-repeat;
   background-size: 100% 100%;
 }

+ 1 - 1
src/views/vent/home/configurable/blue/components/moduleBottom-warn.vue

@@ -39,7 +39,7 @@ function clickHandler() {
   z-index: 999;
   background: url('@/assets/images/fireNew/4-2.png') no-repeat;
   background-size: 100% 100%;
-  padding: 15px 15px 0px 15px;
+  padding: 13px 15px 0px 15px;
 }
 
 .module-content__title__expand {

+ 79 - 79
src/views/vent/home/configurable/blue/components/moduleLeft-warn.vue

@@ -10,82 +10,82 @@
   </div>
 </template>
 <script lang="ts" setup>
-  const props = defineProps<{
-    deviceType: string;
-    title: string;
-    visible: boolean;
-  }>();
-  const emit = defineEmits(['close', 'click']);
-  const deviceType = props.deviceType;
+const props = defineProps<{
+  deviceType: string;
+  title: string;
+  visible: boolean;
+}>();
+const emit = defineEmits(['close', 'click']);
+const deviceType = props.deviceType;
 
-  function closeModel() {
-    emit('close');
-  }
-  function clickHandler() {
-    emit('click');
-  }
+function closeModel() {
+  emit('close');
+}
+function clickHandler() {
+  emit('click');
+}
 </script>
 <style lang="less" scoped>
-  @import '/@/design/theme.less';
+@import '/@/design/theme.less';
 
-  @{theme-deepblue} {
-    .module-content {
-      --image-model_original_title_bg: url('@/assets/images/themify/deepblue/home-container/configurable/model_original_title_bg.png');
-    }
-  }
-  .module-content1 {
-    --bg-height: 28px;
-    color: #fff;
-    box-sizing: border-box;
-    position: absolute;
-    width: 100%;
-    height: 100%;
-    z-index: 999;
-    padding: 15px 15px 0px 15px;
-  }
+@{theme-deepblue} {
   .module-content {
-    --bg-height: 28px;
-    color: #fff;
-    box-sizing: border-box;
-    position: absolute;
-    width: 100%;
-    height: 100%;
-    z-index: 999;
-    background: url('@/assets/images/home-warn/2-1.png') no-repeat;
-    background-size: 100% 100%;
-    padding: 15px 15px 0px 15px;
+    --image-model_original_title_bg: url('@/assets/images/themify/deepblue/home-container/configurable/model_original_title_bg.png');
   }
+}
+.module-content1 {
+  --bg-height: 28px;
+  color: #fff;
+  box-sizing: border-box;
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  z-index: 999;
+  padding: 15px 15px 0px 15px;
+}
+.module-content {
+  --bg-height: 28px;
+  color: #fff;
+  box-sizing: border-box;
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  z-index: 999;
+  background: url('@/assets/images/home-warn/2-1.png') no-repeat;
+  background-size: 100% 100%;
+  padding: 15px 15px 0px 15px;
+}
 
-  .module-content__title__expand {
-    width: 100%;
-    text-align: center;
-    font-weight: bold;
-    font-size: 16px;
-    line-height: 0px;
-  }
+.module-content__title__expand {
+  width: 100%;
+  text-align: center;
+  font-weight: bold;
+  font-size: 16px;
+  line-height: 0px;
+}
 
-  // .module-content__title {
-  //   width: 50%;
-  //   height: var(--bg-height);
-  //   background: url('@/assets/images/home-container/configurable/model_left_title_bg.png') no-repeat;
-  //   background-size: 100% 100%;
-  //   position: relative;
-  //   text-align: right;
-  //   padding: 4px 10% 0 0;
-  // }
+// .module-content__title {
+//   width: 50%;
+//   height: var(--bg-height);
+//   background: url('@/assets/images/home-container/configurable/model_left_title_bg.png') no-repeat;
+//   background-size: 100% 100%;
+//   position: relative;
+//   text-align: right;
+//   padding: 4px 10% 0 0;
+// }
 
-  // 固定在父容器右上角的按钮图标
-  // .action-btn {
-  //   width: 18px;
-  //   height: 18px;
-  //   background: url('@/assets/images/home-container/configurable/expand.svg') no-repeat center;
-  //   position: absolute;
-  //   right: 0;
-  //   top: 0;
-  // }
-  // .close-btn {
-  //   transform: rotate(-90deg);
-  // }
+// 固定在父容器右上角的按钮图标
+// .action-btn {
+//   width: 18px;
+//   height: 18px;
+//   background: url('@/assets/images/home-container/configurable/expand.svg') no-repeat center;
+//   position: absolute;
+//   right: 0;
+//   top: 0;
+// }
+// .close-btn {
+//   transform: rotate(-90deg);
+// }
 
   .module-slot {
     height: calc(100% - 46px);
@@ -94,17 +94,17 @@
     margin-top: 26px;
   }
 
-  // Transition动画相关
-  .v-enter-active,
-  .v-leave-active {
-    transition: all 0.3s ease;
-  }
+// Transition动画相关
+.v-enter-active,
+.v-leave-active {
+  transition: all 0.3s ease;
+}
 
-  .v-enter-from,
-  .v-leave-to {
-    // opacity: 1;
-    transform: translateX(-100%);
-    // transform: scaleY(0);
-    // transform-origin: center top;
-  }
+.v-enter-from,
+.v-leave-to {
+  // opacity: 1;
+  transform: translateX(-100%);
+  // transform: scaleY(0);
+  // transform-origin: center top;
+}
 </style>

+ 19 - 19
src/views/vent/home/configurable/blue/components/originalNew/moduleTopFire.vue

@@ -52,18 +52,18 @@ const emit = defineEmits(['close', 'click']);
   display: flex;
   flex-direction: column;
   .left-img {
-    left: 4%;
+    left: 5%;
     top: 2%;
-    width: 75px;
-    height: 75px;
+    width: 55px;
+    height: 55px;
     cursor: pointer;
     position: absolute;
     background: url('@/assets/images/fireNew/3-4.png') no-repeat;
     background-size: 100% 100%;
   }
   .left-title {
-    left: 3%;
-    top: 85px;
+    left: 2%;
+    top: 65px;
     font-size: 13px;
     font-family: 'douyuFont';
     font-weight: bold;
@@ -78,18 +78,18 @@ const emit = defineEmits(['close', 'click']);
   display: flex;
   flex-direction: column;
   .right-img {
-    right: 4%;
+    right: 5%;
     top: 2%;
-    width: 75px;
-    height: 75px;
+    width: 55px;
+    height: 55px;
     cursor: pointer;
     position: absolute;
     background: url('@/assets/images/fireNew/3-5.png') no-repeat;
     background-size: 100% 100%;
   }
   .right-title {
-    right: 3%;
-    top: 85px;
+    right: 2%;
+    top: 65px;
     font-size: 13px;
     font-family: 'douyuFont';
     font-weight: bold;
@@ -109,8 +109,8 @@ const emit = defineEmits(['close', 'click']);
   align-items: center; /* 垂直居中对齐 */
 }
 .img-left {
-  width: 50px; /* 图片尺寸 */
-  height: 50px;
+  width: 30px; /* 图片尺寸 */
+  height: 30px;
   margin-right: 8px; /* 图片文字间距 */
   background: url('@/assets/images/fireNew/3-3.png') no-repeat center; /* 替换为实际图片路径 */
   background-size: contain;
@@ -122,8 +122,8 @@ const emit = defineEmits(['close', 'click']);
   align-items: center;
 }
 .img-right {
-  width: 50px;
-  height: 50px;
+  width: 30px;
+  height: 30px;
   margin-left: -10px;
   background: url('@/assets/images/fireNew/3-2.png') no-repeat center; /* 替换为实际图片路径 */
   background-size: contain;
@@ -135,20 +135,20 @@ const emit = defineEmits(['close', 'click']);
   display: flex;
   flex-direction: column;
   align-items: center;
-  width: 330px;
+  width: 250px;
 }
 /* 文字样式 */
 .title-left {
   width: 100px;
-  font-size: 20px;
+  font-size: 16px;
 }
 .title-right {
   width: 100px;
-  font-size: 20px;
+  font-size: 16px;
 }
 .center-top {
-  margin-bottom: 14px;
-  font-size: 20px;
+  margin-bottom: 10px;
+  font-size: 16px;
   margin-left: -18px;
 }
 .center-bottom {

+ 9 - 7
src/views/vent/home/configurable/blue/components/yj_gasWarn.vue

@@ -48,31 +48,33 @@ let props = defineProps({
   --image-model_fire-container_A: url('@/assets/images/home-warn/5-1.png');
   --image-model_fire-container_B: url('@/assets/images/home-warn/5-2.png');
   height: 100%;
-  padding: 10px 25px 0px 25px;
+  padding-left: 10px;
+  padding-top: 10px;
   box-sizing: border-box;
 
   .fire-container_A {
     position: relative;
     width: 100%;
-    height: 50%;
+    height: 60%;
+    top: 5%;
   }
 
   .fire-container_B {
     position: relative;
     width: 100%;
-    height: 50%;
+    height: 60%;
   }
   .title-box-label_icon_A {
     width: 20px;
     height: 20px;
-    margin-left: 16px;
+    margin-left: 15px;
     background: url('@/assets/images/home-warn/5-4.svg') no-repeat;
     background-size: 100% 100%;
   }
   .title-box-label_icon_B {
     width: 20px;
     height: 20px;
-    margin-left: 16px;
+    margin-left: 15px;
     background: url('@/assets/images/home-warn/5-5.svg') no-repeat;
     background-size: 100% 100%;
   }
@@ -118,12 +120,12 @@ let props = defineProps({
   }
 
   .title-box_A {
-    width: 100%;
+    width: 88%;
     height: 40px;
   }
 
   .title-box_B {
-    width: 100%;
+    width: 88%;
     height: 40px;
   }
 

+ 40 - 14
src/views/vent/home/configurable/blue/components/yj_ventWarn.vue

@@ -3,6 +3,7 @@
     <div v-for="(item, index) in ventData" :key="index" class="vent_warn_item">
       <div class="risk-label">{{ item.name }}</div>
       <div class="risk_progress_container">
+        <div class="progress_marker_icon" :style="getMarkerIconStyle(item)"></div>
         <div class="risk_progress" :style="getProgressStyle(item)"></div>
       </div>
       <div class="risk-value">{{ item.value }}</div>
@@ -11,16 +12,15 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, watch, onMounted, nextTick } from 'vue';
+import { watch } from 'vue';
 
-let props = defineProps({
+const props = defineProps({
   ventData: {
     type: Array,
-    default: () => {
-      return [];
-    },
+    default: () => [],
   },
 });
+
 const colorMap = {
   报警: ['#62465d', '#f50405'],
   重大风险: ['#60685e', '#f37606'],
@@ -31,17 +31,31 @@ const colorMap = {
 
 const getProgressStyle = (item) => {
   const maxValue = 10;
-  const [startColor, endColor] = colorMap[item.name];
+  const progress = item.value > 0 ? (item.value / maxValue) * 100 : 0;
+  const [startColor, endColor] = colorMap[item.name] || ['#ccc', '#ccc'];
 
   return {
-    width: `${item.value > 0 ? (item.value / maxValue) * 100 : 0}%`,
+    width: `${progress}%`,
     background: `linear-gradient(90deg, ${startColor} 0%, ${endColor} 100%)`,
   };
 };
+
+const getMarkerIconStyle = (item) => {
+  const maxValue = 10;
+  const rawProgress = item.value > 0 ? (item.value / maxValue) * 100 : 0;
+  const finalProgress = Math.max(rawProgress);
+
+  return {
+    left: `${finalProgress}%`,
+    top: '-12px',
+    transform: 'translateX(-50%)',
+  };
+};
+
 watch(
   () => props.ventData,
-  (newV, oldV) => {
-    console.log(props.ventData, '99900');
+  (newV) => {
+    console.log('ventData updated:', newV);
   },
   { immediate: true }
 );
@@ -50,45 +64,57 @@ watch(
 <style lang="less" scoped>
 .vent_warn {
   position: relative;
-  padding-top: 10px;
   height: 100%;
   .vent_warn_item {
     margin-left: 5px;
-    margin-top: 10px;
+    margin-top: 16px;
     padding: 10px 0 10px 20px;
     width: 94%;
-    height: 39px;
+    height: 30px;
     background: url('@/assets/images/home-warn/3-2.png') no-repeat;
     background-size: 100% 100%;
     display: flex;
     justify-content: space-between;
     align-items: center;
+
     .risk-label {
       flex: 1;
       font-size: 14px;
       color: #ffffff;
     }
+
     .risk-value {
       flex: 1;
       margin-right: 10px;
       color: #ffffff;
+      text-align: right;
     }
 
     .risk_progress_container {
+      position: relative;
       margin-left: 20px;
       flex: 3;
       height: 8px;
       margin: 0 10px;
       background: rgba(255, 255, 255, 0.1);
       border-radius: 4px;
-      overflow: hidden;
-      min-width: 0; /* 关键:允许容器收缩 */
+      min-width: 80px;
     }
     .risk_progress {
       height: 100%;
       border-radius: 4px;
       transition: width 0.6s cubic-bezier(0.22, 0.61, 0.36, 1);
     }
+    .progress_marker_icon {
+      position: absolute;
+      display: block;
+      width: 15px;
+      height: 15px;
+      background: url('@/assets/images/home-warn/3-3.png') no-repeat center;
+      background-size: 100% 100%;
+      z-index: 10;
+      pointer-events: none;
+    }
   }
 }
 </style>

+ 152 - 152
src/views/vent/home/configurable/blue/fireNew.vue

@@ -51,173 +51,173 @@
   </div>
 </template>
 <script lang="ts" setup>
-  import { computed, onMounted, onUnmounted } from 'vue';
-  // import { CaretDownOutlined } from '@ant-design/icons-vue';
-  import NewNav from './components/NewNavFire.vue';
-  import { useInitConfigs, useInitPage } from '../hooks/useInit';
-  import ModuleNew from './components/ModuleNew.vue';
-  import ModuleFireNew from './components/ModuleFireNew.vue';
-  import ModuleFireNewDual from './components/ModuleFireNewDual.vue';
-  import VentModal from '/@/components/vent/micro/ventModal.vue';
-  // import { BDFireMock } from './mock';
-  import { getDisHome } from '../configurable.api';
-  // import { EyeFilled } from '@ant-design/icons-vue';
-  import { testConfigNewFire } from '../configurable.data.New';
-  const cfgs = computed(() =>
-    configs.value.filter((_, index) => {
-      return index !== 4 && index !== 3 && index !== 5;
-    })
-  );
-  const cfgA = computed<any>(() =>
-    configs.value.find((_, index) => {
-      return index === 3;
-    })
-  );
-  const cfgB = computed<any>(() =>
-    configs.value.find((_, index) => {
-      return index === 4;
-    })
-  );
-  const cfgTop = computed<any>(() =>
-    configs.value.find((_, index) => {
-      return index === 5;
-    })
-  );
-  const { configs, devicesTypes, fetchConfigs } = useInitConfigs();
-  const { mainTitle, data, updateData /** updateEnhancedConfigs */ } = useInitPage('火灾预警系统');
-  let interval: number | undefined;
-  onMounted(() => {
-    // fetchConfigs('New_fire').then(() => {
-    //   configs.value = testConfigNewFire;
-    //   Promise.resolve(BDFireMock).then(updateData);
-    // });
-    // setInterval(() => {
-    //   Promise.resolve(BDFireMock).then(updateData);
-    // }, 2000);
-    fetchConfigs('New_fire').then(() => {
-      configs.value = testConfigNewFire;
-      getDisHome({
-        dataList: devicesTypes.value.concat('fireAllMineWarn').join(','),
-      }).then(updateData);
-    });
-    setInterval(() => {
-      getDisHome({
-        dataList: devicesTypes.value.concat('fireAllMineWarn').join(','),
-      }).then(updateData);
-    }, 2000);
+import { computed, onMounted, onUnmounted } from 'vue';
+// import { CaretDownOutlined } from '@ant-design/icons-vue';
+import NewNav from './components/NewNavFire.vue';
+import { useInitConfigs, useInitPage } from '../hooks/useInit';
+import ModuleNew from './components/ModuleNew.vue';
+import ModuleFireNew from './components/ModuleFireNew.vue';
+import ModuleFireNewDual from './components/ModuleFireNewDual.vue';
+import VentModal from '/@/components/vent/micro/ventModal.vue';
+// import { BDFireMock } from './mock';
+import { getDisHome } from '../configurable.api';
+// import { EyeFilled } from '@ant-design/icons-vue';
+import { testConfigNewFire } from '../configurable.data.New';
+const cfgs = computed(() =>
+  configs.value.filter((_, index) => {
+    return index !== 4 && index !== 3 && index !== 5;
+  })
+);
+const cfgA = computed<any>(() =>
+  configs.value.find((_, index) => {
+    return index === 3;
+  })
+);
+const cfgB = computed<any>(() =>
+  configs.value.find((_, index) => {
+    return index === 4;
+  })
+);
+const cfgTop = computed<any>(() =>
+  configs.value.find((_, index) => {
+    return index === 5;
+  })
+);
+const { configs, devicesTypes, fetchConfigs } = useInitConfigs();
+const { mainTitle, data, updateData /** updateEnhancedConfigs */ } = useInitPage('火灾预警系统');
+let interval: number | undefined;
+onMounted(() => {
+  // fetchConfigs('New_fire').then(() => {
+  //   configs.value = testConfigNewFire;
+  //   Promise.resolve(BDFireMock).then(updateData);
+  // });
+  // setInterval(() => {
+  //   Promise.resolve(BDFireMock).then(updateData);
+  // }, 2000);
+  fetchConfigs('New_fire').then(() => {
+    configs.value = testConfigNewFire;
+    getDisHome({
+      dataList: devicesTypes.value.concat('fireAllMineWarn').join(','),
+    }).then(updateData);
   });
+  setInterval(() => {
+    getDisHome({
+      dataList: devicesTypes.value.concat('fireAllMineWarn').join(','),
+    }).then(updateData);
+  }, 2000);
+});
 
-  onUnmounted(() => {
-    clearInterval(interval);
-  });
+onUnmounted(() => {
+  clearInterval(interval);
+});
 
-  function redirectTo(url) {
-    window.open(url);
-  }
+function redirectTo(url) {
+  window.open(url);
+}
 </script>
 <style lang="less" scoped>
-  @import '/@/design/theme.less';
+@import '/@/design/theme.less';
 
-  @font-face {
-    font-family: 'douyuFont';
-    src: url('../../../../assets/font/douyuFont.otf');
-  }
-
-  @{theme-deepblue} {
-    .company-home {
-      --image-modal-top: url('@/assets/images/fireNew/1.png');
-    }
-  }
+@font-face {
+  font-family: 'douyuFont';
+  src: url('../../../../assets/font/douyuFont.otf');
+}
 
+@{theme-deepblue} {
   .company-home {
     --image-modal-top: url('@/assets/images/fireNew/1.png');
-    width: 100%;
-    height: 100%;
-    color: @white;
-    position: relative;
-    background: url('@/assets/images/fireNew/FireBj.png') no-repeat center;
+  }
+}
+
+.company-home {
+  --image-modal-top: url('@/assets/images/fireNew/1.png');
+  width: 100%;
+  height: 100%;
+  color: @white;
+  position: relative;
+  background: url('@/assets/images/fireNew/FireBj.png') no-repeat center;
 
-    .top-bg {
-      width: 100%;
+  .top-bg {
+    width: 100%;
+    height: 56px;
+    background: var(--image-modal-top) no-repeat center;
+    position: absolute;
+    z-index: 1;
+    .main-title {
       height: 56px;
-      background: var(--image-modal-top) no-repeat center;
-      position: absolute;
-      z-index: 1;
-      .main-title {
-        height: 56px;
-        font-family: 'douyuFont';
-        font-size: 20px;
-        letter-spacing: 2px;
-        display: flex;
-        justify-content: center;
-        align-items: center;
-      }
-      .top-nav {
-        position: absolute;
-        top: 0;
-        width: 880px;
-        height: 100%;
-        display: flex;
-        justify-content: flex-start;
-      }
-    }
-    .left-t {
-      position: absolute;
-      width: 28%;
-      height: 100%;
-      background: url('@/assets/images/vent/homeNew/leftContent.png') no-repeat center;
-      z-index: 0;
+      font-family: 'douyuFont';
+      font-size: 20px;
+      letter-spacing: 2px;
+      display: flex;
+      justify-content: center;
+      align-items: center;
     }
-    .right-t {
+    .top-nav {
       position: absolute;
-      width: 172%;
+      top: 0;
+      width: 880px;
       height: 100%;
-      background: url('@/assets/images/vent/homeNew/rightContent.png') no-repeat center;
-      z-index: 0;
-    }
-    // .module-left {
-    //   position: absolute;
-    //   width: 450px;
-    //   height: 280px;
-    //   left: 0;
-    // }
-    // .module-right {
-    //   position: absolute;
-    //   width: 450px;
-    //   height: 280px;
-    //   right: 0;
-    // }
-    // .module-bottom {
-    //   position: absolute;
-    //   width: 1000px;
-    //   height: 280px;
-    // }
-    .module-dropdown {
-      padding: 10px;
-      background-image: @vent-configurable-dropdown;
-      border-bottom: 2px solid @vent-configurable-home-light-border;
-      color: @vent-font-color;
-      position: absolute;
-      top: 70px;
-      right: 460px;
-    }
-    .module-dropdown-original {
-      padding: 10px;
-      background-image: @vent-configurable-dropdown;
-      border-bottom: 2px solid @vent-configurable-home-light-border;
-      color: @vent-font-color;
-      position: absolute;
-      top: 70px;
-      right: 460px;
-    }
-    .module-trigger-button {
-      color: @vent-font-color;
-      background-image: @vent-configurable-dropdown;
-      border: none;
-      border-bottom: 2px solid @vent-configurable-home-light-border;
+      display: flex;
+      justify-content: flex-start;
     }
   }
-  :deep(.loading-box) {
-    position: unset;
+  .left-t {
+    position: absolute;
+    width: 28%;
+    height: 100%;
+    background: url('@/assets/images/vent/homeNew/leftContent.png') no-repeat center;
+    z-index: 0;
+  }
+  .right-t {
+    position: absolute;
+    width: 172%;
+    height: 100%;
+    background: url('@/assets/images/vent/homeNew/rightContent.png') no-repeat center;
+    z-index: 0;
+  }
+  // .module-left {
+  //   position: absolute;
+  //   width: 450px;
+  //   height: 280px;
+  //   left: 0;
+  // }
+  // .module-right {
+  //   position: absolute;
+  //   width: 450px;
+  //   height: 280px;
+  //   right: 0;
+  // }
+  // .module-bottom {
+  //   position: absolute;
+  //   width: 1000px;
+  //   height: 280px;
+  // }
+  .module-dropdown {
+    padding: 10px;
+    background-image: @vent-configurable-dropdown;
+    border-bottom: 2px solid @vent-configurable-home-light-border;
+    color: @vent-font-color;
+    position: absolute;
+    top: 70px;
+    right: 460px;
+  }
+  .module-dropdown-original {
+    padding: 10px;
+    background-image: @vent-configurable-dropdown;
+    border-bottom: 2px solid @vent-configurable-home-light-border;
+    color: @vent-font-color;
+    position: absolute;
+    top: 70px;
+    right: 460px;
+  }
+  .module-trigger-button {
+    color: @vent-font-color;
+    background-image: @vent-configurable-dropdown;
+    border: none;
+    border-bottom: 2px solid @vent-configurable-home-light-border;
   }
+}
+:deep(.loading-box) {
+  position: unset;
+}
 </style>

+ 14 - 18
src/views/vent/home/configurable/blue/warnMonitor.vue

@@ -1,23 +1,20 @@
 <template>
-  <div class="fusion-home">
+  <div class="company-home">
     <div class="top-bg">
       <NewNav Title="预警监测管控系统" />
     </div>
     <template v-if="!route.query.deviceType">
-      <div class="main-container">
-        <ModuleDustNew
-          v-for="(cfg, index) in configs"
-          :key="cfg.deviceType + index"
-          :show-style="cfg.showStyle"
-          :module-data="cfg.moduleData"
-          :module-name="cfg.moduleName"
-          :device-type="cfg.deviceType"
-          :data="data"
-          :visible="true"
-        />
-      </div>
+      <ModuleDustNew
+        v-for="(cfg, index) in configs"
+        :key="cfg.deviceType + index"
+        :show-style="cfg.showStyle"
+        :module-data="cfg.moduleData"
+        :module-name="cfg.moduleName"
+        :device-type="cfg.deviceType"
+        :data="data"
+        :visible="true"
+      />
     </template>
-
     <div class="center-area">
       <CenterAreaWarn :echartData="paramData"></CenterAreaWarn>
     </div>
@@ -101,11 +98,12 @@ onUnmounted(() => {
   src: url('/@/assets/font/douyuFont.otf');
 }
 
-.fusion-home {
+.company-home {
   width: 100%;
   height: 100%;
+  color: @white;
   position: relative;
-
+  background: url('@/assets/images/vent/homeNew/bg.png') no-repeat center;
   .top-bg {
     width: 100%;
     height: 56px;
@@ -134,8 +132,6 @@ onUnmounted(() => {
   .main-container {
     width: 100%;
     height: 100%;
-    background: url('@/assets/images/home-warn/bg.png') no-repeat;
-    background-size: 100% 100%;
     .left-area {
       position: absolute;
       left: 0;

+ 426 - 411
src/views/vent/home/configurable/components/detail/ComplexList-FireNew.vue

@@ -4,11 +4,13 @@
   <div class="list flex items-center" :class="`list_${type}`">
     <!-- 部分类型的列表需要加一张图片 -->
     <div :class="`list__image_${type}`"></div>
+
     <!-- 剩下的部分填充剩余宽度 -->
     <div class="flex-grow h-full" :class="`list__wrapper_${type}`">
       <div v-for="(item, i) in listConfig" :key="`vvhccdcl${i}`" :class="`list-item_${type}`">
         <!-- 列表项前面的图标 -->
         <div :class="`list-item__title_${type}`">{{ item.title }}</div>
+
         <!-- 列表项的具体内容填充剩余宽度 -->
         <div v-for="(ctx, j) in item.contents" :key="`vvhccdclc${j}`" :class="`list-item__content_${type}`">
           <div class="list-item__label">{{ ctx.label }}</div>
@@ -19,6 +21,7 @@
     </div>
   </div>
 </template>
+
 <script lang="ts" setup>
 withDefaults(
   defineProps<{
@@ -31,7 +34,7 @@ withDefaults(
         info: string;
       }[];
     }[];
-    /** A B */
+    /** 列表样式类型:A/B/C/D/E/F/G */
     type: string;
   }>(),
   {
@@ -40,14 +43,14 @@ withDefaults(
   }
 );
 
-//   defineEmits(['click']);
+// defineEmits(['click']);
 </script>
+
 <style lang="less" scoped>
-    @import '@/design/theme.less';
-    @import '@/design/theme.less';
-    /* Timeline 相关的样式 */
+@import '@/design/theme.less';
 
-    @{theme-deepblue} {
+// 图片变量定义
+@{theme-deepblue} {
   .list {
     --image-title: url('/@/assets/images/fireNew/7-1.png');
     --image-ListArea: url('/@/assets/images/fireNew/7-2.png');
@@ -55,6 +58,7 @@ withDefaults(
   }
 }
 
+// 基础样式
 .list {
   --image-title: url('/@/assets/images/fireNew/7-1.png');
   --image-ListArea: url('/@/assets/images/fireNew/7-2.png');
@@ -64,434 +68,445 @@ withDefaults(
   width: 100%;
   height: 100%;
 }
-    .list-item_A {
-      position: relative;
-      height: 140px;
-      background-repeat: no-repeat;
-      background-image: var(--image-img-3);
-      background-size: auto 100%;
-      background-position: center;
-    }
-    .list-item__title_A {
-      position: absolute;
-      left: 41%;
-      // font-size: 14px;
-      top: 15px;
-    }
-    // .list-item__content_A {
-    //   position: absolute;
-    //   left: 35%;
-    //   top: 55px;
-    //   display: flex;
-    //   justify-content: space-evenly;
-    // }
-    .list-item__content_A:nth-of-type(2) {
-      position: absolute;
-      top: 15px;
-      left: 14%;
-      width: 22%;
-      text-align: center;
-      display: block;
-
-      .list-item__label {
-        font-size: 18px;
-        margin-bottom: 25px;
-      }
-      .list-item__info {
-        display: none;
-      }
-      .list-item__value {
-        font-size: 24px;
-      }
-    }
-    .list-item__content_A:nth-of-type(3) {
-      position: absolute;
-      left: 41%;
-      top: 55px;
-
-      .list-item__info {
-        display: none;
-      }
-      .list-item__value {
-        font-size: 20px;
-      }
-    }
-    .list-item__content_A:nth-of-type(4) {
-      position: absolute;
-      left: 66%;
-      top: 55px;
-
-      .list-item__info {
-        display: none;
-      }
-      .list-item__value {
-        font-size: 20px;
-      }
-    }
-    .list-item__content_A:nth-of-type(5) {
-      position: absolute;
-      left: 35%;
-      bottom: 10px;
-      display: flex;
-      align-items: center;
-
-      .list-item__info {
-        display: none;
-      }
-      .list-item__value {
-        font-size: 20px;
-        margin-left: 5px;
-      }
-    }
 
-    .list-item_B {
-      // height: 155px;
-      background-repeat: no-repeat;
-      // background-size: 100% 100%;
-      // background-size: auto 100%;
-      background-size: 87% auto;
-      background-position: center;
-      background-image: var(--image-img-7);
-      display: flex;
-      align-items: center;
-      justify-content: space-between;
-      text-align: center;
-      padding: 0 10%;
-      margin-top: 5px;
-      height: 33px;
-
-      .list-item__label {
-        font-size: 11px;
-      }
-      .list-item__value {
-        font-size: 18px;
-        margin-left: 5px;
-      }
-      .list-item__content_B {
-        height: 100%;
-        display: flex;
-        align-items: center;
-        flex-basis: 100px;
-        flex-grow: 1;
-      }
-      .list-item__title_B {
-        width: 40px;
-        text-align: center;
-        margin-right: 50px
-        // height: 30px;
-        // background-size: auto 80%;
-        // background-position: center;
-        // background-repeat: no-repeat;
-      }
-      .list-item__info {
-        display: none;
-      }
-    }
+// 颜色样式
+.list-item__value_red {
+  color: red;
+}
+.list-item__value_orange {
+  color: orange;
+}
+.list-item__value_yellow {
+  color: yellow;
+}
+.list-item__value_green {
+  color: yellowgreen;
+}
+.list-item__value_blue {
+  color: @vent-table-action-link;
+}
+.list-item__value_white {
+  color: white;
+}
+.gallery-item__value_lightblue {
+  color: @vent-configurable-home-light-border;
+}
 
-    .list_C {
-      padding: 5px 10px;
-    }
+// 类型A样式
+.list-item_A {
+  position: relative;
+  height: 140px;
+  background-repeat: no-repeat;
+  background-image: var(--image-img-3);
+  background-size: auto 100%;
+  background-position: center;
+}
 
-    .list__wrapper_C {
-      display: flex;
-      justify-content: space-between;
-      flex-wrap: wrap;
-    }
+.list-item__title_A {
+  position: absolute;
+  left: 41%;
+  top: 15px;
+}
 
-    .list-item_C {
-      position: relative;
-      height: 140px;
-      width: 200px;
-      background-repeat: no-repeat;
-      background-image: var(--image-img-8);
-      background-size: 100% 100%;
-      background-position: left center;
-    }
-    .list-item__title_C {
-      position: absolute;
-      left: 99px;
-      // font-size: 14px;
-      top: 15px;
-    }
-    .list-item__content_C:nth-of-type(2) {
-      position: absolute;
-      top: 15px;
-      left: 19px;
-      width: 30%;
-      text-align: center;
-      display: block;
-
-      .list-item__label {
-        // font-size: 18px;
-        margin-bottom: 25px;
-      }
-      .list-item__info {
-        display: none;
-      }
-      .list-item__value {
-        font-size: 18px;
-      }
-    }
-    .list-item__content_C:nth-of-type(3) {
-      position: absolute;
-      left: 99px;
-      top: 55px;
-
-      .list-item__info {
-        display: none;
-      }
-      .list-item__value {
-        font-size: 18px;
-      }
-    }
+.list-item__content_A:nth-of-type(2) {
+  position: absolute;
+  top: 15px;
+  left: 14%;
+  width: 22%;
+  text-align: center;
+  display: block;
 
+  .list-item__label {
+    font-size: 18px;
+    margin-bottom: 25px;
+  }
 
-    .list_D {
-      padding: 5px 10px;
-    }
+  .list-item__info {
+    display: none;
+  }
 
-    .list__wrapper_D {
-      display: flex;
-      justify-content: space-between;
-      flex-wrap: wrap;
-    }
+  .list-item__value {
+    font-size: 24px;
+  }
+}
 
-    .list-item_D {
-      background-image: var(--image-ListArea);
-      background-size: 100% auto;
-      background-position: center;
-      background-repeat: no-repeat;
-      position: relative;
-       height: 130px;
-      width: 179px;
-       text-align: center;
-       margin-left: 15px;
-       margin-right: 12px;
-       margin-top: 13px;
-    }
-    .list-item__title_D {
-      background-image: var(--image-title);
-      background-size: 100% auto;
-      background-position: center;
-      background-repeat: no-repeat;
-      width: 100%;
-      bottom: 0;
-      font-size: 16px;
-    }
-    .list-item__content_D:nth-of-type(2) {
-              background-image: var(--image-ValueBg);
-      background-size: 100% auto;
-      background-position: center;
-      background-repeat: no-repeat;
-    display: flex;
-    justify-content: space-between;
-    top: 30%;
-    left: 10%;
-    width: 88%;
-    position: absolute;
-    .list-item__info {
-      display: none;
-    }
-    .list-item__value {
-      margin-right: 10px;
-      font-size: 13px;   
-      color: #a9e0eb;
-    }
+.list-item__content_A:nth-of-type(3) {
+  position: absolute;
+  left: 41%;
+  top: 55px;
+
+  .list-item__info {
+    display: none;
   }
-  .list-item__content_D:nth-of-type(3) {
-        background-image: var(--image-ValueBg);
-      background-size: 100% auto;
-      background-position: center;
-      background-repeat: no-repeat;
-    display: flex;
-    justify-content: space-between;
-    top: 62%;
-    left: 10%;
-    width: 77%;
-    position: absolute;
-    .list-item__info {
-      display: none;
-    }
-    .list-item__value {
-      font-size: 13px;
-      margin-right: 10px;
-      color: #a9e0eb;
-    }
+
+  .list-item__value {
+    font-size: 20px;
+  }
+}
+
+.list-item__content_A:nth-of-type(4) {
+  position: absolute;
+  left: 66%;
+  top: 55px;
+
+  .list-item__info {
+    display: none;
+  }
+
+  .list-item__value {
+    font-size: 20px;
+  }
+}
+
+.list-item__content_A:nth-of-type(5) {
+  position: absolute;
+  left: 35%;
+  bottom: 10px;
+  display: flex;
+  align-items: center;
+
+  .list-item__info {
+    display: none;
+  }
+
+  .list-item__value {
+    font-size: 20px;
+    margin-left: 5px;
+  }
+}
+
+// 类型B样式
+.list-item_B {
+  background-repeat: no-repeat;
+  background-size: 87% auto;
+  background-position: center;
+  background-image: var(--image-img-7);
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  text-align: center;
+  padding: 0 10%;
+  margin-top: 5px;
+  height: 33px;
+
+  .list-item__label {
+    font-size: 11px;
   }
-  .list_E {
-    padding: 5px 10px;
+
+  .list-item__value {
+    font-size: 18px;
+    margin-left: 5px;
   }
 
-  .list__wrapper_E {
+  .list-item__content_B {
+    height: 100%;
     display: flex;
-    flex-wrap: wrap;
-    padding: 0 10px;
+    align-items: center;
+    flex-basis: 100px;
+    flex-grow: 1;
   }
 
-  .list-item_E {
-    position: relative;
-    height: 100px;
-    width: 170px;
-    background-repeat: no-repeat;
-    background-image: var(--image-list_bg_1);
-    background-size: 100% auto;
-    background-position: center top;
+  .list-item__title_B {
+    width: 40px;
     text-align: center;
-    margin-bottom: 20px;
-    margin-left: 5px;
+    margin-right: 50px;
   }
-  .list-item__title_E {
-    // position: absolute;
-    width: 100%;
-    // top: 0;
+
+  .list-item__info {
+    display: none;
+  }
+}
+
+// 类型C样式
+.list_C {
+  padding: 5px 10px;
+}
+
+.list__wrapper_C {
+  display: flex;
+  justify-content: space-between;
+  flex-wrap: wrap;
+}
+
+.list-item_C {
+  position: relative;
+  height: 140px;
+  width: 200px;
+  background-repeat: no-repeat;
+  background-image: var(--image-img-8);
+  background-size: 100% 100%;
+  background-position: left center;
+}
+
+.list-item__title_C {
+  position: absolute;
+  left: 99px;
+  top: 15px;
+}
+
+.list-item__content_C:nth-of-type(2) {
+  position: absolute;
+  top: 15px;
+  left: 19px;
+  width: 30%;
+  text-align: center;
+  display: block;
+
+  .list-item__label {
+    margin-bottom: 25px;
+  }
+
+  .list-item__info {
+    display: none;
+  }
+
+  .list-item__value {
+    font-size: 18px;
+  }
+}
+
+.list-item__content_C:nth-of-type(3) {
+  position: absolute;
+  left: 99px;
+  top: 55px;
+
+  .list-item__info {
+    display: none;
+  }
+
+  .list-item__value {
+    font-size: 18px;
+  }
+}
+
+// 类型D样式
+.list_D {
+  padding: 5px 10px;
+}
+
+.list__wrapper_D {
+  display: flex;
+  justify-content: space-between;
+  flex-wrap: wrap;
+  overflow: auto;
+  scrollbar-width: none;
+}
+
+.list__wrapper_D::-webkit-scrollbar {
+  display: flex;
+  width: 0;
+  height: 0;
+}
+
+.list-item_D {
+  background-image: var(--image-ListArea);
+  background-size: 100% auto;
+  background-position: center;
+  background-repeat: no-repeat;
+  position: relative;
+  height: 130px;
+  width: 175px;
+  text-align: center;
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
+
+.list-item_D:nth-child(odd) {
+  margin-left: 25px;
+  margin-right: 5px;
+}
+
+.list-item_D:nth-child(even) {
+  margin-right: 25px;
+  margin-left: 5px;
+}
+
+.list-item__title_D {
+  background-image: var(--image-title);
+  background-size: 100% auto;
+  background-position: center;
+  background-repeat: no-repeat;
+  width: 100%;
+  bottom: 0;
+  font-size: 16px;
+}
+
+.list-item__content_D:nth-of-type(2) {
+  background-image: var(--image-ValueBg);
+  background-size: 100% auto;
+  background-position: center;
+  background-repeat: no-repeat;
+  display: flex;
+  justify-content: space-between;
+  top: 30%;
+  left: 10%;
+  width: 88%;
+  position: absolute;
+
+  .list-item__info {
+    display: none;
+  }
+
+  .list-item__value {
+    margin-right: 10px;
+    font-size: 13px;
+    color: #a9e0eb;
+  }
+}
+
+.list-item__content_D:nth-of-type(3) {
+  background-image: var(--image-ValueBg);
+  background-size: 100% auto;
+  background-position: center;
+  background-repeat: no-repeat;
+  display: flex;
+  justify-content: space-between;
+  top: 62%;
+  left: 10%;
+  width: 77%;
+  position: absolute;
+
+  .list-item__info {
+    display: none;
+  }
+
+  .list-item__value {
+    font-size: 13px;
+    margin-right: 10px;
+    color: #a9e0eb;
+  }
+}
+
+// 类型E样式
+.list_E {
+  padding: 5px 10px;
+}
+
+.list__wrapper_E {
+  display: flex;
+  flex-wrap: wrap;
+  padding: 0 10px;
+}
+
+.list-item_E {
+  position: relative;
+  height: 100px;
+  width: 170px;
+  background-repeat: no-repeat;
+  background-image: var(--image-list_bg_1);
+  background-size: 100% auto;
+  background-position: center top;
+  text-align: center;
+  margin-bottom: 20px;
+  margin-left: 5px;
+}
+
+.list-item__title_E {
+  width: 100%;
+  font-size: 16px;
+  font-weight: bold;
+  margin-top: 10px;
+}
+
+.list-item__content_E:nth-of-type(2) {
+  position: absolute;
+  top: 40%;
+  left: 5%;
+  text-align: left;
+
+  .list-item__info {
+    display: none;
+  }
+
+  .list-item__value {
+    font-size: 16px;
+  }
+}
+
+.list-item__content_E:nth-of-type(3) {
+  position: absolute;
+  top: 40%;
+  right: 5%;
+  text-align: right;
+
+  .list-item__info {
+    display: none;
+  }
+
+  .list-item__value {
     font-size: 16px;
+  }
+}
+
+// 类型F样式
+.list-item_F {
+  background-repeat: no-repeat;
+  background-size: 100% 100%;
+  background-position: center;
+  background-image: @vent-gas-list-item-bg-img;
+  display: flex;
+  align-items: center;
+  padding: 0 5%;
+  margin-top: 5px;
+  height: 50px;
+
+  .list-item__title_F {
+    flex-basis: 25%;
+  }
+
+  .list-item__content_F {
+    flex-basis: 25%;
+    display: flex;
+    align-items: center;
+  }
+
+  .list-item__label::after {
+    content: ':';
+  }
+
+  .list-item__value {
+    font-size: 18px;
+    margin-left: 5px;
     font-weight: bold;
-    margin-top: 10px;
   }
-  .list-item__content_E:nth-of-type(2) {
-    position: absolute;
-    top: 40%;
-    left: 5%;
-    text-align: left;
 
-    .list-item__info {
-      display: none;
-    }
-    .list-item__value {
-      font-size: 16px;
-    }
+  .list-item__info {
+    display: none;
   }
-  .list-item__content_E:nth-of-type(3) {
-    position: absolute;
-    top: 40%;
-    right: 5%;
-    text-align: right;
+}
 
-    .list-item__info {
-      display: none;
-    }
-    .list-item__value {
-      font-size: 16px;
+// 类型G样式
+.list-item_G {
+  margin-top: 5px;
+
+  .list-item__title_G {
+    position: relative;
+    width: 100%;
+    height: 16px;
+    margin-top: 10px;
+    margin-bottom: 5px;
+    padding-left: 8px;
+    top: -2px;
+    background-image: var(--image-linear-gradient-1);
+
+    &::before {
+      position: absolute;
+      content: '';
+      width: 100%;
+      height: 100%;
+      top: 4px;
+      left: 0;
+      background-image: var(--image-linear-gradient-2);
     }
   }
 
-  .list-item_F {
-      // height: 155px;
-      background-repeat: no-repeat;
-      background-size: 100% 100%;
-      // background-size: auto 100%;
-      background-position: center;
-      background-image: @vent-gas-list-item-bg-img;
-      display: flex;
-      align-items: center;
-      // justify-content: space-between;
-      // text-align: center;
-      padding: 0 5%;
-      margin-top: 5px;
-      height: 50px;
-
-      .list-item__title_F {
-        flex-basis: 25%;
-      }
-      .list-item__content_F {
-        flex-basis: 25%;
-      }
-      .list-item__label::after {
-        content: ':';
-      }
-      .list-item__value {
-        font-size: 18px;
-        margin-left: 5px;
-        font-weight: bold;
-      }
-      .list-item__content_F {
-        display: flex;
-        align-items: center;
-      }
-      .list-item__info {
-        display: none;
-      }
-  }
-
-  .list-item_G {
-      // height: 155px;
-      // background-repeat: no-repeat;
-      // background-size: 100% 100%;
-      // background-position: center;
-      // justify-content: space-between;
-      // text-align: center;
-      margin-top: 5px;
-
-      .list-item__title_G {
-        position: relative;
-        width: 100%;
-        height: 16px;
-        margin-top: 10px;
-        margin-bottom: 5px;
-        padding-left: 8px;
-        top: -2px;
-        background-image: var(--image-linear-gradient-1);
-        &::before{
-          position: absolute;
-          content: '';
-          width: 100%;
-          height: 100%;
-          top: 4px;
-          left: 0;
-          background-image: var(--image-linear-gradient-2);
-        }
-      }
-      .list-item__content_G {
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
-        padding: 4px;
-        margin: 8px 0;
-        background-image: var(--image-linear-gradient-3);
-      }
-  }
-
-    // .list-item__title_B_O2 {
-    //   background-image: url(/@/assets/images/home-container/configurable/firehome/O₂.png);
-    // }
-    // .list-item__title_B_CH4 {
-    //   background-image: url(/@/assets/images/home-container/configurable/firehome/CH₄.png);
-    // }
-    // .list-item__title_B_CO {
-    //   background-image: url(/@/assets/images/home-container/configurable/firehome/CO.png);
-    // }
-    // .list-item__title_B_CO2 {
-    //   background-image: url(/@/assets/images/home-container/configurable/firehome/CO₂.png);
-    // }
-
-    // .list-item__label {
-    //   flex-basis: 55%;
-    // }
-    // .list-item__info {
-    //   flex-grow: 1;
-    // }
-    // .list-item__value {
-    //   flex-basis: 30%;
-    // }
-    .list-item__value_red {
-      color: red;
-    }
-    .list-item__value_orange {
-      color: orange;
-    }
-    .list-item__value_yellow {
-      color: yellow;
-    }
-    .list-item__value_green {
-      color: yellowgreen;
-    }
-    .list-item__value_blue {
-      color: @vent-table-action-link;
-    }
-    .list-item__value_white {
-      color: white;
-    }
-    .gallery-item__value_lightblue {
-      color: @vent-configurable-home-light-border;
-    }
-</style>
+  .list-item__content_G {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 4px;
+    margin: 8px 0;
+    background-image: var(--image-linear-gradient-3);
+  }
+}
+</style>

+ 4 - 2
src/views/vent/home/configurable/components/detail/CustomList-fire.vue

@@ -301,13 +301,15 @@ function getIconPath1(item) {
 .list-item_H {
   align-items: center;
   text-align: center;
-  margin: 5px 10px;
+  margin: 10px;
   &:nth-child(2n) {
+    height: 34px;
     background-repeat: no-repeat;
     background-size: 100% 100%;
     background-image: var(--image-list_bg_h);
   }
   &:nth-child(2n + 1) {
+    height: 34px;
     background-repeat: no-repeat;
     background-size: 100% 100%;
     background-image: var(--image-list_bg_h1);
@@ -401,7 +403,7 @@ function getIconPath1(item) {
   height: 35px;
   align-items: center;
   text-align: center;
-  margin: 15px 20px;
+  margin: 10px;
   /* 背景组合A(1,4,5,8) */
   &:nth-child(1),
   &:nth-child(4),

+ 1 - 1
src/views/vent/home/configurable/components/detail/CustomTable-fire.vue

@@ -68,7 +68,7 @@ function get(o, p) {
   display: flex;
   flex-direction: column;
   align-items: center;
-  padding: 5px 0;
+  padding: 13px;
 
   .table__content_label {
     width: 400px;

+ 123 - 116
src/views/vent/home/configurable/components/detail/MiniBoard-FireNew.vue

@@ -36,133 +36,140 @@
   </div>
 </template>
 <script lang="ts" setup>
-  withDefaults(
-    defineProps<{
-      label: string;
-      value?: string;
-      // 告示牌布局,类型为:'val-top' | 'label-top'
-      layout: string;
-      // 告示牌类型,类型为:'A' | 'B' | 'C' | 'D' | 'E' | 'F' |'New' | 'localFannew'
-      type?: string;
-    }>(),
-    {
-      value: '/',
-      type: 'A',
-      layout: 'val-top',
-    }
-  );
+withDefaults(
+  defineProps<{
+    label: string;
+    value?: string;
+    // 告示牌布局,类型为:'val-top' | 'label-top'
+    layout: string;
+    // 告示牌类型,类型为:'A' | 'B' | 'C' | 'D' | 'E' | 'F' |'New' | 'localFannew'
+    type?: string;
+  }>(),
+  {
+    value: '/',
+    type: 'A',
+    layout: 'val-top',
+  }
+);
 
-  // 获取某些 value 对应的特殊的 装饰用的类名
-  function getValueDecoClass(value) {
-    switch (value) {
-      case '低风险':
-        return 'low_risk';
-      case '一般风险':
-        return 'risk';
-      case '较大风险':
-        return 'high_risk';
-      case '报警':
-        return 'warning';
-      default:
-        return '';
-    }
-  }
+// 获取某些 value 对应的特殊的 装饰用的类名
+function getValueDecoClass(value) {
+  switch (value) {
+    case '低风险':
+      return 'low_risk';
+    case '一般风险':
+      return 'risk';
+    case '较大风险':
+      return 'high_risk';
+    case '报警':
+      return 'warning';
+    default:
+      return '';
+  }
+}
 
-  defineEmits(['click']);
+defineEmits(['click']);
 </script>
 <style lang="less" scoped>
-  @import '/@/design/theme.less';
-  @import '/@/design/theme.less';
-
-  @font-face {
-    font-family: 'douyuFont';
-    src: url('/@/assets/font/douyuFont.otf');
-  }
-  @{theme-deepblue} {
-    .mini-board {
-      --image-areaNew: url('/@/assets/images/fireNew/6-1.png');
-      --image-areaNew1: url('/@/assets/images/fireNew/6-2.png');
-      --image-areaNew2: url('/@/assets/images/fireNew/8.png');
-    }
-  }
-
-  .mini-board__label {
-    white-space: nowrap;
-  }
-  .mini-board__value {
-    white-space: nowrap;
-  }
+@import '/@/design/theme.less';
+@import '/@/design/theme.less';
 
+@font-face {
+  font-family: 'douyuFont';
+  src: url('/@/assets/font/douyuFont.otf');
+}
+@{theme-deepblue} {
   .mini-board {
     --image-areaNew: url('/@/assets/images/fireNew/6-1.png');
     --image-areaNew1: url('/@/assets/images/fireNew/6-2.png');
     --image-areaNew2: url('/@/assets/images/fireNew/8.png');
-    height: 50px;
-    line-height: 25px;
-    width: 130px;
-    padding: 0 5px 0 5px;
-    text-align: center;
-    background-size: 100% 100%;
-    position: relative;
-  }
-  .mini-board_H {
-    width: 174px;
-    height: 104px;
-    background-image: var(--image-areaNew);
-    background-size: 100% auto;
-    background-position: center bottom;
-    background-repeat: no-repeat;
-    padding: 33px 0 0 82px;
   }
+}
 
-  .mini-board__value_H {
-    font-size: 16px;
-    font-weight: bold;
-    height: 23px;
-    line-height: 50px;
-    margin-top: 2px;
-    font-family: 'douyuFont';
-  }
-  .mini-board__label_H {
-    line-height: 20px;
-    height: 20px;
-  }
-  .mini-board_E:nth-child(1) {
-    .mini-board__label_E {
-      background-image: var(--image-hycd);
-    }
-  }
-  .mini-board_E:nth-child(2) {
-    .mini-board__label_E {
-      background-image: var(--image-dyfl);
-    }
-  }
-  .mini-board_E:nth-child(3) {
-    .mini-board__label_E {
-      background-image: var(--image-jdjl);
-    }
-  }
+.mini-board__label {
+  white-space: nowrap;
+}
+.mini-board__value {
+  white-space: nowrap;
+}
 
-  .mini-board_H_low_risk:nth-child(1) {
-    background-image: var(--image-areaNew);
-  }
-  .mini-board_H_low_risk:nth-child(2) {
-    background-image: var(--image-areaNew1);
-  }
+.mini-board {
+  --image-areaNew: url('/@/assets/images/fireNew/6-1.png');
+  --image-areaNew1: url('/@/assets/images/fireNew/6-2.png');
+  --image-areaNew2: url('/@/assets/images/fireNew/8.png');
+  height: 50px;
+  line-height: 25px;
+  width: 130px;
+  padding: 0 5px 0 5px;
+  text-align: center;
+  background-size: 100% 100%;
+  position: relative;
+}
+.mini-board_H {
+  width: 150px;
+  height: 62px;
+  background-image: var(--image-areaNew);
+  background-size: 100% 100%;
+  background-position: center bottom;
+  background-repeat: no-repeat;
+  padding: 5px 0 0 80px;
+}
+.mini-board_H:nth-child(odd) {
+  margin-left: 20px;
+}
+.mini-board_H:nth-child(even) {
+  margin-right: 20px;
+}
+.mini-board__value_H {
+  font-size: 12px;
+  font-weight: bold;
+  height: 23px;
+  line-height: 36px;
+  margin-right: 14px;
+  font-family: 'douyuFont';
+}
+.mini-board__label_H {
+  line-height: 10px;
+  height: 20px;
+  margin-right: 3px;
+  font-size: 11px;
+}
+.mini-board_E:nth-child(1) {
+  .mini-board__label_E {
+    background-image: var(--image-hycd);
+  }
+}
+.mini-board_E:nth-child(2) {
+  .mini-board__label_E {
+    background-image: var(--image-dyfl);
+  }
+}
+.mini-board_E:nth-child(3) {
+  .mini-board__label_E {
+    background-image: var(--image-jdjl);
+  }
+}
 
-  .mini-board_F {
-    width: 100px;
-    height: 60px;
-    background-image: var(--image-areaNew2);
-    background-size: 100% 100%;
-    background-position: center bottom;
-    background-repeat: no-repeat;
-  }
-  .mini-board__value_F {
-    font-size: 15px;
-    color: @vent-gas-primary-text;
-  }
-  .mini-board__label_F {
-    line-height: 17px;
-  }
+.mini-board_H_low_risk:nth-child(1) {
+  background-image: var(--image-areaNew);
+}
+.mini-board_H_low_risk:nth-child(2) {
+  background-image: var(--image-areaNew1);
+}
+
+.mini-board_F {
+  width: 100px;
+  height: 60px;
+  background-image: var(--image-areaNew2);
+  background-size: 100% 100%;
+  background-position: center bottom;
+  background-repeat: no-repeat;
+}
+.mini-board__value_F {
+  font-size: 15px;
+  color: @vent-gas-primary-text;
+}
+.mini-board__label_F {
+  line-height: 17px;
+}
 </style>

+ 33 - 27
src/views/vent/home/configurable/components/preset/nitrogenBtnList.vue

@@ -10,19 +10,11 @@
       </div>
     </div>
   </div>
-  <ConfirmModal v-model:visible="modalVisible" @cancel="handleCancel" class="btn-confirm-modal">
-    <SvgIcon class="icon" size="34" name="warning-icon-gas" />
-    <span> {{ currentButton?.content }} </span>
-    <inputPassword v-model:value="password" placeholder="请输入密码校验(必填)" class="pswInput" />
-    <template #footer>
-      <a-button type="primary" @click="handleConfirm">确认</a-button>
-      <a-button type="primary" @click="handleCancel">取消</a-button>
-    </template>
-  </ConfirmModal>
+  <ConfirmModal v-model:visible="modalVisible" @handleCancel="handleCancel" @handleConfirm="handleConfirm" class="btn-confirm-modal"> </ConfirmModal>
 </template>
 
 <script setup lang="ts">
-import ConfirmModal from '@/views/vent/gas/components/modal/confirmModal.vue';
+import ConfirmModal from '@/views/vent/gas/components/modal/confirmModal1.vue';
 import { ref, inject } from 'vue';
 import { InputPassword } from 'ant-design-vue';
 let props = defineProps({
@@ -41,36 +33,51 @@ interface ButtonItem {
   value: string | number;
   label: string;
   content: string;
-  // 可以根据实际需求添加其他属性
 }
 const password = ref('');
 const inputWarn = ref('密码错误请重新输入');
 const modalVisible = ref(false);
-const currentButton = ref<ButtonItem | null>(null); // 记录当前点击的按钮
+const currentButton = ref({}); // 记录当前点击的按钮
 const toggleStatus = (btn: ButtonItem, index) => {
   modalVisible.value = !modalVisible.value;
-  props.buttonList[index].isRunning = !props.buttonList[index].isRunning;
-  const currentBtn = props.buttonList[index];
-  console.log(`【${currentBtn.name}】当前状态:${currentBtn.isRunning ? '开启' : '关闭'}`);
-  // 根据当前状态执行对应的控制功能
-  // if (currentBtn.isRunning) {
-  // } else {
-  // }
+  console.log(btn, '按钮信息');
+  currentButton.value = btn;
 };
 // 注入祖父组件提供的处理函数
 const handleButtonConfirm = inject<(button: ButtonItem) => void>(
   'handleButtonConfirm',
   () => console.warn('未提供handleButtonConfirm函数') // 默认值,避免报错
 );
-const handleConfirm = () => {
-  if (!currentButton.value) return;
-  password.value = '';
-  inputWarn.value = '';
-  handleButtonConfirm(currentButton.value);
-  modalVisible.value = false;
+/**
+ * 处理密码确认回调
+ */
+const handleConfirm = async (password) => {
+  try {
+    // 1. 这里写你的密码校验逻辑(比如调用接口、本地验证等)
+    console.log('用户输入的密码:', password);
+    console.log('控制按钮信息', currentButton.value);
+    // 接口请求
+    await new Promise((resolve) => setTimeout(resolve, 500));
+    // 2. 密码验证成功的逻辑
+    if (password === '123456') {
+      console.log('密码验证成功!');
+    } else {
+      // 抛出错误,让子组件捕获并显示错误提示
+      throw new Error('密码错误,请重新输入');
+    }
+  } catch (error) {
+    // 4. 捕获验证过程中的错误,返回false阻止弹窗关闭
+    console.error('密码验证失败:', error.message);
+    // 返回false,子组件会保持打开状态并显示错误信息
+    return false;
+  }
 };
+
+/**
+ * 处理弹窗取消回调
+ */
 const handleCancel = () => {
-  console.log('取消!!!!!!!!');
+  console.log('关闭弹窗');
   password.value = '';
   inputWarn.value = '';
   modalVisible.value = false;
@@ -145,7 +152,6 @@ const handleCancel = () => {
 .btn-confirm-modal {
   .zxm-modal-content {
     height: 240px !important;
-    background-size: 100% 100%;
     .zxm-modal-body {
       height: 187px !important;
       padding-top: 33px !important;

+ 2 - 2
src/views/vent/home/configurable/components/three3D.vue

@@ -36,10 +36,10 @@
           newP: { x: -2.211555197992825, y: 27.130029732875393, z: 2.3018919451652007 },
           newT: { x: -2.211555197992825, y: -8.604453425019353, z: 2.301856157557903 },
         },
-          jdds: {
+        jdds: {
           render: null,
           group: modalGroup ? modalGroup : null,
-          newP: { x: 0.22197787154285728, y: 29.010792085965782, z:  2.477736279196267},
+          newP: { x: 0.22197787154285728, y: 29.010792085965782, z: 2.477736279196267 },
           newT: { x: 0.22197787154285728, y: -8.604453177192061, z: 2.477698375233975 },
         },
       };

+ 80 - 118
src/views/vent/home/configurable/configurable.api.ts

@@ -10,7 +10,7 @@ enum Api {
   getHomeData = '/safety/ventanalyDevice/homedata',
   getDisHome = '/monitor/disaster/getDisHome',
   getBDDustData = '/monitor/disaster/getDisDustHome',
-  getBDFireData = '/monitor/disaster/getDisFireHome',
+  // getBDFireData = '/monitor/disaster/getDisFireHome',
   getDeviceSys = '/ventanaly-device/monitor/getSysFireHomeInfo',
   getAlarmRecord = '/ventanaly-device/safety/ventanalyAlarmLog/sysLinkDevAlarmLog',
   getTotal = '/safety/ventanalyAlarmLog/total',
@@ -216,35 +216,6 @@ export const getBDDustData = (params) => {
   return cache.get(key) as Promise<any>;
 };
 
-export const getBDFireData = (params) => {
-  const key = `${Api.getBDFireData}?${JSON.stringify(params)}`;
-  if (!cache.has(key)) {
-    cache.set(
-      key,
-      defHttp.post({ url: Api.getBDFireData, params }).finally(() => {
-        cache.delete(key);
-      })
-    );
-  }
-  return (cache.get(key) as Promise<any>).then((res) => {
-    res.pdArray.forEach((e) => {
-      e.arrayFiber.forEach((j) => {
-        j.fibreTemperatureArr = JSON.parse(j.fibreTemperature);
-      });
-    });
-    res.sgGxObj.devGxcw.forEach((e) => {
-      e.fibreTemperatureArr = JSON.parse(e.fibreTemperature);
-    });
-    res.sgGxObj.devSgjc.forEach((e) => {
-      e.o2val = e.o2Val || 0;
-      e.coval = e.coVal || 0;
-      e.gasval = e.gasVal || 0;
-      e.ch2val = e.ch2Val || 0;
-      e.chval = e.chVal || 0;
-    });
-    return res;
-  });
-};
 // 塔山火灾预警页面获取数据接口
 export const getDeviceSys = (params) => {
   const key = `${Api.getDeviceSys}?${JSON.stringify(params)}`;
@@ -277,115 +248,106 @@ export const getAlarmRecord = (params) => {
 };
 
 export const getDisHome = (params) => {
+  // 生成缓存key,基于接口地址和参数
   const key = `${Api.getDisHome}?${JSON.stringify(params)}`;
+
+  // 缓存不存在时发起请求
   if (!cache.has(key)) {
     cache.set(
       key,
       defHttp.post({ url: Api.getDisHome, params }).finally(() => {
+        // 请求完成后删除缓存,保证下次请求能获取最新数据
         cache.delete(key);
       })
     );
   }
+
+  // 返回缓存中的Promise并处理响应数据
   return (cache.get(key) as Promise<any>).then((res) => {
-    if (res.pdArray) {
+    // 1. 处理 pdArray 中的光纤温度数据(解析JSON字符串)
+    if (res.pdArray && Array.isArray(res.pdArray)) {
       res.pdArray.forEach((e) => {
-        e.arrayFiber.forEach((j) => {
-          j.fibreTemperatureArr = JSON.parse(j.fibreTemperature);
-        });
+        // 先校验 arrayFiber 是数组,避免forEach报错
+        if (e.arrayFiber && Array.isArray(e.arrayFiber)) {
+          e.arrayFiber.forEach((j) => {
+            // 安全解析 JSON,避免 undefined/无效JSON 导致报错
+            j.fibreTemperatureArr = safeJsonParse(j.fibreTemperature, []);
+          });
+        }
       });
     }
+
+    // 2. 处理 sgGxObj 下的设备数据
     if (res.sgGxObj) {
-      res.sgGxObj.devGxcw.forEach((e) => {
-        e.fibreTemperatureArr = JSON.parse(e.fibreTemperature);
-      });
-      res.sgGxObj.devSgjc.forEach((e) => {
-        e.o2val = e.o2Val || 0;
-        e.coval = e.coVal || 0;
-        e.gasval = e.gasVal || 0;
-        e.ch2val = e.ch2Val || 0;
-        e.chval = e.chVal || 0;
-      });
+      // 2.1 解析 devGxcw 中的光纤温度
+      if (res.sgGxObj.devGxcw && Array.isArray(res.sgGxObj.devGxcw)) {
+        res.sgGxObj.devGxcw.forEach((e) => {
+          e.fibreTemperatureArr = safeJsonParse(e.fibreTemperature, []);
+        });
+      }
+
+      // 2.2 为 devSgjc 中的气体字段设置默认值
+      if (res.sgGxObj.devSgjc && Array.isArray(res.sgGxObj.devSgjc)) {
+        res.sgGxObj.devSgjc.forEach((e) => {
+          e.o2val = e.o2Val || 0;
+          e.coval = e.coVal || 0;
+          e.gasval = e.gasVal || 0;
+          e.ch2val = e.ch2Val || 0;
+          e.chval = e.chVal || 0;
+        });
+      }
     }
-    if (res.obfObj) {
-      res.obfObj.obfObjModded = [
-        {
-          objType: '氧气',
-          arrayDev: res.obfObj.arrayDev.map((e) => {
-            return {
-              strinstallpos: e.strinstallpos,
-              val: e.o2Val || 0,
-            };
-          }),
-        },
-        {
-          objType: '甲烷',
-          arrayDev: res.obfObj.arrayDev.map((e) => {
-            return {
-              strinstallpos: e.strinstallpos,
-              val: e.ch4Val || 0,
-            };
-          }),
-        },
-        {
-          objType: '一氧化碳',
-          arrayDev: res.obfObj.arrayDev.map((e) => {
-            return {
-              strinstallpos: e.strinstallpos,
-              val: e.coVal || 0,
-            };
-          }),
-        },
-        {
-          objType: '乙炔',
-          arrayDev: res.obfObj.arrayDev.map((e) => {
-            return {
-              strinstallpos: e.strinstallpos,
-              val: e.c2h2Val || 0,
-            };
-          }),
-        },
-        {
-          objType: '二氧化碳',
-          arrayDev: res.obfObj.arrayDev.map((e) => {
-            return {
-              strinstallpos: e.strinstallpos,
-              val: e.co2Val || 0,
-            };
-          }),
-        },
 
-        {
-          objType: '乙烯',
-          arrayDev: res.obfObj.arrayDev.map((e) => {
-            return {
-              strinstallpos: e.strinstallpos,
-              val: e.c2h4Val || 0,
-            };
-          }),
-        },
-        {
-          objType: '压差',
-          arrayDev: res.obfObj.arrayDev.map((e) => {
-            return {
-              strinstallpos: e.strinstallpos,
-              val: e.dpVal || 0,
-            };
-          }),
-        },
-        {
-          objType: '温度',
-          arrayDev: res.obfObj.arrayDev.map((e) => {
-            return {
-              strinstallpos: e.strinstallpos,
-              val: e.tempVal || 0,
-            };
-          }),
-        },
+    // 3. 重组 obfObj 为按气体类型分类的结构
+    if (res.obfObj && Array.isArray(res.obfObj.arrayDev)) {
+      res.obfObj.obfObjModded = [
+        { objType: '氧气', arrayDev: mapObfDev(res.obfObj.arrayDev, 'o2Val') },
+        { objType: '甲烷', arrayDev: mapObfDev(res.obfObj.arrayDev, 'ch4Val') },
+        { objType: '一氧化碳', arrayDev: mapObfDev(res.obfObj.arrayDev, 'coVal') },
+        { objType: '乙炔', arrayDev: mapObfDev(res.obfObj.arrayDev, 'c2h2Val') },
+        { objType: '二氧化碳', arrayDev: mapObfDev(res.obfObj.arrayDev, 'co2Val') },
+        { objType: '乙烯', arrayDev: mapObfDev(res.obfObj.arrayDev, 'c2h4Val') },
+        { objType: '压差', arrayDev: mapObfDev(res.obfObj.arrayDev, 'dpVal') },
+        { objType: '温度', arrayDev: mapObfDev(res.obfObj.arrayDev, 'tempVal') },
       ];
     }
+
     return res;
   });
 };
+
+/**
+ * 安全解析 JSON 字符串的工具函数
+ * @param {string} jsonStr - 要解析的JSON字符串
+ * @param {any} defaultValue - 解析失败/值无效时的默认值
+ * @returns {any} 解析结果或默认值
+ */
+function safeJsonParse(jsonStr, defaultValue = null) {
+  // 先校验值是否为有效字符串
+  if (!jsonStr || typeof jsonStr !== 'string') {
+    return defaultValue;
+  }
+  // 捕获解析异常
+  try {
+    return JSON.parse(jsonStr);
+  } catch (err) {
+    console.warn('JSON解析失败', jsonStr, err);
+    return defaultValue;
+  }
+}
+
+/**
+ * 映射 obfObj.arrayDev 数据的工具函数,减少重复代码
+ * @param {Array} devArray - 设备数组
+ * @param {string} valKey - 要取值的字段名
+ * @returns {Array} 映射后的数组
+ */
+function mapObfDev(devArray, valKey) {
+  return devArray.map((e) => ({
+    strinstallpos: e.strinstallpos,
+    val: e[valKey] || 0,
+  }));
+}
 //获取通风监测预警图表数据
 export const sysTypeWarnList = (params) => {
   const key = `${Api.sysTypeWarnList}?${JSON.stringify(params)}`;

+ 9 - 9
src/views/vent/home/configurable/configurable.data.New.ts

@@ -691,11 +691,11 @@ export const testConfigNewFire: Config[] = [
         items: [
           {
             name: 'board',
-            basis: '40%',
+            basis: '35%',
           },
           {
             name: 'complex_list',
-            basis: '60%',
+            basis: '65%',
           },
         ],
       },
@@ -750,7 +750,7 @@ export const testConfigNewFire: Config[] = [
       preset: [],
     },
     showStyle: {
-      size: 'width:440px;height:300px;',
+      size: 'width:440px;height:280px;',
       version: '保德',
       position: 'top:60px;left:0;',
     },
@@ -786,7 +786,7 @@ export const testConfigNewFire: Config[] = [
           },
           {
             name: 'chart',
-            basis: '65%',
+            basis: '60%',
           },
         ],
       },
@@ -838,7 +838,7 @@ export const testConfigNewFire: Config[] = [
     showStyle: {
       size: 'width:440px;height:280px;',
       version: '保德',
-      position: 'top:360px;left:0;',
+      position: 'top:350px;left:0;',
     },
   },
   {
@@ -1011,7 +1011,7 @@ export const testConfigNewFire: Config[] = [
       ],
     },
     showStyle: {
-      size: 'width:980px;height:300px;',
+      size: 'width:980px;height:280px;',
       version: '保德',
       position: 'bottom:8px;left:470px',
       headerPosition: 'centerBottom',
@@ -1122,9 +1122,9 @@ export const testConfigNewFire: Config[] = [
       to: '',
     },
     showStyle: {
-      size: 'width:980px;height:120px;',
+      size: 'width:800px;height:100px;',
       version: '保德',
-      position: 'top:85px;left:460px;',
+      position: 'top:60px;left:560px;',
       headerPosition: 'centerTop',
     },
   },
@@ -1194,7 +1194,7 @@ export const testConfigNewFire: Config[] = [
       preset: [],
     },
     showStyle: {
-      size: 'width:440px;height: 290px;',
+      size: 'width:440px;height: 280px;',
       version: '保德',
       position: 'top:60px;right:0;',
     },

+ 2 - 2
src/views/vent/home/configurable/configurable.data.ts

@@ -4499,11 +4499,11 @@ export const testConfigWarnMonitor: Config[] = [
         items: [
           {
             name: 'board',
-            basis: '20%',
+            basis: '25%',
           },
           {
             name: 'yj_ventWarn',
-            basis: '80%',
+            basis: '75%',
           },
         ],
       },

+ 3 - 7
src/views/vent/home/configurable/green/components/dz-card.vue

@@ -4,9 +4,7 @@
       <div class="gas-item">
         <div class="detail-box">
           <div class="detail-container">
-
-            <vue3-seamless-scroll hover-stop="true" :list="infoData.dataOn" :hover="true" :step="0.18"
-              class="seamless-warp">
+            <vue3-seamless-scroll hover-stop="true" :list="infoData.dataOn" :hover="true" :step="0.18" class="seamless-warp">
               <div class="detail-item" v-for="(item, index) in infoData.dataOn" :key="index">
                 <div class="item-box">
                   <div class="item-box-label">{{ titleLeft.address }}</div>
@@ -22,8 +20,6 @@
                 </div>
               </div>
             </vue3-seamless-scroll>
-
-
           </div>
         </div>
       </div>
@@ -157,7 +153,7 @@ watch(
             .item-box {
               width: 100%;
               padding: 0px 10px;
-              height: 60px;
+              height: 42px;
               display: flex;
               justify-content: space-between;
               align-items: center;
@@ -250,7 +246,7 @@ watch(
             .item-box {
               width: 100%;
               padding: 0px 10px;
-              height: 60px;
+              height: 42px;
               display: flex;
               justify-content: space-between;
               align-items: center;

+ 8 - 8
src/views/vent/home/configurable/green/components/dz-list.vue

@@ -1,5 +1,5 @@
 <template>
-  <div :class="deviceType == 'deviceManageInfo' ? 'dz-list1' : 'dz-list'">
+  <div :class="deviceType == 'deviceManageInfo' ? 'yj-list1' : 'yj-list'">
     <div :class="deviceType == 'deviceManageInfo' ? 'icons-box1' : 'icons-box'" @mouseleave="resetScroll">
       <template v-for="(item, key) in listOption" :key="key">
         <div class="icon-item">
@@ -63,17 +63,17 @@ watch(
 @import '/@/design/theme.less';
 
 @{theme-deepblue} {
-  .dz-list {
+  .yj-list {
     --image-model_icon-item: url('@/assets/images/themify/deepblue/home-container/configurable/1600.png');
   }
 }
 
-.dz-list {
+.yj-list {
   --image-model_icon-item: url('@/assets/images/home-green/1600.png');
   width: 100%;
   height: 100%;
 }
-.dz-list1 {
+.yj-list1 {
   --image-model_icon-item1: url('@/assets/images/home-warn/8-6.png');
   width: 100%;
   height: 100%;
@@ -92,7 +92,7 @@ watch(
     width: 100%;
     height: 46px;
     background: var(--image-model_icon-item) no-repeat;
-    background-size: 100% 100%;
+    background-size: 85% 100%;
     display: flex;
     align-items: center;
     justify-content: center;
@@ -108,7 +108,7 @@ watch(
       justify-content: space-around;
       position: absolute;
       top: 10px;
-      left: 76px;
+      left: 97;
       color: #ffffffe0;
       font-size: 14px;
       text-align: center;
@@ -159,7 +159,7 @@ watch(
   .icon-item {
     flex: 0 0 calc(50% - 5px);
     background: var(--image-model_icon-item1) no-repeat;
-    background-size: 100% 100%;
+    background-size: 94% 100%;
     height: 46px;
     position: relative;
     display: flex;
@@ -172,7 +172,7 @@ watch(
       justify-content: space-around;
       position: absolute;
       top: 10px;
-      left: 97px;
+      left: 78px;
       color: #ffffffe0;
       font-size: 14px;
       text-align: center;

+ 23 - 11
src/views/vent/home/configurable/vent182.vue

@@ -64,15 +64,17 @@
           </div>
         </div>
       </template>
-      <div
-        v-if="sysDataType === 'all'"
-        :class="{ 'realtime-mode': isDataRealTime }"
-        alt="切换数据模式"
-        class="switch-button report-mode right-525px"
-        @click="switchDataMode"
-      ></div>
-      <div class="switch-button icon-goto2D right-421px" @click="goMicroApp('2.5')"></div>
-      <div class="switch-button icon-goto3D right-481px" @click="goMicroApp('3')"></div>
+      <div class="switch-button-group">
+        <div
+          v-if="sysDataType === 'all'"
+          :class="{ 'realtime-mode': isDataRealTime }"
+          alt="切换数据模式"
+          class="switch-button report-mode"
+          @click="switchDataMode"
+        ></div>
+        <div v-if="hasPermission('show:modalChange')" class="switch-button icon-goto2D" @click="goMicroApp('2.5')"></div>
+        <div class="switch-button icon-goto3D" @click="goMicroApp('3')"></div>
+      </div>
     </template>
   </div>
 </template>
@@ -92,10 +94,12 @@
   import { useRoute, useRouter } from 'vue-router';
   import { useGlobSetting } from '/@/hooks/setting';
   import { testConfigVent182 } from './configurable.data';
+  import { usePermission } from '/@/hooks/web/usePermission';
 
   const { sysDataType = 'monitor', title = '智能通风管控系统' } = useGlobSetting();
   const { configs, isOriginal, isCommon, fetchConfigs } = useInitConfigs();
   const { mainTitle, enhancedConfigs, hiddenList, data, updateData, updateEnhancedConfigs } = useInitPage(title);
+  const { hasPermission } = usePermission();
   const route = useRoute();
   const router = useRouter();
   const isDataRealTime = ref(sysDataType === 'monitor');
@@ -108,7 +112,7 @@
   }
 
   function refresh() {
-    fetchConfigs('vent_182').then(() => {
+    fetchConfigs(isDataRealTime.value ? 'vent_182_realtime' : 'vent_182').then(() => {
       if (!configs.value.length) {
         configs.value = testConfigVent182;
       }
@@ -259,6 +263,13 @@
       border-bottom: 2px solid @vent-configurable-home-light-border;
     }
 
+    .switch-button-group {
+      display: flex;
+      position: absolute;
+      right: 430px;
+      top: 580px;
+    }
+
     .switch-button {
       width: 50px;
       height: 50px;
@@ -266,10 +277,11 @@
       padding: 8px;
       backdrop-filter: blur(10px);
       background-color: rgba(30, 58, 117, 0.418);
-      position: absolute;
+      //position: absolute;
       // right: 5px;
       bottom: 300px;
       z-index: 5;
+      margin: 0 5px;
     }
     .report-mode {
       &::after {

+ 318 - 318
src/views/vent/monitorManager/comment/components/DeviceBaseInfo.vue

@@ -11,344 +11,344 @@
   </BasicModal>
 </template>
 <script lang="ts" setup>
-import { onMounted, ref, defineEmits, onUnmounted, watch, computed } from 'vue';
-import { BasicModal, useModalInner } from '/@/components/Modal';
-import { BasicForm, useForm } from '/@/components/Form/index';
-import { FormSchema } from '/@/components/Form';
-import { getFormSchemaColumns } from '/@/hooks/web/useWebColumns';
-import { list as substationList } from '/@/views/vent/deviceManager/substationTabel/substation.api';
-import { getRegulation as getRegulationList } from '/@/views/vent/deviceManager/substationTabel/substation.api';
-import { list, updateDeviceInfo, updateReportInfo } from '../comment.api';
-import { getAutoScrollContainer } from '/@/utils/common/compUtils';
-import { on } from 'events';
+  import { onMounted, ref, defineEmits, onUnmounted, watch, computed } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form/index';
+  import { FormSchema } from '/@/components/Form';
+  import { getFormSchemaColumns } from '/@/hooks/web/useWebColumns';
+  import { list as substationList } from '/@/views/vent/deviceManager/substationTabel/substation.api';
+  import { getRegulation as getRegulationList } from '/@/views/vent/deviceManager/substationTabel/substation.api';
+  import { list, updateDeviceInfo, updateReportInfo } from '../comment.api';
+  import { getAutoScrollContainer } from '/@/utils/common/compUtils';
+  import { on } from 'events';
 
-const emit = defineEmits(['close', 'register']);
-const props = defineProps({
-  // formSchema: {
-  //   type: Array as PropType<FormSchema[]>,
-  //   default: () => [],
-  // },
-  deviceType: {
-    type: String,
-    default: '',
-  },
-});
-const FormRef = ref();
-const tabType = ref('deviceInfo');
-const formSchema = ref<Array<FormSchema>>([]);
-const formData = ref({});
-// const props.deviceType ref('');
-const deviceType = computed(() => props.deviceType);
-// const arrToFormColumns = (tableHeaderColumns = [], devicetype) => {
-//   const columnList: any[] = [];
-//   tableHeaderColumns.forEach((item: any) => {
-//     let columnsItem;
-//     if (item.type == 1 || item.type == 10) {
-//       columnsItem = {
-//         label: item.des, //_dictText
-//         field: item.monitorcode,
-//         component: item.type == 1 ? 'Input' : item.type == 10 ? 'InputTextArea' : '',
-//       };
-//     } else {
-//       if (item.type == 2 && item['monitorcode'] == 'nsubstationid') {
-//         columnsItem = {
-//           label: item.des, //_dictText
-//           field: item.monitorcode,
-//           component: 'ApiSelect',
-//           componentProps: {
-//             api: substationList,
-//             labelField: 'strname',
-//             valueField: 'id',
-//           },
-//         };
-//       }
-//       if (item.type == 3) {
-//         columnsItem = {
-//           label: item.des, //_dictText
-//           field: item.monitorcode,
-//           component: 'RadioGroup',
-//           defaultValue: 1,
-//           componentProps: () => {
-//             return {
-//               options: [
-//                 { label: '是', value: 1, key: '1' },
-//                 { label: '否', value: 0, key: '2' },
-//               ],
-//               stringToNumber: true,
-//             };
-//           },
-//         };
-//       }
-//       if (item.type == 4) {
-//         columnsItem = {
-//           label: item.des, //_dictText
-//           field: item.monitorcode,
-//           component: 'JDictSelectTag',
-//           componentProps: {
-//             dictCode: item.dict,
-//             placeholder: '请选择',
-//             stringToNumber: true,
-//           },
-//         };
-//       }
-//     }
-//     columnList.push(columnsItem);
-//   });
-//   formSchema.value = columnList
-//   if(tabType.value === 'deviceInfo'){
-//     formSchema.value.unshift(
-//       {
-//         label: '设备id', //_dictText
-//         field: 'id',
-//         component: 'Input',
-//         componentProps: {
-//           disabled: true,
-//           show: false
-//         },
-//       },
-//       {
-//         label: '点表',
-//         field: 'strtype',
-//         component: 'JDictSelectTag',
-//         componentProps: {
-//           dictCode: `${devicetype.split('_')[0]}kind`,
-//           placeholder: '请选择点表',
-//         },
-//       })
-//       formSchema.value.push(
-//       {
-//         label: '备用分站',
-//         field: 'stationids',
-//         component: 'ApiSelect',
-//         componentProps: {
-//           api: substationList,
-//           labelField: 'strname',
-//           valueField: 'id',
-//         },
-//       },
-//     )
-//   }else{
-//     formSchema.value.unshift(
-//       {
-//         label: '设备id', //_dictText
-//         field: 'id',
-//         component: 'Input',
-//         componentProps: {
-//           disabled: true,
-//           show: false
-//         },
-//       })
-//   }
-// };
+  const emit = defineEmits(['close', 'register']);
+  const props = defineProps({
+    // formSchema: {
+    //   type: Array as PropType<FormSchema[]>,
+    //   default: () => [],
+    // },
+    deviceType: {
+      type: String,
+      default: '',
+    },
+  });
+  const FormRef = ref();
+  const tabType = ref('deviceInfo');
+  const formSchema = ref<Array<FormSchema>>([]);
+  const formData = ref({});
+  // const props.deviceType ref('');
+  const deviceType = computed(() => props.deviceType);
+  // const arrToFormColumns = (tableHeaderColumns = [], devicetype) => {
+  //   const columnList: any[] = [];
+  //   tableHeaderColumns.forEach((item: any) => {
+  //     let columnsItem;
+  //     if (item.type == 1 || item.type == 10) {
+  //       columnsItem = {
+  //         label: item.des, //_dictText
+  //         field: item.monitorcode,
+  //         component: item.type == 1 ? 'Input' : item.type == 10 ? 'InputTextArea' : '',
+  //       };
+  //     } else {
+  //       if (item.type == 2 && item['monitorcode'] == 'nsubstationid') {
+  //         columnsItem = {
+  //           label: item.des, //_dictText
+  //           field: item.monitorcode,
+  //           component: 'ApiSelect',
+  //           componentProps: {
+  //             api: substationList,
+  //             labelField: 'strname',
+  //             valueField: 'id',
+  //           },
+  //         };
+  //       }
+  //       if (item.type == 3) {
+  //         columnsItem = {
+  //           label: item.des, //_dictText
+  //           field: item.monitorcode,
+  //           component: 'RadioGroup',
+  //           defaultValue: 1,
+  //           componentProps: () => {
+  //             return {
+  //               options: [
+  //                 { label: '是', value: 1, key: '1' },
+  //                 { label: '否', value: 0, key: '2' },
+  //               ],
+  //               stringToNumber: true,
+  //             };
+  //           },
+  //         };
+  //       }
+  //       if (item.type == 4) {
+  //         columnsItem = {
+  //           label: item.des, //_dictText
+  //           field: item.monitorcode,
+  //           component: 'JDictSelectTag',
+  //           componentProps: {
+  //             dictCode: item.dict,
+  //             placeholder: '请选择',
+  //             stringToNumber: true,
+  //           },
+  //         };
+  //       }
+  //     }
+  //     columnList.push(columnsItem);
+  //   });
+  //   formSchema.value = columnList
+  //   if(tabType.value === 'deviceInfo'){
+  //     formSchema.value.unshift(
+  //       {
+  //         label: '设备id', //_dictText
+  //         field: 'id',
+  //         component: 'Input',
+  //         componentProps: {
+  //           disabled: true,
+  //           show: false
+  //         },
+  //       },
+  //       {
+  //         label: '点表',
+  //         field: 'strtype',
+  //         component: 'JDictSelectTag',
+  //         componentProps: {
+  //           dictCode: `${devicetype.split('_')[0]}kind`,
+  //           placeholder: '请选择点表',
+  //         },
+  //       })
+  //       formSchema.value.push(
+  //       {
+  //         label: '备用分站',
+  //         field: 'stationids',
+  //         component: 'ApiSelect',
+  //         componentProps: {
+  //           api: substationList,
+  //           labelField: 'strname',
+  //           valueField: 'id',
+  //         },
+  //       },
+  //     )
+  //   }else{
+  //     formSchema.value.unshift(
+  //       {
+  //         label: '设备id', //_dictText
+  //         field: 'id',
+  //         component: 'Input',
+  //         componentProps: {
+  //           disabled: true,
+  //           show: false
+  //         },
+  //       })
+  //   }
+  // };
 
-const arrToFormColumns = (tableHeaderColumns = []) => {
-  const columnList: any[] = [];
-  tableHeaderColumns.forEach((item: any) => {
-    let columnsItem;
-    if (item.type == 1 || item.type == 10) {
-      columnsItem = {
-        label: item.des, //_dictText
-        field: item.monitorcode,
-        component: item.type == 1 ? 'Input' : item.type == 10 ? 'InputTextArea' : '',
-      };
-    } else {
-      if (item.type == 2 && item['monitorcode'] == 'nsubstationid') {
-        columnsItem = {
-          label: item.des, //_dictText
-          field: item.monitorcode,
-          component: 'ApiSelect',
-          componentProps: {
-            api: substationList,
-            labelField: 'strname',
-            valueField: 'id',
-          },
-        };
-      }
-      if (item.type == 3) {
-        columnsItem = {
-          label: item.des, //_dictText
-          field: item.monitorcode,
-          component: 'RadioGroup',
-          defaultValue: 1,
-          componentProps: () => {
-            return {
-              options: [
-                { label: '是', value: 1, key: '1' },
-                { label: '否', value: 0, key: '2' },
-              ],
-              stringToNumber: true,
-            };
-          },
-        };
-      }
-      if (item.type == 4) {
+  const arrToFormColumns = (tableHeaderColumns = []) => {
+    const columnList: any[] = [];
+    tableHeaderColumns.forEach((item: any) => {
+      let columnsItem;
+      if (item.type == 1 || item.type == 10) {
         columnsItem = {
           label: item.des, //_dictText
           field: item.monitorcode,
-          component: 'JDictSelectTag',
-          componentProps: {
-            dictCode: item.dict,
-            placeholder: '请选择',
-            // stringToNumber: true,
-          },
+          component: item.type == 1 ? 'Input' : item.type == 10 ? 'InputTextArea' : '',
         };
+      } else {
+        if (item.type == 2 && item['monitorcode'] == 'nsubstationid') {
+          columnsItem = {
+            label: item.des, //_dictText
+            field: item.monitorcode,
+            component: 'ApiSelect',
+            componentProps: {
+              api: substationList,
+              labelField: 'strname',
+              valueField: 'id',
+            },
+          };
+        }
+        if (item.type == 3) {
+          columnsItem = {
+            label: item.des, //_dictText
+            field: item.monitorcode,
+            component: 'RadioGroup',
+            defaultValue: 1,
+            componentProps: () => {
+              return {
+                options: [
+                  { label: '是', value: 1, key: '1' },
+                  { label: '否', value: 0, key: '2' },
+                ],
+                stringToNumber: true,
+              };
+            },
+          };
+        }
+        if (item.type == 4) {
+          columnsItem = {
+            label: item.des, //_dictText
+            field: item.monitorcode,
+            component: 'JDictSelectTag',
+            componentProps: {
+              dictCode: item.dict,
+              placeholder: '请选择',
+              // stringToNumber: true,
+            },
+          };
+        }
+        // date日期
+        if (item.type == 8) {
+          columnsItem = {
+            label: item.des, //_dictText
+            field: item.monitorcode,
+            component: 'DatePicker',
+            componentProps: {
+              showTime: false,
+              valueFormat: 'YYYY-MM-DD',
+              getPopupContainer: getAutoScrollContainer,
+            },
+          };
+        }
+        // 日期+时间
+        if (item.type == 9) {
+          columnsItem = {
+            label: item.des, //_dictText
+            field: item.monitorcode,
+            component: 'DatePicker',
+            componentProps: {
+              showTime: true,
+              valueFormat: 'YYYY-MM-DD HH:mm:ss',
+              getPopupContainer: getAutoScrollContainer,
+            },
+          };
+        }
       }
-      // date日期
-      if (item.type == 8) {
-        columnsItem = {
-          label: item.des, //_dictText
-          field: item.monitorcode,
-          component: 'DatePicker',
-          componentProps: {
-            showTime: false,
-            valueFormat: 'YYYY-MM-DD',
-            getPopupContainer: getAutoScrollContainer,
-          },
-        };
-      }
-      // 日期+时间
-      if (item.type == 9) {
-        columnsItem = {
-          label: item.des, //_dictText
-          field: item.monitorcode,
-          component: 'DatePicker',
-          componentProps: {
-            showTime: true,
-            valueFormat: 'YYYY-MM-DD HH:mm:ss',
-            getPopupContainer: getAutoScrollContainer,
-          },
-        };
-      }
-    }
-    columnList.push(columnsItem);
-  });
-  formSchema.value = columnList;
-  formSchema.value.unshift(
-    {
-      label: '设备id', //_dictText
-      field: 'id',
-      component: 'Input',
-      componentProps: {
-        disabled: true,
+      columnList.push(columnsItem);
+    });
+    formSchema.value = columnList;
+    formSchema.value.unshift(
+      {
+        label: '设备id', //_dictText
+        field: 'id',
+        component: 'Input',
+        componentProps: {
+          disabled: true,
+        },
       },
-    },
-    {
-      label: '点表',
-      field: 'strtype',
-      component: 'JDictSelectTag',
-      componentProps: {
-        dictCode: `${deviceType.value.split('_')[0]}kind`, //${deviceType.value}kind
-        placeholder: '请选择点表',
-        // stringToNumber: true,
+      {
+        label: '点表',
+        field: 'strtype',
+        component: 'JDictSelectTag',
+        componentProps: {
+          dictCode: `${deviceType.value.split('_')[0]}kind`, //${deviceType.value}kind
+          placeholder: '请选择点表',
+          // stringToNumber: true,
+        },
       },
-    },
-    {
-      label: '规程值',
-      field: 'regulation',
-      component: 'ApiSelect',
-      // componentProps: {
-      //   api: getRegulationList({ deviceKind: deviceType.value.split('_')[0] }),
-      //   labelField: 'name',
-      //   valueField: 'id',
-      // },
-      componentProps: ({ formModel }) => {
-        return {
-          api: getRegulationList.bind(null, { deviceKind: deviceType.value.split('_')[0] }),
-          labelField: 'name',
+      {
+        label: '规程值',
+        field: 'regulation',
+        component: 'ApiSelect',
+        // componentProps: {
+        //   api: getRegulationList({ deviceKind: deviceType.value.split('_')[0] }),
+        //   labelField: 'name',
+        //   valueField: 'id',
+        // },
+        componentProps: ({ formModel }) => {
+          return {
+            api: getRegulationList.bind(null, { deviceKind: deviceType.value.split('_')[0] }),
+            labelField: 'name',
+            valueField: 'id',
+            onChange: (e, option) => {
+              if (option) formModel['regulation'] = option['name'];
+            },
+          };
+        },
+      }
+    );
+
+    formSchema.value.push(
+      {
+        label: '备用分站',
+        field: 'stationids',
+        component: 'ApiSelect',
+        componentProps: {
+          api: substationList,
+          labelField: 'strname',
           valueField: 'id',
-          onChange: (e, option) => {
-            if (option) formModel['regulation'] = option['name'];
-          },
-        };
+        },
       },
+      {
+        label: '是否显示',
+        field: 'linkId',
+        component: 'RadioGroup',
+        defaultValue: 1,
+        componentProps: () => {
+          return {
+            options: [
+              { label: '是', value: 1, key: '1' },
+              { label: '否', value: 0, key: '2' },
+            ],
+            stringToNumber: true,
+          };
+        },
+      }
+    );
+  };
+  // 注册 modal
+  const [register, { closeModal, setModalProps }] = useModalInner(async (data) => {
+    tabType.value = data.type;
+    const deviceId = data.deviceId;
+    if (FormRef.value) {
+      setModalProps({ confirmLoading: false });
+      getColumns();
+      resetSchema(formSchema.value);
+      resetFields();
+      const result = await list({ id: deviceId });
+      formData.value = result['records'][0];
+      await setFieldsValue({
+        ...formData.value,
+      });
     }
-  );
+  });
 
-  formSchema.value.push(
-    {
-      label: '备用分站',
-      field: 'stationids',
-      component: 'ApiSelect',
-      componentProps: {
-        api: substationList,
-        labelField: 'strname',
-        valueField: 'id',
-      },
-    },
-    {
-      label: '是否显示',
-      field: 'linkId',
-      component: 'RadioGroup',
-      defaultValue: 1,
-      componentProps: () => {
-        return {
-          options: [
-            { label: '是', value: 1, key: '1' },
-            { label: '否', value: 0, key: '2' },
-          ],
-          stringToNumber: true,
-        };
-      },
+  const [registerForm, { resetSchema, getFieldsValue, setFieldsValue, resetFields }] = useForm({
+    schemas: formSchema.value,
+    showActionButtonGroup: false,
+  });
+
+  function getColumns() {
+    let formSchemaArr = getFormSchemaColumns(tabType.value === 'deviceInfo' ? `${props.deviceType}_edit` : `${props.deviceType}_input`) || [];
+    if (formSchemaArr && formSchemaArr.length < 1) {
+      const arr = props.deviceType.split('_');
+      formSchemaArr = getFormSchemaColumns(tabType.value === 'deviceInfo' ? arr[0] + '_edit' : arr[0] + '_input') || [];
     }
-  );
-};
-// 注册 modal
-const [register, { closeModal, setModalProps }] = useModalInner(async (data) => {
-  tabType.value = data.type;
-  const deviceId = data.deviceId;
-  if (FormRef.value) {
-    setModalProps({ confirmLoading: false });
-    getColumns();
-    resetSchema(formSchema.value);
-    resetFields();
-    const result = await list({ id: deviceId });
-    formData.value = result['records'][0];
-    await setFieldsValue({
-      ...formData.value,
-    });
+    arrToFormColumns(formSchemaArr);
   }
-});
 
-const [registerForm, { resetSchema, getFieldsValue, setFieldsValue, resetFields }] = useForm({
-  schemas: formSchema.value,
-  showActionButtonGroup: false,
-});
-
-function getColumns() {
-  let formSchemaArr = getFormSchemaColumns(tabType.value === 'deviceInfo' ? `${props.deviceType}_edit` : `${props.deviceType}_input`) || [];
-  if (formSchemaArr && formSchemaArr.length < 1) {
-    const arr = props.deviceType.split('_');
-    formSchemaArr = getFormSchemaColumns(tabType.value === 'deviceInfo' ? arr[0] + '_edit' : arr[0] + '_input') || [];
-  }
-  arrToFormColumns(formSchemaArr);
-}
+  watch(
+    () => props.deviceType,
+    () => {
+      getColumns();
+      if (FormRef.value) resetSchema(formSchema.value);
+    }
+  );
 
-watch(
-  () => props.deviceType,
-  () => {
-    getColumns();
-    if (FormRef.value) resetSchema(formSchema.value);
-  }
-);
+  async function onSubmit() {
+    let formValue = getFieldsValue();
+    setModalProps({ confirmLoading: true });
+    if (tabType.value === 'deviceInfo') {
+      await updateDeviceInfo(formValue);
+    } else {
+      await updateReportInfo(formValue);
+    }
 
-async function onSubmit() {
-  let formValue = getFieldsValue();
-  setModalProps({ confirmLoading: true });
-  if (tabType.value === 'deviceInfo') {
-    await updateDeviceInfo(formValue);
-  } else {
-    await updateReportInfo(formValue);
+    setModalProps({ confirmLoading: false });
+    emit('close');
+    closeModal();
   }
 
-  setModalProps({ confirmLoading: false });
-  emit('close');
-  closeModal();
-}
-
-onMounted(async () => {});
-onUnmounted(() => {});
+  onMounted(async () => {});
+  onUnmounted(() => {});
 </script>
 <style scoped lang="less">
-@import '/@/design/theme.less';
-@import '/@/design/vent/modal.less';
+  @import '/@/design/theme.less';
+  @import '/@/design/vent/modal.less';
 </style>

+ 2 - 2
src/views/vent/monitorManager/compressor/nitrogen.data.ts

@@ -253,8 +253,8 @@ export function getMonitorComponent() {
     default:
       // nitrogenHome = defineAsyncComponent(() => import('./components/nitrogenHome_blt.vue'));
       // nitrogenHome = defineAsyncComponent(() => import('./components/nitrogenHome_bet.vue'));
-     // nitrogenHome = defineAsyncComponent(() => import('./components/nitrogenHome.vue'));
-      nitrogenHome = defineAsyncComponent(() => import('./components/nitrogenHome_ln.vue'));
+      nitrogenHome = defineAsyncComponent(() => import('./components/nitrogenHome.vue'));
+      // nitrogenHome = defineAsyncComponent(() => import('./components/nitrogenHome_ln.vue'));
       return nitrogenHome;
   }
 }

+ 4 - 3
src/views/vent/monitorManager/deviceMonitor/components/device/index.vue

@@ -65,8 +65,8 @@
       <div :style="`padding: 5px; height: ${scroll.y + 100}px`">
         <div class="to-small">
           <div class="to-home" @click="toHome()"></div>
-          <div class="to-2D" @click="toHome('2D')"></div>
           <div class="to-3D" @click="toHome('3D')"></div>
+          <div v-if="hasPermission('show:modalChange')" class="to-2D" @click="toHome('2D')"></div>
           <FullscreenOutlined v-if="!tableShow" class="table-show-icon" @click="toHide" />
         </div>
         <div class="device-button-group" v-if="deviceList.length > 0">
@@ -1068,7 +1068,7 @@
 
               // 首次请求获取去所有regulation数据
               if (allRegulations === null) {
-                console.log(allRegulations, 'sssssssss');
+                // console.log(allRegulations, 'sssssssss');
                 const regulationRes = await getRegulation({
                   deviceKind: deviceType.value,
                 });
@@ -1909,7 +1909,7 @@
         padding: 8px;
         backdrop-filter: blur(10px);
         background-color: rgba(30, 58, 117, 0.418);
-        margin-right: 10px;
+
         cursor: pointer;
         &::after {
           width: 34px;
@@ -1933,6 +1933,7 @@
         padding: 8px;
         backdrop-filter: blur(10px);
         background-color: rgba(30, 58, 117, 0.418);
+        margin-right: 10px;
         cursor: pointer;
         &::after {
           width: 34px;

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

@@ -84,6 +84,7 @@
     } else {
       isShow.value = false;
     }
+
     actions.onGlobalStateChange((newState) => {
       const pageObj = newState['pageObj'];
       if (pageObj && pageObj.pageType) {

+ 1 - 1
src/views/vent/monitorManager/fanLocalMonitor/components/conditionAssistance.vue

@@ -212,7 +212,7 @@
       const item = assistanceData.value[key];
       assistanceDataList.push(item);
       lineEquationList.push(
-        `H${parseInt(item['Hz'])} = ${item['a']}Q² ${Number(item['b']) > 0 ? '+' : '-'} ${Math.abs(Number(item['b'])).toFixed(4)}Q ${
+        `F${parseInt(item['Hz'])} = ${item['a']}Q² ${Number(item['b']) > 0 ? '+' : '-'} ${Math.abs(Number(item['b'])).toFixed(4)}Q ${
           Number(item['c']) > 0 ? '+' : '-'
         } ${Math.abs(Number(item['c'])).toFixed(4)}`
       );

+ 4 - 4
src/views/vent/monitorManager/fanLocalMonitor/components/entryThree.vue

@@ -58,10 +58,10 @@
                 selectData.windQuantity2
                   ? selectData.windQuantity2
                   : selectData.m3
-                  ? selectData.m3
-                  : selectData.ductOutletAirVolume_merge
-                  ? selectData.ductOutletAirVolume_merge
-                  : '-'
+                    ? selectData.m3
+                    : selectData.ductOutletAirVolume_merge
+                      ? selectData.ductOutletAirVolume_merge
+                      : '-'
               }}</span>
               <span class="unit"> m³/min</span>
             </p>

+ 15 - 3
src/views/vent/monitorManager/fanLocalMonitor/fanLocal.data.ts

@@ -3585,9 +3585,9 @@ export const option = reactive<EChartsOption>({
       formatter: '{value}',
       color: '#0071A5',
     },
-    min: 360,
-    max: 9000,
-    name: 'Pa',
+    min: 15,
+    max: 60,
+    name: 'Hz',
     nameTextStyle: {
       fontWeight: 600,
       fontSize: 13,
@@ -3804,6 +3804,18 @@ export const initData = () => {
   return data;
 };
 
+export const initDataCY = () => {
+  const data: any[] = [];
+  data.push({
+    a: 0,
+    b: 0.16832181672974755,
+    c: 0.8547171776797801,
+    min: 111,
+    max: 295,
+  });
+  return data;
+};
+
 export const fanInfoData = reactive({
   fj: '一号回风井',
   xh: 'FBCDZ No.29',

+ 4 - 4
src/views/vent/monitorManager/fanLocalMonitor/fanLocalDual.threejs.base.ts

@@ -525,10 +525,10 @@ class ModelContext {
           selectData.airSupplyDistence_merge
             ? selectData.airSupplyDistence_merge
             : selectData.fchimenylength
-            ? selectData.fchimenylength
-            : selectData.airSupplyDistence_merge
-            ? selectData.airSupplyDistence_merge
-            : '-'
+              ? selectData.fchimenylength
+              : selectData.airSupplyDistence_merge
+                ? selectData.airSupplyDistence_merge
+                : '-'
         }`,
         font: 'normal 30px Arial',
         color: '#009900',

+ 126 - 78
src/views/vent/monitorManager/fanLocalMonitor/index.vue

@@ -11,12 +11,16 @@
   <div class="scene-box">
     <div class="top-box" v-if="!loading">
       <div class="top-center row">
-        <div class="vent-flex-row" id="fanLocalSelectDom" v-if="!globalConfig?.simulatedPassword && getDictItemsByCode('fanlocaltype')">
+        <div
+          class="vent-flex-row"
+          id="fanLocalSelectDom"
+          v-if="!globalConfig?.simulatedPassword && getDictItemsByCode(CONSTS.DICT_CODE_FANLOCALTYPE)"
+        >
           <span style="color: var(--vent-font-color); margin-left: 5px">风机类型:</span>
           <JDictSelectTag
             style="width: 180px"
-            v-model:value="devicekide"
-            dictCode="fanlocaltype"
+            v-model:value="devicekind"
+            :dictCode="CONSTS.DICT_CODE_FANLOCALTYPE"
             :showChooseOption="false"
             :getPopupContainer="getPopupContainer"
             @change="changeDeviceKind"
@@ -60,7 +64,7 @@
               fanTitles[0]
             }}</div>
             <div
-              v-if="selectData && ((selectData['install_kind'] && !selectData['install_kind'].endsWith('_1')) || !selectData['install_kind'])"
+              v-if="isShowStandbyFan"
               class="tab-item"
               :class="{ 'tab-item-active-r': warningMonitorRowIndex === 1 }"
               @click="selectDevice('warningMonitorRowIndex', 1)"
@@ -77,12 +81,8 @@
                   </div>
                   <div class="item-name">{{ data.title }}</div>
                   <div v-if="data.dataIndex.startsWith('Fan')">
-                    <div class="item-value" v-if="warningMonitorRowIndex == 0">{{
-                      selectData[data.dataIndex.replace('Fan', 'Fan1')] ? selectData[data.dataIndex.replace('Fan', 'Fan1')] : '-'
-                    }}</div>
-                    <div class="item-value" v-if="warningMonitorRowIndex == 1">{{
-                      selectData[data.dataIndex.replace('Fan', 'Fan2')] ? selectData[data.dataIndex.replace('Fan', 'Fan2')] : '-'
-                    }}</div>
+                    <div class="item-value" v-if="warningMonitorRowIndex == 0">{{ getFanValue(data.dataIndex, 'Fan1') }}</div>
+                    <div class="item-value" v-if="warningMonitorRowIndex == 1">{{ getFanValue(data.dataIndex, 'Fan2') }}</div>
                   </div>
                   <div v-else>
                     <div class="item-value">{{ selectData[data.dataIndex] ? selectData[data.dataIndex] : '-' }}</div>
@@ -91,7 +91,7 @@
               </template>
             </div>
           </div>
-          <div class="container-group" v-if="hasPermission('fanLocal:pjfx')">
+          <div class="container-group" v-if="hasPermission(CONSTS.PERMISSIONS.FAN_LOCAL_PJFX)">
             <div class="warning-header">
               <div class="header-item">
                 <div class="header-title">评价分析</div>
@@ -131,7 +131,7 @@
                 fanTitles[0]
               }}</div>
               <div
-                v-if="selectData && ((selectData['install_kind'] && !selectData['install_kind'].endsWith('_1')) || !selectData['install_kind'])"
+                v-if="isShowStandbyFan"
                 class="tab-item"
                 :class="{ 'tab-item-active-r': dataMonitorRowIndex == 1 }"
                 @click="selectDevice('dataMonitorRowIndex', 1)"
@@ -149,12 +149,8 @@
                         </div>
                         <div class="item-name">{{ state.title }}</div>
                         <div v-if="state.dataIndex.startsWith('Fan')">
-                          <div class="item-value" v-if="dataMonitorRowIndex == 0">{{
-                            selectData[state.dataIndex.replace('Fan', 'Fan1')] ? selectData[state.dataIndex.replace('Fan', 'Fan1')] : '-'
-                          }}</div>
-                          <div class="item-value" v-if="dataMonitorRowIndex == 1">{{
-                            selectData[state.dataIndex.replace('Fan', 'Fan2')] ? selectData[state.dataIndex.replace('Fan', 'Fan2')] : '-'
-                          }}</div>
+                          <div class="item-value" v-if="dataMonitorRowIndex == 0">{{ getFanValue(state.dataIndex, 'Fan1') }}</div>
+                          <div class="item-value" v-if="dataMonitorRowIndex == 1">{{ getFanValue(state.dataIndex, 'Fan2') }}</div>
                         </div>
                         <div v-else>
                           <div class="item-value">{{ selectData[state.dataIndex] ? selectData[state.dataIndex] : '-' }} </div>
@@ -182,38 +178,36 @@
                               <div
                                 class="signal-round"
                                 :class="{
-                                  'signal-round-run': selectData[state.dataIndex.replace('Fan', 'Fan1')] == '0',
+                                  'signal-round-run': getFanValue(state.dataIndex, 'Fan1') == '0',
                                   'signal-round-warning':
-                                    selectData[state.dataIndex.replace('Fan', 'Fan1')] !== undefined &&
-                                    selectData[state.dataIndex.replace('Fan', 'Fan1')] == '1',
-                                  'signal-round-gry': selectData[state.dataIndex.replace('Fan', 'Fan1')] === undefined,
+                                    getFanValue(state.dataIndex, 'Fan1') !== undefined && getFanValue(state.dataIndex, 'Fan1') == '1',
+                                  'signal-round-gry': getFanValue(state.dataIndex, 'Fan1') === undefined,
                                 }"
                               ></div>
                               <div class="vent-margin-l-8">{{
-                                selectData[state.dataIndex.replace('Fan', 'Fan1')] === undefined
+                                getFanValue(state.dataIndex, 'Fan1') === undefined
                                   ? '无状态'
-                                  : selectData[state.dataIndex.replace('Fan', 'Fan1')] == '0'
-                                  ? '正常'
-                                  : '异常'
+                                  : getFanValue(state.dataIndex, 'Fan1') == '0'
+                                    ? '正常'
+                                    : '异常'
                               }}</div>
                             </div>
                             <div class="signal-item" v-if="warningMonitorRowIndex == 1">
                               <div
                                 class="signal-round"
                                 :class="{
-                                  'signal-round-run': selectData[state.dataIndex.replace('Fan', 'Fan2')] == '0',
+                                  'signal-round-run': getFanValue(state.dataIndex, 'Fan2') == '0',
                                   'signal-round-warning':
-                                    selectData[state.dataIndex.replace('Fan', 'Fan2')] != undefined &&
-                                    selectData[state.dataIndex.replace('Fan', 'Fan2')] == '1',
-                                  'signal-round-gry': selectData[state.dataIndex.replace('Fan', 'Fan2')] == undefined,
+                                    getFanValue(state.dataIndex, 'Fan2') != undefined && getFanValue(state.dataIndex, 'Fan2') == '1',
+                                  'signal-round-gry': getFanValue(state.dataIndex, 'Fan2') == undefined,
                                 }"
                               ></div>
                               <div class="vent-margin-l-8">{{
-                                selectData[state.dataIndex.replace('Fan', 'Fan2')] == undefined
+                                getFanValue(state.dataIndex, 'Fan2') == undefined
                                   ? '无状态'
-                                  : selectData[state.dataIndex.replace('Fan', 'Fan2')] == '0'
-                                  ? '正常'
-                                  : '异常'
+                                  : getFanValue(state.dataIndex, 'Fan2') == '0'
+                                    ? '正常'
+                                    : '异常'
                               }}</div>
                             </div>
                           </div>
@@ -271,13 +265,13 @@
               <template v-if="globalConfig.History_Type == 'remote'">
                 <HistoryTable
                   :columns-type="`${selectData.deviceType}`"
-                  :device-type="`${devicekide}`"
+                  :device-type="`${devicekind}`"
                   designScope="fanlocal-history"
                   :scroll="scroll"
                 />
               </template>
               <template v-else>
-                <HistoryTable class="w-100% h-100%" :device-code="`${devicekide}`" :scroll="scroll" dict-code="fanlocal_dict" />
+                <HistoryTable class="w-100% h-100%" :device-code="`${devicekind}`" :scroll="scroll" dict-code="fanlocal_dict" />
               </template>
             </div>
           </a-tab-pane>
@@ -298,14 +292,14 @@
           </a-tab-pane>
           <a-tab-pane key="4" tab="报警历史">
             <div class="tab-item" v-if="activeKey === '4'">
-              <AlarmHistoryTable columns-type="alarm" :device-type="`${devicekide}`" designScope="alarm-history" :scroll="scroll" />
+              <AlarmHistoryTable columns-type="alarm" :device-type="`${devicekind}`" designScope="alarm-history" :scroll="scroll" />
             </div>
           </a-tab-pane>
           <a-tab-pane key="5" tab="操作历史">
             <div class="tab-item" v-if="activeKey === '5'">
               <HandlerHistoryTable
                 columns-type="operator_history"
-                :device-type="`${devicekide}`"
+                :device-type="`${devicekind}`"
                 :device-list-api="baseList"
                 designScope="alarm-history"
                 :scroll="scroll"
@@ -358,7 +352,7 @@
           </a-tab-pane>
         </a-tabs>
         <a-button
-          v-if="hasPermission('btn:reportDown')"
+          v-if="hasPermission(CONSTS.PERMISSIONS.BTN_REPORT_DOWN)"
           type="primary"
           size="small"
           preIcon="ant-design:download-outlined"
@@ -399,10 +393,7 @@
             <a-radio value="stop">停止</a-radio>
           </a-radio-group>
         </div>
-        <div
-          v-if="selectData && ((selectData['install_kind'] && !selectData['install_kind'].endsWith('_1')) || !selectData['install_kind'])"
-          class="startSmoke-select"
-        >
+        <div v-if="isShowStandbyFan" class="startSmoke-select">
           <div class="label">备机:</div>
           <a-radio-group v-model:value="mainWindIsShow2" @change="changeMotor" name="localWind2">
             <a-radio value="open">开启</a-radio>
@@ -418,10 +409,7 @@
           <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
-          v-if="selectData && ((selectData['install_kind'] && !selectData['install_kind'].endsWith('_1')) || !selectData['install_kind'])"
-          class="startSmoke-select"
-        >
+        <div v-if="isShowStandbyFan" 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>
@@ -434,10 +422,7 @@
           <div class="label">主机:</div>
           <div :class="{ 'button-box': true, 'button-disable': false }" @click="handleOk('Fan1remote')">远程/就地切换</div>
         </div>
-        <div
-          v-if="selectData && ((selectData['install_kind'] && !selectData['install_kind'].endsWith('_1')) || !selectData['install_kind'])"
-          class="startSmoke-select"
-        >
+        <div v-if="isShowStandbyFan" class="startSmoke-select">
           <div class="label">备机:</div>
           <div :class="{ 'button-box': true, 'button-disable': false }" @click="handleOk('Fan2remote')">远程/就地切换</div>
         </div>
@@ -445,17 +430,36 @@
       <!-- 调频 -->
       <div class="vent-flex-row input-box" v-if="controlType == 'Fan1Frequency'">
         <div class="label">主风机运行频率(Hz):</div>
-        <a-input-number size="small" v-model:value="fan1FrequencyVal" :min="20" :max="50" :step="0.1" />
+        <a-input-number
+          size="small"
+          v-model:value="fan1FrequencyVal"
+          :min="CONSTS.FAN_FREQ_MIN"
+          :max="CONSTS.FAN_FREQ_MAX"
+          :step="CONSTS.FAN_FREQ_STEP"
+        />
       </div>
       <div class="vent-flex-row input-box" v-if="controlType == 'Fan2Frequency'">
         <div class="label">备风机运行频率(Hz):</div>
-        <a-input-number size="small" v-model:value="fan2FrequencyVal" :min="20" :max="50" :step="0.1" />
+        <!-- 优化:使用常量替代硬编码 -->
+        <a-input-number
+          size="small"
+          v-model:value="fan2FrequencyVal"
+          :min="CONSTS.FAN_FREQ_MIN"
+          :max="CONSTS.FAN_FREQ_MAX"
+          :step="CONSTS.FAN_FREQ_STEP"
+        />
       </div>
       <div class="vent-flex-row input-box" v-if="controlType == 'FanFrequency'">
         <div class="label">风机运行频率(Hz):</div>
-        <a-input-number size="small" v-model:value="fan1FrequencyVal" :min="20" :max="50" :step="0.1" />
+        <!-- 优化:使用常量替代硬编码 -->
+        <a-input-number
+          size="small"
+          v-model:value="fan1FrequencyVal"
+          :min="CONSTS.FAN_FREQ_MIN"
+          :max="CONSTS.FAN_FREQ_MAX"
+          :step="CONSTS.FAN_FREQ_STEP"
+        />
       </div>
-
       <div class="vent-flex-row input-box" v-if="controlType == 'disAirAlarm'">
         <div class="label">漏风率(%):</div>
         <a-input-number size="small" v-model:value="frequencyVal" :min="0" :max="50" :step="0.1" />
@@ -542,7 +546,7 @@
 </template>
 
 <script setup lang="ts">
-  import { ExclamationCircleFilled, ArrowRightOutlined, VideoCameraOutlined } from '@ant-design/icons-vue';
+  import { ExclamationCircleFilled, VideoCameraOutlined } from '@ant-design/icons-vue';
   import {
     onBeforeMount,
     ref,
@@ -558,8 +562,8 @@
     shallowReactive,
     shallowRef,
     onBeforeUnmount,
+    readonly,
   } from 'vue';
-  // import BarSingle from '../../../../components/chart/BarSingle.vue';
   import FanDeviceEcharts from '../comment/FanDeviceEcharts.vue';
   import BarAndLine from '../../../../components/chart/BarAndLine.vue';
   import GroupMonitorTable from '../comment/GroupMonitorTable.vue';
@@ -594,6 +598,37 @@
   import { useMessage } from '/@/hooks/web/useMessage';
   import { cloneDeep } from 'lodash-es';
 
+  // 优化:提取硬编码为模块级常量
+  const CONSTS = {
+    // 频率相关常量
+    FAN_FREQ_MIN: 20,
+    FAN_FREQ_MAX: 50,
+    FAN_FREQ_STEP: 0.1,
+    // 瓦斯报警值常量
+    GAS_WARNING_VAL_DEFAULT: 0.6,
+    GAS_WARNING_VAL1_DEFAULT: 0.8,
+    // 设备类型/字典常量
+    DEVICE_TYPE_FANLOCAL: 'fanlocal',
+    DICT_CODE_FANLOCALTYPE: 'fanlocaltype',
+    // 风机名称常量
+    FAN_NAME_MAIN: '主机',
+    FAN_NAME_STANDBY: '备机',
+    // 滚动高度默认值
+    SCROLL_Y_DEFAULT: 180,
+    // 权限码常量(统一管理,避免拼写错误)
+    PERMISSIONS: {
+      FAN_LOCAL_PJFX: 'fanLocal:pjfx',
+      FAN_LOCAL_SHOW_CAMERA: 'fanlocal:showCamera',
+      BTN_REPORT_DOWN: 'btn:reportDown',
+    } as const,
+  } as const;
+
+  /** 列配置项 */
+  interface FanColumnItem {
+    title: string;
+    value?: string | number;
+  }
+
   const globalConfig = inject('globalConfig');
   const HistoryTable =
     globalConfig.History_Type == 'remote'
@@ -610,12 +645,12 @@
   const router = useRouter();
   const { createConfirm } = useMessage();
   const globSetting = useGlobSetting();
-  const showPlay = ref(hasPermission('fanlocal:showCamera') ? true : false);
+  const showPlay = ref(hasPermission(CONSTS.PERMISSIONS.FAN_LOCAL_SHOW_CAMERA) ? true : false);
   const computedStyle = computed(() => {
-    const hasMatchingPermission = hasPermission('fanLocal:pjfx');
+    const hasMatchingPermission = hasPermission(CONSTS.PERMISSIONS.FAN_LOCAL_PJFX);
     return hasMatchingPermission ? 'max-height: 200px' : '';
   });
-  const fanTitles = globSetting.sysOrgCode === 'zmhjhzmy' ? ['1#风机', '2#风机'] : ['主机', '备机'];
+  const fanTitles = globSetting.sysOrgCode === 'zmhjhzmy' ? ['1#风机', '2#风机'] : [CONSTS.FAN_NAME_MAIN, CONSTS.FAN_NAME_STANDBY];
   const modalTypeArr = reactive({
     leftBtnArr: [
       {
@@ -759,7 +794,7 @@
       },
     ],
   });
-  const sensorList = ref<any[]>([
+  const sensorList = readonly([
     {
       value: '1',
       label: 'T1',
@@ -772,14 +807,14 @@
       value: '3',
       label: 'T3',
     },
-  ]);
+  ] as const);
 
   const scroll = reactive({
-    y: 180,
+    y: CONSTS.SCROLL_Y_DEFAULT,
   });
-  const deviceTypeDicts = getDictItemsByCode('fanlocaltype');
-  const gasWarningVal = ref(0.6); // 瓦斯最大报警值
-  const gasWarningVal1 = ref(0.8); // 瓦斯最大报警值
+  const deviceTypeDicts = getDictItemsByCode(CONSTS.DICT_CODE_FANLOCALTYPE);
+  const gasWarningVal = ref(CONSTS.GAS_WARNING_VAL_DEFAULT);
+  const gasWarningVal1 = ref(CONSTS.GAS_WARNING_VAL1_DEFAULT);
   const playerRef = ref();
   const MonitorDataTable = ref();
   const modalSensor = ref(null);
@@ -809,7 +844,7 @@
   // 设备数据
   const controlType = ref('');
   const modalType = ref('');
-  const mainModelType = ref('fanLocal');
+  const mainModelType = ref(CONSTS.DEVICE_TYPE_FANLOCAL);
   // 监测数据
   const initData = {
     deviceID: '',
@@ -834,17 +869,17 @@
   const currentDeviceData = ref({});
   const rightColumns = ref<BasicColumn[]>([]);
   const leftColumns = ref<BasicColumn[]>([]);
-  const leftColumns1 = ref<any[]>([
+  const leftColumns1 = ref<FanColumnItem[]>([
     { title: '供风充足评价', value: '' },
     { title: '回风流超限评价', value: '' },
     { title: '风筒漏风率评价', value: '' },
   ]);
-  const leftColumns2 = ref<any[]>([
+  const leftColumns2 = ref<FanColumnItem[]>([
     { title: '供风充足评价', value: '充足' },
     { title: '回风流超限评价', value: '正常' },
     { title: '风筒漏风率评价', value: '正常' },
   ]);
-  const devicekide = ref(deviceTypeDicts && deviceTypeDicts.length > 0 ? deviceTypeDicts[0]['value'] : 'fanlocal');
+  const devicekind = ref(deviceTypeDicts && deviceTypeDicts.length > 0 ? deviceTypeDicts[0]['value'] : CONSTS.DEVICE_TYPE_FANLOCAL);
   const deviceType = ref(selectData.deviceType);
   const headElHeight = ref(0);
   let btnClick = ref(true); // 判断按钮是否可点
@@ -898,7 +933,7 @@
   };
 
   const changeDeviceKind = (e) => {
-    devicekide.value = e;
+    devicekind.value = e;
     loading.value = true;
     selectRowIndex.value = -1;
     nextTick(() => {
@@ -932,6 +967,20 @@
     }
   };
 
+  // 优化:封装主备机数据取值方法,避免模板重复逻辑
+  const getFanValue = (dataIndex: string, fanType: 'Fan1' | 'Fan2') => {
+    if (!selectData) return '-';
+    const targetKey = dataIndex.startsWith('Fan') ? dataIndex.replace('Fan', fanType) : dataIndex;
+    return selectData[targetKey] ?? '-';
+  };
+
+  // 优化:重复判断抽离为计算属性,利用缓存减少重复计算
+  const isShowStandbyFan = computed(() => {
+    if (!selectData) return false;
+    const installKind = selectData.install_kind;
+    return installKind ? !installKind.endsWith('_1') : true;
+  });
+
   //报表导出点击
   async function reportDown() {
     openModal1();
@@ -950,7 +999,7 @@
     openModal();
   }
 
-  function goDetailDevice(link需风量DeviceCode) {
+  function goDetailDevice(linkDeviceCode) {
     let linkDeviceId = '';
     if (linkDeviceCode.startsWith('window')) {
       linkDeviceId = linkDeviceInfo.value[linkDeviceCode] ? linkDeviceInfo.value[linkDeviceCode]['id'] : '';
@@ -960,8 +1009,8 @@
 
   //
   async function getDataSource() {
-    if (devicekide.value) {
-      const res = await list({ devicetype: devicekide.value, pagetype: 'normal' });
+    if (devicekind.value) {
+      const res = await list({ devicetype: devicekind.value, pagetype: 'normal' });
       // const res = await list({ devicetype: 'fanlocal', pagetype: 'normal' });
       if (res.msgTxt && res.msgTxt[0] && res.msgTxt[0].datalist && res.msgTxt[0].datalist.length > 0) {
         const dataArr = res.msgTxt[0].datalist || [];
@@ -1053,7 +1102,6 @@
     if (Object.prototype.toString.call(timer) === '[object Null]') {
       timer = await setTimeout(
         async () => {
-          // debugger;
           await getDataSource();
           if (dataSource.value.length > 0 && selectRowIndex.value == -1 && MonitorDataTable.value) {
             // 初始打开页面
@@ -1071,7 +1119,7 @@
             if (dataSource.value.length > 0 && dataSource.value[selectRowIndex.value]) {
               Object.assign(selectData, dataSource.value[selectRowIndex.value]);
             }
-            selectData['modalTyoe'] == selectData['install_kind'];
+            selectData['modalTyoe'] = selectData['install_kind'];
             playSmoke(selectData);
             modelRef.value?.animate?.(selectData);
             addText(selectData, fanDualArray.value);
@@ -1162,7 +1210,7 @@
   function showModal(obj) {
     if (!btnClick.value) return;
     if (obj.key == 'kkjc') {
-      gasWarningVal.value = 0.6;
+      gasWarningVal.value = CONSTS.GAS_WARNING_VAL_DEFAULT;
       // 工况辅助决策
       openAssistance(true, {});
       return;
@@ -1612,7 +1660,7 @@
 
   onMounted(() => {
     const { query } = unref(currentRoute);
-    if (query['deviceType']) devicekide.value = query['deviceType'] as string;
+    if (query['deviceType']) devicekind.value = query['deviceType'] as string;
     if (globalConfig.is2DModel) {
       getMonitor(true);
     } else {

+ 117 - 25
src/views/vent/monitorManager/mainFanMonitor/components/entryThree.vue

@@ -7,7 +7,7 @@
       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="elementTag" id="inputBox1" v-if="backMonitorIsShow">
           <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"
@@ -18,7 +18,7 @@
             >
           </div>
         </div>
-        <div class="elementTag" id="inputBox" v-if="frontMonitorIsShow && false">
+        <div class="elementTag" id="inputBox" v-if="frontMonitorIsShow">
           <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"
@@ -29,7 +29,7 @@
             >
           </div>
         </div>
-        <div class="elementTag" id="inputBox2" v-if="centerMonitorIsShow && false">
+        <div class="elementTag" id="inputBox2" v-if="centerMonitorIsShow">
           <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"
@@ -41,29 +41,29 @@
           </div>
         </div>
         <!-- 18模拟反风锁井 -->
-        <!-- <div v-if="hasPermission('mainFan:ffsjMonitor')" class="elementTag" id="fbm">
+        <div v-if="hasPermission('mainFan:ffsjMonitor_ljh')" 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">
+                <template v-if="explosionDoorData['openGate'] == 1 && explosionDoorData['closeGate'] == 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">
+                <template v-else-if="explosionDoorData['openGate'] == 0 && explosionDoorData['closeGate'] == 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
-                  >
+                  <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'
+                    explosionDoorData['lock1Open'] == 1 &&
+                    explosionDoorData['lock1Close'] == 0 &&
+                    explosionDoorData['lock2Open'] == 1 &&
+                    explosionDoorData['lock2Close'] == 0
                   "
                 >
                   <span class="data-title"><span class="signal-round signal-round-blue vent-margin-r-8"></span>锁1开</span>
@@ -71,36 +71,70 @@
                 </template>
                 <template
                   v-else-if="
-                    selectData['Lock1Open'] == '0' && selectData['Lock1Close'] == 1 && selectData['Lock2Open'] == '0' && selectData['Lock2Close'] == 1
+                    explosionDoorData['lock1Open'] == 0 &&
+                    explosionDoorData['lock1Close'] == 1 &&
+                    explosionDoorData['lock2Open'] == 0 &&
+                    explosionDoorData['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
-                  >
+                  <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"
+              <!-- <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>{{ get(explosionDoorData, 'zhengya') }}</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 class="element-item"
+                ><span class="data-title">井口温度(℃):</span><span>{{ get(explosionDoorData, 'tempreture') }}</span></div
+              >
+              <div class="element-item"
+                ><span class="data-title">甲烷浓度(%):</span><span>{{ get(explosionDoorData, 'ch4') }}</span></div
+              >
+              <div class="element-item"
+                ><span class="data-title">CO浓度(%):</span><span>{{ get(explosionDoorData, 'co') }}</span></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': get(explosionDoorData, 'isRemote') == 1 }"
+                    ></span
+                    >远程</span
+                  >
+                  <span class="data-title"
+                    ><span class="signal-round vent-margin-r-8" :class="{ 'signal-round-blue': get(explosionDoorData, 'isRemote') == 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': get(explosionDoorData, 'isrun') == 1 }"
+                    ></span
+                    >正常</span
+                  >
+                  <span class="data-title"
+                    ><span class="signal-round vent-margin-r-8" :class="{ 'signal-round-blue': get(explosionDoorData, 'isrun') == 0 }"></span
+                    >检修</span
+                  >
+                </div>
               </div>
             </div>
           </div>
-        </div> -->
+        </div>
         <div v-if="hasPermission('mainFan:ffsjMonitor')">
           <div class="elementContent elementContent-r fbm-box">
             <div class="fbm-video">
@@ -236,6 +270,7 @@
 </template>
 <script lang="ts" setup>
   import { usePermission } from '/@/hooks/web/usePermission';
+  import { get } from '@/utils/ventutil';
 
   defineProps<{
     loading: boolean;
@@ -249,4 +284,61 @@
 </script>
 <style scoped lang="less">
   @import '/@/design/vent/modal.less';
+  .threejs-Object-CSS {
+    .elementTag {
+      .elementContent {
+        backdrop-filter: blur(2px);
+        width: 220px;
+        background-color: #00000099;
+        box-shadow: 0px 0px 40px #005a838c inset;
+        border: 2px solid #99b0c38c;
+        padding: 15px 20px 15px 20px;
+        font-size: 15px;
+
+        .element-item {
+          line-height: 38px;
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+        }
+
+        span {
+          color: #f2a500;
+        }
+
+        .data-title {
+          max-width: 190px;
+          display: inline-block;
+          color: var(--vent-font-color);
+        }
+      }
+
+      .elementContent-r {
+        &::before {
+          right: 120px;
+        }
+
+        &::after {
+          right: 270px;
+          border: 4px solid #00ddff;
+          background: #a7a7a766;
+        }
+      }
+
+      .fbm-box {
+        width: auto;
+        display: flex;
+        justify-content: space-between;
+
+        .fbm-video {
+          width: 240px;
+          margin-right: 20px;
+        }
+
+        .fbm-data {
+          width: 200px;
+        }
+      }
+    }
+  }
 </style>

+ 17 - 57
src/views/vent/monitorManager/mainFanMonitor/index.vue

@@ -521,6 +521,13 @@
           <div :class="{ 'button-box': true, 'button-disable': false }" @click="handleOk('Fan3Stop')">停止</div>
         </div>
       </div>
+      <div class="" v-if="modalType == 'sdyc' && hasPermission('fan:sdyc')">
+        <!-- 不互斥控制 -->
+        <div class="startSmoke-select">
+          <div :class="{ 'button-box': true, 'button-disable': false }" @click="handleOk('zdms')">自动模式</div>
+          <div :class="{ 'button-box': true, 'button-disable': false }" @click="handleOk('sdms')">手动模式</div>
+        </div>
+      </div>
       <!-- 防爆门控制 -->
       <!-- <div class="" v-if="modalType == 'fbm'">
         <div class="vent-margin-t-20">
@@ -669,6 +676,7 @@
   import { useMessage } from '/@/hooks/web/useMessage';
   import { BorderBox8 as DvBorderBox8 } from '@kjgl77/datav-vue3';
   import { useRouter } from 'vue-router';
+  // import ConditionAssistance from './components/conditionAssistanceCY.vue';
   import ConditionAssistance from './components/conditionAssistance.vue';
   import setValueSetting from './components/setValueSetting.vue';
   import AnalysisTable from './components/AnalysisTable.vue';
@@ -961,7 +969,7 @@
         linkDevices.filter((item) => {
           if (item.deviceType == 'faultEqu') {
             faultDevice = item;
-          } else if (item.deviceType == 'antiwindLock') {
+          } else if (item.deviceType == 'antiwindLock' || item.deviceType == 'explosionProof') {
             // 反风锁井
             doorDevice = item;
           }
@@ -1594,6 +1602,14 @@
           .catch((err) => {
             createMessage.error(err.message);
           });
+      } else {
+        start(flag ? flag : modalType.value)
+          .then(() => {
+            createMessage.success('指令下发成功');
+          })
+          .catch((err) => {
+            createMessage.error(err.message);
+          });
       }
       modalIsShow.value = false;
     };
@@ -2252,62 +2268,6 @@
     font-size: 28px;
   }
 
-  .threejs-Object-CSS {
-    .elementTag {
-      .elementContent {
-        backdrop-filter: blur(2px);
-        width: 220px;
-        background-color: #00000099;
-        box-shadow: 0px 0px 40px #005a838c inset;
-        border: 2px solid #99b0c38c;
-        padding: 15px 20px 15px 20px;
-        font-size: 15px;
-
-        .element-item {
-          line-height: 38px;
-          display: flex;
-          justify-content: space-between;
-        }
-
-        span {
-          color: #f2a500;
-        }
-
-        .data-title {
-          display: inline-block;
-          color: var(--vent-font-color);
-        }
-      }
-
-      .elementContent-r {
-        &::before {
-          right: 120px;
-        }
-
-        &::after {
-          right: 270px;
-          border: 4px solid #00ddff;
-          background: #a7a7a766;
-        }
-      }
-
-      .fbm-box {
-        width: auto;
-        display: flex;
-        justify-content: space-between;
-
-        .fbm-video {
-          width: 240px;
-          margin-right: 20px;
-        }
-
-        .fbm-data {
-          width: 200px;
-        }
-      }
-    }
-  }
-
   .btn {
     padding: 4px 12px;
     position: relative;

+ 253 - 5
src/views/vent/monitorManager/mainFanMonitor/main.data.ts

@@ -418,6 +418,16 @@ export const modalTypeArr = {
       value: '自主联控',
       permission: 'fan:zhlk',
     },
+    {
+      key: 'sdyc',
+      value: '手动/远程',
+      permission: 'fan:sdyc',
+    },
+    {
+      key: 'zzcz',
+      value: '终止操作',
+      permission: 'fan:zzcz',
+    },
   ],
 };
 
@@ -811,9 +821,10 @@ export const assistanceData = {
 };
 
 export const setOption = (deviceType?) => {
-  let yMax = 4500;
+  let yMax = 4500,
+    yMin = 0;
   const { sysOrgCode } = useGlobSetting();
-  // const sysOrgCode = 'yjmdhswmk';
+  // const sysOrgCode = 'sdmtjtbetmk';
   if (sysOrgCode == 'sdmtjtdltmk') {
     // 这里判断白家渠还是五当沟
     if (deviceType == 'fanmain_bjq') {
@@ -851,6 +862,9 @@ export const setOption = (deviceType?) => {
     yMax = 5000;
   } else if (sysOrgCode == 'yjmdhswmk') {
     yMax = 5000;
+  } else if (deviceType == 'cy') {
+    yMax = 4000;
+    yMin = 800;
   } else {
     yMax = 4500;
   }
@@ -913,8 +927,8 @@ export const setOption = (deviceType?) => {
         formatter: '{value}',
         color: '#0071A5',
       },
-      min: 0,
-      max: yMax, //武当沟
+      min: yMin,
+      max: yMax,
       name: 'Pa',
       nameTextStyle: {
         fontWeight: 600,
@@ -1159,10 +1173,244 @@ export const initData1 = () => {
   return data;
 };
 
+export const initData2 = () => {
+  const assistanceData = {
+    // '30': {
+    //   angle: 0,
+    //   Hz: 30.0, //频率
+    //   a: -0.17624, //二次项系数
+    //   b: 43.40947, //一次项系数
+    //   c: 97.60705999999982, //常数项系数
+    //   min: 93.0, //风量下限
+    //   max: 171.0, //风量上限
+    // },
+    // '31': {
+    //   angle: 0,
+    //   Hz: 31.0,
+    //   a: -0.17624,
+    //   b: 43.427094,
+    //   c: 95.43614590000016,
+    //   min: 96.1,
+    //   max: 174.4,
+    // },
+    // '32': {
+    //   angle: 0,
+    //   Hz: 32.0,
+    //   a: -0.17624,
+    //   b: 43.444718,
+    //   c: 93.26435059999994,
+    //   min: 99.2,
+    //   max: 177.8,
+    // },
+    // '33': {
+    //   angle: 0,
+    //   Hz: 33.0,
+    //   a: -0.17624,
+    //   b: 43.462342,
+    //   c: 91.09167409999964,
+    //   min: 102.3,
+    //   max: 180.2,
+    // },
+    // '34': {
+    //   angle: 0,
+    //   Hz: 34.0,
+    //   a: -0.17624,
+    //   b: 43.479966000000005,
+    //   c: 88.91811639999969,
+    //   min: 105.4,
+    //   max: 182.0,
+    // },
+    // '35': {
+    //   angle: 0,
+    //   Hz: 35.0,
+    //   a: -0.17624,
+    //   b: 43.49759,
+    //   c: 86.74367749999965,
+    //   min: 108.5,
+    //   max: 184.5,
+    // },
+    '36': {
+      angle: 0,
+      Hz: 36.0,
+      a: -0.014,
+      b: 11.01024,
+      c: 414.6167,
+      min: 330,
+      max: 710,
+    },
+    // '37': {
+    //   angle: 0,
+    //   Hz: 37.0,
+    //   a: -0.17624,
+    //   b: 43.532838,
+    //   c: 82.3921561000002,
+    //   min: 114.7,
+    //   max: 190.8,
+    // },
+    // '38': {
+    //   angle: 0,
+    //   Hz: 38.0,
+    //   a: -0.17624,
+    //   b: 43.550462,
+    //   c: 80.21507359999941,
+    //   min: 117.8,
+    //   max: 193.0,
+    // },
+    '39': {
+      angle: 0,
+      Hz: 39.0,
+      a: -0.0132,
+      b: 10.500657,
+      c: 785.54678,
+      min: 370,
+      max: 760,
+    },
+    // '40': {
+    //   angle: 0,
+    //   Hz: 40.0,
+    //   a: -0.17624,
+    //   b: 43.58571,
+    //   c: 75.85826499999985,
+    //   min: 123.0,
+    //   max: 197.0,
+    // },
+    // '41': {
+    //   angle: 0,
+    //   Hz: 41.0,
+    //   a: -0.17624,
+    //   b: 43.603334,
+    //   c: 73.67853890000015,
+    //   min: 125.1,
+    //   max: 199.4,
+    // },
+    '42': {
+      angle: 0,
+      Hz: 42.0,
+      a: -0.012,
+      b: 10.03424,
+      c: 971.438895,
+      min: 390,
+      max: 820,
+    },
+    // '43': {
+    //   angle: 0,
+    //   Hz: 43.0,
+    //   a: -0.17624,
+    //   b: 43.638582,
+    //   c: 69.31644309999956,
+    //   min: 130.3,
+    //   max: 205.8,
+    // },
+    // '44': {
+    //   angle: 0,
+    //   Hz: 44.0,
+    //   a: -0.17624,
+    //   b: 43.656206000000005,
+    //   c: 67.13407339999958,
+    //   min: 132.4,
+    //   max: 208.5,
+    // },
+    // '45': {
+    //   angle: 0,
+    //   Hz: 45.0,
+    //   a: -0.17624,
+    //   b: 43.67383,
+    //   c: 64.9508224999995,
+    //   min: 134.5,
+    //   max: 211.8,
+    // },
+    '46': {
+      angle: 0,
+      Hz: 46.0,
+      a: -0.012,
+      b: 10.551334,
+      c: 967.32433,
+      min: 430,
+      max: 865,
+    },
+    // '47': {
+    //   angle: 0,
+    //   Hz: 47.0,
+    //   a: -0.17624,
+    //   b: 43.709078,
+    //   c: 60.58167709999998,
+    //   min: 138.7,
+    //   max: 217.8,
+    // },
+    // '48': {
+    //   angle: 0,
+    //   Hz: 48.0,
+    //   a: -0.17624,
+    //   b: 43.726702,
+    //   c: 58.39578259999962,
+    //   min: 140.8,
+    //   max: 219.2,
+    // },
+    '49': {
+      angle: 0,
+      Hz: 49.0,
+      a: -0.0115,
+      b: 10.8013546,
+      c: 913.4355668,
+      min: 460,
+      max: 921,
+    },
+    // '50': {
+    //   angle: 0,
+    //   Hz: 50.0,
+    //   a: -0.17624,
+    //   b: 43.76195,
+    //   c: 54.021349999999984,
+    //   min: 146.0,
+    //   max: 223.0,
+    // },
+  };
+  // const num = 21;
+  // const a = -0.17624; //-0.17624,
+  // const m = 2770.6411125432646,
+  //   n = -124.15442010894235;
+  // let i = 21;
+  const data = [];
+  for (const key in assistanceData) {
+    // i -= 1;
+    // const p = i;
+    const item = assistanceData[key];
+    // item['b'] = 2 * a * (n + p);
+    // item['c'] = a * (n + p) * (n + p) + m - i * 50;
+    // if (sysOrgCode == 'sdmtjtdltmk') {
+    //   // 大柳塔井整体向右下移动
+    //   item['min'] = item['min'] + 20;
+    //   item['max'] = item['max'] + 20;
+    //   item['b'] = 2 * a * (n + p) + 3;
+    //   item['c'] = a * (n + p) * (n + p) + m - i * 60;
+    // } else {
+    //   item['b'] = 2 * a * (n + p);
+    //   item['c'] = a * (n + p) * (n + p) + m - i * 50;
+    //   // //白家渠
+    //   // a = -0.13424;
+    //   // item['a'] = Math.round((a + 0.002 * (num - i)) * 10000) / 10000;
+    //   // item['min'] = item['min'] + 50 + (num - i) * 2;
+    //   // item['max'] = item['max'] + 65 + (num - i) * 3.5;
+    //   // item['b'] = 2 * item['a'] * (n + p) + 8.8;
+    //   // item['c'] = item['a'] * (n + p) * (n + p) + m - i * 60;
+    //   // //武当沟
+    //   // a = -0.17724;
+    //   // item['a'] = Math.round((a - 0.002 * (num - i)) * 10000) / 10000;
+    //   // item['min'] = item['min'] - 40 - (num - i) * 1;
+    //   // item['max'] = item['max'] - 60 - (num - i) * 0.5;
+    //   // item['b'] = 2 * item['a'] * (n + p) - 16.5;
+    //   // item['c'] = item['a'] * (n + p) * (n + p) + m - i * 40;
+    // }
+    data.push(item);
+  }
+  console.log(data);
+  return data;
+};
+
 // 大柳塔武当沟
 export const initData = (deviceType?) => {
   const { sysOrgCode } = useGlobSetting();
-  // const sysOrgCode = 'yjmdhswmk';
+  // const sysOrgCode = 'sdmtjtbetmk';
   if (sysOrgCode == 'sdmtjtdltmk') {
     return initDataDlt(deviceType);
   } else if (sysOrgCode == 'sdmtjtswmk') {

+ 707 - 59
src/views/vent/monitorManager/nitrogenMonitor/index.vue

@@ -13,78 +13,726 @@
       :alarm-history-config="{}"
       strtype="sys_nitrogen"
     >
-      <template #default="{ monitorData }">
-        <div id="nitrogen2D" class="w-full h-full flex justify-center items-center overflow-hidden">
+      <template #default="{ monitorActive }">
+        <div v-show="monitorActive" id="nitrogen2D" class="w-full h-full flex justify-center items-center overflow-hidden">
           <!-- <a-spin :spinning="loading" /> -->
           <!-- <div id="sprayCSS3D" v-show="!loading" style="width: 100%; height: 100%; position: absolute; overflow: hidden; pointer-events: none"> </div> -->
           <!-- <div class="nitrogen2D"></div> -->
-          <modelTip :statusData="monitorData"></modelTip>
+          <ModelTip :statusData="readData" />
         </div>
       </template>
     </BasicMonitoring>
   </div>
 </template>
 <script setup lang="ts">
-import { onMounted, ref } from 'vue';
-import BasicMonitoring from '/@/components/vent/BasicMonitoring.vue';
-import { useInitConfigs, useInitPage } from '../../home/configurable/hooks/useInit';
-import { nitrogenConfigs } from './nitrogen.data';
-import modelTip from './model-tip.vue';
-import { list } from './nitorgen.api';
-
-const loading = ref(false);
-const { configs, fetchConfigs } = useInitConfigs();
-const { updateEnhancedConfigs, updateData } = useInitPage('智能注氮管控系统');
-const monitorData = ref<any[]>([]);
-function refresh() {
-  // fetchConfigs(isDataRealTime.value ? 'vent_realtime' : 'vent').then(() => {
-  fetchConfigs('nitrogen').then(() => {
-    configs.value = nitrogenConfigs;
-    // updateEnhancedConfigs(configs.value);
-    list({ devicetype: 'nitrogen', pagetype: 'normal' }).then(updateData, (res) => {
-      if (res && res.msgTxt[0]) {
-        const dataList: any[] = [];
-        if (res.msgTxt[0].datalist && res.msgTxt[0].datalist.length > 0) {
-          res.msgTxt[0].datalist.forEach((data: any) => {
-            const readData = data.readData;
-            data = Object.assign(data, readData);
-            dataList.push(data);
-          });
-          monitorData.value = res.msgTxt[0].dataList;
-        }
-      }
+  import { onMounted, ref } from 'vue';
+  import BasicMonitoring from '/@/components/vent/BasicMonitoring.vue';
+  import { useInitConfigs, useInitPage } from '../../home/configurable/hooks/useInit';
+  // import { nitrogenConfigs } from './nitrogen.data';
+  import ModelTip from './model-tip.vue';
+  import { list } from './nitorgen.api';
+
+  // const loading = ref(false);
+  const { configs, fetchConfigs } = useInitConfigs();
+  const { updateEnhancedConfigs } = useInitPage('智能注氮管控系统');
+  const readData = ref({});
+  function refresh() {
+    // fetchConfigs(isDataRealTime.value ? 'vent_realtime' : 'vent').then(() => {
+    fetchConfigs('nitrogen').then(() => {
+      // configs.value = nitrogenConfigs;
+      updateEnhancedConfigs(configs.value);
+      getDataSource();
+      // list({ devicetype: 'nitrogen', pagetype: 'normal' }).then((res) => {
+      //   const res1 = {
+      //     cmd: 'monitordata',
+      //     msgTxt: [
+      //       {
+      //         datalist: [
+      //           {
+      //             msgType: null,
+      //             deviceID: '2014560252506329090',
+      //             strname: '制氮机',
+      //             strinstallpos: '制氮机',
+      //             fsectarea: 'null',
+      //             planedVa: null,
+      //             regulation: null,
+      //             stationname: '制氮机1#PLC',
+      //             stationtype: 'plc',
+      //             deviceType: 'nitrogen_auto',
+      //             typeName: '制氮机',
+      //             netStatus: 1,
+      //             warnFlag: 0,
+      //             warnLevel: 0,
+      //             warnLevel_str: '正常',
+      //             syswarnLevel: null,
+
+      //             syswarnLevel_str: null,
+
+      //             syswarnLevel_des: null,
+
+      //             warnTime: null,
+
+      //             readTime: '2026-02-09 10:27:25',
+
+      //             warnDes: '',
+
+      //             frontGateOpenCtrl: null,
+
+      //             rearGateOpenCtrl: null,
+
+      //             readData: {
+      //               Fan2ZdjJT: '0',
+
+      //               Fan1LgqJcqGz: '0',
+
+      //               Fan2jqwd: '27.98',
+
+      //               Fan2GdWdGGz: '0',
+
+      //               Fan1GdWdGGz: '0',
+
+      //               Fan2dqyl: '1.04',
+
+      //               Fan2tjf: '0.00',
+
+      //               Fan1SwjQx: '1',
+
+      //               Fan1JrqJcqGz: '0',
+
+      //               Fan2yqtyl: '1.14',
+
+      //               Fan2hjwd: '17.30',
+
+      //               Fan1JxgUab: '10.37',
+
+      //               Fan1LnfsJcqGz: '0',
+
+      //               Fan2KyjStatus_str: '运行',
+
+      //               Fan2JqWdGGz: '0',
+
+      //               Fan1JqWdGGz: '0',
+
+      //               Fan1Lgjpqyl: '0.71',
+
+      //               Fan1jrqTemp: '47.70',
+
+      //               Fan1CxgCos: '0.00',
+
+      //               Fan1gdyl: '0.69',
+
+      //               Fan1CxgUca: '10.34',
+
+      //               Fan2KyjStatus: '1',
+
+      //               Fan2JCxgp: '420.75',
+
+      //               Fan1XxCw: '0',
+
+      //               Fan1Yxzt: '0',
+
+      //               Fan1CxgUbc: '10.34',
+
+      //               Fan2CxgUab: '10.08',
+
+      //               Fan2Cxgp: '417.47',
+
+      //               Fan1GdYlGGz: '0',
+
+      //               Fan1yxsj: '0.00',
+
+      //               Fan2yqtTemp: '78.90',
+
+      //               Fan2kyjTemp: '1.12',
+
+      //               Fan1xnyq: '-0.04',
+
+      //               Fan2JxgLc: '27.97',
+
+      //               Fan2JxgLb: '27.96',
+
+      //               Fan1pqwd: '23.43',
+
+      //               Fan1JCxgF: '0.81',
+
+      //               Fan2dqwd: '38.06',
+
+      //               Fan1JCxgQ: '125.71',
+
+      //               Fan1jqyl: '0.72',
+
+      //               Fan2CxgUb: '5.80',
+
+      //               Fan2Yxzt_str: '运行',
+
+      //               Fan2JCxgQ: '124.16',
+
+      //               Fan2CxgUc: '5.81',
+
+      //               Fan2dqnd: '97.48',
+
+      //               Fan2JrqWdGGz: '0',
+
+      //               Fan1CxgUab: '10.34',
+
+      //               Fan2CxgF: '0.00',
+
+      //               Fan2JrqJcqGz: '0',
+
+      //               Fan2CxgQ: '217.04',
+
+      //               Fan2JxgLa: '28.29',
+
+      //               Fan1KyjStatus_str: '停止',
+
+      //               Fan1lxyc: '0.01',
+
+      //               Fan1Lntemp: '10.70',
+
+      //               Fan1zyyl: '0.00',
+
+      //               Fan2kyjdl: '0.00',
+
+      //               Fan1ll: '1226.02',
+
+      //               Fan2JCxgF: '0.86',
+
+      //               Fan2Lntemp: '40.80',
+
+      //               Fan1JCxgp: '351.93',
+
+      //               Fan1gdwd: '46.09',
+
+      //               Fan2CxgUa: '5.82',
+
+      //               Fan2JxgUb: '5.80',
+
+      //               Fan1ljll: '35308516.00',
+
+      //               Fan2JtZgz: '0',
+
+      //               Fan2JxgUa: '5.80',
+
+      //               Fan2CxgLa: '28.36',
+
+      //               Fan2CxgLb: '24.36',
+
+      //               Fan2CxgLc: '28.24',
+
+      //               Fan1ZdjJT: '0',
+
+      //               Fan2JxgUc: '5.81',
+
+      //               Fan1ckyl: '0.72',
+
+      //               Fan2GdYlGGz: '0',
+
+      //               Fan1BrJcqGz: '0',
+
+      //               sign: '0',
+
+      //               Fan1hjwd: '11.03',
+
+      //               Fan1Cxgp: '0.00',
+
+      //               Fan1JrqWdGGz: '0',
+
+      //               Fan2OHigh: '0',
+
+      //               Fan2JxgUbc: '10.04',
+
+      //               Fan1ZfTepm: '16.70',
+
+      //               Fan1JxgUb: '5.98',
+
+      //               Fan1JxgUc: '5.97',
+
+      //               Fan1JxgUa: '5.99',
+
+      //               Fan1JtZgz: '0',
+
+      //               Fan1jqwd: '25.60',
+
+      //               Fan1dqyl: '0.65',
+
+      //               Fan1Yxzt_str: '停止',
+
+      //               Fan2JxgCos: '0.00',
+
+      //               Fan2ll: '1279.52',
+
+      //               Fan1KyjStatus: '0',
+
+      //               Fan2yxsj: '0.00',
+
+      //               Fan2JxgUca: '10.06',
+
+      //               Fan2Yxzt: '1',
+
+      //               Fan2BrJcqGz: '0',
+
+      //               Fan1CxgUc: '5.96',
+
+      //               Fan1CxgUb: '5.96',
+
+      //               Fan1CxgUa: '5.98',
+
+      //               Fan2gdyl: '1.08',
+
+      //               Fan1LgjGyBh: '0',
+
+      //               Fan2XxCw: '0',
+
+      //               Fan1OHigh: '0',
+
+      //               Fan1kyjTemp: '0.72',
+
+      //               Fan2lxyc: '0.01',
+
+      //               Fan2LgqJcqGz: '0',
+
+      //               Fan1dqnd: '0.00',
+
+      //               Fan1LgjZhGz: '0',
+
+      //               Fan1kyjdl: '0.00',
+
+      //               Fan1JxgCos: '0.00',
+
+      //               Fan2zyyl: '0.66',
+
+      //               Fan2LgjZhGz: '0',
+
+      //               Fan2CxgCos: '0.89',
+
+      //               Fan2jqyl: '1.12',
+
+      //               Fan1CxgLc: '0.00',
+
+      //               Fan1CxgLb: '0.00',
+
+      //               Fan2xnyq: '119.36',
+
+      //               Fan1CxgLa: '0.00',
+
+      //               Fan2pqwd: '20.23',
+
+      //               Fan2CxgUbc: '10.05',
+
+      //               Fan2LgjDyBh: '0',
+
+      //               Fan1dqwd: '35.07',
+
+      //               Fan1LgjDyBh: '0',
+
+      //               Fan1JxgUca: '10.35',
+
+      //               Fan1yqtyl: '0.00',
+
+      //               Fan2jrqTemp: '51.50',
+
+      //               Fan2Lgjpqyl: '1.11',
+
+      //               Fan1CxgF: '0.00',
+
+      //               Fan1SwjQx_str: '打开',
+
+      //               Fan2ckyl: '1.12',
+
+      //               Fan2ljll: '13907482.00',
+
+      //               tTime: '2026-02-09 10:27:23',
+
+      //               Fan1CxgQ: '0.00',
+
+      //               Fan1JxgLc: '24.02',
+
+      //               Fan2ZfTepm: '6.10',
+
+      //               Fan1JxgLa: '24.09',
+
+      //               Fan1JxgLb: '24.16',
+
+      //               Fan2LnfsJcqGz: '0',
+
+      //               Fan2JxgUab: '10.04',
+
+      //               Fan1tjf: '-0.05',
+
+      //               Fan2LgjGyBh: '0',
+
+      //               Fan2CxgUca: '10.07',
+
+      //               isRun: '-2',
+
+      //               Fan1yqtTemp: '11.20',
+
+      //               Fan1JxgUbc: '10.36',
+
+      //               Fan2gdwd: '46.22',
+      //             },
+
+      //             readDataDes: null,
+
+      //             limits: null,
+
+      //             summaryHour: [],
+
+      //             summaryDay: [],
+
+      //             history: [],
+
+      //             dayhistory: [],
+
+      //             totalInfo: null,
+
+      //             sign: null,
+
+      //             cameras: [],
+
+      //             links: [],
+
+      //             avgParam: {},
+
+      //             other1: null,
+
+      //             other2: null,
+
+      //             other3: null,
+
+      //             remarkInfo: null,
+
+      //             linkInfo: null,
+
+      //             addrIndex: null,
+
+      //             warnLogNotOkCount: 0,
+
+      //             otherInfo: null,
+
+      //             orderNum: 0,
+
+      //             testFlag: 0,
+
+      //             strRemark: null,
+
+      //             isAutosync: null,
+
+      //             isUnderground: '0',
+
+      //             groupNumber: '',
+      //           },
+      //         ],
+      //         avginfo: {
+      //           warnFlag: {
+      //             value: 0,
+      //           },
+      //         },
+      //         typeName: '制氮机',
+
+      //         type: 'nitrogen_auto',
+      //       },
+      //       {
+      //         subtype: 'sys_nitrogen',
+
+      //         datalist: [
+      //           {
+      //             msgType: null,
+
+      //             deviceID: '2014623636824182785',
+
+      //             strname: '制氮系统',
+
+      //             strinstallpos: '制氮系统',
+
+      //             fsectarea: 'null',
+
+      //             planedVa: null,
+
+      //             regulation: null,
+
+      //             stationname: null,
+
+      //             stationtype: null,
+
+      //             deviceType: 'sys_nitrogen',
+
+      //             typeName: '注氮',
+
+      //             netStatus: 1,
+
+      //             warnFlag: 0,
+
+      //             warnLevel: 0,
+
+      //             warnLevel_str: '正常',
+
+      //             syswarnLevel: null,
+
+      //             syswarnLevel_str: null,
+
+      //             syswarnLevel_des: null,
+
+      //             warnTime: null,
+
+      //             readTime: '2026-02-09 10:27:25',
+
+      //             warnDes: null,
+
+      //             frontGateOpenCtrl: null,
+
+      //             rearGateOpenCtrl: null,
+
+      //             readData: {
+      //               sign: '0',
+
+      //               tTime: '2026-02-09 10:27:20',
+
+      //               isRun: '-2',
+      //             },
+
+      //             readDataDes: null,
+
+      //             limits: null,
+
+      //             summaryHour: [],
+
+      //             summaryDay: [],
+
+      //             history: [],
+
+      //             dayhistory: [],
+
+      //             totalInfo: {
+      //               unitNum: 0,
+      //             },
+
+      //             sign: null,
+
+      //             cameras: [],
+
+      //             links: [],
+
+      //             avgParam: {},
+
+      //             other1: null,
+
+      //             other2: null,
+
+      //             other3: null,
+
+      //             remarkInfo: null,
+
+      //             linkInfo: null,
+
+      //             addrIndex: null,
+
+      //             warnLogNotOkCount: 0,
+
+      //             otherInfo: null,
+
+      //             orderNum: null,
+
+      //             testFlag: null,
+
+      //             strRemark: null,
+
+      //             isAutosync: null,
+
+      //             coalThickness: '',
+
+      //             coalSeamThick: '',
+
+      //             faceRetM3: '',
+
+      //             totalDrillingType: '',
+
+      //             coalProduction: '',
+
+      //             drillinFootage: '',
+
+      //             strikelength: '',
+
+      //             totalRetM3: '',
+
+      //             useM3Perent: '',
+
+      //             ventRationality: '',
+
+      //             cuteyeLength: '',
+
+      //             totalGasVolume: '',
+
+      //             gasLevel: '',
+
+      //             supportNum: '',
+
+      //             totalComplteQuantity: '',
+
+      //             stepIn1: '',
+
+      //             stepIn2: '',
+
+      //             totalOutM3: '',
+
+      //             coalReserves: '',
+
+      //             faceIntM3: '',
+
+      //             cumulativeScalar: '',
+
+      //             totalIntM3: '',
+
+      //             totalAverageRate: '',
+
+      //             equalarea: '',
+
+      //             coalSpoCha: '',
+
+      //             thickness: '',
+
+      //             unitNum: '',
+
+      //             meterCount1: '',
+
+      //             meterCount2: '',
+
+      //             goafName: '',
+
+      //             meterCount3: '',
+
+      //             meterCount4: '',
+
+      //             meterCount5: '',
+
+      //             faceLen: '',
+
+      //             linkEmptyFlag: '',
+
+      //             totalDate: '',
+
+      //             stepOut1: '',
+
+      //             mainCoalseam: '',
+
+      //             drillingType: '',
+
+      //             coalSeam: '',
+
+      //             stepOut2: '',
+
+      //             pressureRationality1: '',
+
+      //             pressureRationality2: '',
+
+      //             userM3: '',
+
+      //             gasReserves: '',
+
+      //             linkEmpty: '',
+
+      //             beginDrainageDate: '',
+
+      //             goafManage: '',
+
+      //             dischargeGasEmission: '',
+
+      //             useM3: '',
+
+      //             ventCapacity: '',
+
+      //             stepMid2: '',
+
+      //             stepMid1: '',
+
+      //             zoneRationality1: '',
+
+      //             manageCoalMethod: '',
+
+      //             zoneRationality2: '',
+
+      //             miningArea: '',
+
+      //             minFirePeriod: '',
+
+      //             cumulativeFlow: '',
+
+      //             scalarRecognition: '',
+
+      //             coalSeamAngle: '',
+
+      //             coalseam: '',
+
+      //             totalPlanM3: '',
+
+      //             facePlanM3: '',
+
+      //             coalSeamThickness: '',
+
+      //             workingFaceLengeh: '',
+
+      //             workingFaceHeight: '',
+
+      //             originalGasContent: '',
+
+      //             workingFaceQieyanLength: '',
+
+      //             workingFaceZouxiangLength: '',
+
+      //             panel: '',
+
+      //             leakage: '',
+
+      //             flength: '',
+      //           },
+      //         ],
+
+      //         typeName: '综合监测系统',
+
+      //         type: 'sys',
+      //       },
+      //     ],
+      //   };
+      // });
     });
-  });
-}
+  }
+  async function getDataSource() {
+    const res = await list({ devicetype: 'nitrogen', pagetype: 'normal' });
+    let dataSource: any = [];
+    dataSource = res.msgTxt[0] && res.msgTxt[0].datalist ? res.msgTxt[0].datalist[0] : {};
+    if (dataSource) {
+      readData.value = Object.assign(dataSource, dataSource.readData);
+    }
+  }
 
-function initInterval() {
-  setInterval(() => {
+  function initInterval() {
+    setInterval(() => {
+      refresh();
+    }, 60000);
+  }
+  onMounted(() => {
     refresh();
-  }, 60000);
-}
-onMounted(() => {
-  refresh();
-  initInterval();
-});
+    initInterval();
+  });
 </script>
 
 <style lang="less" scoped>
-.spray-wrapper {
-  width: 100%;
-  height: 100%;
-}
-
-#spray3D {
-  pointer-events: all;
-}
-
-.spray-wrapper :deep(.list-item_L .list-item__icon_L) {
-  background-image: url('/@/assets/images/home-container/configurable/minehome/list-icon-file.png');
-}
-.spray-wrapper :deep(.list-item_N:nth-child(1)) {
-  background-image: url('/@/assets/images/home-container/configurable/minehome/list-bg-n5.png');
-}
-.spray-wrapper :deep(.list-item_N:nth-child(2)) {
-  background-image: url('/@/assets/images/home-container/configurable/minehome/list-bg-n6.png');
-}
+  .spray-wrapper {
+    width: 100%;
+    height: 100%;
+  }
+
+  #spray3D {
+    pointer-events: all;
+  }
+
+  .spray-wrapper :deep(.list-item_L .list-item__icon_L) {
+    background-image: url('/@/assets/images/home-container/configurable/minehome/list-icon-file.png');
+  }
+  .spray-wrapper :deep(.list-item_N:nth-child(1)) {
+    background-image: url('/@/assets/images/home-container/configurable/minehome/list-bg-n5.png');
+  }
+  .spray-wrapper :deep(.list-item_N:nth-child(2)) {
+    background-image: url('/@/assets/images/home-container/configurable/minehome/list-bg-n6.png');
+  }
 </style>

+ 43 - 16
src/views/vent/monitorManager/nitrogenMonitor/model-tip.vue

@@ -8,38 +8,38 @@
       <div class="zdj-ll">
         <div class="cq-zdj-item">
           <span class="text-label">累计流量(Nm³):</span>
-          <span class="text-val">{{ statusData.Fan1ljll }}</span>
+          <span class="text-val">{{ currentData?.Fan1ljll }}</span>
         </div>
       </div>
       <div class="zdj-ll1">
         <div class="cq-zdj-item">
           <span class="text-label">累计流量(Nm³):</span>
-          <span class="text-val">{{ statusData.Fan2ljll }}</span>
+          <span class="text-val">{{ currentData?.Fan2ljll }}</span>
         </div>
       </div>
       <!-- 加热器温度 -->
       <div class="jrq1">
         <div class="cq-zdj-item">
           <span class="text-label">加热器</span>
-          <span class="text-val">{{ statusData.Fan1jrqTemp }}℃</span>
+          <span class="text-val">{{ currentData?.Fan1jrqTemp }}℃</span>
         </div>
       </div>
       <div class="jrq2">
         <div class="cq-zdj-item">
-          <span class="text-val">{{ statusData.Fan2jrqTemp }}℃</span>
+          <span class="text-val">{{ currentData?.Fan2jrqTemp }}℃</span>
         </div>
       </div>
       <!-- 滤芯 -->
       <div class="filter">
         <div class="cq-zdj-item">
           <span class="text-label">滤芯1</span>
-          <span class="text-val">{{ statusData.Fan1lxyc }}MPa</span>
+          <span class="text-val">{{ currentData?.Fan1lxyc }}MPa</span>
         </div>
       </div>
       <div class="filter2">
         <div class="cq-zdj-item">
           <span class="text-label">滤芯2</span>
-          <span class="text-val">{{ statusData.Fan2lxyc }}MPa</span>
+          <span class="text-val">{{ currentData?.Fan2lxyc }}MPa</span>
         </div>
       </div>
       <!-- 活性炭 -->
@@ -52,43 +52,43 @@
       <!-- 上方浓度  流量  氮气压力  氮气温度 -->
       <div class="nd1">
         <span class="text-label">浓度</span>
-        <span class="text-val">{{ statusData.Fan1dqnd }}%</span>
+        <span class="text-val">{{ currentData?.Fan1dqnd }}%</span>
       </div>
       <div class="ll1">
         <span class="text-label">流量</span>
-        <span class="text-val">{{ statusData.Fan1ll }}Nm³/h</span>
+        <span class="text-val">{{ currentData?.Fan1ll }}Nm³/h</span>
       </div>
       <div class="dqyl1">
         <span class="text-label">氮气压力</span>
-        <span class="text-val">{{ statusData.Fan1dqyl }}MPa</span>
+        <span class="text-val">{{ currentData?.Fan1dqyl }}MPa</span>
       </div>
       <div class="dqwd1">
         <span class="text-label">氮气温度</span>
-        <span class="text-val">{{ statusData.Fan1dqwd }}℃</span>
+        <span class="text-val">{{ currentData?.Fan1dqwd }}℃</span>
       </div>
       <!-- 底部浓度  流量  氮气压力  氮气温度 -->
       <div class="nd">
         <span class="text-label">浓度</span>
-        <span class="text-val">{{ statusData.Fan2dqnd }}%</span>
+        <span class="text-val">{{ currentData?.Fan2dqnd }}%</span>
       </div>
       <div class="ll">
         <span class="text-label">流量</span>
-        <span class="text-val">{{ statusData.Fan2ll }}Nm³/h</span>
+        <span class="text-val">{{ currentData?.Fan2ll }}Nm³/h</span>
       </div>
       <div class="dqyl">
         <span class="text-label">氮气压力</span>
-        <span class="text-val">{{ statusData.Fan2dqyl }}MPa</span>
+        <span class="text-val">{{ currentData?.Fan2dqyl }}MPa</span>
       </div>
       <div class="dqwd">
         <span class="text-label">氮气温度</span>
-        <span class="text-val">{{ statusData.Fan2dqwd }}℃</span>
+        <span class="text-val">{{ currentData?.Fan2dqwd }}℃</span>
       </div>
     </div>
   </div>
 </template>
-
+ 
 <script setup lang="ts">
-import { ref } from 'vue';
+import { ref, watch } from 'vue';
 
 let props = defineProps({
   statusData: {
@@ -98,6 +98,33 @@ let props = defineProps({
     },
   },
 });
+// 当前要显示的数据
+const currentData = ref<any>(null);
+
+// 监听 statusData 的变化
+watch(
+  () => props.statusData,
+  (newVal) => {
+    console.log('statusData changed:', newVal);
+    // 检查是否有数据
+    if (newVal && Object.keys(newVal).length > 0) {
+      // 处理数据 - 根据你的数据结构调整
+      if (Array.isArray(newVal) && newVal.length > 0) {
+        currentData.value = newVal[0]?.readData || {};
+      } else if (newVal.readData) {
+        currentData.value = newVal.readData;
+      } else {
+        currentData.value = newVal;
+      }
+    } else {
+      currentData.value = null;
+    }
+  },
+  {
+    immediate: true, // 立即执行一次,检查初始值
+    deep: true, // 深度监听,对象内部属性变化也会触发
+  }
+);
 </script>
 
 <style lang="less" scoped>

+ 5 - 238
src/views/vent/monitorManager/sprayMonitor/index.vue

@@ -12,8 +12,8 @@
       :alarm-history-config="{}"
       strtype="sys_surface_juejin"
     >
-      <template #default="{ monitorData }">
-        <div id="spray3D" class="w-full h-full flex justify-center items-center overflow-hidden">
+      <template #default="{ monitorActive }">
+        <div v-show="monitorActive" id="spray3D" class="w-full h-full flex justify-center items-center overflow-hidden">
           <a-spin :spinning="loading" />
           <div id="sprayCSS3D" v-show="!loading" style="width: 100%; height: 100%; position: absolute; overflow: hidden; pointer-events: none">
             <!-- <FourBorderBg id="sprayCSS3DEnvA">
@@ -39,245 +39,12 @@
 <script setup lang="ts">
   import { onMounted, ref } from 'vue';
   import BasicMonitoring from '/@/components/vent/BasicMonitoring.vue';
-  import { Config } from '../../deviceManager/configurationTable/types';
+  // import { Config } from '../../deviceManager/configurationTable/types';
   import { useInitConfigs } from '../../home/configurable/hooks/useInit';
   import { mountedThree, setModelType } from './spray.three';
-  import FourBorderBg from '/@/components/vent/fourBorderBg.vue';
-  import { get } from 'lodash-es';
   import { zuhuabengConfigs } from './spray.data';
 
-  const defaultConfigs: Config[] = [
-    {
-      deviceType: '',
-      moduleName: '压力传感器',
-      pageType: 'spray',
-      moduleData: {
-        header: {
-          show: false,
-          readFrom: '',
-          selector: {
-            show: false,
-            value: '',
-          },
-          slot: {
-            show: false,
-            value: '',
-          },
-        },
-        background: {
-          show: false,
-          type: 'video',
-          link: '',
-        },
-        layout: {
-          direction: 'row',
-          items: [
-            {
-              name: 'board',
-              basis: '100%',
-            },
-          ],
-        },
-        board: [
-          {
-            type: 'P',
-            layout: 'label-top',
-            items: [
-              {
-                label: '在线数量',
-                value: '1',
-              },
-              {
-                label: '异常数量',
-                value: '0',
-              },
-            ],
-            readFrom: '',
-          },
-        ],
-      },
-      showStyle: {
-        size: 'width:440px;height:250px;',
-        version: '保德',
-        position: 'top:80px;left:10px;',
-      },
-    },
-    {
-      deviceType: '',
-      moduleName: '喷洒实况',
-      pageType: 'spray',
-      moduleData: {
-        header: {
-          show: false,
-          readFrom: '',
-          selector: {
-            show: false,
-            value: '',
-          },
-          slot: {
-            show: false,
-            value: '',
-          },
-        },
-        background: {
-          show: false,
-          type: 'video',
-          link: '',
-        },
-        layout: {
-          direction: 'row',
-          items: [
-            {
-              name: 'table',
-              basis: '100%',
-            },
-          ],
-        },
-        table: [
-          {
-            type: 'A',
-            columns: [
-              {
-                name: '支架',
-                prop: 'strinstallpos',
-              },
-              {
-                name: '工作状态',
-                prop: 'readData.online_str',
-              },
-            ],
-            readFrom: 'spray_auto',
-          },
-        ],
-        preset: [],
-      },
-      showStyle: {
-        size: 'width:440px;height:430px;',
-        version: '保德',
-        position: 'top:350px;left:10px;',
-      },
-    },
-    {
-      deviceType: '',
-      moduleName: '场景信息总览',
-      pageType: 'spray',
-      moduleData: {
-        header: { show: false, readFrom: '', selector: { show: false, value: '' }, slot: { show: false, value: '' } },
-        background: { show: false, type: 'video', link: '' },
-        layout: {
-          direction: 'column',
-          items: [
-            {
-              name: 'list',
-              basis: '50%',
-              overflow: false,
-            },
-            {
-              name: 'partition',
-              basis: 'auto',
-              overflow: false,
-            },
-            {
-              name: 'list',
-              basis: 'auto',
-              overflow: false,
-            },
-            {
-              name: 'partition',
-              basis: 'auto',
-              overflow: false,
-            },
-            {
-              name: 'list',
-              basis: 'auto',
-              overflow: false,
-            },
-          ],
-        },
-        list: [
-          {
-            type: 'L',
-            readFrom: '',
-            items: [
-              {
-                label: '绑定控制箱名称',
-                value: '${spray_auto[0].strinstallpos}',
-              },
-              {
-                label: '控制箱地址',
-                value: '${spray_auto[0].readData.snumber}',
-              },
-              {
-                label: '故障从机',
-                value: '${spray_auto[0].readData.Cjgz}',
-              },
-              {
-                label: '浮球液位报警',
-                value: '${spray_auto[0].readData.minimumgrouting}',
-              },
-              {
-                label: '输送管道压力',
-                value: '${spray_auto[0].readData.zjbhglpaV}',
-              },
-            ],
-          },
-          {
-            type: 'N',
-            readFrom: '',
-            items: [
-              {
-                label: '启动状态',
-                value: '${spray_auto[0].readData.online_str}',
-                color: 'blue',
-              },
-              {
-                label: '报警状态',
-                value: '${spray_auto[0].warnLevel_str}',
-              },
-            ],
-          },
-          {
-            type: 'N',
-            readFrom: '',
-            items: [
-              {
-                label: '启动状态',
-                value: '${spray_auto[0].readData.online_str}',
-                color: 'blue',
-              },
-              {
-                label: '故障状态',
-                value: '${spray_auto[0].readData.warnLevel_str}',
-              },
-            ],
-          },
-        ],
-        partition: [
-          {
-            type: 'A',
-            readFrom: '',
-            layout: 'icon-pre',
-            label: '送料电机',
-            icon: '/src/assets/images/home-container/configurable/tashanhome/partition-icon-1.png',
-          },
-          {
-            type: 'A',
-            readFrom: '',
-            layout: 'icon-pre',
-            label: '输送电机',
-            icon: '/src/assets/images/home-container/configurable/tashanhome/partition-icon-2.png',
-          },
-        ],
-      },
-      showStyle: {
-        size: 'width:440px;height:700px;',
-        version: '原版',
-        position: 'top:80px;right:10px;',
-      },
-    },
-  ];
-
-  const { configs, fetchConfigs } = useInitConfigs();
+  const { configs } = useInitConfigs();
   const loading = ref(false);
 
   // const operations = ref([
@@ -378,7 +145,7 @@
     configs.value = zuhuabengConfigs;
     loading.value = true;
     mountedThree('#spray3D', ['#sprayCSS3D', '#sprayCSS3DEnvA', '#sprayCSS3DEnvB']).then(() => {
-      setModelType('spray').finally(() => {
+      setModelType('pensha').finally(() => {
         loading.value = false;
       });
     });

+ 19 - 2
src/views/vent/monitorManager/sprayMonitor/spray.three.ts

@@ -1,9 +1,12 @@
 import * as THREE from 'three';
 import UseThree from '../../../../utils/threejs/useThree';
 import ModelContext from './spray.threejs.base';
+import PenshaContext from './spray.threejs.pensha';
 import { animateCamera } from '/@/utils/threejs/util';
 import useEvent from '../../../../utils/threejs/useEvent';
 
+type modelName = 'spray' | 'pensha';
+
 /** 模型总控制器 */
 let model: UseThree;
 /** 当前展示的具体模型的 Object3D 对象 */
@@ -11,7 +14,7 @@ let group: THREE.Object3D;
 /** 具体模型内容列表,包含此模型总控制器下的所有可用的具体模型内容 */
 const modelContextList: {
   /** 当前模型类型,在控制器下有多个具体模型时分辨它们 */
-  type: string;
+  type: modelName;
   /** 模型的具体内容,即负责模型导入、绘制的上下文对象,一个控制器可以新建多个 */
   context?: ModelContext;
 }[] = [];
@@ -52,11 +55,12 @@ function initEventListender() {
 // }
 
 /** 设置模型类型并切换,不同的类型通常对应不同的具体模型,在模型总控制器下的具体模型会根据传入的参数彼此交互、切换 */
-export function setModelType(modelType: 'spray') {
+export function setModelType(modelType: modelName) {
   return new Promise((resolve, reject) => {
     if (!model) return reject('模型未初始化');
     modelContextList.forEach(({ type, context }) => {
       if (!context) return reject('模型未初始化');
+      // 如果是指定类型的模型,则显示该模型,否则应该检查是否已经存在该模型,如果存在则隐藏
       if (modelType === type) {
         group = context?.group as THREE.Object3D;
         setTimeout(async () => {
@@ -66,6 +70,12 @@ export function setModelType(modelType: 'spray') {
           await animateCamera({ x: -693, y: 474, z: 398 }, { x: 0, y: 0, z: 0 }, context.cameraPostion, context.orbitTarget, model, 0.8);
           resolve(null);
         }, 400);
+      } else {
+        const group = context?.group;
+        const name = group?.name;
+        if (name && group && model.scene?.getObjectByName(name)) {
+          model.scene?.remove(group);
+        }
       }
     });
   });
@@ -87,6 +97,13 @@ export function mountedThree(sceneSelctor: string, cssSelectors: string[]) {
       type: 'spray',
       context: model1,
     });
+    const model2 = new PenshaContext(model);
+    await model2.mountedThree();
+    model2.initCssElement(selectors);
+    modelContextList.push({
+      type: 'pensha',
+      context: model2,
+    });
 
     initEventListender();
     model.animate();

+ 108 - 0
src/views/vent/monitorManager/sprayMonitor/spray.threejs.pensha.ts

@@ -0,0 +1,108 @@
+import * as THREE from 'three';
+import { CSS3DSprite } from 'three/examples/jsm/renderers/CSS3DRenderer';
+import { setModalCenter } from '/@/utils/threejs/util';
+// import { setModalCenter } from '/@/utils/threejs/util';
+// import * as dat from 'dat.gui';
+// const gui = new dat.GUI();
+// gui.domElement.style = 'position:absolute;top:100px;left:10px;z-index:99999999999999';
+
+class ModelContext {
+  model;
+  modelName = 'spray-pensha';
+  // modelName = 'spray-ts';
+  // modelName = 'dedust';
+  group: THREE.Object3D | null = null;
+  /** 本模型内支持的锚点位置数组 */
+  anchors: [number, number, number][] = [
+    [1.5, 0.5, 1.6],
+    // [0, 0, 0],
+    // 右侧传感器
+    [1.3, 0.5, 1.6],
+    // [-25.847, 8.783, 3.267],
+    // [31.142, 8.783, 3.267],
+  ];
+  /** 本模型显示的css详情元素数组 */
+  cssSprites: CSS3DSprite[] = [];
+  /** 初始化时用到的摄像头位置参数,可以供外部访问或初始化时使用 */
+  cameraPostion = {
+    x: 2.0063999826191625,
+    y: 0.3963997081529602,
+    z: 3.275298992446514,
+  };
+  /** 初始化时用到的滑轨目标参数,可以供外部访问或初始化时使用,配合摄像头可以实现更精确的控制 */
+  orbitTarget = {
+    x: 2.023679325280445,
+    y: -0.1461634482645532,
+    z: 0.04677126793423104,
+  };
+
+  constructor(model) {
+    this.model = model;
+  }
+
+  addLight() {
+    const directionalLight = new THREE.DirectionalLight(0xffffff, 1.2);
+    directionalLight.position.set(6.3, 28, 20);
+    this.group?.add(directionalLight);
+    directionalLight.target = this.group as THREE.Object3D;
+
+    const pointLight = new THREE.PointLight(0xffffff, 1, 1000);
+    pointLight.position.set(45, 51, -4.1);
+    pointLight.shadow.bias = 0.05;
+    this.model.scene.add(pointLight);
+  }
+
+  /** 初始化css元素,将css元素选择器传入,该方法会将这些元素按顺序放入本模型支持的锚点中 */
+  initCssElement(selectors: string[]) {
+    selectors.forEach((selector, index) => {
+      const element = document.querySelector(selector) as HTMLElement;
+      if (element) {
+        const css3D = new CSS3DSprite(element);
+        this.cssSprites.push(css3D);
+        css3D.name = selector;
+        css3D.scale.set(0.002, 0.002, 0.002);
+        // const ff = gui.addFolder(`css元素${index}`);
+        // ff.add(css3D.position, 'x', -100, 100);
+        // ff.add(css3D.position, 'y', -100, 100);
+        // ff.add(css3D.position, 'z', -100, 100);
+        if (index < this.anchors.length) {
+          const [x, y, z] = this.anchors[index];
+          css3D.position.set(x, y, z);
+          this.group?.add(css3D);
+        } else {
+          console.warn(`指定的元素${selector}没有合适的位置放置`);
+        }
+      }
+    });
+  }
+
+  /** 清除css元素 */
+  clearCssElement() {
+    this.cssSprites.forEach((sprite) => {
+      this.group?.remove(sprite);
+    });
+    this.cssSprites = [];
+  }
+
+  mountedThree() {
+    return new Promise((resolve) => {
+      this.model.setGLTFModel([this.modelName]).then(async (gltf) => {
+        this.group = gltf[0];
+        if (this.group) {
+          setModalCenter(this.group);
+          resolve(null);
+          // this.addLight();
+        }
+      });
+    });
+  }
+
+  destroy() {
+    if (this.model) {
+      this.model.clearGroup(this.group);
+      this.model = null;
+      this.group = null;
+    }
+  }
+}
+export default ModelContext;

+ 2 - 2
src/views/vent/monitorManager/sprayMonitor/backup.vue → src/views/vent/monitorManager/sprayMonitor/tashan.vue

@@ -12,8 +12,8 @@
       :alarm-history-config="{}"
       strtype="sys_surface_juejin"
     >
-      <template #default="{ monitorData }">
-        <div id="spray3D" class="w-full h-full flex justify-center items-center overflow-hidden">
+      <template #default="{ monitorActive, monitorData }">
+        <div v-show="monitorActive" id="spray3D" class="w-full h-full flex justify-center items-center overflow-hidden">
           <a-spin :spinning="loading" />
           <div id="sprayCSS3D" v-show="!loading" style="width: 100%; height: 100%; position: absolute; overflow: hidden; pointer-events: none">
             <FourBorderBg id="sprayCSS3DEnvA">