Przeglądaj źródła

修改添加部件信息、上传报告等权限

liujiejie 14 godzin temu
rodzic
commit
0754d0d9ce

+ 38 - 33
.env.dev

@@ -1,7 +1,7 @@
 ###
 ###
  # @Author: your name
  # @Author: your name
  # @Date: 2025-07-17 14:14:27
  # @Date: 2025-07-17 14:14:27
- # @LastEditTime: 2026-04-15 10:57:56
+ # @LastEditTime: 2026-04-17 15:09:38
  # @LastEditors: milo-MacBook-Pro.local
  # @LastEditors: milo-MacBook-Pro.local
  # @Description: In User Settings Edit
  # @Description: In User Settings Edit
  # @FilePath: /performance-test/.env.dev
  # @FilePath: /performance-test/.env.dev
@@ -11,38 +11,43 @@ VUE_APP_THEM="green"
 VUE_APP_ISSHOWHD='default'
 VUE_APP_ISSHOWHD='default'
 VUE_APP_Helath='dev'
 VUE_APP_Helath='dev'
 VUE_APP_TITLE='机组功率曲线异常检测数据分析系统'
 VUE_APP_TITLE='机组功率曲线异常检测数据分析系统'
-#外网
-VUE_APP_MAPVIEW= "http://106.120.102.238:18000/tiles/{z}/{x}/{y}.png"
-VUE_APP_UPLOAD="http://106.120.102.238:16700/energy-manage-service/api/check/upload"
-VUE_APP_APIPROXY='http://106.120.102.238:16600' #生产外网
-# VUE_APP_APIPROXY='http://106.120.102.238:16600' #开发外网
-VUE_APP_MAP=http://106.120.102.238:18080
+# 外网
+# VUE_APP_MAPVIEW= "http://106.120.102.238:18000/tiles/{z}/{x}/{y}.png"
+# VUE_APP_UPLOAD="http://106.120.102.238:16700/energy-manage-service/api/check/upload"
+# # 生产外网
+# VUE_APP_APIPROXY='http://106.120.102.238:16700'
+# 开发外网
+# VUE_APP_APIPROXY='http://106.120.102.238:16600'
+# VUE_APP_MAP=http://106.120.102.238:18080
+# VUE_APP_APIPROXY='http://106.120.102.238:16600' 
+
+
 # VUE_APP_WZLAPIPROXY='http://106.120.102.238:18080/WindTransDev'
 # VUE_APP_WZLAPIPROXY='http://106.120.102.238:18080/WindTransDev'
 # VUE_APP_ETLAPIPROXY='http://106.120.102.238:18080/WindTransDev'
 # VUE_APP_ETLAPIPROXY='http://106.120.102.238:18080/WindTransDev'
-VUE_APP_WZLAPIPROXY='http://106.120.102.238:58880/transDataWeb'
-VUE_APP_ETLAPIPROXY='http://106.120.102.238:58880/transDataWeb'
-VUE_APP_AnalysisMultiAPIPROXY='http://106.120.102.238:28999/AnalysisMulti'
-#自定义算法文佳 目前无法使用,可能是服务未启动
-VUE_APP_sAlgorithmAPIPROXY='http://106.120.102.238:58880'
-VUE_APP_databaseApiAPIPROXY='http://106.120.102.238:58880'
-#暂时不知下载报告内网dev 环境地址
-VUE_APP_downLoadChartAPIPROXY='http://106.120.102.238:58880'
-VUE_APP_downLoadChartAPIPROXY='http://0.0.0.0:3001'
-#内网
-# VUE_APP_UPLOAD="http://192.168.50.235/energy-manage-service/api/check/upload"
-# VUE_APP_MAPVIEW=/tiles/{z}/{x}/{y}.png
-# VUE_APP_MAP=http://192.168.50.235
-# # http://192.168.50.235:16200 开发内网地址
-# VUE_APP_APIPROXY='http://192.168.50.235:16300' # 生产数据库
-# VUE_APP_WZLAPIPROXY='http://192.168.50.241:9002'
-# # VUE_APP_ETLAPIPROXY='http://192.168.50.241:9002'
-# VUE_APP_ETLAPIPROXY='http://192.168.50.241:9000/transDataWebProd/'
-# # #自定义算法文佳 目前无法使用,可能是服务未启动
-# VUE_APP_sAlgorithmAPIPROXY='http://192.168.50.235:8998/AnalysisMulti'
-# VUE_APP_databaseApiAPIPROXY='http://192.168.50.234:3002'
-# #暂时不知健康评估内网dev 环境地址
-# VUE_APP_AnalysisMultiAPIPROXY='http://192.168.50.235:8998/AnalysisMulti'
+# VUE_APP_WZLAPIPROXY='http://106.120.102.238:58880/transDataWeb'
+# VUE_APP_ETLAPIPROXY='http://106.120.102.238:58880/transDataWeb'
+# VUE_APP_AnalysisMultiAPIPROXY='http://106.120.102.238:28999/AnalysisMulti'
+# #自定义算法文佳 目前无法使用,可能是服务未启动
+# VUE_APP_sAlgorithmAPIPROXY='http://106.120.102.238:58880'
+# VUE_APP_databaseApiAPIPROXY='http://106.120.102.238:58880'
 # #暂时不知下载报告内网dev 环境地址
 # #暂时不知下载报告内网dev 环境地址
-# # VUE_APP_downLoadChartAPIPROXY='http://106.120.102.238:58880'
-# # VUE_APP_downLoadChartAPIPROXY='http://192.168.50.234:3001'
-# VUE_APP_downLoadChartAPIPROXY='http://0.0.0.0:3001'
+# VUE_APP_downLoadChartAPIPROXY='http://106.120.102.238:58880'
+# VUE_APP_downLoadChartAPIPROXY='http://0.0.0.0:3001'
+#内网
+VUE_APP_UPLOAD="http://192.168.50.235/energy-manage-service/api/check/upload"
+VUE_APP_MAPVIEW=/tiles/{z}/{x}/{y}.png
+VUE_APP_MAP=http://192.168.50.235
+# http://192.168.50.235:16200 开发内网地址
+VUE_APP_APIPROXY='http://192.168.50.235:16300' # 生产数据库
+VUE_APP_WZLAPIPROXY='http://192.168.50.241:9002'
+# VUE_APP_ETLAPIPROXY='http://192.168.50.241:9002'
+VUE_APP_ETLAPIPROXY='http://192.168.50.241:9000/transDataWebProd/'
+# #自定义算法文佳 目前无法使用,可能是服务未启动
+VUE_APP_sAlgorithmAPIPROXY='http://192.168.50.235:8998/AnalysisMulti'
+VUE_APP_databaseApiAPIPROXY='http://192.168.50.234:3002'
+#暂时不知健康评估内网dev 环境地址
+VUE_APP_AnalysisMultiAPIPROXY='http://192.168.50.235:8998/AnalysisMulti'
+#暂时不知下载报告内网dev 环境地址
+# VUE_APP_downLoadChartAPIPROXY='http://106.120.102.238:58880'
+# VUE_APP_downLoadChartAPIPROXY='http://192.168.50.234:3001'
+VUE_APP_downLoadChartAPIPROXY='http://0.0.0.0:3001'

+ 34 - 17
.env.jl

@@ -3,10 +3,27 @@ VUE_APP_ISSHOWHD='default'
 VUE_APP_Helath='dev'
 VUE_APP_Helath='dev'
 VUE_APP_TITLE='机组功率曲线异常检测数据分析系统'
 VUE_APP_TITLE='机组功率曲线异常检测数据分析系统'
 VUE_APP_PROJECT='jl'
 VUE_APP_PROJECT='jl'
+
+# 吉林打包专用
+# VUE_APP_MAPVIEW="http://127.0.0.1:8080/tiles/{z}/{x}/{y}.png"
+VUE_APP_MAPVIEW=/tiles/{z}/{x}/{y}.png
+VUE_APP_UPLOAD="http://127.0.0.1:8080/energy-manage-service/api/check/upload"
+VUE_APP_UPLOAD="http://127.0.0.1:8080/energy-manage-service/api/check/upload"
+VUE_APP_APIPROXY='http://127.0.0.1:8080'
+VUE_APP_MAP='http://127.0.0.1:8080'
+VUE_APP_WZLAPIPROXY='http://127.0.0.1:8080/WindTransDev'
+VUE_APP_ETLAPIPROXY='http://127.0.0.1:8080/WindTransDev'
+VUE_APP_AnalysisMultiAPIPROXY='http://127.0.0.1:8080/AnalysisMulti'
+#自定义算法文佳 目前无法使用,可能是服务未启动
+VUE_APP_sAlgorithmAPIPROXY='http://127.0.0.1:8080'
+VUE_APP_databaseApiAPIPROXY='http://127.0.0.1:8080'
+# #暂时不知下载报告内网dev 环境地址
+VUE_APP_downLoadChartAPIPROXY='http://127.0.0.1:8080'
 #吉林外网
 #吉林外网
-# VUE_APP_UPLOAD="http://192.168.50.235/energy-manage-service/api/check/upload"
+
+# VUE_APP_UPLOAD="http://127.0.0.1:8080/energy-manage-service/api/check/upload"
 # VUE_APP_MAPVIEW=/tiles/{z}/{x}/{y}.png
 # VUE_APP_MAPVIEW=/tiles/{z}/{x}/{y}.png
-# VUE_APP_MAP=http://192.168.50.235
+# VUE_APP_MAP='http://127.0.0.1:8080'
 # #16300 生产数据库 
 # #16300 生产数据库 
 # VUE_APP_APIPROXY='http://192.168.50.235:16200' 
 # VUE_APP_APIPROXY='http://192.168.50.235:16200' 
 # VUE_APP_WZLAPIPROXY='http://192.168.50.241:9002'
 # VUE_APP_WZLAPIPROXY='http://192.168.50.241:9002'
@@ -19,20 +36,20 @@ VUE_APP_PROJECT='jl'
 # VUE_APP_AnalysisMultiAPIPROXY='http://192.168.50.235:8998/AnalysisMulti'
 # VUE_APP_AnalysisMultiAPIPROXY='http://192.168.50.235:8998/AnalysisMulti'
 # #暂时不知下载报告内网dev 环境地址
 # #暂时不知下载报告内网dev 环境地址
 # VUE_APP_downLoadChartAPIPROXY='http://192.168.50.234:3001'
 # VUE_APP_downLoadChartAPIPROXY='http://192.168.50.234:3001'
-# # VUE_APP_downLoadChartAPIPROXY='http://0.0.0.0:3001'
+# VUE_APP_downLoadChartAPIPROXY='http://0.0.0.0:3001'
 
 
 #公司 生产配置
 #公司 生产配置
-VUE_APP_MAPVIEW= "http://106.120.102.238:18000/tiles/{z}/{x}/{y}.png"
-VUE_APP_MAPVIEW=/tiles/{z}/{x}/{y}.png
-VUE_APP_UPLOAD="http://106.120.102.238:16700/energy-manage-service/api/check/upload"
-#外网生产 26500  外网开发16700 
-VUE_APP_APIPROXY='http://106.120.102.238:16700'
-VUE_APP_MAP='http://106.120.102.238:18080'
-VUE_APP_WZLAPIPROXY='http://106.120.102.238:58880/transDataWeb'
-VUE_APP_ETLAPIPROXY='http://106.120.102.238:58880/transDataWeb'
-VUE_APP_AnalysisMultiAPIPROXY='http://106.120.102.238:18000/AnalysisMulti'
-#自定义算法文佳 目前无法使用,可能是服务未启动
-VUE_APP_sAlgorithmAPIPROXY='http://106.120.102.238:58880'
-VUE_APP_databaseApiAPIPROXY='http://106.120.102.238:58880'
-# #暂时不知下载报告内网dev 环境地址
-VUE_APP_downLoadChartAPIPROXY='http://106.120.102.238:58880'
+# VUE_APP_MAPVIEW= "http://106.120.102.238:18000/tiles/{z}/{x}/{y}.png"
+# VUE_APP_MAPVIEW=/tiles/{z}/{x}/{y}.png
+# VUE_APP_UPLOAD="http://106.120.102.238:16700/energy-manage-service/api/check/upload"
+# #外网演示环境 26500  外网开发16700 
+# VUE_APP_APIPROXY='http://106.120.102.238:16700'
+# VUE_APP_MAP='http://106.120.102.238:18080'
+# VUE_APP_WZLAPIPROXY='http://106.120.102.238:58880/transDataWeb'
+# VUE_APP_ETLAPIPROXY='http://106.120.102.238:58880/transDataWeb'
+# VUE_APP_AnalysisMultiAPIPROXY='http://106.120.102.238:18000/AnalysisMulti'
+# #自定义算法文佳 目前无法使用,可能是服务未启动
+# VUE_APP_sAlgorithmAPIPROXY='http://106.120.102.238:58880'
+# VUE_APP_databaseApiAPIPROXY='http://106.120.102.238:58880'
+# # #暂时不知下载报告内网dev 环境地址
+# VUE_APP_downLoadChartAPIPROXY='http://106.120.102.238:58880'

+ 2 - 2
downLoadServer/.env

@@ -9,8 +9,8 @@ API_BASE_URL=http://0.0.0.0:3001
 
 
 MINIO_ENDPOINT=192.168.50.234 #minio 生产
 MINIO_ENDPOINT=192.168.50.234 #minio 生产
 MINIO_PORT=6900 #minio 生产
 MINIO_PORT=6900 #minio 生产
