Bladeren bron

修改振动页面吉林需求 修改故障诊断页面展示

liujiejie 1 week geleden
bovenliggende
commit
0fe3487ef0

+ 201 - 94
src/views/health/components/data/failureFrequency.json

@@ -2,7 +2,7 @@
   {
     "type": "Speed meas. point",
     "name": "Speed",
-    "ratio": 1,
+    "ratio": 1.0,
     "id": 1,
     "brand": null,
     "typeno": null,
@@ -14,7 +14,7 @@
   {
     "type": "Bearing",
     "name": "GEN NDE",
-    "ratio": 1,
+    "ratio": 1.0,
     "id": 2,
     "brand": "SKF",
     "typeno": "6324",
@@ -43,7 +43,7 @@
   {
     "type": "Stator",
     "name": "Stator",
-    "ratio": 1,
+    "ratio": 1.0,
     "id": 3,
     "brand": null,
     "typeno": null,
@@ -53,18 +53,18 @@
     "faultFrequencies": [
       {
         "name": "Stator, Pole pass freq.",
-        "multiple": 4
+        "multiple": 4.0
       },
       {
         "name": "Stator, Grid freq.",
-        "multiple": 0
+        "multiple": 0.0
       }
     ]
   },
   {
     "type": "Rotor",
     "name": "Rotor",
-    "ratio": 1,
+    "ratio": 1.0,
     "id": 4,
     "brand": null,
     "typeno": null,
@@ -74,18 +74,18 @@
     "faultFrequencies": [
       {
         "name": "Rotor, Rotor bars",
-        "multiple": 0
+        "multiple": 0.0
       },
       {
         "name": "Rotor, Grid freq.",
-        "multiple": 0
+        "multiple": 0.0
       }
     ]
   },
   {
     "type": "Bearing",
     "name": "GEN-DE",
-    "ratio": 1,
+    "ratio": 1.0,
     "id": 5,
     "brand": "SKF",
     "typeno": "6326 M/C3VL2071",
@@ -113,8 +113,8 @@
   },
   {
     "type": "Gear wheel",
-    "name": "GearL",
-    "ratio": 1,
+    "name": "GearIMS",
+    "ratio": 0.295918375,
     "id": 6,
     "brand": null,
     "typeno": null,
@@ -123,15 +123,15 @@
     "speedPointSrc": "/points/10",
     "faultFrequencies": [
       {
-        "name": "GearL",
-        "multiple": 29
+        "name": "GearIMS",
+        "multiple": 29.0
       }
     ]
   },
   {
     "type": "Gear wheel",
-    "name": "GearH",
-    "ratio": 0.295918375,
+    "name": "GearHHS",
+    "ratio": 1.0,
     "id": 7,
     "brand": null,
     "typeno": null,
@@ -140,14 +140,14 @@
     "speedPointSrc": "/points/10",
     "faultFrequencies": [
       {
-        "name": "GearH",
-        "multiple": 29
+        "name": "GearHHS",
+        "multiple": 29.0
       }
     ]
   },
   {
     "type": "Planet gear",
-    "name": "PG",
+    "name": "LSS",
     "ratio": 0.06445747,
     "id": 9,
     "brand": null,
@@ -157,64 +157,64 @@
     "speedPointSrc": "/points/10",
     "faultFrequencies": [
       {
-        "name": "PG",
-        "multiple": 2.320469
+        "name": "LSS",
+        "multiple": 6.12345934
       },
       {
-        "name": "PG Planet shaft",
-        "multiple": 0.8086482
+        "name": "LSS Planet shaft",
+        "multiple": 0.177827746
       },
       {
-        "name": "PG BPFO",
-        "multiple": 15.8171587
+        "name": "LSS BPFO",
+        "multiple": 3.47831059
       },
       {
-        "name": "PG BPFI",
-        "multiple": 18.1460667
+        "name": "LSS BPFI",
+        "multiple": 3.99045467
       },
       {
-        "name": "PG BSF",
-        "multiple": 5.62819147
+        "name": "LSS BSF",
+        "multiple": 1.237681
       },
       {
-        "name": "PG FTF",
-        "multiple": 0.380064666
+        "name": "LSS FTF",
+        "multiple": 0.08357904
       }
     ]
   },
   {
     "type": "Bearing",
     "name": "MB02",
-    "ratio": 0.105475858,
+    "ratio": 0.2783391,
     "id": 10,
     "brand": "SKF",
-    "typeno": "241/630 ECAK30/W33",
+    "typeno": "241/530 ECAK30/W33",
     "rotatingrace": 0,
     "speedPointId": 10,
     "speedPointSrc": "/points/10",
     "faultFrequencies": [
       {
         "name": "MB02 BPFO",
-        "multiple": 0.9714326
+        "multiple": 2.56071949
       },
       {
         "name": "MB02 BPFI",
-        "multiple": 1.24356031
+        "multiple": 3.28440118
       },
       {
         "name": "MB02 BSF",
-        "multiple": 0.4103011
+        "multiple": 1.07438886
       },
       {
         "name": "MB02 FTF",
-        "multiple": 0.046409376
+        "multiple": 0.1224692
       }
     ]
   },
   {
     "type": "Shaft",
     "name": "MainShaft",
-    "ratio": 0.105475858,
+    "ratio": 0.2783391,
     "id": 11,
     "brand": null,
     "typeno": null,
@@ -224,14 +224,14 @@
     "faultFrequencies": [
       {
         "name": "MainShaft",
-        "multiple": 0.105475858
+        "multiple": 0.2783391
       }
     ]
   },
   {
     "type": "Bearing",
     "name": "MB01",
-    "ratio": 0.105475858,
+    "ratio": 0.2783391,
     "id": 12,
     "brand": "SKF",
     "typeno": "241/600 ECAK30/W33",
@@ -241,26 +241,26 @@
     "faultFrequencies": [
       {
         "name": "MB01 BPFO",
-        "multiple": 0.970377862
+        "multiple": 2.56071949
       },
       {
         "name": "MB01 BPFI",
-        "multiple": 1.2446152
+        "multiple": 3.28440118
       },
       {
         "name": "MB01 BSF",
-        "multiple": 0.4071368
+        "multiple": 1.07438886
       },
       {
         "name": "MB01 FTF",
-        "multiple": 0.046409376
+        "multiple": 0.1224692
       }
     ]
   },
   {
     "type": "Impeller",
     "name": "Blade",
-    "ratio": 0.105475858,
+    "ratio": 0.2783391,
     "id": 13,
     "brand": null,
     "typeno": null,
@@ -270,32 +270,73 @@
     "faultFrequencies": [
       {
         "name": "Blade",
-        "multiple": 0.3164276
+        "multiple": 0.835017264
       }
     ]
   },
   {
-    "type": "Gear wheel",
-    "name": "PG02",
-    "ratio": 0.06445747,
-    "id": 14,
-    "brand": null,
-    "typeno": null,
+    "type": "Bearing",
+    "name": "HSS-NU2228",
+    "ratio": 1.0,
+    "id": 16,
+    "brand": "SKF",
+    "typeno": "NU2228",
     "rotatingrace": 0,
     "speedPointId": 10,
     "speedPointSrc": "/points/10",
     "faultFrequencies": [
       {
-        "name": "PG02",
-        "multiple": 6.51020432
+        "name": "HSS-NU2228 BPFO",
+        "multiple": 7.36
+      },
+      {
+        "name": "HSS-NU2228 BPFI",
+        "multiple": 9.63
+      },
+      {
+        "name": "HSS-NU2228 BSF",
+        "multiple": 3.68
+      },
+      {
+        "name": "HSS-NU2228 FTF",
+        "multiple": 0.43
       }
     ]
   },
   {
-    "type": "Gear wheel",
-    "name": "PG01",
-    "ratio": 0.295918375,
-    "id": 15,
+    "type": "Bearing",
+    "name": "HSS-QJ328",
+    "ratio": 1.0,
+    "id": 17,
+    "brand": "SKF",
+    "typeno": "QJ328",
+    "rotatingrace": 0,
+    "speedPointId": 10,
+    "speedPointSrc": "/points/10",
+    "faultFrequencies": [
+      {
+        "name": "HSS-QJ328 BPFO",
+        "multiple": 4.93
+      },
+      {
+        "name": "HSS-QJ328 BPFI",
+        "multiple": 7.06
+      },
+      {
+        "name": "HSS-QJ328 BSF",
+        "multiple": 2.23
+      },
+      {
+        "name": "HSS-QJ328 FTF",
+        "multiple": 0.41
+      }
+    ]
+  },
+  {
+    "type": "Fixed coupling",
+    "name": "固定联轴节HHS",
+    "ratio": 1.0,
+    "id": 25,
     "brand": null,
     "typeno": null,
     "rotatingrace": 0,
@@ -303,96 +344,162 @@
     "speedPointSrc": "/points/10",
     "faultFrequencies": [
       {
-        "name": "PG01",
-        "multiple": 6.51020432
+        "name": "固定联轴节HHS",
+        "multiple": 1.0
       }
     ]
   },
   {
     "type": "Bearing",
-    "name": "GEAR-NU2228",
+    "name": "IMS-32956",
     "ratio": 0.295918375,
-    "id": 16,
+    "id": 26,
     "brand": "SKF",
-    "typeno": "NU2228",
+    "typeno": "32956",
     "rotatingrace": 0,
     "speedPointId": 10,
     "speedPointSrc": "/points/10",
     "faultFrequencies": [
       {
-        "name": "GEAR-NU2228 BPFO",
-        "multiple": 2.1779592
+        "name": "IMS-32956 BPFO",
+        "multiple": 5.50112247
       },
       {
-        "name": "GEAR-NU2228 BPFI",
-        "multiple": 2.849694
+        "name": "IMS-32956 BPFI",
+        "multiple": 6.332653
       },
       {
-        "name": "GEAR-NU2228 BSF",
-        "multiple": 1.0889796
+        "name": "IMS-32956 BSF",
+        "multiple": 2.01520419
       },
       {
-        "name": "GEAR-NU2228 FTF",
-        "multiple": 0.1272449
+        "name": "IMS-32956 FTF",
+        "multiple": 0.13612245
       }
     ]
   },
   {
     "type": "Bearing",
-    "name": "GEAR-QJ328",
+    "name": "IMS-NU1052",
     "ratio": 0.295918375,
-    "id": 17,
+    "id": 27,
     "brand": "SKF",
-    "typeno": "QJ328",
+    "typeno": "NU1052",
     "rotatingrace": 0,
     "speedPointId": 10,
     "speedPointSrc": "/points/10",
     "faultFrequencies": [
       {
-        "name": "GEAR-QJ328 BPFO",
-        "multiple": 1.45887756
+        "name": "IMS-NU1052 BPFO",
+        "multiple": 3.18408179
       },
       {
-        "name": "GEAR-QJ328 BPFI",
-        "multiple": 2.08918381
+        "name": "IMS-NU1052 BPFI",
+        "multiple": 3.915
       },
       {
-        "name": "GEAR-QJ328 BSF",
-        "multiple": 0.659898
+        "name": "IMS-NU1052 BSF",
+        "multiple": 1.42040825
       },
       {
-        "name": "GEAR-QJ328 FTF",
-        "multiple": 0.121326536
+        "name": "IMS-NU1052 FTF",
+        "multiple": 0.130204082
       }
     ]
   },
   {
     "type": "Bearing",
-    "name": "GEAR-7318",
-    "ratio": 1,
-    "id": 18,
+    "name": "IMS-NU2324",
+    "ratio": 1.35853434,
+    "id": 28,
     "brand": "SKF",
-    "typeno": "7318 BECBM",
+    "typeno": "NU2324",
     "rotatingrace": 0,
     "speedPointId": 10,
     "speedPointSrc": "/points/10",
     "faultFrequencies": [
       {
-        "name": "GEAR-7318 BPFO",
-        "multiple": 4.96
+        "name": "IMS-NU2324 BPFO",
+        "multiple": 6.602477
       },
       {
-        "name": "GEAR-7318 BPFI",
-        "multiple": 7.04
+        "name": "IMS-NU2324 BPFI",
+        "multiple": 9.68635
       },
       {
-        "name": "GEAR-7318 BSF",
-        "multiple": 2.14
+        "name": "IMS-NU2324 BSF",
+        "multiple": 3.45067716
       },
       {
-        "name": "GEAR-7318 FTF",
-        "multiple": 0.41
+        "name": "IMS-NU2324 FTF",
+        "multiple": 0.543413758
+      }
+    ]
+  },
+  {
+    "type": "Planet gear",
+    "name": "IMS",
+    "ratio": 0.295918375,
+    "id": 29,
+    "brand": null,
+    "typeno": null,
+    "rotatingrace": 0,
+    "speedPointId": 10,
+    "speedPointSrc": "/points/10",
+    "faultFrequencies": [
+      {
+        "name": "IMS",
+        "multiple": 29.8877563
+      },
+      {
+        "name": "IMS Planet shaft",
+        "multiple": 0.8011869
+      },
+      {
+        "name": "IMS BPFO",
+        "multiple": 15.671216
+      },
+      {
+        "name": "IMS BPFI",
+        "multiple": 17.9786358
+      },
+      {
+        "name": "IMS BSF",
+        "multiple": 5.576261
+      },
+      {
+        "name": "IMS FTF",
+        "multiple": 0.376557857
+      }
+    ]
+  },
+  {
+    "type": "Bearing",
+    "name": "lSS NNCF5048",
+    "ratio": 0.2783391,
+    "id": 30,
+    "brand": "SKF",
+    "typeno": "NNCF5038V",
+    "rotatingrace": 0,
+    "speedPointId": 10,
+    "speedPointSrc": "/points/10",
+    "faultFrequencies": [
+      {
+        "name": "lSS NNCF5048 BPFO",
+        "multiple": 3.04224634
+      },
+      {
+        "name": "lSS NNCF5048 BPFI",
+        "multiple": 3.91344762
+      },
+      {
+        "name": "lSS NNCF5048 BSF",
+        "multiple": 1.09387267
+      },
+      {
+        "name": "lSS NNCF5048 FTF",
+        "multiple": 0.119685806
       }
     ]
   }
-]
+]