-MINIO_ACCESS_KEY=iPzAnpgos6YETnZi7rNI
-MINIO_SECRET_KEY=iexajFlKr9tXq7oef0nbTPxsaiIgnB9QndW0wngX
+MINIO_ACCESS_KEY=6VkF2ul6X7udr7RLsG2W
+MINIO_SECRET_KEY=jtBuqZ80biRWQf6sbwzDQJwHtEBicPjkZBtvjTrA
 # MINIO_ACCESS_KEY=haH1vePq7unSp4TG1One
 # MINIO_ACCESS_KEY=haH1vePq7unSp4TG1One
 # MINIO_SECRET_KEY=idxO5SAjboUYERpDICgHgBoHX7bcYv355lMQANt6
 # MINIO_SECRET_KEY=idxO5SAjboUYERpDICgHgBoHX7bcYv355lMQANt6
 CHROME_PATH=/Applications/Google Chrome.app/Contents/MacOS/Google Chrome
 CHROME_PATH=/Applications/Google Chrome.app/Contents/MacOS/Google Chrome

+ 1 - 0
src/views/admin/cockpitManage/Index.vue

@@ -62,6 +62,7 @@
 
 
     <Rightdata
     <Rightdata
       v-show="ShowRi"
       v-show="ShowRi"
+      v-hasPermi="['home:performance:performanceData']"
       ref="childRef"
       ref="childRef"
       :maplistArr="maplistArr"
       :maplistArr="maplistArr"
       :defaultdata="defaultdata"
       :defaultdata="defaultdata"

+ 5 - 6
src/views/admin/cockpitManage/component/leftdata.vue

@@ -1,6 +1,6 @@
 <template>
 <template>
   <div class="backbone">
   <div class="backbone">
-    <div class="BOXYI">
+    <div class="BOXYI" v-hasPermi="['home:performance:performanceData']">
       <h3>任务统计</h3>
       <h3>任务统计</h3>
       <div class="BOXsan">
       <div class="BOXsan">
         <div class="YC02" @click="getwgl">
         <div class="YC02" @click="getwgl">
@@ -96,7 +96,6 @@
         </div>
         </div>
       </el-dialog>
       </el-dialog>
     </div>
     </div>
-
     <!-- 入库信息 -->
     <!-- 入库信息 -->
     <!-- 离线 -->
     <!-- 离线 -->
     <div class="BOX" v-hasPermi="['home:offlinedata']">
     <div class="BOX" v-hasPermi="['home:offlinedata']">
@@ -419,7 +418,7 @@ export default {
     filteredWindList() {
     filteredWindList() {
       return this.searchvalue
       return this.searchvalue
         ? this.windList.filter((item) =>
         ? this.windList.filter((item) =>
-            item.windFarmName.includes(this.searchvalue)
+            item.windFarmName.includes(this.searchvalue),
           )
           )
         : this.windList;
         : this.windList;
     },
     },
@@ -478,7 +477,7 @@ export default {
       // 打开新窗口
       // 打开新窗口
       const newWindow = window.open(
       const newWindow = window.open(
         "/home/performance/assetssMag?id=195",
         "/home/performance/assetssMag?id=195",
-        "_blank"
+        "_blank",
       );
       );
       // 等待窗口加载完成后发送消息
       // 等待窗口加载完成后发送消息
       newWindow.onload = () => {
       newWindow.onload = () => {
@@ -492,7 +491,7 @@ export default {
       // 打开新窗口
       // 打开新窗口
       const newWindow = window.open(
       const newWindow = window.open(
         "/home/performance/assetssMag?id=195",
         "/home/performance/assetssMag?id=195",
-        "_blank"
+        "_blank",
       );
       );
       // 等待窗口加载完成后发送消息
       // 等待窗口加载完成后发送消息
       newWindow.onload = () => {
       newWindow.onload = () => {
@@ -507,7 +506,7 @@ export default {
       // 打开新窗口
       // 打开新窗口
       const newWindow = window.open(
       const newWindow = window.open(
         "/home/performance/assetssMag?id=195",
         "/home/performance/assetssMag?id=195",
-        "_blank"
+        "_blank",
       );
       );
       // 等待窗口加载完成后发送消息
       // 等待窗口加载完成后发送消息
       newWindow.onload = () => {
       newWindow.onload = () => {

+ 29 - 6
src/views/health/components/malfunction/bearing.vue

@@ -104,17 +104,17 @@
           <el-empty
           <el-empty
             v-if="!filteredTrendChartList.length"
             v-if="!filteredTrendChartList.length"
             description="暂无数据"
             description="暂无数据"
-            style="padding: 46px 0"
+            style="padding: 46px 0; height: 100%"
           ></el-empty>
           ></el-empty>
           <div v-else class="mpoint-charts">
           <div v-else class="mpoint-charts">
-            <el-carousel :key="trendCarouselKey">
+            <el-carousel :height="mpointCarouselHeight" :key="trendCarouselKey">
               <el-carousel-item
               <el-carousel-item
                 v-for="(chart, index) in filteredTrendChartList"
                 v-for="(chart, index) in filteredTrendChartList"
                 :key="`${chart.title}-${index}`"
                 :key="`${chart.title}-${index}`"
               >
               >
                 <div class="mpoint-chart-item">
                 <div class="mpoint-chart-item">
                   <Eecharts
                   <Eecharts
-                    style="height: 260px"
+                    style="height: 90%"
                     :xData="chart.xData"
                     :xData="chart.xData"
                     :yData="chart.yData"
                     :yData="chart.yData"
                     :yNames="chart.yNames"
                     :yNames="chart.yNames"
@@ -194,6 +194,7 @@ export default {
       trendChartList: [],
       trendChartList: [],
       selectedFunctionTypeName: "",
       selectedFunctionTypeName: "",
       selectedMonitorId: "",
       selectedMonitorId: "",
+      mpointCarouselHeight: "65vh",
     };
     };
   },
   },
   created() {},
   created() {},
@@ -312,7 +313,7 @@ export default {
     statusCards() {
     statusCards() {
       const { total, normal, warning, danger } = this.statusSummary;
       const { total, normal, warning, danger } = this.statusSummary;
       const toRatio = (count) =>
       const toRatio = (count) =>
-        total > 0 ? `${((count / total) * 100).toFixed(0)}%` : "0%";
+        total > 0 ? `${((count / total) * 100).toFixed(2)}%` : "0%";
       return [
       return [
         {
         {
           key: "normal",
           key: "normal",
@@ -594,10 +595,11 @@ export default {
   gap: 10px;
   gap: 10px;
   .TopBox {
   .TopBox {
     width: 40%;
     width: 40%;
+    height: 76vh;
   }
   }
   .bottomBox {
   .bottomBox {
     width: 59%;
     width: 59%;
-    height: 360px;
+    height: 68.5vh;
   }
   }
 }
 }
 h4 {
 h4 {
@@ -610,12 +612,13 @@ h4 {
   justify-content: space-around;
   justify-content: space-around;
   .rightdiv {
   .rightdiv {
     width: 100%;
     width: 100%;
+    height: 100%;
     .monitor-panel {
     .monitor-panel {
       border-radius: 14px;
       border-radius: 14px;
       padding: 14px;
       padding: 14px;
       min-height: 360px;
       min-height: 360px;
+      height: 90%;
       box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
       box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
-      height: 360px;
       overflow-y: auto;
       overflow-y: auto;
     }
     }
     .monitor-empty {
     .monitor-empty {
@@ -760,12 +763,32 @@ h4 {
 
 
 .mpoint-charts {
 .mpoint-charts {
   display: block;
   display: block;
+  height: 65vh;
+  box-sizing: border-box;
+  ::v-deep .el-carousel {
+    height: 100%;
+  }
+  ::v-deep .el-carousel__container {
+    height: 100%;
+  }
+  ::v-deep .el-carousel__item {
+    height: 100%;
+  }
 }
 }
 
 
 .mpoint-chart-item {
 .mpoint-chart-item {
   border: 1px solid #f0f0f0;
   border: 1px solid #f0f0f0;
   border-radius: 4px;
   border-radius: 4px;
   padding: 8px 10px;
   padding: 8px 10px;
+  box-sizing: border-box;
+  height: 90%;
+  min-height: 0;
+  display: flex;
+  flex-direction: column;
+  > * {
+    flex: 1;
+    min-height: 0;
+  }
 }
 }
 
 
 .status-legend {
 .status-legend {

+ 30 - 5
src/views/health/components/malfunction/dissymmetry.vue

@@ -102,17 +102,17 @@
           <el-empty
           <el-empty
             v-if="!filteredTrendChartList.length"
             v-if="!filteredTrendChartList.length"
             description="暂无数据"
             description="暂无数据"
-            style="padding: 48px 0"
+            style="padding: 46px 0; height: 100%"
           ></el-empty>
           ></el-empty>
           <div v-else class="mpoint-charts">
           <div v-else class="mpoint-charts">
-            <el-carousel :key="trendCarouselKey">
+            <el-carousel :height="mpointCarouselHeight" :key="trendCarouselKey">
               <el-carousel-item
               <el-carousel-item
                 v-for="(chart, index) in filteredTrendChartList"
                 v-for="(chart, index) in filteredTrendChartList"
                 :key="`${chart.title}-${index}`"
                 :key="`${chart.title}-${index}`"
               >
               >
                 <div class="mpoint-chart-item">
                 <div class="mpoint-chart-item">
                   <Eecharts
                   <Eecharts
-                    style="height: 260px"
+                    style="height: 90%"
                     :xData="chart.xData"
                     :xData="chart.xData"
                     :yData="chart.yData"
                     :yData="chart.yData"
                     :yNames="chart.yNames"
                     :yNames="chart.yNames"
@@ -195,6 +195,7 @@ export default {
       trendChartList: [],
       trendChartList: [],
       selectedFunctionTypeName: "",
       selectedFunctionTypeName: "",
       selectedMonitorId: "",
       selectedMonitorId: "",
+      mpointCarouselHeight: "65vh",
     };
     };
   },
   },
   created() {},
   created() {},
@@ -314,7 +315,7 @@ export default {
     statusCards() {
     statusCards() {
       const { total, normal, warning, danger } = this.statusSummary;
       const { total, normal, warning, danger } = this.statusSummary;
       const toRatio = (count) =>
       const toRatio = (count) =>
-        total > 0 ? `${((count / total) * 100).toFixed(0)}%` : "0%";
+        total > 0 ? `${((count / total) * 100).toFixed(2)}%` : "0%";
       return [
       return [
         {
         {
           key: "normal",
           key: "normal",
@@ -602,10 +603,11 @@ export default {
   gap: 10px;
   gap: 10px;
   .TopBox {
   .TopBox {
     width: 40%;
     width: 40%;
+    height: 76vh;
   }
   }
   .bottomBox {
   .bottomBox {
     width: 59%;
     width: 59%;
-    height: 360px;
+    height: 68.5vh;
   }
   }
 }
 }
 h4 {
 h4 {
@@ -618,11 +620,14 @@ h4 {
   justify-content: space-around;
   justify-content: space-around;
   .rightdiv {
   .rightdiv {
     width: 100%;
     width: 100%;
+    height: 100%;
     .monitor-panel {
     .monitor-panel {
       border-radius: 14px;
       border-radius: 14px;
       padding: 14px;
       padding: 14px;
       min-height: 360px;
       min-height: 360px;
+      height: 90%;
       box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
       box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
+      overflow-y: auto;
     }
     }
     .monitor-empty {
     .monitor-empty {
       display: flex;
       display: flex;
@@ -766,12 +771,32 @@ h4 {
 
 
 .mpoint-charts {
 .mpoint-charts {
   display: block;
   display: block;
+  height: 65vh;
+  box-sizing: border-box;
+  ::v-deep .el-carousel {
+    height: 100%;
+  }
+  ::v-deep .el-carousel__container {
+    height: 100%;
+  }
+  ::v-deep .el-carousel__item {
+    height: 100%;
+  }
 }
 }
 
 
 .mpoint-chart-item {
 .mpoint-chart-item {
   border: 1px solid #f0f0f0;
   border: 1px solid #f0f0f0;
   border-radius: 4px;
   border-radius: 4px;
   padding: 8px 10px;
   padding: 8px 10px;
+  box-sizing: border-box;
+  height: 90%;
+  min-height: 0;
+  display: flex;
+  flex-direction: column;
+  > * {
+    flex: 1;
+    min-height: 0;
+  }
 }
 }
 
 
 .status-legend {
 .status-legend {

+ 37 - 11
src/views/health/components/malfunction/gear.vue

@@ -111,10 +111,10 @@
           <el-empty
           <el-empty
             v-if="!filteredTrendChartList.length"
             v-if="!filteredTrendChartList.length"
             description="暂无数据"
             description="暂无数据"
-            style="padding: 46px 0"
+            style="padding: 46px 0; height: 100%"
           ></el-empty>
           ></el-empty>
           <div v-else class="mpoint-charts">
           <div v-else class="mpoint-charts">
-            <el-carousel :key="trendCarouselKey">
+            <el-carousel :height="mpointCarouselHeight" :key="trendCarouselKey">
               <el-carousel-item
               <el-carousel-item
                 v-for="(chart, index) in filteredTrendChartList"
                 v-for="(chart, index) in filteredTrendChartList"
                 :key="`${chart.title}-${index}`"
                 :key="`${chart.title}-${index}`"
@@ -122,7 +122,7 @@
                 <div class="mpoint-chart-item">
                 <div class="mpoint-chart-item">
                   <!-- <p class="mpoint-chart-title">{{ chart.title }}</p> -->
                   <!-- <p class="mpoint-chart-title">{{ chart.title }}</p> -->
                   <Eecharts
                   <Eecharts
-                    style="height: 260px"
+                    style="height: 90%"
                     :xData="chart.xData"
                     :xData="chart.xData"
                     :yData="chart.yData"
                     :yData="chart.yData"
                     :yNames="chart.yNames"
                     :yNames="chart.yNames"
@@ -205,6 +205,8 @@ export default {
       trendChartList: [],
       trendChartList: [],
       selectedFunctionTypeName: "",
       selectedFunctionTypeName: "",
       selectedMonitorId: "",
       selectedMonitorId: "",
+      /** 与下方 .mpoint-charts 高度一致;须传给 el-carousel 的 height,轮播内部轨道才会拉高 */
+      mpointCarouselHeight: "65vh",
     };
     };
   },
   },
   created() {},
   created() {},
@@ -323,7 +325,7 @@ export default {
     statusCards() {
     statusCards() {
       const { total, normal, warning, danger } = this.statusSummary;
       const { total, normal, warning, danger } = this.statusSummary;
       const toRatio = (count) =>
       const toRatio = (count) =>
-        total > 0 ? `${((count / total) * 100).toFixed(0)}%` : "0%";
+        total > 0 ? `${((count / total) * 100).toFixed(2)}%` : "0%";
       return [
       return [
         {
         {
           key: "normal",
           key: "normal",
@@ -620,10 +622,11 @@ export default {
   gap: 10px;
   gap: 10px;
   .TopBox {
   .TopBox {
     width: 40%;
     width: 40%;
+    height: 76vh;
   }
   }
   .bottomBox {
   .bottomBox {
     width: 59%;
     width: 59%;
-    height: 360px;
+    height: 68.5vh;
   }
   }
 }
 }
 h4 {
 h4 {
@@ -634,6 +637,7 @@ h4 {
 .TopBox {
 .TopBox {
   // height: 280px;
   // height: 280px;
   display: flex;
   display: flex;
+
   justify-content: space-around;
   justify-content: space-around;
 
 
   .leftdiv {
   .leftdiv {
@@ -720,13 +724,15 @@ h4 {
 
 
   .rightdiv {
   .rightdiv {
     width: 100%;
     width: 100%;
+    height: 100%;
     .monitor-panel {
     .monitor-panel {
+      height: 90%;
       // border: 1px solid #e4e7ed;
       // border: 1px solid #e4e7ed;
       border-radius: 14px;
       border-radius: 14px;
       // background: #f5f7fa;
       // background: #f5f7fa;
+
       padding: 14px;
       padding: 14px;
-      // min-height: 360px;
-      height: 360px;
+
       overflow-y: auto;
       overflow-y: auto;
       box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
       box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
     }
     }
@@ -740,7 +746,8 @@ h4 {
       display: flex;
       display: flex;
       align-items: center;
       align-items: center;
       justify-content: center;
       justify-content: center;
-      min-height: 260px;
+      // min-height: 260px;
+      height: 100%;
       background: #fff;
       background: #fff;
       border-radius: 10px;
       border-radius: 10px;
     }
     }
@@ -877,6 +884,7 @@ h4 {
   justify-content: space-around;
   justify-content: space-around;
   box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
   box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
   border-radius: 12px;
   border-radius: 12px;
+
   .BtLeft {
   .BtLeft {
     // border: 1px solid rgb(231, 231, 231);
     // border: 1px solid rgb(231, 231, 231);
     width: 100%;
     width: 100%;
@@ -929,16 +937,34 @@ h4 {
 }
 }
 
 
 .mpoint-charts {
 .mpoint-charts {
-  // display: grid;
-  // grid-template-columns: repeat(2, minmax(0, 1fr));
-  // gap: 16px;
   display: block;
   display: block;
+  height: 65vh;
+  box-sizing: border-box;
+  /* 兜底:与 el-carousel :height 同步,避免组件内部默认 300px 截断 */
+  ::v-deep .el-carousel {
+    height: 100%;
+  }
+  ::v-deep .el-carousel__container {
+    height: 100%;
+  }
+  ::v-deep .el-carousel__item {
+    height: 100%;
+  }
 }
 }
 
 
 .mpoint-chart-item {
 .mpoint-chart-item {
   border: 1px solid #f0f0f0;
   border: 1px solid #f0f0f0;
   border-radius: 4px;
   border-radius: 4px;
   padding: 8px 10px;
   padding: 8px 10px;
+  box-sizing: border-box;
+  height: 90%;
+  min-height: 0;
+  display: flex;
+  flex-direction: column;
+  > * {
+    flex: 1;
+    min-height: 0;
+  }
 }
 }
 
 
 .mpoint-chart-title {
 .mpoint-chart-title {

+ 572 - 288
src/views/health/components/malfunction/loose.vue

@@ -1,137 +1,127 @@
 <template>
 <template>
-  <div>
-    <div class="TopBox">
-      <div class="rightdiv">
-        <el-table
-          ref="multipleTable"
-          :data="tableData"
-          tooltip-effect="dark"
-          style="width: 100%"
-          stripe
-          border
+  <div class="gear-container">
+    <div class="gear-header">
+      <div class="header-title">
+        <p class="title">松动诊断状态总览</p>
+        <p class="sub-title">共 {{ statusSummary.total }} 个监测点</p>
+      </div>
+      <div class="header-stat">
+        <div
+          v-for="item in statusCards"
+          :key="item.key"
+          class="stat-item"
+          :class="`is-${item.key}`"
         >
         >
-          <el-table-column
-            type="index"
-            label="序号"
-            align="center"
-            width="100"
-            :index="bearingTableIndex"
-          >
-          </el-table-column>
-          <!-- <el-table-column fixed type="selection" width="55"> </el-table-column> -->
-          <el-table-column prop="functionTypeName" label="名称" align="left">
-          </el-table-column>
-          <!-- <el-table-column prop="timeStamp" label="时间" align="center">
-          </el-table-column> -->
-
-          <el-table-column label="状态" align="center" width="150">
-            <template slot-scope="scope">
-              <el-tag
-                size="mini"
-                :type="statusTagType(scope.row.status)"
-                effect="dark"
-              >
-                {{ formatStatusText(scope.row.status) }}
-              </el-tag>
-            </template>
-          </el-table-column>
-          <!-- <el-table-column prop="" label="操作" align="center">
-            <template slot-scope="scope">
-              <el-button
-                type="text"
-                size="small"
-                @click="handleDetail(scope.row)"
-                >查看振动分析</el-button
-              >
-            </template>
-          </el-table-column> -->
-        </el-table>
-        <div class="fenye">
-          <p></p>
-          <!-- <p><span>状态码说明:</span>0正常,1报警,2危险</p> -->
-          <el-pagination
-            small
-            @current-change="handleCurrentChange"
-            :current-page="currentPage"
-            layout="total, prev, pager, next"
-            :total="totalCount"
-            :page-size="pageSize"
-          ></el-pagination>
+          <el-divider direction="vertical" class="header-divider"></el-divider>
+          <div class="stat-item-content">
+            <div class="icon-wrap">
+              <i :class="item.icon"></i>
+            </div>
+            <div class="stat-content">
+              <p class="label">{{ item.label }}</p>
+              <p class="count">{{ item.count }}</p>
+              <p class="ratio">占比 {{ item.ratio }}</p>
+            </div>
+          </div>
+          <div class="stat-nobox"></div>
         </div>
         </div>
       </div>
       </div>
-
-      <!-- <div class="leftdiv">
-        <div class="stateBox">
-          <h4>松动状态</h4>
-          <div class="state">
-            <p :style="{ backgroundColor: bearingStateColors.innerRing }">
-              松动
-            </p>
+    </div>
+    <div class="gear">
+      <div class="TopBox">
+        <div class="rightdiv">
+          <div class="monitor-panel">
+            <h4>松动监测点列表</h4>
+            <div v-if="!tableData.length" class="monitor-empty">
+              <el-empty description="暂无监测点数据"></el-empty>
+            </div>
+            <div v-else class="monitor-list">
+              <div
+                v-for="(row, index) in tableData"
+                :key="row.id || `${row.functionTypeName}-${index}`"
+                class="monitor-item"
+                :class="{
+                  'is-active':
+                    selectedFunctionTypeName &&
+                    selectedFunctionTypeName === rowFunctionTypeName(row),
+                }"
+                @click="selectMonitorPoint(row)"
+              >
+                <div
+                  class="index-badge"
+                  :class="`is-${statusTagType(row.status)}`"
+                >
+                  {{ bearingTableIndex(index) }}
+                </div>
+                <div class="item-main">
+                  <p class="item-name">
+                    {{ monitorDisplayName(row) }}
+                  </p>
+                  <p class="item-line">监测类型:{{ monitorTypeText(row) }}</p>
+                </div>
+                <div class="item-right">
+                  <span
+                    class="status-pill"
+                    :class="`is-${statusTagType(row.status)}`"
+                  >
+                    <i class="status-dot"></i>
+                    {{ formatStatusText(row.status) }}
+                  </span>
+                </div>
+              </div>
+            </div>
           </div>
           </div>
-          <el-button
-            type="primary"
-            size="small"
-            @click="automaticDiagnosis"
-            class="btn-auto"
-            :loading="loading"
-            >诊断</el-button
-          >
-        </div>
-
-        <div class="Btn">
-          <div style="height: 200px">
-            <bingTwo :statistics="statistics"> </bingTwo>
-          </div>
-
-          <div class="minp">
-            <p class="PText">
-              <span><i class="color1"></i>正常</span
-              ><span><i class="color2"></i>报警</span
-              ><span><i class="color3"></i>危险</span>
-            </p>
-
-            <h4>诊断步骤:</h4>
-            <p>1、选择振动测点与起止时间,点击“查询;</p>
-            <p>2、点击“诊断”输出松动状态结果。</p>
+          <div class="fenye">
+            <p></p>
+            <el-pagination
+              small
+              @current-change="handleCurrentChange"
+              :current-page="currentPage"
+              layout="total, prev, pager, next"
+              :total="totalCount"
+              :page-size="pageSize"
+            ></el-pagination>
           </div>
           </div>
         </div>
         </div>
-      </div> -->
-    </div>
-    <div class="bottomBox">
-      <div class="BtLeft">
-        <h4>松动故障状态趋势图</h4>
-        <p class="status-legend">
-          <span class="legend-label">状态码说明:</span>
-          <span class="legend-item is-normal"
-            ><i class="legend-dot"></i>0 正常</span
-          >
-          <span class="legend-item is-warning"
-            ><i class="legend-dot"></i>1 报警</span
-          >
-          <span class="legend-item is-danger"
-            ><i class="legend-dot"></i>2 危险</span
-          >
-        </p>
-        <el-empty
-          v-if="!trendChartList.length"
-          description="暂无数据"
-          style="padding: 48px 0"
-        ></el-empty>
-        <div v-else class="mpoint-charts">
-          <div
-            v-for="(chart, index) in trendChartList"
-            :key="`${chart.title}-${index}`"
-            class="mpoint-chart-item"
-          >
-            <!-- <p class="mpoint-chart-title">{{ chart.title }}</p> -->
-            <Eecharts
-              style="height: 260px"
-              :xData="chart.xData"
-              :yData="chart.yData"
-              :yNames="chart.yNames"
-              yAxisName="故障状态"
-              :jumpContext="chart.jumpContext"
-            ></Eecharts>
+      </div>
+      <div class="bottomBox">
+        <div class="BtLeft">
+          <h4>松动故障状态趋势图:</h4>
+          <p class="status-legend">
+            <span class="legend-label">状态码说明:</span>
+            <span class="legend-item is-normal"
+              ><i class="legend-dot"></i>0 正常</span
+            >
+            <span class="legend-item is-warning"
+              ><i class="legend-dot"></i>1 报警</span
+            >
+            <span class="legend-item is-danger"
+              ><i class="legend-dot"></i>2 危险</span
+            >
+          </p>
+          <el-empty
+            v-if="!filteredTrendChartList.length"
+            description="暂无数据"
+            style="padding: 46px 0; height: 100%"
+          ></el-empty>
+          <div v-else class="mpoint-charts">
+            <el-carousel :height="mpointCarouselHeight" :key="trendCarouselKey">
+              <el-carousel-item
+                v-for="(chart, index) in filteredTrendChartList"
+                :key="`${chart.title}-${index}`"
+              >
+                <div class="mpoint-chart-item">
+                  <Eecharts
+                    style="height: 90%"
+                    :xData="chart.xData"
+                    :yData="chart.yData"
+                    :yNames="chart.yNames"
+                    yAxisName="故障状态"
+                    :jumpContext="chart.jumpContext"
+                  ></Eecharts>
+                </div>
+              </el-carousel-item>
+            </el-carousel>
           </div>
           </div>
         </div>
         </div>
       </div>
       </div>
@@ -140,12 +130,9 @@
 </template>
 </template>
 
 
 <script>
 <script>
-import axios from "axios";
 import Eecharts from "./mpointEecharts.vue";
 import Eecharts from "./mpointEecharts.vue";
-import bingTwo from "./bingTwo.vue";
-import { cacheEntryPossiblyAdded, log10, promisify } from "plotly.js-dist";
 export default {
 export default {
-  components: { Eecharts, bingTwo },
+  components: { Eecharts },
   props: {
   props: {
     codedata: {
     codedata: {
       type: Array,
       type: Array,
@@ -175,7 +162,6 @@ export default {
   data() {
   data() {
     return {
     return {
       tableData: [],
       tableData: [],
-      // 添加新的数据属性用于绑定输入框的值
       transmissionRatio: "",
       transmissionRatio: "",
       bearingPitchDiameter: "",
       bearingPitchDiameter: "",
       rollingElementDiameter: "",
       rollingElementDiameter: "",
@@ -185,27 +171,26 @@ export default {
       vibrationSpeedDangerThreshold: "",
       vibrationSpeedDangerThreshold: "",
       envelopeTotalAlarmThreshold: "",
       envelopeTotalAlarmThreshold: "",
       envelopeTotalDangerThreshold: "",
       envelopeTotalDangerThreshold: "",
-      // 分页
-
       currentPage: 1,
       currentPage: 1,
       total: 1,
       total: 1,
       pageSize: 10,
       pageSize: 10,
-
       xData: [],
       xData: [],
       yData: [],
       yData: [],
-      // 颜色判断
       bearingStateColors: {
       bearingStateColors: {
         innerRing: "#80808057",
         innerRing: "#80808057",
         outerRing: "#80808057",
         outerRing: "#80808057",
         rollingElement: "#80808057",
         rollingElement: "#80808057",
         cage: "#80808057",
         cage: "#80808057",
       },
       },
-      hasError: false, // 标志是否已经显示过错误提示
+      hasError: false,
       statistics: {},
       statistics: {},
       dialogVisible: false,
       dialogVisible: false,
       results: [],
       results: [],
-      loading: false, // 控制加载状态
+      loading: false,
       trendChartList: [],
       trendChartList: [],
+      selectedFunctionTypeName: "",
+      selectedMonitorId: "",
+      mpointCarouselHeight: "65vh",
     };
     };
   },
   },
   created() {},
   created() {},
@@ -213,9 +198,27 @@ export default {
     codedata: {
     codedata: {
       handler(newVal) {
       handler(newVal) {
         this.tableData = newVal;
         this.tableData = newVal;
+        const exists = this.tableData.some(
+          (row) =>
+            this.resolveFunctionTypeNameByRow(row) ===
+            this.selectedFunctionTypeName,
+        );
+        if (!exists) {
+          if (this.tableData.length) {
+            this.selectedFunctionTypeName = this.resolveFunctionTypeNameByRow(
+              this.tableData[0],
+            );
+            this.selectedMonitorId = this.tableData[0]?.id
+              ? String(this.tableData[0].id)
+              : "";
+          } else {
+            this.selectedFunctionTypeName = "";
+            this.selectedMonitorId = "";
+          }
+        }
       },
       },
-      immediate: true, // 组件创建时立刻执行一次
-      deep: true, // 如果 codedata 是复杂对象,建议加上
+      immediate: true,
+      deep: true,
     },
     },
     echartsdata: {
     echartsdata: {
       handler(newVal) {
       handler(newVal) {
@@ -241,16 +244,27 @@ export default {
                 null
                 null
               );
               );
             });
             });
+            const normalizedFunctionTypeName = this.normalizeFunctionTypeName(
+              item?.functionTypeName ||
+                inner.find((d) => d?.functionTypeName)?.functionTypeName ||
+                pointRows.find((r) => r?.functionTypeName)?.functionTypeName ||
+                item?.functionTypeNameCn ||
+                item?.pointNameCn ||
+                item?.mesurePointName ||
+                "",
+            );
             return {
             return {
               title,
               title,
               xData,
               xData,
               yData: [seriesY],
               yData: [seriesY],
               yNames: [title],
               yNames: [title],
+              functionTypeName: normalizedFunctionTypeName,
               jumpContext: {
               jumpContext: {
                 companyCode: this.fieldCode,
                 companyCode: this.fieldCode,
                 windCode: this.fieldCode,
                 windCode: this.fieldCode,
                 windTurbineNumber: this.windTurbineNumber,
                 windTurbineNumber: this.windTurbineNumber,
                 itemKey: item?.itemKey ?? item?.item_key,
                 itemKey: item?.itemKey ?? item?.item_key,
+                functionTypeName: normalizedFunctionTypeName,
                 id: item?.id,
                 id: item?.id,
                 mesurePointName: item?.mesurePointName || title,
                 mesurePointName: item?.mesurePointName || title,
                 detectionPointCn: item?.pointNameCn || title,
                 detectionPointCn: item?.pointNameCn || title,
@@ -266,13 +280,107 @@ export default {
             };
             };
           })
           })
           .filter((it) => it.xData.length);
           .filter((it) => it.xData.length);
+        if (!this.selectedFunctionTypeName && this.tableData.length) {
+          this.selectedFunctionTypeName = this.resolveFunctionTypeNameByRow(
+            this.tableData[0],
+          );
+          this.selectedMonitorId = this.tableData[0]?.id
+            ? String(this.tableData[0].id)
+            : "";
+        }
       },
       },
       immediate: true,
       immediate: true,
       deep: true,
       deep: true,
     },
     },
   },
   },
 
 
+  computed: {
+    statusSummary() {
+      const rows = Array.isArray(this.tableData) ? this.tableData : [];
+      const total = rows.length;
+      const summary = { total, normal: 0, warning: 0, danger: 0 };
+      rows.forEach((row) => {
+        const code = Number(row?.status);
+        if (code === 0) summary.normal += 1;
+        else if (code === 1) summary.warning += 1;
+        else if (code === 2) summary.danger += 1;
+      });
+      return summary;
+    },
+    statusCards() {
+      const { total, normal, warning, danger } = this.statusSummary;
+      const toRatio = (count) =>
+        total > 0 ? `${((count / total) * 100).toFixed(0)}%` : "0%";
+      return [
+        {
+          key: "normal",
+          label: "正常",
+          count: normal,
+          ratio: toRatio(normal),
+          icon: "el-icon-check",
+        },
+        {
+          key: "warning",
+          label: "报警",
+          count: warning,
+          ratio: toRatio(warning),
+          icon: "el-icon-warning-outline",
+        },
+        {
+          key: "danger",
+          label: "危险",
+          count: danger,
+          ratio: toRatio(danger),
+          icon: "el-icon-warning",
+        },
+      ];
+    },
+    filteredTrendChartList() {
+      const selected = this.selectedFunctionTypeName;
+      if (!selected) return this.trendChartList;
+      return this.trendChartList.filter(
+        (chart) => this.chartFunctionTypeName(chart) === selected,
+      );
+    },
+    trendCarouselKey() {
+      return `trend-${this.selectedFunctionTypeName || "all"}`;
+    },
+  },
   methods: {
   methods: {
+    normalizeFunctionTypeName(name) {
+      return typeof name === "string" ? name.trim() : "";
+    },
+    rowFunctionTypeName(row) {
+      return this.normalizeFunctionTypeName(
+        row?.functionTypeName ? String(row.functionTypeName) : "",
+      );
+    },
+    resolveFunctionTypeNameByRow(row) {
+      const direct = this.rowFunctionTypeName(row);
+      if (direct) return direct;
+      const rowId =
+        row?.id !== undefined && row?.id !== null ? String(row.id) : "";
+      if (!rowId) return "";
+      const chart = this.trendChartList.find((item) => {
+        const rows = item?.jumpContext?.pointRows;
+        if (!Array.isArray(rows)) return false;
+        return rows.some(
+          (r) =>
+            r?.id !== undefined && r?.id !== null && String(r.id) === rowId,
+        );
+      });
+      return chart ? this.chartFunctionTypeName(chart) : "";
+    },
+    monitorDisplayName(row) {
+      return row?.functionTypeName || row?.detectionPointCn || "-";
+    },
+    chartFunctionTypeName(chart) {
+      return this.normalizeFunctionTypeName(
+        chart?.jumpContext?.functionTypeName
+          ? String(chart.jumpContext.functionTypeName)
+          : "",
+      );
+    },
     statusTagType(status) {
     statusTagType(status) {
       const code = Number(status);
       const code = Number(status);
       if (code === 0) return "success";
       if (code === 0) return "success";
@@ -289,10 +397,22 @@ export default {
       if (code === -1) return "未定义";
       if (code === -1) return "未定义";
       return String(status);
       return String(status);
     },
     },
+    monitorTypeText(row) {
+      return (
+        row?.measureTypeName ||
+        row?.monitorType ||
+        row?.monitorTypeName ||
+        "振动-速度"
+      );
+    },
+    selectMonitorPoint(row) {
+      this.selectedMonitorId =
+        row?.id !== undefined && row?.id !== null ? String(row.id) : "";
+      this.selectedFunctionTypeName = this.resolveFunctionTypeNameByRow(row);
+    },
     handleDetail(row) {
     handleDetail(row) {
       const itemKey = row.itemKey ?? row.item_key;
       const itemKey = row.itemKey ?? row.item_key;
       const payload = {
       const payload = {
-        /** 与故障诊断页「单位」一致(selecttree 的 companyCode / codeNumber) */
         companyCode: this.fieldCode,
         companyCode: this.fieldCode,
         windCode: this.fieldCode,
         windCode: this.fieldCode,
         windTurbineNumber: this.windTurbineNumber,
         windTurbineNumber: this.windTurbineNumber,
@@ -301,7 +421,6 @@ export default {
         mesurePointName: row.mesurePointName,
         mesurePointName: row.mesurePointName,
         detectionPointCn: row.detectionPointCn,
         detectionPointCn: row.detectionPointCn,
         timeStamp: row.timeStamp,
         timeStamp: row.timeStamp,
-        /** 频谱特征线需要 RPM → Hz;接口字段可能为 rotationalSpeed / otherData.RPM */
         rotationalSpeed: row.rotationalSpeed,
         rotationalSpeed: row.rotationalSpeed,
         samplingFrequency: row.samplingFrequency,
         samplingFrequency: row.samplingFrequency,
         otherData:
         otherData:
@@ -336,218 +455,383 @@ export default {
     },
     },
     handleCurrentChange(val) {
     handleCurrentChange(val) {
       console.log(`当前页: ${val}`);
       console.log(`当前页: ${val}`);
-
-      this.currentPage = val; // 子组件内部更新当前页
-      this.$emit("updatePage", this.currentPage); // 通知父组件,把当前页传出去
+      this.currentPage = val;
+      this.$emit("updatePage", this.currentPage);
     },
     },
 
 
     reset() {
     reset() {
-      // 重置状态
+      this.tableData = [];
       this.xData = [];
       this.xData = [];
       this.yData = [];
       this.yData = [];
       this.statistics = {};
       this.statistics = {};
       this.trendChartList = [];
       this.trendChartList = [];
+      this.selectedFunctionTypeName = "";
+      this.selectedMonitorId = "";
       this.bearingStateColors = {
       this.bearingStateColors = {
         innerRing: "#80808057",
         innerRing: "#80808057",
         outerRing: "#80808057",
         outerRing: "#80808057",
         rollingElement: "#80808057",
         rollingElement: "#80808057",
         cage: "#80808057",
         cage: "#80808057",
       };
       };
-      this.tableData = [];
     },
     },
   },
   },
 };
 };
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
-h4 {
-  margin-bottom: 5px;
-  font-weight: 600;
+.gear-container {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  margin-top: -10px;
 }
 }
-.TopBox {
-  // height: 280px;
+
+.gear-header {
   display: flex;
   display: flex;
-  justify-content: space-around;
-  .leftdiv {
-    width: 50%;
+  align-items: stretch;
+  border-radius: 12px;
+  background: #f5f7fa5e;
+  overflow: hidden;
+  box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
+
+  .header-title {
+    width: 230px;
+    padding: 16px 18px;
     display: flex;
     display: flex;
-    justify-content: space-around;
-    .stateBox {
-      .state {
+    flex-direction: column;
+    justify-content: center;
+    .title {
+      margin: 0;
+      font-size: 18px;
+      font-weight: 700;
+      color: #303133;
+    }
+    .sub-title {
+      margin: 6px 0 0;
+      font-size: 13px;
+      color: #909399;
+    }
+  }
+
+  .header-stat {
+    flex: 1;
+    display: grid;
+    grid-template-columns: repeat(3, minmax(0, 1fr));
+    ::v-deep .header-divider.el-divider--vertical {
+      height: 48px;
+      margin: 0 8px 0 0;
+      width: 1px;
+      background-color: #dcdfe65e;
+    }
+    .stat-item {
+      padding: 10px 18px;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      gap: 10px;
+      &:last-child {
+        border-right: 0;
+      }
+      .icon-wrap {
+        width: 42px;
+        height: 42px;
+        border-radius: 50%;
+        color: #fff;
         display: flex;
         display: flex;
-        flex-direction: column;
-        justify-content: center; /* 整体居中 */
         align-items: center;
         align-items: center;
-        height: 200px;
-        gap: 20px; /* 控制间距 */
-        p {
-          width: 150px;
-          height: 40px;
-          background: rgb(227, 227, 227);
-          color: rgb(50, 50, 50);
-          text-align: center;
-          align-content: center;
-          font-weight: 600;
-        }
-      }
-      .btn-auto {
-        margin-top: 10px;
-        width: 100%;
+        justify-content: center;
+        font-size: 22px;
       }
       }
-    }
-    .Btn {
-      width: 50%;
-      display: flex;
-      flex-direction: column; /* 垂直排列 */
-      justify-content: space-between; /* 顶部和底部对齐 */
-      min-height: 100px; /* 确保容器有足够高度 */
-      margin: 10px 0;
-      .PText {
+      .stat-item-content {
         display: flex;
         display: flex;
-        justify-content: space-around;
-
-        span {
-          display: flex;
-          align-items: center;
-          i {
-            width: 30px;
-            height: 15px;
-            margin-right: 5px;
-          }
-          .color1 {
-            background-color: #8ae359;
-          }
-          .color2 {
-            background-color: #eecb5f;
-          }
-          .color3 {
-            background-color: #f7715f;
-          }
+        align-items: center;
+        gap: 10px;
+      }
+      .stat-content {
+        .label,
+        .count,
+        .ratio {
+          margin: 0;
+          line-height: 1.2;
+        }
+        .label {
+          font-size: 14px;
+          color: #303133;
+          font-weight: 500;
+        }
+        .count {
+          margin-top: 2px;
+          font-size: 30px;
+          font-weight: 700;
+          color: #303133;
+        }
+        .ratio {
+          margin-top: 2px;
+          font-size: 12px;
+          color: #909399;
         }
         }
       }
       }
-      margin: 10px 0;
-      .minp {
-        font-size: 12px;
-        margin-top: auto; /* 自动推到最底部 */
+      &.is-normal .icon-wrap {
+        background: #008080;
       }
       }
-    }
-  }
-  .rightdiv {
-    width: 100%;
-    .fenye {
-      display: flex;
-      justify-content: space-between;
-      margin: 5px 0;
-      font-size: 11px;
-      line-height: 30px;
-      span {
-        font-weight: 600;
+      &.is-warning .icon-wrap {
+        background: #e6a23c;
+      }
+      &.is-danger .icon-wrap {
+        background: #f56c6c;
       }
       }
     }
     }
   }
   }
 }
 }
 
 
-.bottomBox {
+.gear {
+  height: 100%;
+  display: flex;
+  justify-content: space-between;
+  gap: 10px;
+  .TopBox {
+    width: 40%;
+    height: 76vh;
+  }
+  .bottomBox {
+    width: 59%;
+    height: 68.5vh;
+  }
+}
+h4 {
+  margin-bottom: 5px;
+  font-weight: 600;
+}
+
+.TopBox {
   display: flex;
   display: flex;
   justify-content: space-around;
   justify-content: space-around;
-  margin-top: 10px;
-  .status-legend {
-    margin: 4px 0 8px;
-    font-size: 12px;
-    color: #909399;
-    display: flex;
-    align-items: center;
-    gap: 12px;
-    .legend-label {
-      color: #606266;
+  .rightdiv {
+    width: 100%;
+    height: 100%;
+    .monitor-panel {
+      border-radius: 14px;
+      padding: 14px;
+      min-height: 360px;
+      height: 90%;
+      box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
+      overflow-y: auto;
+    }
+    .monitor-empty {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      min-height: 260px;
+      background: #fff;
+      border-radius: 10px;
+    }
+    .monitor-list {
+      display: flex;
+      flex-direction: column;
+      gap: 10px;
+    }
+    .monitor-item {
+      position: relative;
+      display: flex;
+      align-items: center;
+      gap: 14px;
+      border: 1px solid #e4e7ed;
+      border-radius: 10px;
+      background: #fff;
+      padding: 14px 16px;
+      cursor: pointer;
+      transition: all 0.2s ease;
+      &.is-active {
+        background: #f0f5f7;
+      }
+      &.is-active::before {
+        content: "";
+        position: absolute;
+        left: 0;
+        top: 10px;
+        bottom: 10px;
+        width: 4px;
+        background: #74727224;
+        box-shadow: 0 0 0 2px #24a16600 inset;
+        border-radius: 0 4px 4px 0;
+      }
     }
     }
-    .legend-item {
+    .index-badge {
+      width: 30px;
+      height: 30px;
+      border-radius: 50%;
+      color: #fff;
+      font-size: 16px;
+      font-weight: 700;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      flex-shrink: 0;
+      &.is-success {
+        background: #008080;
+      }
+      &.is-warning {
+        background: #e6a23c;
+      }
+      &.is-danger {
+        background: #f56c6c;
+      }
+      &.is-info {
+        background: #909399;
+      }
+    }
+    .item-main {
+      flex: 1;
+      min-width: 0;
+      .item-name {
+        margin: 0 0 8px;
+        font-size: 16px;
+        font-weight: 700;
+        color: #1f2d3d;
+      }
+      .item-line {
+        margin: 0;
+        font-size: 14px;
+        line-height: 1.5;
+        color: #6b7280;
+      }
+    }
+    .item-right {
+      display: flex;
+      align-items: center;
+      gap: 12px;
+    }
+    .status-pill {
       display: inline-flex;
       display: inline-flex;
       align-items: center;
       align-items: center;
-      gap: 4px;
-      .legend-dot {
-        width: 8px;
-        height: 8px;
+      gap: 6px;
+      border-radius: 12px;
+      padding: 10px 16px;
+      font-size: 16px;
+      font-weight: 700;
+      line-height: 1;
+      .status-dot {
+        width: 10px;
+        height: 10px;
         border-radius: 50%;
         border-radius: 50%;
         background: currentColor;
         background: currentColor;
       }
       }
-      &.is-normal {
-        color: #008080;
+      &.is-success {
+        color: #21a366;
+        background: #e7f6ee;
       }
       }
       &.is-warning {
       &.is-warning {
-        color: #e6a23c;
+        color: #d48806;
+        background: #fff6e5;
       }
       }
       &.is-danger {
       &.is-danger {
-        color: #f56c6c;
+        color: #f04438;
+        background: #ffebee;
+      }
+      &.is-info {
+        color: #606266;
+        background: #f4f4f5;
+      }
+    }
+    .fenye {
+      display: flex;
+      justify-content: flex-end;
+      margin: 10px 0 0;
+      font-size: 12px;
+      line-height: 30px;
+      span {
+        font-weight: 600;
       }
       }
     }
     }
   }
   }
+}
 
 
+.bottomBox {
+  display: flex;
+  justify-content: space-around;
+  box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
+  border-radius: 12px;
   .BtLeft {
   .BtLeft {
-    border: 1px solid rgb(231, 231, 231);
     width: 100%;
     width: 100%;
-    padding: 10px;
-  }
-  .BtRight {
-    // width: 49%;
-    border: 1px solid rgb(231, 231, 231);
-    padding: 10px;
-    .BtRightDiv {
-      padding: 0 10px;
-      h4 {
-        font-size: 14px;
-      }
-      .BtRightP {
-        line-height: 30px;
-        font-size: 12px;
-        display: flex;
-        margin-bottom: 5px;
-        width: 50%;
-        span {
-          margin-left: 10px;
-          .el-input {
-            width: 100px;
-          }
-        }
-        .label-text {
-          width: 140px;
-        }
-      }
-      .canshu {
-        display: flex;
-        flex-wrap: wrap;
-        justify-content: space-between;
-      }
-    }
-  }
-  .dialog-footer {
-    margin-top: 20px;
-    text-align: right;
+    padding: 10px 20px;
   }
   }
 }
 }
 
 
 .mpoint-charts {
 .mpoint-charts {
-  display: grid;
-  grid-template-columns: repeat(2, minmax(0, 1fr));
-  gap: 16px;
+  display: block;
+  height: 65vh;
+  box-sizing: border-box;
+  ::v-deep .el-carousel {
+    height: 100%;
+  }
+  ::v-deep .el-carousel__container {
+    height: 100%;
+  }
+  ::v-deep .el-carousel__item {
+    height: 100%;
+  }
 }
 }
 
 
 .mpoint-chart-item {
 .mpoint-chart-item {
   border: 1px solid #f0f0f0;
   border: 1px solid #f0f0f0;
   border-radius: 4px;
   border-radius: 4px;
   padding: 8px 10px;
   padding: 8px 10px;
+  box-sizing: border-box;
+  height: 90%;
+  min-height: 0;
+  display: flex;
+  flex-direction: column;
+  > * {
+    flex: 1;
+    min-height: 0;
+  }
 }
 }
 
 
-.mpoint-chart-title {
-  margin: 0 0 6px;
-  font-size: 13px;
-  color: #303133;
+.status-legend {
+  margin: 4px 0 8px;
+  font-size: 12px;
+  color: #909399;
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  .legend-label {
+    color: #606266;
+  }
+  .legend-item {
+    display: inline-flex;
+    align-items: center;
+    gap: 4px;
+    .legend-dot {
+      width: 8px;
+      height: 8px;
+      border-radius: 50%;
+      background: currentColor;
+    }
+    &.is-normal {
+      color: #008080;
+    }
+    &.is-warning {
+      color: #e6a23c;
+    }
+    &.is-danger {
+      color: #f56c6c;
+    }
+  }
 }
 }
 
 
-@media (max-width: 1200px) {
-  .mpoint-charts {
-    grid-template-columns: 1fr;
-  }
+::v-deep .el-carousel__indicator .el-carousel__button {
+  background-color: rgba(144, 147, 153, 0.45);
+  opacity: 1;
+}
+::v-deep .el-carousel__indicator.is-active .el-carousel__button {
+  background-color: #24a166;
+}
+::v-deep .el-carousel__indicator:hover .el-carousel__button {
+  background-color: #24a166aa;
+}
+::v-deep .el-carousel__indicators--horizontal {
+  bottom: -10px !important;
 }
 }
 ::v-deep .el-table__cell {
 ::v-deep .el-table__cell {
   padding: 2px 0;
   padding: 2px 0;

+ 573 - 287
src/views/health/components/malfunction/misalignment.vue

@@ -1,136 +1,127 @@
 <template>
 <template>
-  <div>
-    <div class="TopBox">
-      <div class="rightdiv">
-        <el-table
-          ref="multipleTable"
-          :data="tableData"
-          tooltip-effect="dark"
-          style="width: 100%"
-          stripe
-          border
+  <div class="gear-container">
+    <div class="gear-header">
+      <div class="header-title">
+        <p class="title">不平衡诊断状态总览</p>
+        <p class="sub-title">共 {{ statusSummary.total }} 个监测点</p>
+      </div>
+      <div class="header-stat">
+        <div
+          v-for="item in statusCards"
+          :key="item.key"
+          class="stat-item"
+          :class="`is-${item.key}`"
         >
         >
-          <el-table-column
-            type="index"
-            label="序号"
-            align="center"
-            width="100"
-            :index="bearingTableIndex"
-          >
-          </el-table-column>
-          <!-- <el-table-column fixed type="selection" width="55"> </el-table-column> -->
-          <el-table-column prop="functionTypeName" label="名称" align="left">
-          </el-table-column>
-          <!-- <el-table-column prop="timeStamp" label="时间" align="center">
-          </el-table-column> -->
-          <el-table-column label="状态" align="center" width="150">
-            <template slot-scope="scope">
-              <el-tag
-                size="mini"
-                :type="statusTagType(scope.row.status)"
-                effect="dark"
-              >
-                {{ formatStatusText(scope.row.status) }}
-              </el-tag>
-            </template>
-          </el-table-column>
-          <!-- <el-table-column prop="" label="操作" align="center">
-            <template slot-scope="scope">
-              <el-button
-                type="text"
-                size="small"
-                @click="handleDetail(scope.row)"
-                >查看振动分析</el-button
-              >
-            </template>
-          </el-table-column> -->
-        </el-table>
-        <div class="fenye">
-          <!-- <p><span>状态码说明:</span>0正常,1报警,2危险</p> -->
-          <p></p>
-          <el-pagination
-            small
-            @current-change="handleCurrentChange"
-            :current-page="currentPage"
-            layout="total, prev, pager, next"
-            :total="totalCount"
-            :page-size="pageSize"
-          ></el-pagination>
+          <el-divider direction="vertical" class="header-divider"></el-divider>
+          <div class="stat-item-content">
+            <div class="icon-wrap">
+              <i :class="item.icon"></i>
+            </div>
+            <div class="stat-content">
+              <p class="label">{{ item.label }}</p>
+              <p class="count">{{ item.count }}</p>
+              <p class="ratio">占比 {{ item.ratio }}</p>
+            </div>
+          </div>
+          <div class="stat-nobox"></div>
         </div>
         </div>
       </div>
       </div>
-
-      <!-- <div class="leftdiv">
-        <div class="stateBox">
-          <h4>不平衡状态</h4>
-          <div class="state">
-            <p :style="{ backgroundColor: bearingStateColors.innerRing }">
-              不平衡
-            </p>
+    </div>
+    <div class="gear">
+      <div class="TopBox">
+        <div class="rightdiv">
+          <div class="monitor-panel">
+            <h4>不平衡监测点列表</h4>
+            <div v-if="!tableData.length" class="monitor-empty">
+              <el-empty description="暂无监测点数据"></el-empty>
+            </div>
+            <div v-else class="monitor-list">
+              <div
+                v-for="(row, index) in tableData"
+                :key="row.id || `${row.functionTypeName}-${index}`"
+                class="monitor-item"
+                :class="{
+                  'is-active':
+                    selectedFunctionTypeName &&
+                    selectedFunctionTypeName === rowFunctionTypeName(row),
+                }"
+                @click="selectMonitorPoint(row)"
+              >
+                <div
+                  class="index-badge"
+                  :class="`is-${statusTagType(row.status)}`"
+                >
+                  {{ bearingTableIndex(index) }}
+                </div>
+                <div class="item-main">
+                  <p class="item-name">
+                    {{ monitorDisplayName(row) }}
+                  </p>
+                  <p class="item-line">监测类型:{{ monitorTypeText(row) }}</p>
+                </div>
+                <div class="item-right">
+                  <span
+                    class="status-pill"
+                    :class="`is-${statusTagType(row.status)}`"
+                  >
+                    <i class="status-dot"></i>
+                    {{ formatStatusText(row.status) }}
+                  </span>
+                </div>
+              </div>
+            </div>
           </div>
           </div>
-          <el-button
-            type="primary"
-            size="small"
-            @click="automaticDiagnosis"
-            class="btn-auto"
-            :loading="loading"
-            >诊断</el-button
-          >
-        </div>
-
-        <div class="Btn">
-          <div style="height: 200px">
-            <bingTwo :statistics="statistics"> </bingTwo>
-          </div>
-
-          <div class="minp">
-            <p class="PText">
-              <span><i class="color1"></i>正常</span
-              ><span><i class="color2"></i>报警</span
-              ><span><i class="color3"></i>危险</span>
-            </p>
-
-            <h4>诊断步骤:</h4>
-            <p>1、选择振动测点与起止时间,点击“查询;</p>
-            <p>2、点击“诊断”输出不平衡状态结果。</p>
+          <div class="fenye">
+            <p></p>
+            <el-pagination
+              small
+              @current-change="handleCurrentChange"
+              :current-page="currentPage"
+              layout="total, prev, pager, next"
+              :total="totalCount"
+              :page-size="pageSize"
+            ></el-pagination>
           </div>
           </div>
         </div>
         </div>
-      </div> -->
-    </div>
-    <div class="bottomBox">
-      <div class="BtLeft">
-        <h4>不平衡故障状态趋势图</h4>
-        <p class="status-legend">
-          <span class="legend-label">状态码说明:</span>
-          <span class="legend-item is-normal"
-            ><i class="legend-dot"></i>0 正常</span
-          >
-          <span class="legend-item is-warning"
-            ><i class="legend-dot"></i>1 报警</span
-          >
-          <span class="legend-item is-danger"
-            ><i class="legend-dot"></i>2 危险</span
-          >
-        </p>
-        <el-empty
-          v-if="!trendChartList.length"
-          description="暂无数据"
-          style="padding: 48px 0"
-        ></el-empty>
-        <div v-else class="mpoint-charts">
-          <div
-            v-for="(chart, index) in trendChartList"
-            :key="`${chart.title}-${index}`"
-            class="mpoint-chart-item"
-          >
-            <!-- <p class="mpoint-chart-title">{{ chart.title }}</p> -->
-            <Eecharts
-              style="height: 260px"
-              :xData="chart.xData"
-              :yData="chart.yData"
-              :yNames="chart.yNames"
-              yAxisName="故障状态"
-              :jumpContext="chart.jumpContext"
-            ></Eecharts>
+      </div>
+      <div class="bottomBox">
+        <div class="BtLeft">
+          <h4>不平衡故障状态趋势图:</h4>
+          <p class="status-legend">
+            <span class="legend-label">状态码说明:</span>
+            <span class="legend-item is-normal"
+              ><i class="legend-dot"></i>0 正常</span
+            >
+            <span class="legend-item is-warning"
+              ><i class="legend-dot"></i>1 报警</span
+            >
+            <span class="legend-item is-danger"
+              ><i class="legend-dot"></i>2 危险</span
+            >
+          </p>
+          <el-empty
+            v-if="!filteredTrendChartList.length"
+            description="暂无数据"
+            style="padding: 46px 0; height: 100%"
+          ></el-empty>
+          <div v-else class="mpoint-charts">
+            <el-carousel :height="mpointCarouselHeight" :key="trendCarouselKey">
+              <el-carousel-item
+                v-for="(chart, index) in filteredTrendChartList"
+                :key="`${chart.title}-${index}`"
+              >
+                <div class="mpoint-chart-item">
+                  <Eecharts
+                    style="height: 90%"
+                    :xData="chart.xData"
+                    :yData="chart.yData"
+                    :yNames="chart.yNames"
+                    yAxisName="故障状态"
+                    :jumpContext="chart.jumpContext"
+                  ></Eecharts>
+                </div>
+              </el-carousel-item>
+            </el-carousel>
           </div>
           </div>
         </div>
         </div>
       </div>
       </div>
@@ -139,12 +130,9 @@
 </template>
 </template>
 
 
 <script>
 <script>
-import axios from "axios";
 import Eecharts from "./mpointEecharts.vue";
 import Eecharts from "./mpointEecharts.vue";
-import bingTwo from "./bingTwo.vue";
-import { cacheEntryPossiblyAdded, log10, promisify } from "plotly.js-dist";
 export default {
 export default {
-  components: { Eecharts, bingTwo },
+  components: { Eecharts },
   props: {
   props: {
     codedata: {
     codedata: {
       type: Array,
       type: Array,
@@ -174,7 +162,6 @@ export default {
   data() {
   data() {
     return {
     return {
       tableData: [],
       tableData: [],
-      // 添加新的数据属性用于绑定输入框的值
       transmissionRatio: "",
       transmissionRatio: "",
       bearingPitchDiameter: "",
       bearingPitchDiameter: "",
       rollingElementDiameter: "",
       rollingElementDiameter: "",
@@ -184,27 +171,26 @@ export default {
       vibrationSpeedDangerThreshold: "",
       vibrationSpeedDangerThreshold: "",
       envelopeTotalAlarmThreshold: "",
       envelopeTotalAlarmThreshold: "",
       envelopeTotalDangerThreshold: "",
       envelopeTotalDangerThreshold: "",
-      // 分页
-
       currentPage: 1,
       currentPage: 1,
       total: 1,
       total: 1,
       pageSize: 10,
       pageSize: 10,
-
       xData: [],
       xData: [],
       yData: [],
       yData: [],
-      // 颜色判断
       bearingStateColors: {
       bearingStateColors: {
         innerRing: "#80808057",
         innerRing: "#80808057",
         outerRing: "#80808057",
         outerRing: "#80808057",
         rollingElement: "#80808057",
         rollingElement: "#80808057",
         cage: "#80808057",
         cage: "#80808057",
       },
       },
-      hasError: false, // 标志是否已经显示过错误提示
+      hasError: false,
       statistics: {},
       statistics: {},
       dialogVisible: false,
       dialogVisible: false,
       results: [],
       results: [],
-      loading: false, // 控制加载状态
+      loading: false,
       trendChartList: [],
       trendChartList: [],
+      selectedFunctionTypeName: "",
+      selectedMonitorId: "",
+      mpointCarouselHeight: "65vh",
     };
     };
   },
   },
   created() {},
   created() {},
@@ -212,9 +198,27 @@ export default {
     codedata: {
     codedata: {
       handler(newVal) {
       handler(newVal) {
         this.tableData = newVal;
         this.tableData = newVal;
+        const exists = this.tableData.some(
+          (row) =>
+            this.resolveFunctionTypeNameByRow(row) ===
+            this.selectedFunctionTypeName,
+        );
+        if (!exists) {
+          if (this.tableData.length) {
+            this.selectedFunctionTypeName = this.resolveFunctionTypeNameByRow(
+              this.tableData[0],
+            );
+            this.selectedMonitorId = this.tableData[0]?.id
+              ? String(this.tableData[0].id)
+              : "";
+          } else {
+            this.selectedFunctionTypeName = "";
+            this.selectedMonitorId = "";
+          }
+        }
       },
       },
-      immediate: true, // 组件创建时立刻执行一次
-      deep: true, // 如果 codedata 是复杂对象,建议加上
+      immediate: true,
+      deep: true,
     },
     },
     echartsdata: {
     echartsdata: {
       handler(newVal) {
       handler(newVal) {
@@ -240,16 +244,27 @@ export default {
                 null
                 null
               );
               );
             });
             });
+            const normalizedFunctionTypeName = this.normalizeFunctionTypeName(
+              item?.functionTypeName ||
+                inner.find((d) => d?.functionTypeName)?.functionTypeName ||
+                pointRows.find((r) => r?.functionTypeName)?.functionTypeName ||
+                item?.functionTypeNameCn ||
+                item?.pointNameCn ||
+                item?.mesurePointName ||
+                "",
+            );
             return {
             return {
               title,
               title,
               xData,
               xData,
               yData: [seriesY],
               yData: [seriesY],
               yNames: [title],
               yNames: [title],
+              functionTypeName: normalizedFunctionTypeName,
               jumpContext: {
               jumpContext: {
                 companyCode: this.fieldCode,
                 companyCode: this.fieldCode,
                 windCode: this.fieldCode,
                 windCode: this.fieldCode,
                 windTurbineNumber: this.windTurbineNumber,
                 windTurbineNumber: this.windTurbineNumber,
                 itemKey: item?.itemKey ?? item?.item_key,
                 itemKey: item?.itemKey ?? item?.item_key,
+                functionTypeName: normalizedFunctionTypeName,
                 id: item?.id,
                 id: item?.id,
                 mesurePointName: item?.mesurePointName || title,
                 mesurePointName: item?.mesurePointName || title,
                 detectionPointCn: item?.pointNameCn || title,
                 detectionPointCn: item?.pointNameCn || title,
@@ -265,13 +280,107 @@ export default {
             };
             };
           })
           })
           .filter((it) => it.xData.length);
           .filter((it) => it.xData.length);
+        if (!this.selectedFunctionTypeName && this.tableData.length) {
+          this.selectedFunctionTypeName = this.resolveFunctionTypeNameByRow(
+            this.tableData[0],
+          );
+          this.selectedMonitorId = this.tableData[0]?.id
+            ? String(this.tableData[0].id)
+            : "";
+        }
       },
       },
       immediate: true,
       immediate: true,
       deep: true,
       deep: true,
     },
     },
   },
   },
 
 
+  computed: {
+    statusSummary() {
+      const rows = Array.isArray(this.tableData) ? this.tableData : [];
+      const total = rows.length;
+      const summary = { total, normal: 0, warning: 0, danger: 0 };
+      rows.forEach((row) => {
+        const code = Number(row?.status);
+        if (code === 0) summary.normal += 1;
+        else if (code === 1) summary.warning += 1;
+        else if (code === 2) summary.danger += 1;
+      });
+      return summary;
+    },
+    statusCards() {
+      const { total, normal, warning, danger } = this.statusSummary;
+      const toRatio = (count) =>
+        total > 0 ? `${((count / total) * 100).toFixed(2)}%` : "0.00%";
+      return [
+        {
+          key: "normal",
+          label: "正常",
+          count: normal,
+          ratio: toRatio(normal),
+          icon: "el-icon-check",
+        },
+        {
+          key: "warning",
+          label: "报警",
+          count: warning,
+          ratio: toRatio(warning),
+          icon: "el-icon-warning-outline",
+        },
+        {
+          key: "danger",
+          label: "危险",
+          count: danger,
+          ratio: toRatio(danger),
+          icon: "el-icon-warning",
+        },
+      ];
+    },
+    filteredTrendChartList() {
+      const selected = this.selectedFunctionTypeName;
+      if (!selected) return this.trendChartList;
+      return this.trendChartList.filter(
+        (chart) => this.chartFunctionTypeName(chart) === selected,
+      );
+    },
+    trendCarouselKey() {
+      return `trend-${this.selectedFunctionTypeName || "all"}`;
+    },
+  },
   methods: {
   methods: {
+    normalizeFunctionTypeName(name) {
+      return typeof name === "string" ? name.trim() : "";
+    },
+    rowFunctionTypeName(row) {
+      return this.normalizeFunctionTypeName(
+        row?.functionTypeName ? String(row.functionTypeName) : "",
+      );
+    },
+    resolveFunctionTypeNameByRow(row) {
+      const direct = this.rowFunctionTypeName(row);
+      if (direct) return direct;
+      const rowId =
+        row?.id !== undefined && row?.id !== null ? String(row.id) : "";
+      if (!rowId) return "";
+      const chart = this.trendChartList.find((item) => {
+        const rows = item?.jumpContext?.pointRows;
+        if (!Array.isArray(rows)) return false;
+        return rows.some(
+          (r) =>
+            r?.id !== undefined && r?.id !== null && String(r.id) === rowId,
+        );
+      });
+      return chart ? this.chartFunctionTypeName(chart) : "";
+    },
+    monitorDisplayName(row) {
+      return row?.functionTypeName || row?.detectionPointCn || "-";
+    },
+    chartFunctionTypeName(chart) {
+      return this.normalizeFunctionTypeName(
+        chart?.jumpContext?.functionTypeName
+          ? String(chart.jumpContext.functionTypeName)
+          : "",
+      );
+    },
     statusTagType(status) {
     statusTagType(status) {
       const code = Number(status);
       const code = Number(status);
       if (code === 0) return "success";
       if (code === 0) return "success";
@@ -288,10 +397,22 @@ export default {
       if (code === -1) return "未定义";
       if (code === -1) return "未定义";
       return String(status);
       return String(status);
     },
     },
+    monitorTypeText(row) {
+      return (
+        row?.measureTypeName ||
+        row?.monitorType ||
+        row?.monitorTypeName ||
+        "振动-速度"
+      );
+    },
+    selectMonitorPoint(row) {
+      this.selectedMonitorId =
+        row?.id !== undefined && row?.id !== null ? String(row.id) : "";
+      this.selectedFunctionTypeName = this.resolveFunctionTypeNameByRow(row);
+    },
     handleDetail(row) {
     handleDetail(row) {
       const itemKey = row.itemKey ?? row.item_key;
       const itemKey = row.itemKey ?? row.item_key;
       const payload = {
       const payload = {
-        /** 与故障诊断页「单位」一致(selecttree 的 companyCode / codeNumber) */
         companyCode: this.fieldCode,
         companyCode: this.fieldCode,
         windCode: this.fieldCode,
         windCode: this.fieldCode,
         windTurbineNumber: this.windTurbineNumber,
         windTurbineNumber: this.windTurbineNumber,
@@ -300,7 +421,6 @@ export default {
         mesurePointName: row.mesurePointName,
         mesurePointName: row.mesurePointName,
         detectionPointCn: row.detectionPointCn,
         detectionPointCn: row.detectionPointCn,
         timeStamp: row.timeStamp,
         timeStamp: row.timeStamp,
-        /** 频谱特征线需要 RPM → Hz;接口字段可能为 rotationalSpeed / otherData.RPM */
         rotationalSpeed: row.rotationalSpeed,
         rotationalSpeed: row.rotationalSpeed,
         samplingFrequency: row.samplingFrequency,
         samplingFrequency: row.samplingFrequency,
         otherData:
         otherData:
@@ -335,217 +455,383 @@ export default {
     },
     },
     handleCurrentChange(val) {
     handleCurrentChange(val) {
       console.log(`当前页: ${val}`);
       console.log(`当前页: ${val}`);
-
-      this.currentPage = val; // 子组件内部更新当前页
-      this.$emit("updatePage", this.currentPage); // 通知父组件,把当前页传出去
+      this.currentPage = val;
+      this.$emit("updatePage", this.currentPage);
     },
     },
 
 
     reset() {
     reset() {
-      // 重置状态
+      this.tableData = [];
       this.xData = [];
       this.xData = [];
       this.yData = [];
       this.yData = [];
       this.statistics = {};
       this.statistics = {};
       this.trendChartList = [];
       this.trendChartList = [];
+      this.selectedFunctionTypeName = "";
+      this.selectedMonitorId = "";
       this.bearingStateColors = {
       this.bearingStateColors = {
         innerRing: "#80808057",
         innerRing: "#80808057",
         outerRing: "#80808057",
         outerRing: "#80808057",
         rollingElement: "#80808057",
         rollingElement: "#80808057",
         cage: "#80808057",
         cage: "#80808057",
       };
       };
-      this.tableData = [];
     },
     },
   },
   },
 };
 };
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
-h4 {
-  margin-bottom: 5px;
-  font-weight: 600;
+.gear-container {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  margin-top: -10px;
 }
 }
-.TopBox {
-  // height: 280px;
+
+.gear-header {
   display: flex;
   display: flex;
-  justify-content: space-around;
-  .leftdiv {
-    width: 50%;
+  align-items: stretch;
+  border-radius: 12px;
+  background: #f5f7fa5e;
+  overflow: hidden;
+  box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
+
+  .header-title {
+    width: 230px;
+    padding: 16px 18px;
     display: flex;
     display: flex;
-    justify-content: space-around;
-    .stateBox {
-      .state {
+    flex-direction: column;
+    justify-content: center;
+    .title {
+      margin: 0;
+      font-size: 18px;
+      font-weight: 700;
+      color: #303133;
+    }
+    .sub-title {
+      margin: 6px 0 0;
+      font-size: 13px;
+      color: #909399;
+    }
+  }
+
+  .header-stat {
+    flex: 1;
+    display: grid;
+    grid-template-columns: repeat(3, minmax(0, 1fr));
+    ::v-deep .header-divider.el-divider--vertical {
+      height: 48px;
+      margin: 0 8px 0 0;
+      width: 1px;
+      background-color: #dcdfe65e;
+    }
+    .stat-item {
+      padding: 10px 18px;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      gap: 10px;
+      &:last-child {
+        border-right: 0;
+      }
+      .icon-wrap {
+        width: 42px;
+        height: 42px;
+        border-radius: 50%;
+        color: #fff;
         display: flex;
         display: flex;
-        flex-direction: column;
-        justify-content: center; /* 整体居中 */
         align-items: center;
         align-items: center;
-        height: 200px;
-        gap: 20px; /* 控制间距 */
-        p {
-          width: 150px;
-          height: 40px;
-          background: rgb(227, 227, 227);
-          color: rgb(50, 50, 50);
-          text-align: center;
-          align-content: center;
-          font-weight: 600;
-        }
+        justify-content: center;
+        font-size: 22px;
       }
       }
-      .btn-auto {
-        margin-top: 10px;
-        width: 100%;
-      }
-    }
-    .Btn {
-      width: 50%;
-      display: flex;
-      flex-direction: column; /* 垂直排列 */
-      justify-content: space-between; /* 顶部和底部对齐 */
-      min-height: 100px; /* 确保容器有足够高度 */
-      margin: 10px 0;
-      .PText {
+      .stat-item-content {
         display: flex;
         display: flex;
-        justify-content: space-around;
-
-        span {
-          display: flex;
-          align-items: center;
-          i {
-            width: 30px;
-            height: 15px;
-            margin-right: 5px;
-          }
-          .color1 {
-            background-color: #8ae359;
-          }
-          .color2 {
-            background-color: #eecb5f;
-          }
-          .color3 {
-            background-color: #f7715f;
-          }
+        align-items: center;
+        gap: 10px;
+      }
+      .stat-content {
+        .label,
+        .count,
+        .ratio {
+          margin: 0;
+          line-height: 1.2;
+        }
+        .label {
+          font-size: 14px;
+          color: #303133;
+          font-weight: 500;
+        }
+        .count {
+          margin-top: 2px;
+          font-size: 30px;
+          font-weight: 700;
+          color: #303133;
+        }
+        .ratio {
+          margin-top: 2px;
+          font-size: 12px;
+          color: #909399;
         }
         }
       }
       }
-      margin: 10px 0;
-      .minp {
-        font-size: 12px;
-        margin-top: auto; /* 自动推到最底部 */
+      &.is-normal .icon-wrap {
+        background: #008080;
       }
       }
-    }
-  }
-  .rightdiv {
-    width: 100%;
-    .fenye {
-      display: flex;
-      justify-content: space-between;
-      margin: 5px 0;
-      font-size: 11px;
-      line-height: 30px;
-      span {
-        font-weight: 600;
+      &.is-warning .icon-wrap {
+        background: #e6a23c;
+      }
+      &.is-danger .icon-wrap {
+        background: #f56c6c;
       }
       }
     }
     }
   }
   }
 }
 }
 
 
-.bottomBox {
+.gear {
+  height: 100%;
+  display: flex;
+  justify-content: space-between;
+  gap: 10px;
+  .TopBox {
+    width: 40%;
+    height: 76vh;
+  }
+  .bottomBox {
+    width: 59%;
+    height: 68.5vh;
+  }
+}
+h4 {
+  margin-bottom: 5px;
+  font-weight: 600;
+}
+
+.TopBox {
   display: flex;
   display: flex;
   justify-content: space-around;
   justify-content: space-around;
-  margin-top: 10px;
-  .status-legend {
-    margin: 4px 0 8px;
-    font-size: 12px;
-    color: #909399;
-    display: flex;
-    align-items: center;
-    gap: 12px;
-    .legend-label {
-      color: #606266;
+  .rightdiv {
+    width: 100%;
+    height: 100%;
+    .monitor-panel {
+      border-radius: 14px;
+      padding: 14px;
+      min-height: 360px;
+      height: 90%;
+      box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
+      overflow-y: auto;
     }
     }
-    .legend-item {
-      display: inline-flex;
+    .monitor-empty {
+      display: flex;
       align-items: center;
       align-items: center;
-      gap: 4px;
-      .legend-dot {
-        width: 8px;
-        height: 8px;
-        border-radius: 50%;
-        background: currentColor;
+      justify-content: center;
+      min-height: 260px;
+      background: #fff;
+      border-radius: 10px;
+    }
+    .monitor-list {
+      display: flex;
+      flex-direction: column;
+      gap: 10px;
+    }
+    .monitor-item {
+      position: relative;
+      display: flex;
+      align-items: center;
+      gap: 14px;
+      border: 1px solid #e4e7ed;
+      border-radius: 10px;
+      background: #fff;
+      padding: 14px 16px;
+      cursor: pointer;
+      transition: all 0.2s ease;
+      &.is-active {
+        background: #f0f5f7;
+      }
+      &.is-active::before {
+        content: "";
+        position: absolute;
+        left: 0;
+        top: 10px;
+        bottom: 10px;
+        width: 4px;
+        background: #74727224;
+        box-shadow: 0 0 0 2px #24a16600 inset;
+        border-radius: 0 4px 4px 0;
       }
       }
-      &.is-normal {
-        color: #008080;
+    }
+    .index-badge {
+      width: 30px;
+      height: 30px;
+      border-radius: 50%;
+      color: #fff;
+      font-size: 16px;
+      font-weight: 700;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      flex-shrink: 0;
+      &.is-success {
+        background: #008080;
       }
       }
       &.is-warning {
       &.is-warning {
-        color: #e6a23c;
+        background: #e6a23c;
       }
       }
       &.is-danger {
       &.is-danger {
-        color: #f56c6c;
+        background: #f56c6c;
+      }
+      &.is-info {
+        background: #909399;
       }
       }
     }
     }
-  }
-  .BtLeft {
-    border: 1px solid rgb(231, 231, 231);
-    width: 100%;
-    padding: 10px;
-  }
-  .BtRight {
-    // width: 49%;
-    border: 1px solid rgb(231, 231, 231);
-    padding: 10px;
-    .BtRightDiv {
-      padding: 0 10px;
-      h4 {
+    .item-main {
+      flex: 1;
+      min-width: 0;
+      .item-name {
+        margin: 0 0 8px;
+        font-size: 16px;
+        font-weight: 700;
+        color: #1f2d3d;
+      }
+      .item-line {
+        margin: 0;
         font-size: 14px;
         font-size: 14px;
+        line-height: 1.5;
+        color: #6b7280;
       }
       }
-      .BtRightP {
-        line-height: 30px;
-        font-size: 12px;
-        display: flex;
-        margin-bottom: 5px;
-        width: 50%;
-        span {
-          margin-left: 10px;
-          .el-input {
-            width: 100px;
-          }
-        }
-        .label-text {
-          width: 140px;
-        }
+    }
+    .item-right {
+      display: flex;
+      align-items: center;
+      gap: 12px;
+    }
+    .status-pill {
+      display: inline-flex;
+      align-items: center;
+      gap: 6px;
+      border-radius: 12px;
+      padding: 10px 16px;
+      font-size: 16px;
+      font-weight: 700;
+      line-height: 1;
+      .status-dot {
+        width: 10px;
+        height: 10px;
+        border-radius: 50%;
+        background: currentColor;
       }
       }
-      .canshu {
-        display: flex;
-        flex-wrap: wrap;
-        justify-content: space-between;
+      &.is-success {
+        color: #21a366;
+        background: #e7f6ee;
+      }
+      &.is-warning {
+        color: #d48806;
+        background: #fff6e5;
+      }
+      &.is-danger {
+        color: #f04438;
+        background: #ffebee;
+      }
+      &.is-info {
+        color: #606266;
+        background: #f4f4f5;
+      }
+    }
+    .fenye {
+      display: flex;
+      justify-content: flex-end;
+      margin: 10px 0 0;
+      font-size: 12px;
+      line-height: 30px;
+      span {
+        font-weight: 600;
       }
       }
     }
     }
   }
   }
-  .dialog-footer {
-    margin-top: 20px;
-    text-align: right;
+}
+
+.bottomBox {
+  display: flex;
+  justify-content: space-around;
+  box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
+  border-radius: 12px;
+  .BtLeft {
+    width: 100%;
+    padding: 10px 20px;
   }
   }
 }
 }
 
 
 .mpoint-charts {
 .mpoint-charts {
-  display: grid;
-  grid-template-columns: repeat(2, minmax(0, 1fr));
-  gap: 16px;
+  display: block;
+  height: 65vh;
+  box-sizing: border-box;
+  ::v-deep .el-carousel {
+    height: 100%;
+  }
+  ::v-deep .el-carousel__container {
+    height: 100%;
+  }
+  ::v-deep .el-carousel__item {
+    height: 100%;
+  }
 }
 }
 
 
 .mpoint-chart-item {
 .mpoint-chart-item {
   border: 1px solid #f0f0f0;
   border: 1px solid #f0f0f0;
   border-radius: 4px;
   border-radius: 4px;
   padding: 8px 10px;
   padding: 8px 10px;
+  box-sizing: border-box;
+  height: 90%;
+  min-height: 0;
+  display: flex;
+  flex-direction: column;
+  > * {
+    flex: 1;
+    min-height: 0;
+  }
 }
 }
 
 
-.mpoint-chart-title {
-  margin: 0 0 6px;
-  font-size: 13px;
-  color: #303133;
+.status-legend {
+  margin: 4px 0 8px;
+  font-size: 12px;
+  color: #909399;
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  .legend-label {
+    color: #606266;
+  }
+  .legend-item {
+    display: inline-flex;
+    align-items: center;
+    gap: 4px;
+    .legend-dot {
+      width: 8px;
+      height: 8px;
+      border-radius: 50%;
+      background: currentColor;
+    }
+    &.is-normal {
+      color: #008080;
+    }
+    &.is-warning {
+      color: #e6a23c;
+    }
+    &.is-danger {
+      color: #f56c6c;
+    }
+  }
 }
 }
 
 
-@media (max-width: 1200px) {
-  .mpoint-charts {
-    grid-template-columns: 1fr;
-  }
+::v-deep .el-carousel__indicator .el-carousel__button {
+  background-color: rgba(144, 147, 153, 0.45);
+  opacity: 1;
+}
+::v-deep .el-carousel__indicator.is-active .el-carousel__button {
+  background-color: #24a166;
+}
+::v-deep .el-carousel__indicator:hover .el-carousel__button {
+  background-color: #24a166aa;
+}
+::v-deep .el-carousel__indicators--horizontal {
+  bottom: -10px !important;
 }
 }
 ::v-deep .el-table__cell {
 ::v-deep .el-table__cell {
   padding: 2px 0;
   padding: 2px 0;

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

@@ -110,9 +110,9 @@ import axios from "axios";
 export default {
 export default {
   components: {
   components: {
     selecttree,
     selecttree,
+    Gear,
     Bearing,
     Bearing,
     Dissymmetry,
     Dissymmetry,
-    Gear,
     Loose,
     Loose,
     Misalignment,
     Misalignment,
     Temperature,
     Temperature,
@@ -418,7 +418,7 @@ export default {
             this.tabData[tab].totalCount = res.data.data.totalSize || 0;
             this.tabData[tab].totalCount = res.data.data.totalSize || 0;
 
 
             if (!data.length) {
             if (!data.length) {
-              this.$message.warning("暂无诊断数据");
+              this.$message.warning("暂无温度诊断数据");
               this.tabData[tab].echartsdata = emptyEchartsData;
               this.tabData[tab].echartsdata = emptyEchartsData;
               return Promise.resolve("skip trend");
               return Promise.resolve("skip trend");
             }
             }

+ 9 - 7
src/views/ledger/milltype.vue

@@ -152,6 +152,7 @@
               style="color: #666"
               style="color: #666"
               @click="ONunit(scope.row)"
               @click="ONunit(scope.row)"
               type="text"
               type="text"
+              v-hasPermi="['home:ledger:modules']"
               size="small"
               size="small"
               >部件信息</el-button
               >部件信息</el-button
             >
             >
@@ -444,10 +445,11 @@ export default {
           { required: true, message: "请输入叶轮直径", trigger: "blur" },
           { required: true, message: "请输入叶轮直径", trigger: "blur" },
           { validator: this.validateNumber, trigger: ["blur", "change"] },
           { validator: this.validateNumber, trigger: ["blur", "change"] },
         ],
         ],
-         rotationalSpeedRatio: [ // 新增传动比字段的验证规则
-    { required: true, message: "请输入传动比", trigger: "blur" },
-    { validator: this.validateNumber, trigger: ["blur", "change"] }, // 假设您也有验证数字的自定义验证器
-  ],
+        rotationalSpeedRatio: [
+          // 新增传动比字段的验证规则
+          { required: true, message: "请输入传动比", trigger: "blur" },
+          { validator: this.validateNumber, trigger: ["blur", "change"] }, // 假设您也有验证数字的自定义验证器
+        ],
       },
       },
       particularsdata: [],
       particularsdata: [],
       // 新增信息
       // 新增信息
@@ -597,7 +599,7 @@ export default {
         callback(new Error("该项不能为空"));
         callback(new Error("该项不能为空"));
       } else if (!numberRegex.test(value)) {
       } else if (!numberRegex.test(value)) {
         callback(
         callback(
-          new Error("该项必须为不超过四位数且小数点后不超过三位数的数字")
+          new Error("该项必须为不超过四位数且小数点后不超过三位数的数字"),
         );
         );
       } else {
       } else {
         callback();
         callback();
@@ -644,7 +646,7 @@ export default {
         (res) => {
         (res) => {
           this.unusualdialog = true;
           this.unusualdialog = true;
           this.particularsdata = res.data;
           this.particularsdata = res.data;
-        }
+        },
       );
       );
     },
     },
     //分页数据切换
     //分页数据切换
@@ -675,7 +677,7 @@ export default {
             this.ruleForm[key] = item[key];
             this.ruleForm[key] = item[key];
           });
           });
           console.log(res);
           console.log(res);
-        }
+        },
       );
       );
       this.title = "编辑机型";
       this.title = "编辑机型";
       this.nuedialog = true;
       this.nuedialog = true;

+ 6 - 6
src/views/ledger/windsite.vue

@@ -727,7 +727,7 @@ export default {
       if (city) {
       if (city) {
         this.newform.city = Object.assign(
         this.newform.city = Object.assign(
           {},
           {},
-          { areaId: city.areaId, city: city.city }
+          { areaId: city.areaId, city: city.city },
         );
         );
       }
       }
     },
     },
@@ -826,7 +826,7 @@ export default {
         if (this.SFMvalue === "2") {
         if (this.SFMvalue === "2") {
           // 转换经度和纬度
           // 转换经度和纬度
           const longitudeDecimal = this.$convertDMSToDecimal(
           const longitudeDecimal = this.$convertDMSToDecimal(
-            this.form.longitude
+            this.form.longitude,
           );
           );
           const latitudeDecimal = this.$convertDMSToDecimal(this.form.latitude);
           const latitudeDecimal = this.$convertDMSToDecimal(this.form.latitude);
           console.log(longitudeDecimal, latitudeDecimal, "...........");
           console.log(longitudeDecimal, latitudeDecimal, "...........");
@@ -850,7 +850,7 @@ export default {
         // Prepare the params object
         // Prepare the params object
         const params = JSON.parse(JSON.stringify(this.form));
         const params = JSON.parse(JSON.stringify(this.form));
         params.anemometerTowerRelationDtos = this.processData(
         params.anemometerTowerRelationDtos = this.processData(
-          this.form.anemometerTowerRelationDtos
+          this.form.anemometerTowerRelationDtos,
         );
         );
         params.cityName = this.form.cityName.city;
         params.cityName = this.form.cityName.city;
         params.cityId = this.form.cityName.areaId;
         params.cityId = this.form.cityName.areaId;
@@ -928,7 +928,7 @@ export default {
       getWindFieldContractByFieldCode({ fieldCode: row.fieldCode }).then(
       getWindFieldContractByFieldCode({ fieldCode: row.fieldCode }).then(
         (res) => {
         (res) => {
           this.curvelist = res.data;
           this.curvelist = res.data;
-        }
+        },
       );
       );
     },
     },
     handleClick(row) {
     handleClick(row) {
@@ -973,7 +973,7 @@ export default {
 
 
           this.form.anemometerTowerRelationDtos = item.anemometerTowerByFieldVos
           this.form.anemometerTowerRelationDtos = item.anemometerTowerByFieldVos
             ? item.anemometerTowerByFieldVos.map(
             ? item.anemometerTowerByFieldVos.map(
-                (element) => element.anemometerCode
+                (element) => element.anemometerCode,
               )
               )
             : [];
             : [];
           this.detail = row;
           this.detail = row;
@@ -1114,7 +1114,7 @@ export default {
         getWindEngineGroupListByFieldCode({ fieldCode: row.fieldCode }).then(
         getWindEngineGroupListByFieldCode({ fieldCode: row.fieldCode }).then(
           (res) => {
           (res) => {
             this.batchList = res.data;
             this.batchList = res.data;
-          }
+          },
         );
         );
         this.drawer = true;
         this.drawer = true;
       } else {
       } else {

+ 14 - 14
src/views/performance/assetssDetail.vue

@@ -1,8 +1,8 @@
 <!--
 <!--
  * @Author: your name
  * @Author: your name
  * @Date: 2024-05-27 09:25:45
  * @Date: 2024-05-27 09:25:45
- * @LastEditTime: 2025-08-08 10:29:01
- * @LastEditors: bogon
+ * @LastEditTime: 2026-04-17 15:02:49
+ * @LastEditors: milo-MacBook-Pro.local
  * @Description: In User Settings Edit
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/performance/assetssDetail.vue
  * @FilePath: /performance-test/src/views/performance/assetssDetail.vue
 -->
 -->
@@ -269,7 +269,7 @@
                       <el-button
                       <el-button
                         @click="
                         @click="
                           downLoadCsv(
                           downLoadCsv(
-                            powerCurveDom && powerCurveDom.powerCurveTableData
+                            powerCurveDom && powerCurveDom.powerCurveTableData,
                           )
                           )
                         "
                         "
                         >导出表格数据</el-button
                         >导出表格数据</el-button
@@ -878,19 +878,19 @@ export default {
             this.graphFilesData = [];
             this.graphFilesData = [];
             const generalFilesData = await this.filterJsonData(
             const generalFilesData = await this.filterJsonData(
               generalFiles,
               generalFiles,
-              "总图"
+              "总图",
             );
             );
             //分图数据
             //分图数据
             const graphChartData = await this.filterJsonData(
             const graphChartData = await this.filterJsonData(
               response.data[0].diagramRelations,
               response.data[0].diagramRelations,
-              "分图"
+              "分图",
             );
             );
             // 过滤掉 null 或没有 chartsData 的项
             // 过滤掉 null 或没有 chartsData 的项
             this.generalFilesData = generalFilesData.filter(
             this.generalFilesData = generalFilesData.filter(
-              (item) => item && item.chartsData
+              (item) => item && item.chartsData,
             );
             );
             this.graphFilesData = graphChartData.filter(
             this.graphFilesData = graphChartData.filter(
-              (item) => item && item.chartsData
+              (item) => item && item.chartsData,
             );
             );
             // // 这是处理分图数据的逻辑如果有数据,处理并设置图表数据
             // // 这是处理分图数据的逻辑如果有数据,处理并设置图表数据
             // if (
             // if (
@@ -946,7 +946,7 @@ export default {
                     ...resultChartsData.data,
                     ...resultChartsData.data,
                   },
                   },
                   powerCurveTableData: this.creatPowerCurveTableData(
                   powerCurveTableData: this.creatPowerCurveTableData(
-                    resultChartsData.data
+                    resultChartsData.data,
                   ).filter((val) => val !== undefined),
                   ).filter((val) => val !== undefined),
                 };
                 };
               } else if (type === "分图") {
               } else if (type === "分图") {
@@ -957,13 +957,13 @@ export default {
                     formInfoFieldEngineCode: this.formInfo.fieldEngineCode,
                     formInfoFieldEngineCode: this.formInfo.fieldEngineCode,
                   },
                   },
                   powerCurveTableData: this.creatPowerCurveTableData(
                   powerCurveTableData: this.creatPowerCurveTableData(
-                    resultChartsData.data
+                    resultChartsData.data,
                   )
                   )
                     .filter((val) => val !== undefined)
                     .filter((val) => val !== undefined)
                     .filter(
                     .filter(
                       (item) =>
                       (item) =>
                         item?.enginName === this.formInfo.fieldEngineCode ||
                         item?.enginName === this.formInfo.fieldEngineCode ||
-                        item?.enginCode === this.formInfo.fieldEngineCode
+                        item?.enginCode === this.formInfo.fieldEngineCode,
                     ),
                     ),
                 };
                 };
               }
               }
@@ -980,7 +980,7 @@ export default {
             //html 展示
             //html 展示
             return null; // 返回 null 以确保没有 undefined
             return null; // 返回 null 以确保没有 undefined
           }
           }
-        })
+        }),
       );
       );
     },
     },
     creatPowerCurveTableData(data) {
     creatPowerCurveTableData(data) {
@@ -1035,11 +1035,11 @@ export default {
                 if (analysisType === "yaw_error") {
                 if (analysisType === "yaw_error") {
                   this.csvHeaders = Object.keys(result.data[0]);
                   this.csvHeaders = Object.keys(result.data[0]);
                   this.csvData = result.data.filter(
                   this.csvData = result.data.filter(
-                    (row) => Object.keys(row).length
+                    (row) => Object.keys(row).length,
                   ); // 过滤空行
                   ); // 过滤空行
                 } else if (analysisType === "production_indicator") {
                 } else if (analysisType === "production_indicator") {
                   this.productionIndicatorCsvHeader.push(
                   this.productionIndicatorCsvHeader.push(
-                    Object.keys(result.data[0])
+                    Object.keys(result.data[0]),
                   );
                   );
                   this.productionIndicatorCsvData.push({
                   this.productionIndicatorCsvData.push({
                     data: result.data
                     data: result.data
@@ -1094,7 +1094,7 @@ export default {
         ...data.map((row) => row.join(",")),
         ...data.map((row) => row.join(",")),
       ].join("\n");
       ].join("\n");
       const fileName = this.windEngineGroupList.filter(
       const fileName = this.windEngineGroupList.filter(
-        (item) => item.engineCode === this.formInfo.fieldEngineCode
+        (item) => item.engineCode === this.formInfo.fieldEngineCode,
       );
       );
       downLoadCsvFile(csvContent, "风机有功功率数据");
       downLoadCsvFile(csvContent, "风机有功功率数据");
       // downLoadCsvFile(csvContent, fileName[0].engineName);
       // downLoadCsvFile(csvContent, fileName[0].engineName);

+ 10 - 7
src/views/performance/assetssMag.vue

@@ -1,12 +1,13 @@
 <template>
 <template>
   <div class="global-variable">
   <div class="global-variable">
     <div class="condition">
     <div class="condition">
-      <!-- <el-alert
+      <el-alert
         title="分析前请核对数据是否导入,点击查看是否导入即可查看"
         title="分析前请核对数据是否导入,点击查看是否导入即可查看"
         type="warning"
         type="warning"
         show-icon
         show-icon
+        v-hasPermi="['home:offlinedata']"
       >
       >
-      </el-alert> -->
+      </el-alert>
       <el-form
       <el-form
         :inline="true"
         :inline="true"
         ref="ruleForm"
         ref="ruleForm"
@@ -251,6 +252,7 @@
           align="center"
           align="center"
           label="报告"
           label="报告"
           min-width="120"
           min-width="120"
+          v-hasPermi="['home:performance:uploadReport']"
         >
         >
           <template slot-scope="scope">
           <template slot-scope="scope">
             <el-dropdown v-if="scope.row.reportVos.length > 0" trigger="click">
             <el-dropdown v-if="scope.row.reportVos.length > 0" trigger="click">
@@ -321,7 +323,7 @@
             >
             >
               下载图表
               下载图表
             </el-button> -->
             </el-button> -->
-            <el-button
+            <!-- <el-button
               @click="handleDownLoadChart(scope.row)"
               @click="handleDownLoadChart(scope.row)"
               type="text"
               type="text"
               size="small"
               size="small"
@@ -332,13 +334,14 @@
               "
               "
             >
             >
               下载报告
               下载报告
-            </el-button>
-            <!-- <el-button
+            </el-button> -->
+            <el-button
+              v-hasPermi="['home:performance:uploadReport']"
               @click="abnormalDialog(scope.row, '上传报告')"
               @click="abnormalDialog(scope.row, '上传报告')"
               type="text"
               type="text"
               size="small"
               size="small"
               >上传报告</el-button
               >上传报告</el-button
-            > -->
+            >
             <el-button
             <el-button
               @click="handleAssetss(scope.row)"
               @click="handleAssetss(scope.row)"
               type="text"
               type="text"
@@ -493,7 +496,7 @@ import {
 import { downloadDocx } from "@/utils/common";
 import { downloadDocx } from "@/utils/common";
 import { getFieldInfo } from "@/api/overview";
 import { getFieldInfo } from "@/api/overview";
 import axios from "axios";
 import axios from "axios";
-import pLimit from "p-limit";
+// import pLimit from "p-limit";
 import { allAnalysisType } from "./js/allAnalysisType";
 import { allAnalysisType } from "./js/allAnalysisType";
 import { mapMutations, mapState } from "vuex";
 import { mapMutations, mapState } from "vuex";
 import {
 import {