+ 23 - 26
src/views/health/components/malfunction/bearing.vue

@@ -7,32 +7,29 @@
           :data="tableData"
           tooltip-effect="dark"
           style="width: 100%"
-          height="250"
+          height="450"
         >
           <!-- <el-table-column fixed type="selection" width="55"> </el-table-column> -->
           <el-table-column
-            prop="timeStamp"
-            label="时间"
+            prop="bearingType"
+            label="序号"
             align="center"
-            width="150"
+            width="100"
           >
           </el-table-column>
-          <el-table-column
-            prop="samplingFrequency"
-            label="采样频率(Hz)"
-            align="center"
-          >
+          <el-table-column prop="timeStamp" label="轴承类型" align="center">
           </el-table-column>
-          <el-table-column
-            prop="rotationalSpeed"
-            label="转速(rpm)"
-            align="center"
-          >
+          <el-table-column prop="timeStamp" label="时间" align="center">
+          </el-table-column>
+          <el-table-column prop="samplingFrequency" label="品牌" align="center">
+          </el-table-column>
+          <el-table-column prop="rotationalSpeed" label="型号" align="center">
+          </el-table-column>
+          <el-table-column prop="rotationalSpeed" label="状态" align="center">
           </el-table-column>
         </el-table>
         <div class="fenye">
           <p><span>状态码说明:</span>0正常,1报警,2危险,-1未定义</p>
-
           <el-pagination
             @current-change="handleCurrentChange"
             :current-page="currentPage"
@@ -43,7 +40,7 @@
           ></el-pagination>
         </div>
       </div>
-      <div class="leftdiv">
+      <!-- <div class="leftdiv">
         <div class="stateBox">
           <h4>轴承状态</h4>
           <div class="state">
@@ -84,13 +81,13 @@
 
             <h4>诊断步骤:</h4>
             <p>1、选择振动测点与起止时间,点击“查询”;</p>
-            <!-- <p>2、对轴承“参数设置”输入相关信息;</p> -->
+
             <p>2、点击“诊断”输出最终轴承状态结果。</p>
           </div>
         </div>
-      </div>
+      </div> -->
     </div>
-    <div class="bottomBox">
+    <!-- <div class="bottomBox">
       <div class="BtLeft">
         <h4>轴承状态趋势图</h4>
         <el-empty
@@ -112,7 +109,7 @@
           yAxisName="轴承故障状态"
         ></Eecharts>
       </div>
-    </div>
+    </div> -->
   </div>
 </template>
 
@@ -242,7 +239,7 @@ export default {
         .then((res) => {
           if (res.data.code === 405) {
             this.$message.warning(
-              res.data.message || "当前采集频率不适合进行诊断分析"
+              res.data.message || "当前采集频率不适合进行诊断分析",
             );
             return; // 终止后续数据处理逻辑
           }
@@ -274,13 +271,13 @@ export default {
           };
 
           this.bearingStateColors.innerRing = setColor(
-            result.BPFI?.max_status || 0
+            result.BPFI?.max_status || 0,
           );
           this.bearingStateColors.outerRing = setColor(
-            result.BPFO?.max_status || 0
+            result.BPFO?.max_status || 0,
           );
           this.bearingStateColors.rollingElement = setColor(
-            result.BSF?.max_status || 0
+            result.BSF?.max_status || 0,
           );
           this.bearingStateColors.cage = setColor(result.FTF?.max_status || 0);
         })
@@ -317,7 +314,7 @@ h4 {
 }
 
 .TopBox {
-  height: 280px;
+  // height: 280px;
   display: flex;
   justify-content: space-around;
 
@@ -404,7 +401,7 @@ h4 {
   }
 
   .rightdiv {
-    width: 50%;
+    width: 100%;
 
     .fenye {
       display: flex;

+ 6 - 6
src/views/health/components/malfunction/dissymmetry.vue

@@ -44,7 +44,7 @@
         </div>
       </div>
 
-      <div class="leftdiv">
+      <!-- <div class="leftdiv">
         <div class="stateBox">
           <h4>不对中状态</h4>
           <div class="state">
@@ -79,9 +79,9 @@
             <p>2、点击“诊断”输出不对中状态结果。</p>
           </div>
         </div>
-      </div>
+      </div> -->
     </div>
-    <div class="bottomBox">
+    <!-- <div class="bottomBox">
       <div class="BtLeft">
         <h4>不对中故障状态趋势图</h4>
         <el-empty
@@ -98,7 +98,7 @@
           yAxisName="不对中故障状态"
         ></Eecharts>
       </div>
-    </div>
+    </div> -->
   </div>
 </template>
 
@@ -239,7 +239,7 @@ export default {
           this.$set(
             this,
             "xData",
-            this.tableData.map((item) => item.timeStamp)
+            this.tableData.map((item) => item.timeStamp),
           );
           console.log(this.yData, "yData11");
           console.log(this.xData, "xData22");
@@ -365,7 +365,7 @@ h4 {
     }
   }
   .rightdiv {
-    width: 50%;
+    width: 100%;
     .fenye {
       display: flex;
       justify-content: space-between;

+ 8 - 9
src/views/health/components/malfunction/gear.vue

@@ -43,7 +43,7 @@
           ></el-pagination>
         </div>
       </div>
-      <div class="leftdiv">
+      <!-- <div class="leftdiv">
         <div class="stateBox">
           <h4>齿轮状态</h4>
           <div class="state">
@@ -78,13 +78,12 @@
 
             <h4>诊断步骤:</h4>
             <p>1、选择振动测点与起止时间,点击“查询”;</p>
-            <!-- <p>2、对齿轮“参数设置”输入相关信息;</p> -->
             <p>2、点击“诊断”输出最终齿轮状态结果。</p>
           </div>
         </div>
-      </div>
+      </div> -->
     </div>
-    <div class="bottomBox">
+    <!-- <div class="bottomBox">
       <div class="BtLeft">
         <h4>齿轮状态趋势图</h4>
         <el-empty
@@ -101,7 +100,7 @@
           yAxisName="齿轮故障状态"
         ></Eecharts>
       </div>
-    </div>
+    </div> -->
   </div>
 </template>
 
@@ -267,10 +266,10 @@ export default {
 
             // 根据 max_status 设置颜色
             this.bearingStateColors.innerRing = setColor(
-              result.crack?.max_status ?? -1
+              result.crack?.max_status ?? -1,
             );
             this.bearingStateColors.outerRing = setColor(
-              result.wear?.max_status ?? -1
+              result.wear?.max_status ?? -1,
             );
           }
         })
@@ -309,7 +308,7 @@ h4 {
 }
 
 .TopBox {
-  height: 280px;
+  // height: 280px;
   display: flex;
   justify-content: space-around;
 
@@ -396,7 +395,7 @@ h4 {
   }
 
   .rightdiv {
-    width: 50%;
+    width: 100%;
 
     .fenye {
       display: flex;

+ 7 - 8
src/views/health/components/malfunction/loose.vue

@@ -32,7 +32,6 @@
         </el-table>
         <div class="fenye">
           <p><span>状态码说明:</span>0正常,1报警,2危险,-1未定义</p>
-
           <el-pagination
             small
             @current-change="handleCurrentChange"
@@ -44,7 +43,7 @@
         </div>
       </div>
 
-      <div class="leftdiv">
+      <!-- <div class="leftdiv">
         <div class="stateBox">
           <h4>松动状态</h4>
           <div class="state">
@@ -79,9 +78,9 @@
             <p>2、点击“诊断”输出松动状态结果。</p>
           </div>
         </div>
-      </div>
+      </div> -->
     </div>
-    <div class="bottomBox">
+    <!-- <div class="bottomBox">
       <div class="BtLeft">
         <h4>松动故障状态趋势图</h4>
         <el-empty
@@ -98,7 +97,7 @@
           yAxisName="松动故障状态"
         ></Eecharts>
       </div>
-    </div>
+    </div> -->
   </div>
 </template>
 
@@ -228,7 +227,7 @@ export default {
         .then((res) => {
           if (res.data.code === 405) {
             this.$message.warning(
-              res.data.message || "当前采集频率不适合进行诊断分析"
+              res.data.message || "当前采集频率不适合进行诊断分析",
             );
             return; // 终止后续数据处理逻辑
           }
@@ -238,7 +237,7 @@ export default {
           this.$set(
             this,
             "xData",
-            this.tableData.map((item) => item.timeStamp)
+            this.tableData.map((item) => item.timeStamp),
           );
           console.log(this.yData, "yData11");
           console.log(this.xData, "xData22");
@@ -364,7 +363,7 @@ h4 {
     }
   }
   .rightdiv {
-    width: 50%;
+    width: 100%;
     .fenye {
       display: flex;
       justify-content: space-between;

+ 6 - 6
src/views/health/components/malfunction/misalignment.vue

@@ -44,7 +44,7 @@
         </div>
       </div>
 
-      <div class="leftdiv">
+      <!-- <div class="leftdiv">
         <div class="stateBox">
           <h4>不平衡状态</h4>
           <div class="state">
@@ -79,9 +79,9 @@
             <p>2、点击“诊断”输出不平衡状态结果。</p>
           </div>
         </div>
-      </div>
+      </div> -->
     </div>
-    <div class="bottomBox">
+    <!-- <div class="bottomBox">
       <div class="BtLeft">
         <h4>不平衡故障状态趋势图</h4>
         <el-empty
@@ -98,7 +98,7 @@
           yAxisName="不平衡故障状态"
         ></Eecharts>
       </div>
-    </div>
+    </div> -->
   </div>
 </template>
 
@@ -239,7 +239,7 @@ export default {
           this.$set(
             this,
             "xData",
-            this.tableData.map((item) => item.timeStamp)
+            this.tableData.map((item) => item.timeStamp),
           );
           console.log(this.yData, "yData11");
           console.log(this.xData, "xData22");
@@ -364,7 +364,7 @@ h4 {
     }
   }
   .rightdiv {
-    width: 50%;
+    width: 100%;
     .fenye {
       display: flex;
       justify-content: space-between;

+ 11 - 12
src/views/health/components/malfunction/temperature.vue

@@ -38,7 +38,7 @@
         </div>
       </div>
 
-      <div class="leftdiv">
+      <!-- <div class="leftdiv">
         <div class="stateBox">
           <h4>温度状态</h4>
           <div class="state">
@@ -79,10 +79,10 @@
             <p>2、点击“查询”输出最终温度状态结果。</p>
           </div>
         </div>
-      </div>
+      </div> -->
     </div>
 
-    <div class="bottomBox">
+    <!-- <div class="bottomBox">
       <div class="tu" v-for="(item, index) in chartList" :key="index">
         <el-empty v-if="!hasData(item.data)" description="暂无数据" />
         <zhexian
@@ -93,11 +93,10 @@
           :chartData="item.data"
           :fieldCode="fieldCode"
           :windTurbineNumber="windTurbineNumber"
-            :bearingKey="item.bearingKey"
-          
+          :bearingKey="item.bearingKey"
         />
       </div>
-    </div>
+    </div> -->
   </div>
 </template>
 
@@ -191,7 +190,7 @@ export default {
         sortedList.map((i) => ({
           title: i.title,
           hasData: this.hasData(i.data),
-        }))
+        })),
       );
 
       return sortedList;
@@ -230,13 +229,13 @@ export default {
       // 遍历四个温度类型,分别判断它们对应的tableData是否有异常或报警
       for (const [label, key] of Object.entries(statusMap)) {
         const items = this.tableData.filter(
-          (item) => item.temp_channel === label
+          (item) => item.temp_channel === label,
         );
         if (items.length === 0) {
           updatedColors[key] = colorMap.unknown; // 无数据,灰色
         } else {
           const hasDanger = items.some(
-            (item) => item.status === "异常" || item.status === "危险"
+            (item) => item.status === "异常" || item.status === "危险",
           );
           updatedColors[key] = hasDanger ? colorMap.danger : colorMap.normal;
         }
@@ -313,7 +312,7 @@ h4 {
   display: flex;
   justify-content: space-around;
   .leftdiv {
-    width: 50%;
+    width: 100%;
     display: flex;
     justify-content: space-around;
     .stateBox {
@@ -340,7 +339,7 @@ h4 {
       }
     }
     .Btn {
-      width: 50%;
+      width: 10%;
       display: flex;
       flex-direction: column; /* 垂直排列 */
       justify-content: space-between; /* 顶部和底部对齐 */
@@ -377,7 +376,7 @@ h4 {
     }
   }
   .rightdiv {
-    width: 50%;
+    width: 100%;
     .fenye {
       display: flex;
       //justify-content: center;

+ 126 - 88
src/views/health/components/spectrogramchartsNew.vue

@@ -136,120 +136,142 @@ export default {
       BGshow: false,
       PXshow: false,
       spectrumList: {},
-
       GBcheckList: [
         { val: "单指针", checked: false, disabled: false },
         { val: "谐波光标", checked: false, disabled: false },
         { val: "边带光标", checked: false, disabled: false },
         { val: "移动峰值", checked: false, disabled: false },
       ],
-
       featureGroups: [
         {
           label: "轴承故障",
           type: "bearing",
           children: [
-            "GEN-DE BPFO",
-            "GEN-DE BPFI",
-            "GEN-DE BSF",
-            "GEN-DE FTF",
-            "GEN-NDE BPFO",
-            "GEN-NDE BPFI",
-            "GEN-NDE BSF",
-            "GEN-NDE FTF",
-          ],
-        },
-        {
-          label: "MB01",
-          type: "Bearing",
-          children: ["MB01 BPFO", "MB01 BPFI", "MB01 BSF", "MB01 FTF"],
-        },
-        {
-          label: "MB02",
-          type: "Bearing",
-          children: ["MB02 BPFO", "MB02 BPFI", "MB02 BSF", "MB02 FTF"],
-        },
-        {
-          label: "GEAR-NU2228",
-          type: "Bearing",
-          children: [
-            "GEAR-NU2228 BPFO",
-            "GEAR-NU2228 BPFI",
-            "GEAR-NU2228 BSF",
-            "GEAR-NU2228 FTF",
-          ],
-        },
-        {
-          label: "GEAR-QJ328",
-          type: "Bearing",
-          children: [
-            "GEAR-QJ328 BPFO",
-            "GEAR-QJ328 BPFI",
-            "GEAR-QJ328 BSF",
-            "GEAR-QJ328 FTF",
-          ],
-        },
-        {
-          label: "GEAR-7318",
-          type: "Bearing",
-          children: [
-            "GEAR-7318 BPFO",
-            "GEAR-7318 BPFI",
-            "GEAR-7318 BSF",
-            "GEAR-7318 FTF",
+            {
+              // GEN
+              label: "发电机轴承",
+              type: "bearing",
+              children: [
+                "GEN NDE BPFO",
+                "GEN NDE BPFI",
+                "GEN NDE BSF",
+                "GEN NDE FTF",
+                "GEN-DE BPFO",
+                "GEN-DE BPFI",
+                "GEN-DE BSF",
+                "GEN-DE FTF",
+              ],
+            },
+            {
+              label: "主轴轴承",
+              type: "bearing",
+              children: [
+                // 主轴轴承
+                "MB01 BPFO",
+                "MB01 BPFI",
+                "MB01 BSF",
+                "MB01 FTF",
+                "MB02 BPFO",
+                "MB02 BPFI",
+                "MB02 BSF",
+                "MB02 FTF",
+              ],
+            },
+            {
+              label: "LSS",
+              type: "bearing",
+              children: [
+                // 行星啮合频率(本质GMF)
+
+                "lSS NNCF5048 BPFO",
+                "lSS NNCF5048 BPFI",
+                "lSS NNCF5048 BSF",
+                "lSS NNCF5048 FTF",
+              ],
+            },
+            {
+              label: "HSS",
+              type: "bearing",
+              children: [
+                // HSS
+                "HSS-NU2228 BPFO",
+                "HSS-NU2228 BPFI",
+                "HSS-NU2228 BSF",
+                "HSS-NU2228 FTF",
+                "HSS-QJ328 BPFO",
+                "HSS-QJ328 BPFI",
+                "HSS-QJ328 BSF",
+                "HSS-QJ328 FTF",
+              ],
+            },
+            {
+              label: "IMS",
+              type: "bearing",
+              children: [
+                // IMS
+                "IMS-32956 BPFO",
+                "IMS-32956 BPFI",
+                "IMS-32956 BSF",
+                "IMS-32956 FTF",
+                "IMS-NU1052 BPFO",
+                "IMS-NU1052 BPFI",
+                "IMS-NU1052 BSF",
+                "IMS-NU1052 FTF",
+                "IMS-NU2324 BPFO",
+                "IMS-NU2324 BPFI",
+                "IMS-NU2324 BSF",
+                "IMS-NU2324 FTF",
+              ],
+            },
+            {
+              label: "行星轴承(LSS/IMS)",
+              type: "bearing",
+              children: [
+                // 行星轴承(LSS/IMS)
+                "LSS BPFO",
+                "LSS BPFI",
+                "LSS BSF",
+                "LSS FTF",
+                "IMS BPFO", //中间轴
+                "IMS BPFI",
+                "IMS BSF",
+                "IMS FTF",
+              ],
+            },
           ],
         },
         {
           label: "转动基频",
           type: "rotation",
-          children: ["转速频率", "Rotor, Rotor bars", "Rotor, Grid freq."],
+          children: [
+            "Speed",
+            "MainShaft",
+            "LSS Planet shaft",
+            "IMS Planet shaft",
+            "Rotor, Rotor bars",
+            "固定联轴节HHS",
+          ],
         },
         {
           label: "结构频率",
           type: "structure",
           children: [
-            "叶片频率",
+            "Blade",
             "Stator, Pole pass freq.",
             "Stator, Grid freq.",
+            "Rotor, Grid freq.",
           ],
         },
         {
           label: "齿轮特征",
-          type: "Gear wheel",
-          children: ["GearL", "GearH"],
-        },
-        {
-          label: "PG02",
-          type: "Gear wheel",
-          children: ["PG02"],
-        },
-        {
-          label: "PG01",
-          type: "Gear wheel",
-          children: ["PG01"],
-        },
-        {
-          label: "PG 行星轮",
-          type: "Planet gear",
+          type: "gear",
           children: [
-            "PG",
-            "PG Planet shaft",
-            "PG BPFO",
-            "PG BPFI",
-            "PG BSF",
-            "PG FTF",
+            "GearIMS",
+            "GearHHS",
+            "LSS", //小齿轮
+            "IMS", //中间轴
           ],
         },
-        {
-          label: "MainShaft",
-          type: "Shaft",
-          children: ["MainShaft"],
-        },
-        {
-          label: "Blade",
-          type: "Impeller",
-          children: ["Blade"],
-        },
       ],
       // 每个分类单独存
       selectedMap: {
@@ -284,10 +306,26 @@ export default {
       return this.featureGroups.map((group) => ({
         label: group.label,
         value: group.label,
-        children: group.children.map((item) => ({
-          label: item,
-          value: item,
-        })),
+        children: group.children.map((item) => {
+          // 其它分组:子项为字符串;「轴承故障」为 { label, children: string[] } 多一级
+          if (typeof item === "string") {
+            return { label: item, value: item };
+          }
+          if (item && Array.isArray(item.children)) {
+            return {
+              label: item.label,
+              value: item.label,
+              children: item.children.map((leaf) => ({
+                label: leaf,
+                value: leaf,
+              })),
+            };
+          }
+          return {
+            label: item.label,
+            value: item.label,
+          };
+        }),
       }));
     },
     spectrumXLabel() {

+ 10 - 1
src/views/health/components/timedomainchartsNew.vue

@@ -522,7 +522,15 @@ export default {
     // 更新图表数据
     updateChart(data, labels) {
       if (!this.chartInstance) return; // Check if chartInstance is available
-      console.log(this.timeList, "updataChart  时域图");
+      console.log(this.timeList, this.timeListTwo, "updataChart  时域图");
+
+      // x 轴上界:以最后一个 X 为基准 + 2(避免刻度 nice 到更大的整数)
+      const lastX =
+        Array.isArray(labels) && labels.length
+          ? Number(labels[labels.length - 1])
+          : NaN;
+      const xMax = Number.isFinite(lastX) ? Math.floor(lastX + 2) : undefined;
+
       const option = {
         // title: {
         //   text: this.timeList.title,
@@ -559,6 +567,7 @@ export default {
             color: "#333",
             padding: [15, 0, 0, 0], // 增加X轴标题和轴线的距离
           },
+          max: xMax,
           axisLabel: {
             margin: 1, // 增加数值标签和轴线的间距
             formatter: (value) => value,

+ 288 - 296
src/views/health/components/waterfallChart.vue

@@ -5,7 +5,17 @@
       <!-- 频率范围 -->
       <div class="box full-row">
         <div class="panel-block">
-          <span class="label1">频率</span>
+          <span class="label1">坐标轴</span>
+          <el-select
+            v-model="axisMode"
+            size="mini"
+            style="width: 120px"
+            @change="handleAxisModeChange"
+          >
+            <el-option label="频率(Hz)" value="hz" />
+            <el-option label="阶次(Order)" value="order" />
+          </el-select>
+          <span class="label1">{{ axisModeLabel }}</span>
           <el-input v-model="freqMin" size="mini" placeholder="下限" />
           <span>~</span>
           <el-input v-model="freqMax" size="mini" placeholder="上限" />
@@ -13,18 +23,21 @@
             应用
           </el-button>
         </div>
-      </div>
-    </div>
-    <div class="control-panel">
-      <!-- 手动标注 -->
-      <div class="panel-block">
-        <span class="label1">标注</span>
-        <el-input v-model="manualFreq" size="mini" placeholder="频率" />
-        <el-input v-model="multiple" size="mini" placeholder="倍频" />
-        <el-button size="mini" type="success" @click="handleMark"
-          >标注</el-button
-        >
-        <el-button size="mini" type="info" @click="removeMark">清除</el-button>
+        <!-- 手动标注 -->
+        <div class="panel-block">
+          <span class="label1">标注</span>
+          <el-input v-model="manualFreq" size="mini" placeholder="频率/阶次" />
+          <el-input v-model="multiple" size="mini" placeholder="倍频" />
+          <el-button size="mini" type="success" @click="handleMark"
+            >标注</el-button
+          >
+          <el-button size="mini" type="info" @click="removeMark"
+            >清除</el-button
+          >
+        </div>
+        <p class="hint-text">
+          提示:点击某一采样时刻对应的谱线可高亮该时间(红色);再点同一条谱线取消高亮。
+        </p>
       </div>
     </div>
   </div>
@@ -49,17 +62,26 @@ export default {
       freqMax: "",
       manualFreq: "",
       multiple: 1,
+      axisMode: "hz", // hz | order
 
       freqRange: null, // 当前频率范围
       markLines: [], // 标注线
+      /** 点击高亮的采样时间(时间戳 ms),null 表示未选中 */
+      highlightedTime: null,
     };
   },
+  computed: {
+    axisModeLabel() {
+      return this.axisMode === "order" ? "阶次" : "频率";
+    },
+  },
 
   watch: {
     data: {
       deep: true,
       immediate: true,
       handler() {
+        this.highlightedTime = null;
         this.$nextTick(() => {
           this.renderChart();
         });
@@ -67,7 +89,42 @@ export default {
     },
   },
 
+  beforeDestroy() {
+    const el = this.$refs.chart;
+    if (el && typeof el.removeAllListeners === "function") {
+      el.removeAllListeners("plotly_click");
+      el.removeAllListeners("plotly_relayout");
+    }
+    if (el && typeof Plotly.purge === "function") {
+      Plotly.purge(el);
+    }
+  },
+
   methods: {
+    calcOrder(freq, rpm) {
+      const f = Number(freq);
+      const r = Number(rpm);
+      if (!Number.isFinite(f) || !Number.isFinite(r) || r <= 0) return null;
+      return (60 * f) / r;
+    },
+    toAxisValue(freq, rpm) {
+      if (this.axisMode === "order") {
+        return this.calcOrder(freq, rpm);
+      }
+      const f = Number(freq);
+      return Number.isFinite(f) ? f : null;
+    },
+    handleAxisModeChange() {
+      // 单位切换后,旧范围/标注的单位语义失效,清空避免误读
+      this.freqRange = null;
+      this.freqMin = "";
+      this.freqMax = "";
+      this.markLines = [];
+      this.manualFreq = "";
+      this.multiple = 1;
+      this.highlightedTime = null;
+      this.renderChart();
+    },
     /** ✅ 时间解析(核心修复) */
     parseTime(time) {
       if (typeof time === "number") {
@@ -88,6 +145,18 @@ export default {
         d.getDate(),
       )} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
     },
+
+    /** ✅ 轴刻度:仅显示 月/日 时:分 */
+    formatTimeTick(t) {
+      const d = new Date(t);
+      const pad = (n) => String(n).padStart(2, "0");
+      const md = `${pad(d.getMonth() + 1)}/${pad(d.getDate())}`;
+      const hh = pad(d.getHours());
+      const mm = pad(d.getMinutes());
+      // 00:00 不展示时间(避免“0点”刷屏)
+      if (hh === "00" && mm === "00") return md;
+      return `${md} ${hh}:${mm}`;
+    },
     renderChart() {
       if (!this.data.length) return;
 
@@ -96,7 +165,7 @@ export default {
    ========================== */
       const groupMap = new Map();
 
-      this.data.forEach(([freq, amp, time, Hz]) => {
+      this.data.forEach(([freq, amp, time, Hz, RPM]) => {
         const t = this.parseTime(time);
         const key = `${t}_${Hz}`;
 
@@ -108,7 +177,7 @@ export default {
           });
         }
 
-        groupMap.get(key).points.push([freq, amp]);
+        groupMap.get(key).points.push([freq, amp, RPM]);
       });
 
       /** =========================
@@ -117,6 +186,13 @@ export default {
       const groups = [...groupMap.values()];
       //   .sort((a, b) => a.time - b.time);
 
+      if (
+        this.highlightedTime != null &&
+        !groups.some((g) => g.time === this.highlightedTime)
+      ) {
+        this.highlightedTime = null;
+      }
+
       /** =========================
    * 3. 同时间分组(关键!!!)
    ========================== */
@@ -133,6 +209,13 @@ export default {
    * 4. traces
    ========================== */
       const traces = [];
+      let maxAmp = 0;
+
+      // ✅ 时间轴等间隔:用索引轴 + ticktext(避免时间戳间隔不均导致视觉“挤/拉”)
+      const uniqueTimes = Array.from(new Set(groups.map((g) => g.time))).sort(
+        (a, b) => a - b,
+      );
+      const timeToIndex = new Map(uniqueTimes.map((t, i) => [t, i]));
 
       groups.forEach((group, globalIndex) => {
         const { time, Hz, points } = group;
@@ -141,32 +224,42 @@ export default {
         const index = sameTimeGroups.indexOf(group);
         const total = sameTimeGroups.length;
 
-        const spread = 200; // 👈 控制展开宽度(可调)
-
-        // 👉 同时间“对称展开”
-        const offsetX = total > 1 ? (index - (total - 1) / 2) * spread : 0;
+        // 同一时间点若存在多组(不同采样频率 Hz),用极小偏移避免完全重叠(不影响刻度显示)
+        const offsetX = total > 1 ? (index - (total - 1) / 2) * 0.08 : 0;
+        const xBase = timeToIndex.get(time) ?? 0;
 
         const x = [];
         const y = [];
         const z = [];
         const text = [];
+        const lineColor = [];
 
         points.sort((a, b) => a[0] - b[0]);
 
-        points.forEach(([freq, amp]) => {
+        points.forEach(([freq, amp, rpm]) => {
+          const axisY = this.toAxisValue(freq, rpm);
+          if (axisY == null) return;
           if (this.freqRange) {
-            if (freq < this.freqRange.min || freq > this.freqRange.max) return;
+            if (axisY < this.freqRange.min || axisY > this.freqRange.max)
+              return;
           }
 
-          x.push(time + offsetX); // 👈 关键
-          y.push(freq);
-          z.push(amp + globalIndex * 0.02); // 防Z重叠
+          // 轴映射:X=采样时间(等间隔),Y=频率(Hz),Z=幅值
+          x.push(xBase + offsetX);
+          y.push(axisY);
+          z.push(amp);
+          if (Number.isFinite(amp)) {
+            maxAmp = Math.max(maxAmp, Number(amp));
+          }
+          lineColor.push(amp);
 
+          const order = this.calcOrder(freq, rpm);
           text.push(
             `时间: ${this.formatTime(time)}<br>` +
               `采样频率: ${Hz} Hz<br>` +
               `频率: ${freq.toFixed(2)} Hz<br>` +
-              `幅值: ${amp.toFixed(3)}`,
+              `阶次: ${order == null ? "-" : order.toFixed(3)}<br>` +
+              `加速度(m/s^2): ${amp.toFixed(3)}`,
           );
         });
 
@@ -177,35 +270,48 @@ export default {
           text,
           type: "scatter3d",
           mode: "lines",
-          opacity: 0.7,
-          line: {
-            width: 2,
-            color: "#162961",
-          },
+          opacity: 0.78,
+          line:
+            this.highlightedTime != null && time === this.highlightedTime
+              ? {
+                  width: 3,
+                  color: "#ff0000",
+                }
+              : {
+                  width: 2,
+                  color: lineColor,
+                  colorscale: "Viridis",
+                  cauto: true,
+                },
           showlegend: false,
           hovertemplate: "%{text}<extra></extra>",
         });
       });
 
+      this._dataTraceCount = groups.length;
+      this._traceIndexToTime = groups.map((g) => g.time);
+
       /** =========================
-   * 5. X轴刻度(时间 + Hz)
+   * 5. 采样时间刻度(仅 月/日 时:分
    ========================== */
-      const tickvals = groups.map((g) => g.time);
-
-      const ticktext = groups.map((g) => {
-        return `${this.formatTime(g.time)}-${g.Hz}Hz`;
-      });
+      const tickvals = uniqueTimes.map((_, i) => i);
+      const ticktext = uniqueTimes.map((t) => this.formatTimeTick(t));
+      // 仅用于 z 轴标签:去掉 0,避免原点出现两个“0”标签
+      const zTickVals =
+        maxAmp > 0
+          ? Array.from({ length: 5 }, (_, i) => ((i + 1) * maxAmp) / 5)
+          : [];
 
       /** =========================
    * 6. 标注线
    ========================== */
       if (this.markLines.length) {
-        const minTime = Math.min(...groups.map((g) => g.time));
-        const maxTime = Math.max(...groups.map((g) => g.time));
+        const minX = 0;
+        const maxX = Math.max(0, uniqueTimes.length - 1);
 
         this.markLines.forEach((line) => {
           traces.push({
-            x: [minTime, maxTime],
+            x: [minX, maxX],
             y: [line.freq, line.freq],
             z: [0, 0],
             type: "scatter3d",
@@ -228,49 +334,92 @@ export default {
    ========================== */
       const layout = {
         // title: "3D 瀑布频谱图",
-        paper_bgcolor: "#f5f7fa",
-        margin: { l: 0, r: 0, b: 0, t: 40 },
+        paper_bgcolor: "#ffffff",
+        plot_bgcolor: "#ffffff",
+        margin: { l: 0, r: 0, b: 10, t: 26 },
 
         scene: {
           xaxis: {
-            autorange: "reversed",
-            title: "时间",
+            // title: "采样时间",
+            title: "",
             tickvals,
             ticktext,
-            tickangle: 30,
-            gridcolor: "#fff",
-            backgroundcolor: "#e0e7f1",
+            tickangle: 45,
+            gridcolor: "#e5e5e5",
+            zeroline: false,
+            showspikes: false,
+            ticks: "outside",
+            tickfont: { size: 10, color: "#606266" },
+            titlefont: { size: 10, color: "#606266" },
+            backgroundcolor: "#ffffff",
             showbackground: true,
+            showline: true,
+            // linecolor: "#dcdfe6",
+            linecolor: "#e5e5e5",
+            linewidth: 1,
           },
           yaxis: {
-            title: "频率 (Hz)",
-            gridcolor: "#fff",
-            backgroundcolor: "#e0e7f1",
+            title: this.axisMode === "order" ? "阶次 (Order)" : "频率 (Hz)",
+            gridcolor: "#e5e5e5",
+            zeroline: false,
+            showspikes: false,
+            ticks: "outside",
+            tickfont: { size: 9, color: "#606266" },
+            titlefont: { size: 10, color: "#606266" },
+            backgroundcolor: "#fafafa",
             showbackground: true,
+            showline: true,
+            // linecolor: "#dcdfe6",
+            linecolor: "#e5e5e5",
+            linewidth: 1,
+            // tickangle: 90,
           },
           zaxis: {
-            title: "幅值",
-            gridcolor: "#fff",
-            backgroundcolor: "#e0e7f1",
+            title: "",
+            gridcolor: "#e5e5e5",
+            zeroline: false,
+            showspikes: false,
+            ticks: "outside",
+            tickangle: 90,
+            tickmode: "array",
+            tickvals: zTickVals,
+            tickformat: ".2f",
+            tickfont: { size: 10, color: "#606266" },
+            titlefont: {
+              size: 10,
+              color: "#606266",
+            },
+            backgroundcolor: "#fbfbfb",
             showbackground: true,
+            showline: true,
+            // linecolor: "#dcdfe6",
+            linecolor: "#e5e5e5",
+            linewidth: 1,
           },
           aspectmode: "manual",
-          aspectratio: { x: 2.0, y: 2.8, z: 1.2 },
+          // X 采用等间隔索引后,整体更均衡
+          aspectratio: { x: 2.4, y: 3.2, z: 1.1 },
+          //   annotations: [
+          //     {
+          //       text: "加速度(m/s^2)",
+          //       x: 0.92, // 右上角位置可微调
+          //       y: 0.98,
+          //       xref: "paper",
+          //       yref: "paper",
+          //       showarrow: false,
+          //       font: { size: 10, color: "#606266" },
+          //       align: "right",
+          //     },
+          //   ],
           camera: {
-            up: {
-              x: -0.1644947035315824,
-              y: -0.07969781808287146,
-              z: 0.9831529638377166,
-            },
-            center: {
-              x: -0.052807476121180814,
-              y: 0.02451796399554085,
-              z: -0.022911006648570736,
-            },
+            up: { x: 0, y: 0, z: 1 },
+            center: { x: 0, y: 0, z: 0 },
+            // 让时间轴视觉更贴近底边
+            // eye: { x: 2.6, y: 1.2, z: 0.8 },
             eye: {
-              x: 2.980700714870927,
-              y: 1.6273671421077383,
-              z: 0.6145682420564063,
+              x: 2.8547815750982184,
+              y: 0.3847735823828088,
+              z: 0.7363229242526919,
             },
             projection: {
               type: "orthographic",
@@ -280,234 +429,63 @@ export default {
       };
 
       /** =========================
-   * 8. 渲染
+   * 8. 渲染(先卸监听再 react,避免重绘过程中 plotly_click 重入 renderChart 栈溢出)
    ========================== */
-      Plotly.react(this.$refs.chart, traces, layout, {
+      const plotEl = this.$refs.chart;
+      if (!plotEl) return;
+
+      if (typeof plotEl.removeAllListeners === "function") {
+        plotEl.removeAllListeners("plotly_relayout");
+        plotEl.removeAllListeners("plotly_click");
+      }
+
+      const plotOpts = {
         responsive: true,
-        displayModeBar: false,
+        displayModeBar: true,
+        displaylogo: false,
+        modeBarButtonsToRemove: [
+          "zoom3d",
+          "pan3d",
+          "orbitRotation",
+          "tableRotation",
+          "resetCameraDefault3d",
+          "resetCameraLastSave3d",
+          "hoverClosest3d",
+        ],
+      };
+
+      const done = Plotly.react(plotEl, traces, layout, plotOpts);
+      const bind = () => this._bindPlotlyEvents();
+      if (done && typeof done.then === "function") {
+        done.then(bind).catch(bind);
+      } else {
+        this.$nextTick(bind);
+      }
+    },
+
+    _bindPlotlyEvents() {
+      const plotElement = this.$refs.chart;
+      if (!plotElement || typeof plotElement.on !== "function") return;
+
+      plotElement.on("plotly_relayout", (eventData) => {
+        if (eventData && eventData["scene.camera"]) {
+          console.log("当前相机视角:", eventData["scene.camera"]);
+        }
+      });
+      plotElement.on("plotly_click", (ev) => {
+        const pt = ev.points && ev.points[0];
+        if (!pt || pt.curveNumber == null) return;
+        const n = this._dataTraceCount || 0;
+        if (pt.curveNumber >= n) return;
+        const t =
+          this._traceIndexToTime && this._traceIndexToTime[pt.curveNumber];
+        if (t == null) return;
+        this.highlightedTime = this.highlightedTime === t ? null : t;
+        this.$nextTick(() => {
+          this.renderChart();
+        });
       });
     },
-    //     renderChart() {
-    //       if (!this.data.length) return;
-
-    //       /** =========================
-    //  * 1. 分组
-    //  ========================== */
-    //       //   console.log(this.data, "groupMap");
-    //       const groupMap = new Map();
-
-    //       this.data.forEach(([freq, amp, time, Hz]) => {
-    //         const t = this.parseTime(time);
-    //         const key = `${t}_${Hz}`; // 👈 用key区分
-
-    //         if (!groupMap.has(key)) {
-    //           groupMap.set(key, {
-    //             time: t,
-    //             Hz,
-    //             points: [],
-    //           });
-    //         }
-
-    //         groupMap.get(key).points.push([freq, amp]);
-    //       });
-
-    //       /** =========================
-    //  * 2. 排序
-    //  ========================== */
-    //       const times = [...groupMap.keys()].sort((a, b) => a - b);
-
-    //       /** =========================
-    //  * 3. traces
-    //  ========================== */
-    //       const traces = [];
-    //       times.forEach((time, index) => {
-    //         const points = groupMap.get(time);
-
-    //         points.sort((a, b) => a[0] - b[0]);
-
-    //         const x = [];
-    //         const y = [];
-    //         const z = [];
-    //         const text = [];
-
-    //         // const offset = index * 0.02;
-
-    //         points.forEach(([freq, amp]) => {
-    //           if (this.freqRange) {
-    //             if (freq < this.freqRange.min || freq > this.freqRange.max) return;
-    //           }
-    //           const offsetX = index * 50; // 👈 新增
-    //           const offsetZ = index * 0.02; // 👈 你已有
-    //           x.push(time + offsetX);
-    //           y.push(freq);
-    //           z.push(amp + offsetZ);
-
-    //           text.push(
-    //             `时间: ${this.formatTime(time)}<br>` +
-    //               `频率: ${freq.toFixed(2)} Hz<br>` +
-    //               `幅值: ${amp.toFixed(3)}`,
-    //           );
-    //         });
-
-    //         traces.push({
-    //           x,
-    //           y,
-    //           z,
-    //           text,
-    //           type: "scatter3d",
-    //           mode: "lines",
-    //           opacity: 0.7,
-    //           line: {
-    //             width: 2,
-    //             color: "#162961",
-    //           },
-    //           showlegend: false,
-    //           hovertemplate: "%{text}<extra></extra>",
-    //         });
-    //       });
-    //       /** =========================
-    //        * ✅ 标注线(修复重复问题)
-    //        ========================== */
-    //       if (this.markLines.length) {
-    //         const minTime = Math.min(...times);
-    //         const maxTime = Math.max(...times);
-
-    //         this.markLines.forEach((line) => {
-    //           traces.push({
-    //             x: [minTime, maxTime],
-    //             y: [line.freq, line.freq],
-    //             z: [0, 0],
-    //             type: "scatter3d",
-    //             mode: "lines+text",
-    //             line: {
-    //               color: "#ff0000",
-    //               width: 4,
-    //               dash: "dash",
-    //             },
-    //             text: ["", line.label],
-
-    //             textposition: "top right",
-    //             showlegend: false,
-    //             hovertemplate: `${line.label}<extra></extra>`,
-    //           });
-    //         });
-    //       }
-
-    //       // 👉 抽稀刻度(防止重叠)
-    //       const step = Math.ceil(times.length / 8);
-    //       const tickvals = times.filter((_, i) => i % step === 0);
-    //       const ticktext = tickvals.map(this.formatTime);
-
-    //       /** =========================
-    //          * 5. 布局(工业风)
-    //          ========================== */
-    //       const layout = {
-    //         title: "3D 瀑布频谱图",
-    //         paper_bgcolor: "#f5f7fa",
-    //         margin: { l: 0, r: 0, b: 0, t: 40 },
-
-    //         scene: {
-    //           xaxis: {
-    //             autorange: "reversed", //轴方向
-    //             title: "时间",
-    //             tickvals,
-    //             ticktext,
-    //             dtick: "D3",
-    //             // showbackground: true,
-    //             // backgroundcolor: "#ffffff",
-    //             // gridcolor: "#e0e7f1",
-    //             gridcolor: "#fff",
-    //             backgroundcolor: "#e0e7f1",
-    //             showbackground: true,
-    //             linecolor: "black",
-    //             ticks: "outside",
-    //             ticklen: 10,
-    //             tickcolor: "black",
-    //             zeroline: false,
-    //             tickangle: -10,
-    //             tickangle: 30, // 👈 倾斜
-    //             margin: {
-    //               l: 20,
-    //               r: 120, // 👉 关键!!给时间留空间
-    //               t: 40,
-    //               b: 40,
-    //             },
-    //           },
-    //           yaxis: {
-    //             title: "频率 (Hz)",
-    //             gridcolor: "#fff",
-    //             backgroundcolor: "#e0e7f1",
-    //             showbackground: true,
-    //             linecolor: "black",
-    //             ticks: "outside",
-    //             ticklen: 10,
-    //             tickcolor: "black",
-    //             zeroline: false,
-    //             tickangle: -10,
-    //             tickangle: 0,
-    //           },
-    //           zaxis: {
-    //             title: "幅值",
-    //             gridcolor: "#fff",
-    //             backgroundcolor: "#e0e7f1",
-    //             showbackground: true,
-    //             linecolor: "black",
-    //             ticks: "outside",
-    //             ticklen: 10,
-    //             tickcolor: "black",
-    //             zeroline: false,
-    //             tickangle: -10,
-    //             tickangle: 0,
-    //           },
-    //           /** ✅ 核心1:比例(决定“扁不扁”) */
-    //           //   bgcolor: "#e5ecf6",
-    //           //   gridcolor: "#fff",
-    //           aspectmode: "manual",
-    //           aspectratio: { x: 2.0, y: 2.8, z: 1.2 },
-    //           /** ✅ 核心2:相机(决定轴在哪边) */
-    //           camera: {
-    //             up: {
-    //               x: -0.1644947035315824,
-    //               y: -0.07969781808287146,
-    //               z: 0.9831529638377166,
-    //             },
-    //             center: {
-    //               x: -0.052807476121180814,
-    //               y: 0.02451796399554085,
-    //               z: -0.022911006648570736,
-    //             },
-    //             eye: {
-    //               x: 2.980700714870927,
-    //               y: 1.6273671421077383,
-    //               z: 0.6145682420564063,
-    //             },
-    //             projection: {
-    //               type: "orthographic",
-    //             },
-    //           },
-    //         },
-    //       };
-
-    //       /** =========================
-    //          * 6. 渲染
-    //          ========================== */
-    //       Plotly.react(this.$refs.chart, traces, layout, {
-    //         responsive: true,
-    //         displayModeBar: false,
-    //       });
-    //       // 监听图表的 relayout 事件,获取并输出相机视角
-    //       const plotElement = document.getElementById(`waterfall-chart`);
-    //       plotElement.on("plotly_relayout", function (eventData) {
-    //         // 在每次布局变更时,打印当前相机视角
-    //         if (eventData["scene.camera"]) {
-    //           console.log(
-    //             "当前相机视角:",
-    //             eventData["scene.camera"],
-    //             eventData["scene.aspectratio"],
-    //           );
-    //         }
-    //       });
-    //     },
     removeMark() {
       this.markLines = [];
       this.manualFreq = "";
@@ -533,9 +511,13 @@ export default {
       const newLines = [];
 
       for (let i = 1; i <= multi; i++) {
+        const val = base * i;
         newLines.push({
-          freq: base * i,
-          label: `${i}x (${base * i}Hz)`,
+          freq: val,
+          label:
+            this.axisMode === "order"
+              ? `${i}x (${val}Order)`
+              : `${i}x (${val}Hz)`,
         });
       }
 
@@ -567,9 +549,17 @@ export default {
 .full-row {
   width: 100%;
   .panel-block {
-    width: 45%;
+    width: 65%;
+    margin: 5px 0;
   }
 }
+.hint-text {
+  width: 100%;
+  margin: 4px 0 0;
+  font-size: 12px;
+  color: #909399;
+  line-height: 1.45;
+}
 .panel-block {
   display: flex;
   align-items: center;
@@ -583,8 +573,9 @@ export default {
 .label1 {
   font-size: 12px;
   color: #666;
-  display: inline-block;
-  width: 75px;
+  /* 父级是 flex:仅写 width 会被 flex-shrink 压缩,需固定 flex 基准且不收缩 */
+  flex: 0 0 40px;
+  box-sizing: border-box;
 }
 
 .btn-group {
@@ -630,7 +621,8 @@ export default {
   gap: 6px;
 }
 
+/* 勿用 flex:1,否则会拉伸子项,内联 width 无法按预期生效 */
 .el-select {
-  flex: 1;
+  flex: 0 0 auto;
 }
 </style>

+ 34 - 2
src/views/health/malfunction.vue

@@ -64,7 +64,7 @@
         </el-select>
       </p>
 
-      <p>
+      <!-- <p>
         采样频率:
         <el-select
           v-model="tabConditions[activeTab].frequencyvalue"
@@ -80,13 +80,14 @@
             :value="item"
           />
         </el-select>
-      </p>
+      </p> -->
       <p>
         时间:
         <el-date-picker
           size="small"
           v-model="tabConditions[activeTab].timevalue"
           type="datetimerange"
+          :picker-options="pickerOptions"
           range-separator="至"
           start-placeholder="开始日期"
           end-placeholder="结束日期"
@@ -188,6 +189,37 @@ export default {
           component: "Temperature",
         },
       ],
+      pickerOptions: {
+        shortcuts: [
+          {
+            text: "最近一周",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+              picker.$emit("pick", [start, end]);
+            },
+          },
+          {
+            text: "最近一个月",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+              picker.$emit("pick", [start, end]);
+            },
+          },
+          {
+            text: "最近三个月",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
+              picker.$emit("pick", [start, end]);
+            },
+          },
+        ],
+      },
       tabConditions: {
         Bearing: {},
         Gear: {},

+ 641 - 0
src/views/health/malfunctionOld.vue

@@ -0,0 +1,641 @@
+<template>
+  <div class="global-variable">
+    <!-- tab 切换 -->
+    <el-tabs v-model="activeName" @tab-click="handleClick">
+      <el-tab-pane
+        v-for="item in menuItems"
+        :key="item.component"
+        :label="item.name"
+        :name="item.component"
+      />
+    </el-tabs>
+
+    <!-- 查询条件 -->
+    <div class="searchbox">
+      <p>
+        单位:
+        <selecttree
+          size="small"
+          style="width: 180px"
+          placeholder="请选择所属公司"
+          :list="tabConditions[activeTab].parentOpt"
+          type="1"
+          v-model="tabConditions[activeTab].companyCode"
+          @change="parentChange"
+          :defaultParentProps="{
+            children: 'children',
+            label: 'companyName',
+            value: 'codeNumber',
+          }"
+        />
+      </p>
+      <p>
+        风机:
+        <el-select
+          size="small"
+          style="width: 150px"
+          v-model="tabConditions[activeTab].unitvalue"
+          @change="getchedian"
+          placeholder="请选择"
+        >
+          <el-option
+            v-for="item in tabConditions[activeTab].unitoptions"
+            :key="item.engineCode"
+            :label="item.engineName"
+            :value="item.engineCode"
+          />
+        </el-select>
+      </p>
+      <p>
+        测点:
+        <el-select
+          v-model="tabConditions[activeTab].monitoringvalue"
+          size="small"
+          clearable
+          placeholder="请选择"
+          :disabled="activeTab === 'Temperature'"
+        >
+          <el-option
+            v-for="item in tabConditions[activeTab].monitoringoptions"
+            :key="item.itemKey"
+            :label="item.itemValue"
+            :value="item.itemKey"
+          />
+        </el-select>
+      </p>
+
+      <p>
+        采样频率:
+        <el-select
+          v-model="tabConditions[activeTab].frequencyvalue"
+          size="small"
+          clearable
+          placeholder="请选择"
+          :disabled="activeTab === 'Temperature'"
+        >
+          <el-option
+            v-for="item in tabConditions[activeTab].frequencyoptions"
+            :key="item"
+            :label="item"
+            :value="item"
+          />
+        </el-select>
+      </p>
+      <p>
+        时间:
+        <el-date-picker
+          size="small"
+          v-model="tabConditions[activeTab].timevalue"
+          type="datetimerange"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        />
+        <!-- :picker-options="datePickerOptions" -->
+      </p>
+      <el-button
+        type="primary"
+        size="small"
+        @click="onSearchClick"
+        class="search-btn"
+        >查询</el-button
+      >
+    </div>
+
+    <!-- 动态组件内容 -->
+    <div class="main-body">
+      <keep-alive>
+        <component
+          ref="activeComponent"
+          :is="activeTab"
+          :codedata="tabData[activeTab].codedata"
+          v-bind="
+            activeTab === 'Temperature'
+              ? { echartsdata: tabData.Temperature.echartsdata }
+              : {}
+          "
+          :totalCount="tabData[activeTab].totalCount"
+          :totalPage="tabData[activeTab].totalPage"
+          :fieldCode="tabConditions[activeTab].companyCode"
+          :windTurbineNumber="tabConditions[activeTab].unitvalue"
+          @updatePage="handlePageChange"
+        />
+      </keep-alive>
+    </div>
+  </div>
+</template>
+
+<script>
+import * as FileSaver from "file-saver";
+import * as XLSX from "xlsx";
+import {
+  getSysOrganizationAuthTreeByRoleId,
+  windEngineGrouPage,
+  queryDetectionDic,
+} from "@/api/ledger.js";
+import selecttree from "../../components/selecttree.vue";
+import Bearing from "./components/malfunction/bearing.vue";
+import Dissymmetry from "./components/malfunction/dissymmetry.vue";
+import Gear from "./components/malfunction/gear.vue";
+import Loose from "./components/malfunction/loose.vue";
+import Misalignment from "./components/malfunction/misalignment.vue";
+import Temperature from "./components/malfunction/temperature.vue";
+import axios from "axios";
+
+export default {
+  components: {
+    selecttree,
+    Bearing,
+    Dissymmetry,
+    Gear,
+    Loose,
+    Misalignment,
+    Temperature,
+  },
+  data() {
+    return {
+      activeTab: "Bearing",
+      activeName: "Bearing",
+      menuItems: [
+        {
+          name: "轴承诊断",
+          icon: require("@/assets/img/ZC.png"),
+          component: "Bearing",
+        },
+        {
+          name: "齿轮诊断",
+          icon: require("@/assets/img/SZ.png"),
+          component: "Gear",
+        },
+        {
+          name: "不对中诊断",
+          icon: require("@/assets/img/DZ.png"),
+          component: "Dissymmetry",
+        },
+        {
+          name: "不平衡诊断",
+          icon: require("@/assets/img/DC.png"),
+          component: "Misalignment",
+        },
+        {
+          name: "松动诊断",
+          icon: require("@/assets/img/SD.png"),
+          component: "Loose",
+        },
+        {
+          name: "温度诊断",
+          icon: require("@/assets/img/WD.png"),
+          component: "Temperature",
+        },
+      ],
+      tabConditions: {
+        Bearing: {},
+        Gear: {},
+        Dissymmetry: {},
+        Misalignment: {},
+        Loose: {},
+        Temperature: {},
+      },
+      tabData: {
+        Bearing: { codedata: [], totalCount: 0, totalPage: 0 },
+        Gear: { codedata: [], totalCount: 0, totalPage: 0 },
+        Dissymmetry: { codedata: [], totalCount: 0, totalPage: 0 },
+        Misalignment: { codedata: [], totalCount: 0, totalPage: 0 },
+        Loose: { codedata: [], totalCount: 0, totalPage: 0 },
+        Temperature: {
+          codedata: [],
+          totalCount: 0,
+          totalPage: 0,
+          echartsdata: {},
+        },
+      },
+      datePickerOptions: {
+        onPick: ({ minDate, maxDate }) => {
+          if (minDate && !maxDate) {
+            const maxTime = new Date(
+              minDate.getTime() + 30 * 24 * 60 * 60 * 1000,
+            );
+            this.datePickerOptions.disabledDate = (time) => {
+              return (
+                time.getTime() < minDate.getTime() ||
+                time.getTime() > maxTime.getTime()
+              );
+            };
+          } else {
+            this.datePickerOptions.disabledDate = () => false;
+          }
+        },
+        disabledDate: () => false,
+      },
+      //demo
+    };
+  },
+
+  created() {
+    for (const key in this.tabConditions) {
+      this.tabConditions[key] = this.defaultCondition();
+    }
+    if (process.env.VUE_APP_Helath === "demo") {
+      this.GETtreedemo();
+    } else {
+      this.GETtree();
+    }
+  },
+  methods: {
+    defaultCondition() {
+      return {
+        unitvalue: "",
+        unitoptions: [],
+        companyCode: "",
+        parentOpt: [],
+        timevalue: [],
+        monitoringvalue: "",
+        monitoringoptions: [],
+        frequencyvalue: "",
+        frequencyoptions: [],
+      };
+    },
+    handleClick(tab) {
+      this.activeTab = tab.name;
+    },
+
+    async GETtree() {
+      const res = await getSysOrganizationAuthTreeByRoleId();
+      const treedata = res.data;
+      const processed = this.processTreeData(treedata);
+
+      for (const key in this.tabConditions) {
+        this.tabConditions[key].parentOpt = processed;
+      }
+    },
+
+    // demo演示所需,必要时可删除
+    async GETtreedemo() {
+      const res = await getSysOrganizationAuthTreeByRoleId();
+      const treedata = res.data;
+      const processed = this.processTreeData(treedata);
+
+      for (const key in this.tabConditions) {
+        const tab = this.tabConditions[key];
+        tab.parentOpt = processed;
+
+        // 默认公司
+        tab.companyCode = "WOF046400029";
+
+        // 默认风机列表和选中值
+        tab.unitoptions = [{ engineCode: "WOG01315", engineName: "#37" }];
+        tab.unitvalue = "WOG01315";
+
+        // 默认测点列表和选中值
+        if (key === "Gear") {
+          // Gear tab:保留主轴承 + 齿轮箱测点
+          this.$set(tab, "monitoringoptions", [
+            {
+              itemKey: "gearbox_first_stage_planet_large_ring_radial_vibration",
+              itemValue: "齿轮箱一级行星级大齿圈径向振动",
+            },
+          ]);
+          this.$set(
+            tab,
+            "monitoringvalue",
+            "gearbox_first_stage_planet_large_ring_radial_vibration",
+          );
+        } else {
+          // 其他 tab:只保留主轴承测点
+          this.$set(tab, "monitoringoptions", [
+            {
+              itemKey: "main_bearing_radial_horizontal_vibration",
+              itemValue: "主轴承径向水平振动",
+            },
+          ]);
+          this.$set(
+            tab,
+            "monitoringvalue",
+            "main_bearing_radial_horizontal_vibration",
+          );
+        }
+
+        // 默认采样频率
+        tab.frequencyoptions = ["12800", "25600", "51200"];
+        tab.frequencyvalue = "12800";
+
+        // 默认时间范围
+        tab.timevalue = [
+          new Date("2024-07-01 00:00:00"),
+          new Date("2024-12-31 00:00:00"),
+        ];
+      }
+
+      this.$nextTick(() => {
+        this.tabConditions[this.activeTab].companyCode = "WOF046400029";
+      });
+    },
+    parentChange(data) {
+      const condition = this.tabConditions[this.activeTab];
+      if (!data?.codeNumber) return;
+      condition.unitvalue = "";
+      condition.monitoringvalue = "";
+      condition.frequencyvalue = "";
+
+      windEngineGrouPage({
+        fieldCode: data.codeNumber,
+        pageNum: 1,
+        pageSize: 99,
+      }).then((res) => {
+        condition.unitoptions = res.data.list;
+      });
+
+      axios
+        .get(`/ETLapi/waveData/getAllSamplingFrequency/${data.codeNumber}`)
+        .then((res) => {
+          condition.frequencyoptions = (res.data.datas || [])
+            .map((item) => Number(item))
+            .filter((num) => num >= 2000)
+            .map((num) => num.toString());
+        });
+
+      condition.companyCode = data.codeNumber;
+    },
+    getchedian(value) {
+      const condition = this.tabConditions[this.activeTab];
+      const companyCode = condition.companyCode;
+
+      axios
+        .post(`/ETLapi/waveData/getAllMesurePointName/${companyCode}/${value}`)
+        .then((res) => {
+          condition.monitoringvalue = "";
+          if (res.data.code === 500) {
+            condition.monitoringoptions = [];
+            this.$message({
+              message: "当前风场不存在测点和采样频率数据" || "未知错误",
+              type: "warning",
+            });
+            return;
+          }
+          if (res.data.code === 200) {
+            const datas = Array.isArray(res.data.datas) ? res.data.datas : [];
+            condition.monitoringoptions = datas;
+            if (datas.length === 0) {
+              this.$message({ message: "暂无数据", type: "warning" });
+            }
+          }
+        })
+        .catch((err) => {
+          console.error("测点请求失败:", err);
+          condition.monitoringoptions = [];
+        });
+    },
+    processTreeData(treeData) {
+      const processedData = [];
+      function processNode(node) {
+        if (node.codeType === "field") {
+          node.companyName = node.fieldName;
+        }
+        if (node.children && node.children.length > 0) {
+          node.children.forEach(processNode);
+        }
+      }
+      treeData.forEach((root) => {
+        processNode(root);
+        processedData.push(root);
+      });
+      return processedData;
+    },
+    onSearchClick() {
+      this.conditions(1, false); // 主动查询,第一页,非分页触发
+    },
+    handlePageChange(page) {
+      this.conditions(page, true); // true 表示分页触发
+    },
+
+    conditions(page, isPageChange = false) {
+      const tab = this.activeTab;
+      this.$nextTick(() => {
+        const comp = this.$refs.activeComponent;
+        if (comp && typeof comp.reset === "function") {
+          comp.reset();
+        }
+      });
+
+      // 把后面接口请求部分放进延迟里,确保 reset 先执行完成
+      setTimeout(() => {
+        const condition = this.tabConditions[tab];
+
+        if (tab === "Temperature") {
+          const temperature = {
+            windCode: condition.companyCode,
+            windTurbineNumberList: [condition.unitvalue],
+            startTime: this.$formatDateTWO(condition.timevalue[0]),
+            endTime: this.$formatDateTWO(condition.timevalue[1]),
+            pageNo: page,
+            pageSize: 500,
+          };
+
+          const loading = this.$loading({
+            lock: true,
+            text: "温度诊断数据加载中,请稍候…",
+            spinner: "el-icon-loading",
+            background: "rgba(0, 0, 0, 0.8)",
+          });
+
+          const emptyEchartsData = {
+            gearbox_oil: { timestamps: [], values: [] },
+            generator_drive_end: { timestamps: [], values: [] },
+            generator_nondrive_end: { timestamps: [], values: [] },
+            main_bearing: { timestamps: [], values: [] },
+          };
+
+          const trendRequest = () => {
+            const trendStart = performance.now();
+            return axios
+              .post("/AnalysisMulti/SPRT/trend", temperature)
+              .then((res) => {
+                const trendEnd = performance.now();
+                console.log(
+                  `温度诊断 trend 接口耗时: ${(
+                    (trendEnd - trendStart) /
+                    1000
+                  ).toFixed(2)}s`,
+                );
+                const echartsdata = res.data.data || {};
+                this.tabData[tab].echartsdata = echartsdata;
+              })
+              .catch((err) => {
+                console.error("趋势图请求失败:", err);
+                this.$message.error("温度趋势图数据请求失败,请稍后再试");
+              });
+          };
+
+          const thresholdStart = performance.now();
+          const thresholdReq = axios
+            .post("/AnalysisMulti/temperature/threshold", temperature)
+            .then((res) => {
+              const thresholdEnd = performance.now();
+              console.log(
+                `温度诊断 threshold 接口耗时: ${(
+                  (thresholdEnd - thresholdStart) /
+                  1000
+                ).toFixed(2)}s`,
+              );
+
+              const data = res.data.data.records || [];
+              this.tabData[tab].codedata = data;
+              this.tabData[tab].totalCount = res.data.data.totalSize || 0;
+
+              if (!data.length) {
+                this.$message.warning("暂无诊断数据");
+                this.tabData[tab].echartsdata = emptyEchartsData;
+                return Promise.resolve("skip trend");
+              }
+
+              return isPageChange ? Promise.resolve() : trendRequest();
+            })
+            .catch((err) => {
+              console.error("温度诊断请求失败:", err);
+              this.$message.error("温度诊断数据请求失败,请稍后再试");
+            });
+
+          Promise.resolve(thresholdReq).finally(() => {
+            loading.close();
+          });
+        } else {
+          const params = {
+            samplingFrequency: condition.frequencyvalue,
+            windCode: condition.companyCode,
+            windTurbineNumberList: [condition.unitvalue],
+            mesureNameList: [condition.monitoringvalue],
+            startTime: this.$formatDateTWO(condition.timevalue[0]),
+            endTime: this.$formatDateTWO(condition.timevalue[1]),
+            pageNo: page,
+            pageSize: 10,
+          };
+
+          const startTime = performance.now();
+          axios
+            .post("/ETLapi/waveData/getMesureDataWithSF", params)
+            .then((res) => {
+              const endTime = performance.now();
+              console.log(
+                `其他 tab 接口请求耗时: ${(endTime - startTime).toFixed(2)}ms`,
+              );
+
+              const data = res.data || {};
+              this.tabData[tab].codedata = data.datas || [];
+              this.tabData[tab].totalCount = data.totalCount || 0;
+              this.tabData[tab].totalPage = data.totalPage || 0;
+            });
+        }
+      }, 300); // 延迟100毫秒执行接口请求逻辑
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.global-variable {
+  padding: 10px;
+}
+.head {
+  padding: 5px 0;
+  display: flex;
+  justify-content: space-between;
+
+  ul {
+    display: flex;
+    width: 500px;
+    height: 65px;
+    justify-content: space-between;
+    li {
+      cursor: pointer;
+      padding: 8px 12px 0 12px;
+      border-radius: 4px;
+      transition: all 0.3s;
+
+      &:hover {
+        background-color: #f0f0f0;
+      }
+
+      &.active {
+        background-color: #e6f7ff;
+        border-bottom: 2px solid var(--header-bg);
+
+        span {
+          color: var(--header-bg);
+          font-weight: bold;
+        }
+      }
+
+      .Simg {
+        width: 30px;
+        height: 30px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        margin: 0 auto;
+
+        img {
+          max-width: 100%;
+          max-height: 100%;
+        }
+      }
+
+      span {
+        display: block;
+        font-size: 12px;
+        font-weight: 600;
+        text-align: center;
+        margin-top: 5px;
+      }
+    }
+  }
+}
+.searchbox {
+  display: flex;
+  margin-bottom: 10px;
+  align-items: center;
+  flex-wrap: wrap;
+  // padding-top: 10px;
+  p {
+    margin-right: 10px;
+    margin-bottom: 0;
+    margin-top: 10px;
+    display: flex;
+    align-items: center;
+
+    .el-select,
+    .el-date-picker {
+      margin-left: 10px;
+    }
+  }
+
+  .el-select {
+    width: 180px;
+  }
+  .search-btn {
+    margin-top: 10px;
+  }
+}
+.main-body {
+  margin-top: 10px;
+  // border: 1px solid #ebeef5;
+  border-radius: 4px;
+  padding: 10px;
+  background: #fff;
+}
+
+.el-range-editor.el-input__inner {
+  width: 370px;
+}
+
+/* 放在你的组件 <style scoped> 或全局样式里 */
+.custom-loading-style .el-loading-spinner i {
+  font-size: 26px;
+  color: #409eff; /* Element UI 主色调,更柔和 */
+}
+
+.custom-loading-style .el-loading-text {
+  font-size: 14px;
+  color: #303133;
+  letter-spacing: 0.5px;
+}
+</style>

+ 34 - 14
src/views/health/vibration.vue

@@ -15,7 +15,7 @@
           </div>
           <div @click="generateWaterfall" class="picture">
             <img src="@/assets/analyse/3d.png" alt="" />
-            <p>瀑布图</p>
+            <p>频谱瀑布图</p>
           </div>
           <div @click="generate('1')" class="picture">
             <img src="@/assets/analyse/03.png" alt="" />
@@ -142,7 +142,7 @@
                 />
                 <!--  -->
               </div>
-              <div v-if="item.name === '瀑布图'">
+              <div v-if="item.name === '频谱瀑布图'">
                 <waterfallChart
                   :windCode="windCode"
                   :ids="ids === null ? currentRow.id : ids"
@@ -275,7 +275,7 @@ export default {
       if (hasTrend) {
         this.tendency();
       }
-      const hasWaterfall = this.fourList.some((i) => i.name === "瀑布图");
+      const hasWaterfall = this.fourList.some((i) => i.name === "频谱瀑布图");
       if (hasWaterfall) {
         this.getWaterfallData();
       }
@@ -458,7 +458,8 @@ export default {
 
       if (!config) return;
 
-      const index = this.fourList.findIndex((i) => i.id === item.id);
+      const chartId = item.id;
+      const index = this.fourList.findIndex((i) => i.id === chartId);
       if (index === -1) return;
 
       this.$set(this.fourList[index], "loading", true);
@@ -466,6 +467,9 @@ export default {
       axios
         .post(config.url, config.params)
         .then((res) => {
+          const idx = this.fourList.findIndex((i) => i.id === chartId);
+          if (idx === -1) return;
+
           let data = res.data;
 
           if (typeof data === "string") {
@@ -483,14 +487,16 @@ export default {
           }
 
           const newItem = {
-            ...this.fourList[index],
+            ...this.fourList[idx],
             [config.key]: data,
           };
 
-          this.$set(this.fourList, index, newItem);
+          this.$set(this.fourList, idx, newItem);
         })
         .finally(() => {
-          this.$set(this.fourList[index], "loading", false);
+          const idx = this.fourList.findIndex((i) => i.id === chartId);
+          if (idx === -1) return;
+          this.$set(this.fourList[idx], "loading", false);
         });
     },
     // 🌟 生成趋势图(只负责创建)
@@ -538,7 +544,8 @@ export default {
       this.fetchTrendChart(newItem);
     },
     fetchTrendChart(item) {
-      const index = this.fourList.findIndex((i) => i.id === item.id);
+      const chartId = item.id;
+      const index = this.fourList.findIndex((i) => i.id === chartId);
       if (index === -1) return;
       console.log(this.measurementPointData, "this.measurementPointData");
       this.$set(this.fourList[index], "loading", true);
@@ -550,6 +557,9 @@ export default {
           analysisType: "trend",
         })
         .then((res) => {
+          const idx = this.fourList.findIndex((i) => i.id === chartId);
+          if (idx === -1) return;
+
           let data = res.data;
 
           if (typeof data === "string") {
@@ -557,14 +567,16 @@ export default {
           }
 
           const newItem = {
-            ...this.fourList[index],
+            ...this.fourList[idx],
             qsList: data,
           };
 
-          this.$set(this.fourList, index, newItem);
+          this.$set(this.fourList, idx, newItem);
         })
         .finally(() => {
-          this.$set(this.fourList[index], "loading", false);
+          const idx = this.fourList.findIndex((i) => i.id === chartId);
+          if (idx === -1) return;
+          this.$set(this.fourList[idx], "loading", false);
         });
     },
     generateWaterfall() {
@@ -572,14 +584,14 @@ export default {
         this.$message.warning("请先选择节点");
         return;
       }
-      const exist = this.fourList.some((i) => i.name === "瀑布图");
+      const exist = this.fourList.some((i) => i.name === "频谱瀑布图");
       if (exist) {
         this.$message.warning("瀑布图已存在");
         return;
       }
 
       this.fourList.push({
-        name: "瀑布图",
+        name: "频谱瀑布图",
         id: Date.now(),
         isMinimized: false,
       });
@@ -592,9 +604,13 @@ export default {
       const seq = Date.now();
       this.waterfallSeq = seq;
 
-      const index = this.fourList.findIndex((i) => i.name === "瀑布图");
+      const getWaterfallIndex = () =>
+        this.fourList.findIndex((i) => i && i.name === "频谱瀑布图");
+      let index = getWaterfallIndex();
       if (index === -1) return;
 
+      // 组件/卡片可能在异步期间被关闭或列表被重建,所有 $set 前都要确保目标仍存在
+      if (!this.fourList[index]) return;
       this.$set(this.fourList[index], "loading", true);
 
       try {
@@ -696,12 +712,16 @@ export default {
         console.log("优化后点数:", waterfallData.length);
 
         // ✅ 不要深拷贝!!!
+        index = getWaterfallIndex();
+        if (index === -1 || !this.fourList[index]) return;
         this.$set(this.fourList[index], "waterfallData", waterfallData);
       } catch (e) {
         console.error(e);
         this.$message.error("瀑布图请求失败");
       } finally {
         if (this.waterfallSeq !== seq) return;
+        index = getWaterfallIndex();
+        if (index === -1 || !this.fourList[index]) return;
         this.$set(this.fourList[index], "loading", false);
       }
     },