Browse Source

各环境区分配置

liujiejie 4 tháng trước cách đây
mục cha
commit
1556dbfc3b

+ 26 - 3
.env.demo

@@ -1,15 +1,38 @@
 ###
  # @Author: your name
  # @Date: 2025-07-17 15:36:25
- # @LastEditTime: 2025-07-17 15:36:34
+ # @LastEditTime: 2025-07-23 09:22:01
  # @LastEditors: bogon
  # @Description: In User Settings Edit
  # @FilePath: /performance-test/.env.demo
 ### 
-VUE_APP_MAPVIEW=http://192.168.50.235/tiles/{z}/{x}/{y}.png
+
 #这里需要在router/index.js 文件中进行判断cockpitComponent;
 #VUE_APP_HOME_COM="../views/admin/cockpitManage/Index.vue"
 VUE_APP_THEM="green"
 VUE_APP_ISSHOWHD='default'
+
+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:18998/api/energy-manage-service/api/check/upload"
+# VUE_APP_APIPROXY='http://106.120.102.238:18998/api'
+# VUE_APP_WZLAPIPROXY='http://106.120.102.238:18998/transDataWeb'
+# VUE_APP_ETLAPIPROXY='http://106.120.102.238:18998/transDataWeb'
+# VUE_APP_AnalysisMultiAPIPROXY='http://106.120.102.238:18998/AnalysisMulti'
+# #自定义算法文佳 目前无法使用,可能是服务未启动
+# VUE_APP_sAlgorithmAPIPROXY='http://106.120.102.238:18998/sAlgorithm'
+# VUE_APP_databaseApiAPIPROXY='http://106.120.102.238:18998/databaseApi'
+# VUE_APP_downLoadChartAPIPROXY='http://106.120.102.238:18998/downLoadChart'
+
+#内网
 VUE_APP_UPLOAD="http://192.168.50.235/energy-manage-service/api/check/upload"
-VUE_APP_TITLE='机组功率曲线异常检测数据分析系统'
+VUE_APP_MAPVIEW=http://192.168.50.235/tiles/{z}/{x}/{y}.png
+VUE_APP_APIPROXY='http://192.168.50.235:16600'
+VUE_APP_WZLAPIPROXY='http://192.168.50.235:8998/transDataWeb'
+VUE_APP_ETLAPIPROXY='http://192.168.50.235:8998/transDataWeb'
+VUE_APP_AnalysisMultiAPIPROXY='http://192.168.50.235:8998/AnalysisMulti'
+# #自定义算法文佳 目前无法使用,可能是服务未启动
+VUE_APP_sAlgorithmAPIPROXY='http://192.168.50.235:8666'
+VUE_APP_databaseApiAPIPROXY='http://192.168.50.234:3002'
+VUE_APP_downLoadChartAPIPROXY='http://192.168.50.235:8999/downLoadChart'

+ 30 - 8
.env.dev

@@ -1,17 +1,39 @@
 ###
  # @Author: your name
  # @Date: 2025-07-17 14:14:27
- # @LastEditTime: 2025-07-17 15:56:15
- # @LastEditors: bogon
+ # @LastEditTime: 2025-07-22 13:58:52
+ # @LastEditors: milo-MacBook-Pro.local
  # @Description: In User Settings Edit
  # @FilePath: /performance-test/.env.dev
-### 
-# 地图外网
-# VUE_APP_MAPVIEW=http://106.120.102.238:18000/tiles/{z}/{x}/{y}.png
-VUE_APP_MAPVIEW=http://192.168.50.235/tiles/{z}/{x}/{y}.png
 #这里需要在router/index.js 文件中进行判断cockpitComponent;
 #VUE_APP_HOME_COM="../views/admin/cockpitManage/Index.vue"
 VUE_APP_THEM="green"
 VUE_APP_ISSHOWHD='default'
-VUE_APP_UPLOAD="http://192.168.50.235/energy-manage-service/api/check/upload"
-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:16700'
+VUE_APP_WZLAPIPROXY='http://106.120.102.238:18080/WindTransDev'
+VUE_APP_ETLAPIPROXY='http://106.120.102.238:18080/WindTransDev'
+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_UPLOAD="http://192.168.50.235/energy-manage-service/api/check/upload"
+# VUE_APP_MAPVIEW=http://192.168.50.235/tiles/{z}/{x}/{y}.png
+# VUE_APP_APIPROXY='http://192.168.50.235:16200'
+# VUE_APP_WZLAPIPROXY='http://192.168.50.241:9001'
+# VUE_APP_ETLAPIPROXY='http://192.168.50.241:9001'
+# # #自定义算法文佳 目前无法使用,可能是服务未启动
+# VUE_APP_sAlgorithmAPIPROXY='http://192.168.50.235:8666'
+# VUE_APP_databaseApiAPIPROXY='http://192.168.50.234:3002'
+# #暂时不知健康评估内网dev 环境地址
+# VUE_APP_AnalysisMultiAPIPROXY=''
+# #暂时不知下载报告内网dev 环境地址
+# VUE_APP_downLoadChartAPIPROXY=''

+ 2 - 2
.env.hd

@@ -1,8 +1,8 @@
 ###
  # @Author: your name
  # @Date: 2025-07-17 14:13:20
- # @LastEditTime: 2025-07-17 15:55:05
- # @LastEditors: bogon
+ # @LastEditTime: 2025-07-22 13:35:37
+ # @LastEditors: milo-MacBook-Pro.local
  # @Description: In User Settings Edit
  # @FilePath: /performance-test/.env.hd
 ### 

+ 151 - 0
demo.html

@@ -0,0 +1,151 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
+    <style>
+      body {
+        margin: 0;
+      }
+      #plot {
+        width: 100vw;
+        height: 100vh;
+      }
+    </style>
+  </head>
+  <body>
+    <div id="plot"></div>
+    <script>
+      const xLabels = ["A", "B", "C"];
+      const yLabels = ["Monday", "Tuesday", "Wednesday"];
+      const dataMatrix = [
+        [5, 8, 6],
+        [4, 2, 9],
+        [7, 3, 5],
+      ];
+
+      // 构造立方体柱子
+      const traces = [];
+      const barWidth = 0.8;
+
+      for (let xi = 0; xi < xLabels.length; xi++) {
+        for (let yi = 0; yi < yLabels.length; yi++) {
+          const height = dataMatrix[yi][xi];
+
+          const x = xi;
+          const y = yi;
+          const z = 0;
+
+          // 立方体8个顶点
+          const vertices = [
+            [x, y, z],
+            [x + barWidth, y, z],
+            [x + barWidth, y + barWidth, z],
+            [x, y + barWidth, z],
+            [x, y, z + height],
+            [x + barWidth, y, z + height],
+            [x + barWidth, y + barWidth, z + height],
+            [x, y + barWidth, z + height],
+          ];
+
+          // 每个立方体的面(两个三角形组成一个面)
+          const faces = [
+            [0, 1, 2],
+            [0, 2, 3], // bottom
+            [4, 5, 6],
+            [4, 6, 7], // top
+            [0, 1, 5],
+            [0, 5, 4], // front
+            [1, 2, 6],
+            [1, 6, 5], // right
+            [2, 3, 7],
+            [2, 7, 6], // back
+            [3, 0, 4],
+            [3, 4, 7], // left
+          ];
+
+          // 拆解为 Plotly mesh3d 的输入格式
+          const xVals = vertices.map((v) => v[0]);
+          const yVals = vertices.map((v) => v[1]);
+          const zVals = vertices.map((v) => v[2]);
+
+          const i = faces.map((f) => f[0]);
+          const j = faces.map((f) => f[1]);
+          const k = faces.map((f) => f[2]);
+
+          traces.push({
+            type: "mesh3d",
+            x: xVals,
+            y: yVals,
+            z: zVals,
+            i,
+            j,
+            k,
+            opacity: 1,
+            color: `rgb(${50 + xi * 50}, ${100 + yi * 50}, 200)`,
+            flatshading: true,
+            showscale: false,
+          });
+        }
+      }
+
+      const layout = {
+        scene: {
+          xaxis: {
+            title: "X",
+            tickvals: [0.4, 1.4, 2.4],
+            ticktext: xLabels,
+          },
+          yaxis: {
+            title: "Y",
+            tickvals: [0.4, 1.4, 2.4],
+            ticktext: yLabels,
+          },
+          zaxis: { title: "Z" },
+          aspectratio: {
+            x: 2.2,
+            y: 1.7,
+            z: 1,
+          },
+          bgcolor: "#e5ecf6",
+          aspectmode: "manual",
+          gridcolor: "#fff",
+          camera: {
+            up: {
+              x: 0.200292643688136,
+              y: 0.2488259353493132,
+              z: 0.947612004346693,
+            },
+            center: {
+              x: -0.052807476121180814,
+              y: 0.02451796399554085,
+              z: -0.022911006648570736,
+            },
+            eye: {
+              x: -2.126379643342493,
+              y: -2.551422475965373,
+              z: 1.0917667684145647,
+            },
+            projection: {
+              type: "orthographic",
+            },
+          },
+          staticPlot: false,
+          showlegend: true,
+          legend: {
+            itemsizing: "constant", // ✅ 统一图例 marker 大小
+            font: {
+              size: 12,
+            },
+            marker: {
+              size: 10,
+            },
+          },
+        },
+        margin: { t: 50, b: 50, l: 50, r: 50 },
+      };
+
+      Plotly.newPlot("plot", traces, layout);
+    </script>
+  </body>
+</html>

+ 1 - 1
src/utils/request.js

@@ -88,7 +88,7 @@ service.interceptors.response.use(
           type: "error",
           duration: 5 * 1000,
         });
-        router.push("/login");
+        // router.push("/login");
       } else {
         Message({
           message: response.data.message || error.message,

+ 1 - 1
src/views/admin/cockpitManage/component/bottomData.vue

@@ -293,7 +293,7 @@ export default {
         fieldName: 120,
         engineName: 140,
       },
-      // 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
+      // 。。。。。。。。。。。。。。。。。。
       tableData: [],
       tableColumns: [
         { prop: "optionByName" },

+ 10 - 1
src/views/overview/components/fault_all/index.vue

@@ -1,7 +1,7 @@
 <!--
  * @Author: your name
  * @Date: 2025-01-13 13:56:55
- * @LastEditTime: 2025-06-11 09:39:04
+ * @LastEditTime: 2025-07-24 10:49:46
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/overview/components/fault_all/index.vue
@@ -52,6 +52,14 @@
           "
           :zongFaultCsvData="zongFaultCsvData"
         ></FaultAll>
+        <Time3DBarChart
+          :setUpImgData="setUpImgData"
+          :key="'Time3DChart'"
+          :index="`${new Date().getTime()}` + 'fen'"
+          :ref="'Time3DChart'"
+          :fileAddr="item.fileAddr"
+        >
+        </Time3DBarChart>
         <template v-for="(itemCsv, indCsv) in zongFaultCsvData">
           <el-table
             max-height="500"
@@ -140,6 +148,7 @@ import DicCard from "@/views/overview/components/dicCard/index.vue";
 import FilterChart from "@/views/overview/components/filterChart/index.vue";
 import FaultAll from "@/views/performance/components/chartsCom/FaultAll.vue";
 import TinymceEditor from "@/components/Tinymce.vue";
+import Time3DBarChart from "@/views/performance/components/chartsCom/Time3DBarChart.vue";
 import {
   analysisDetail,
   queryAnalysisedEngine,

+ 7 - 1
src/views/overview/components/fault_unit/index.vue

@@ -1,7 +1,7 @@
 <!--
  * @Author: your name
  * @Date: 2025-01-13 13:56:12
- * @LastEditTime: 2025-06-20 14:04:22
+ * @LastEditTime: 2025-07-24 09:59:21
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/overview/components/fault_unit/index.vue
@@ -34,6 +34,12 @@
       </el-alert>
       <div class="chartsBox" v-if="fenFaultCsvData.length > 0">
         <FaultUnit
+          :faultTitledata="[
+            '机组故障时长与故障次数分析',
+            '故障机组',
+            '故障时长(小时)',
+            '故障次数',
+          ]"
           :faultTypes="
             fenFaultCsvData &&
             fenFaultCsvData[0] &&

+ 13 - 0
src/views/overview/components/min_pitch1/index.vue

@@ -0,0 +1,13 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-07-24 17:07:30
+ * @LastEditTime: 2025-07-24 17:10:19
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/overview/components/min_pitch1/index.vue
+-->
+<template>
+  <div>变桨频次</div>
+</template>
+<script></script>
+<style></style>

+ 13 - 0
src/views/overview/components/min_pitch2/index.vue

@@ -0,0 +1,13 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-07-24 17:07:43
+ * @LastEditTime: 2025-07-24 17:10:33
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/overview/components/min_pitch2/index.vue
+-->
+<template>
+  <div>变桨角度差异</div>
+</template>
+<script></script>
+<style></style>

+ 13 - 0
src/views/overview/components/min_pitch3/index.vue

@@ -0,0 +1,13 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-07-24 17:07:55
+ * @LastEditTime: 2025-07-24 17:10:50
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/overview/components/min_pitch3/index.vue
+-->
+<template>
+  <div>变桨电机温升</div>
+</template>
+<script></script>
+<style></style>

+ 381 - 0
src/views/overview/components/production_indicator1/index.vue

@@ -0,0 +1,381 @@
+<!-- production_indicator1 损失电量 -->
+<template>
+  <div class="type-variable">
+    <!-- 损失电量分析 -->
+    <div class="left scroller">
+      <FilterChart
+        :setUpimg="[]"
+        :windList="windEngineGroupList"
+        @getEnfineList="getEnfineList"
+        @handlePrevious="handlePrevious"
+        @handleNext="handleNext"
+      ></FilterChart>
+      <el-alert type="info" :closable="false">
+        <template v-slot:title>
+          <div style="display: flex; align-items: center">
+            <i
+              class="el-icon-info"
+              style="font-size: 20px; margin-right: 5px"
+            ></i>
+            <h3>分析说明:</h3>
+          </div>
+        </template>
+        <div style="font-size: 12px; margin-top: 10px">
+          风向玫瑰图是一种表示某地不同风向出现频率分布的图表。
+        </div>
+      </el-alert>
+      <div class="titleCharts">分析分图 :</div>
+      <div
+        class="scroller"
+        v-if="diagramRelationsDatas && diagramRelationsDatas.length > 0"
+      >
+        <template>
+          <el-row>
+            <el-col
+              :xs="24"
+              :sm="24"
+              :md="24"
+              :lg="12"
+              :xl="12"
+              v-for="(item, itemind) in diagramRelationsDatas"
+              :key="itemind"
+            >
+              <WindRoseChart
+                :setUpImgData="setUpImgData"
+                :key="item.fieldEngineCode + itemind"
+                :inds="`${new Date().getTime()}` + itemind"
+                :ref="item.fieldEngineCode"
+                :fileAddr="item.fileAddr"
+              ></WindRoseChart>
+            </el-col>
+          </el-row>
+        </template>
+      </div>
+      <el-empty description="暂无分析记录" v-else></el-empty>
+      <el-dialog
+        v-if="isShowDescription"
+        title="添加评论"
+        :visible="isShow"
+        width="30%"
+        v-dialogDrag
+        :modal="false"
+        :lock-scroll="false"
+        :modal-append-to-body="false"
+        @close="handleClose"
+      >
+        <el-tabs value="first">
+          <el-tab-pane label="意见描述" name="first">
+            <TinymceEditor
+              ref="editor"
+              v-model="comment"
+              @input="handleEditorInput($event)"
+              @onClick="onClick"
+            >
+            </TinymceEditor>
+          </el-tab-pane>
+        </el-tabs>
+        <el-row
+          type="flex"
+          class="row-bg"
+          justify="end"
+          style="margin: 20px 60px 0 0"
+        >
+          <el-col :span="2">
+            <el-button type="primary" size="small" @click="handleComment"
+              >提交评论</el-button
+            >
+          </el-col>
+        </el-row>
+      </el-dialog>
+    </div>
+    <div class="right" v-if="isShowTinymceEditorCom">
+      <DicCard
+        :batchCode="initBatchCode"
+        :analysisTypeCode="analysisTypeCode"
+        :commentDescriptionVos="commentDescriptionVos"
+      >
+      </DicCard>
+    </div>
+  </div>
+</template>
+
+<script>
+import WindRoseChart from "@/views/performance/components/chartsCom/WindRoseChart.vue";
+import DicCard from "@/views/overview/components/dicCard/index.vue";
+import FilterChart from "@/views/overview/components/filterChart/index.vue";
+import TinymceEditor from "@/components/Tinymce.vue";
+import {
+  analysisDetail,
+  queryAnalysisedEngine,
+  analysisCommentEdit,
+} from "@/api/performance";
+
+export default {
+  name: "windDirectionFrequency",
+  components: {
+    DicCard,
+    FilterChart,
+    TinymceEditor,
+    WindRoseChart,
+  },
+  props: {
+    initBatchCode: {
+      default: "",
+      type: String,
+    },
+    isShowTinymceEditorCom: {
+      default: true,
+      type: Boolean,
+    },
+    isShow: {
+      default: false,
+      type: Boolean,
+    },
+    analysisTypeCode: {
+      default: "",
+      type: String,
+    },
+    batchCodeList: {
+      default: "",
+      type: Array,
+    },
+  },
+  data() {
+    return {
+      form: {
+        value2: "",
+      },
+      setUpImgData: [],
+      comment: "",
+      options: [],
+      windEngineGroupList: [], //批次风机列表
+      fieldEngineCodes: [], //选中风机
+      generalFilesDatas: [], //总图
+      diagramRelationsDatas: [], //分图
+      commentDescriptionVos: [], //评论列表
+      editableTabs: [],
+      isShowDescription: false,
+    };
+  },
+  watch: {
+    isShow() {
+      if (this.isShow) {
+        if (!this.isShowDescription) {
+          this.$message({
+            message: "当前分析模型暂无分析,不能进行评论操作",
+            type: "warning",
+          });
+          this.$emit("setIsShow");
+        }
+      }
+    },
+    initBatchCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+    analysisTypeCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+  },
+  mounted() {
+    if (this.initBatchCode && this.analysisTypeCode) {
+      this.fetchData(); // 调用合并后的函数
+    }
+  },
+  methods: {
+    handleClose() {
+      //关闭评论弹框
+      this.$emit("setIsShow");
+    },
+    async handleComment() {
+      try {
+        await analysisCommentEdit({
+          batchCode: this.initBatchCode,
+          analysisTypeCode: this.analysisTypeCode,
+          commentList: this.editableTabs.map((item) => {
+            return {
+              commentTypeCode: item.commentTypeCode,
+              comment: item.commentTypeName === "分析评论" ? this.comment : "",
+            };
+          }),
+        });
+        this.$message({
+          type: "success",
+          message: "保存成功",
+        });
+        this.comment = "";
+        this.getAnalysisDetail();
+        this.$emit("setIsShow");
+      } catch (e) {
+        console.error(e);
+        this.loading = false;
+      }
+    },
+    onSubmit() {
+      console.log("submit!");
+    },
+    // 合并后的函数,处理数据请求
+    async fetchData() {
+      try {
+        // 获取分析详情
+        await this.getAnalysisDetail();
+
+        // 获取风机列表
+        await this.getWindEnfineList(this.initBatchCode, this.analysisTypeCode);
+      } catch (err) {
+        console.error("Failed to fetch data:", err);
+      }
+    },
+    // 获取分析详情接口
+    async getAnalysisDetail() {
+      try {
+        const result = await analysisDetail({
+          batchCode: this.initBatchCode,
+          analysisTypeCode: this.analysisTypeCode,
+          fieldEngineCodes:
+            this.fieldEngineCodes.length === 0
+              ? undefined
+              : this.fieldEngineCodes.join(","),
+        });
+        if (result.data.length > 0) {
+          this.isShowDescription = true;
+        }
+        if (
+          result.data &&
+          result.data[0] &&
+          result.data[0].commentTypeRelations
+        ) {
+          this.editableTabs = result.data[0].commentTypeRelations;
+        }
+        //当前评论展示获取
+        if (
+          result.data &&
+          result.data[0] &&
+          result.data[0].commentDescriptionVos
+        ) {
+          this.commentDescriptionVos = result.data[0].commentDescriptionVos;
+        }
+        this.generalFilesDatas =
+          (result.data &&
+            result.data[0] &&
+            result.data[0].generalFiles &&
+            result.data[0].generalFiles.filter((item) =>
+              item.fileAddr.endsWith(".json")
+            )) ||
+          []; //总图数据
+        this.diagramRelationsDatas =
+          (result.data &&
+            result.data[0] &&
+            result.data[0].diagramRelations &&
+            result.data[0].diagramRelations.filter((item) =>
+              item.fileAddr.endsWith(".json")
+            )) ||
+          [];
+      } catch (err) {
+        console.error("Failed to fetch analysis details:", err);
+      }
+    },
+
+    // 请求风机列表
+    async getWindEnfineList(batchCode, analysisTypeCode) {
+      // console.log("请求风机列表 分钟级");
+      const resEngineList = await queryAnalysisedEngine({
+        batchCode: batchCode,
+        analysisTypeCode,
+      });
+      this.windEngineGroupList = resEngineList.data;
+    },
+    handleEditorInput(index, newVal) {
+      // 更新对应的 comment 值
+      // 如果该功能没有实现,可以删除这个方法
+    },
+    //获取选中风机list
+    getEnfineList(data, setUpImg) {
+      this.fieldEngineCodes = data;
+      this.setUpImgData = [...setUpImg];
+      this.getAnalysisDetail();
+    },
+    //下一条
+    handleNext() {
+      const index = this.batchCodeList.findIndex(
+        (item) => item === this.initBatchCode
+      );
+      if (index === this.batchCodeList.length - 1) {
+        this.$message.warning("已经是最后一个分析结果了");
+        return;
+      }
+      this.$emit("setInitBathCode", this.batchCodeList[index + 1]);
+    },
+    //上一条
+    handlePrevious() {
+      const index = this.batchCodeList.findIndex(
+        (item) => item === this.initBatchCode
+      );
+      if (index === 0) {
+        this.$message.warning("没有上一条了");
+        return;
+      }
+      this.$emit("setInitBathCode", this.batchCodeList[index - 1]);
+    },
+    onClick() {},
+  },
+};
+</script>
+<style scoped lang="scss">
+.type-variable {
+  display: flex;
+  height: 90%;
+  overflow: hidden;
+  .left {
+    width: 30%;
+    height: 100%;
+    overflow: auto;
+    padding: 20px;
+    flex: 1;
+    /* 滚动条整体样式 */
+    &::-webkit-scrollbar {
+      width: 6px; /* 滚动条宽度 */
+    }
+
+    /* 滚动条轨道 */
+    &::-webkit-scrollbar-track {
+      background: #f5f7fa;
+      border-radius: 3px;
+    }
+
+    /* 滚动条滑块 */
+    &::-webkit-scrollbar-thumb {
+      background: #c0c4cc;
+      border-radius: 3px;
+    }
+
+    /* 滚动条滑块悬停时 */
+    &::-webkit-scrollbar-thumb:hover {
+      background: #909399;
+    }
+  }
+  .right {
+    width: 0px;
+    height: 100%;
+    overflow: hidden;
+  }
+}
+.el-dialog__wrapper {
+  position: relative !important;
+}
+::v-deep .el-dialog {
+  position: fixed !important;
+  z-index: 999 !important;
+  top: 50%;
+  left: 50%;
+  transform: translate(0, -50%);
+}
+.titleCharts {
+  font-size: 16px;
+  font-weight: 500;
+  margin-top: 20px;
+}
+</style>

+ 495 - 0
src/views/overview/components/production_indicator2/index.vue

@@ -0,0 +1,495 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-07-23 10:53:25
+ * @LastEditTime: 2025-07-24 10:01:15
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/overview/components/production_indicator2/index.vue
+-->
+
+<template>
+  <div class="type-variable">
+    <!-- production_indicator2 整体状态评估 -->
+    <div class="left scroller">
+      <FilterChart
+        :setUpimg="[]"
+        :isShowEngList="false"
+        :windList="windEngineGroupList"
+        @getEnfineList="getEnfineList"
+        @handlePrevious="handlePrevious"
+        @handleNext="handleNext"
+      ></FilterChart>
+      <el-alert type="info" :closable="false">
+        <template v-slot:title>
+          <div style="display: flex; align-items: center">
+            <i
+              class="el-icon-info"
+              style="font-size: 20px; margin-right: 5px"
+            ></i>
+            <h3>分析说明:</h3>
+          </div>
+        </template>
+        <div style="font-size: 12px; margin-top: 10px">
+          对各个机组整体状态评估的发电量和故障时长进行统计
+        </div>
+      </el-alert>
+      <div class="chartsBox" v-if="fenFaultCsvData.length > 0">
+        <FaultUnit
+          :faultTitledata="[
+            '机组发电量VS故障停机时长',
+            '故障时长(小时)',
+            '发电量(MWH)',
+            '故障次数',
+          ]"
+          :faultTypes="
+            fenFaultCsvData &&
+            fenFaultCsvData[0] &&
+            fenFaultCsvData[0].data.map((item) => item.wind_turbine_name)
+          "
+          :faultCounts="
+            fenFaultCsvData &&
+            fenFaultCsvData[0] &&
+            fenFaultCsvData[0].data.map((item) => Number(item.count))
+          "
+          :faultDurations="
+            fenFaultCsvData &&
+            fenFaultCsvData[0] &&
+            fenFaultCsvData[0].data.map((item) => Number(item.fault_time))
+          "
+          :fenFaultCsvData="fenFaultCsvData"
+        ></FaultUnit>
+        <!-- <template v-for="itemCsv in fenFaultCsvData">
+          <el-table
+            max-height="500"
+            :data="filteredFenData(itemCsv)"
+            :default-sort="{ prop: 'fault_time', order: 'descending' }"
+            border
+            style="width: 100%"
+            align="center"
+          >
+            <el-table-column prop="wind_turbine_name" label="机组" sortable>
+            </el-table-column>
+            <el-table-column prop="count" sortable label="故障次数(次)">
+            </el-table-column>
+            <el-table-column
+              prop="fault_time"
+              sortable
+              label="故障时长(小时)"
+            >
+              <template slot-scope="scope">
+                {{ Number(scope.row.fault_time).toFixed(2) }}
+              </template>
+            </el-table-column>
+            <el-table-column align="right">
+              <template slot="header" slot-scope="scope">
+                <el-input
+                  v-model="searchFen"
+                  size="mini"
+                  placeholder="输入风机名称关键字搜索"
+                />
+              </template>
+            </el-table-column>
+          </el-table>
+        </template> -->
+      </div>
+      <el-empty description="暂无分析记录" v-else></el-empty>
+      <el-dialog
+        v-if="isShowDescription"
+        title="添加评论"
+        :visible="isShow"
+        width="30%"
+        v-dialogDrag
+        :modal="false"
+        :lock-scroll="false"
+        :modal-append-to-body="false"
+        @close="handleClose"
+      >
+        <el-tabs value="first">
+          <el-tab-pane label="意见描述" name="first">
+            <TinymceEditor
+              ref="editor"
+              v-model="comment"
+              @input="handleEditorInput($event)"
+              @onClick="onClick"
+            >
+            </TinymceEditor>
+          </el-tab-pane>
+        </el-tabs>
+        <el-row
+          type="flex"
+          class="row-bg"
+          justify="end"
+          style="margin: 20px 60px 0 0"
+        >
+          <el-col :span="2">
+            <el-button type="primary" size="small" @click="handleComment"
+              >提交评论</el-button
+            >
+          </el-col>
+        </el-row>
+      </el-dialog>
+    </div>
+    <div class="right" v-if="isShowTinymceEditorCom">
+      <DicCard
+        :batchCode="initBatchCode"
+        :analysisTypeCode="'fault'"
+        :commentDescriptionVos="commentDescriptionVos"
+      >
+      </DicCard>
+    </div>
+  </div>
+</template>
+<script>
+import DicCard from "@/views/overview/components/dicCard/index.vue";
+import FilterChart from "@/views/overview/components/filterChart/index.vue";
+import FaultUnit from "@/views/performance/components/chartsCom/FaultUnit.vue";
+import TinymceEditor from "@/components/Tinymce.vue";
+import {
+  analysisDetail,
+  queryAnalysisedEngine,
+  analysisCommentEdit,
+} from "@/api/performance";
+import Papa from "papaparse";
+import axios from "axios";
+export default {
+  name: "fault_unit",
+  components: {
+    DicCard,
+    FilterChart,
+    FaultUnit,
+    TinymceEditor,
+  },
+  props: {
+    initBatchCode: {
+      default: "",
+      type: String,
+    },
+    isShowTinymceEditorCom: {
+      default: true,
+      type: Boolean,
+    },
+    analysisTypeCode: {
+      default: "",
+      type: String,
+    },
+    batchCodeList: {
+      default: "",
+      type: Array,
+    },
+    isShow: {
+      default: false,
+      type: Boolean,
+    },
+  },
+  data() {
+    return {
+      searchFen: "",
+      form: {
+        value2: "",
+      },
+      setUpImgData: [],
+      isShowDescription: false,
+      commentDescriptionVos: [], //评论列表
+      windEngineGroupList: [], //批次风机列表
+      fieldEngineCodes: [], //选中风机
+      comment: "",
+      options: [],
+      zongFaultCsvHeader: [],
+      zongFaultCsvData: [],
+      fenFaultCsvData: [],
+      fenFaultCsvHeader: [],
+      editableTabs: [],
+    };
+  },
+  computed: {
+    // 根据搜索关键字过滤数据
+    filteredFenData() {
+      return (itemCsv) => {
+        // 如果有搜索关键词,则过滤数据
+        if (this.searchFen) {
+          return itemCsv.data.filter((item) => {
+            return item.wind_turbine_name
+              .toLowerCase()
+              .includes(this.searchFen.toLowerCase());
+          });
+        }
+        // 没有搜索关键词时返回所有数据
+        console.log(itemCsv.data);
+        return itemCsv.data;
+      };
+    },
+  },
+  watch: {
+    initBatchCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+    analysisTypeCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+    isShow() {
+      if (this.isShow) {
+        if (!this.isShowDescription) {
+          this.$message({
+            message: "当前分析模型暂无分析,不能进行评论操作",
+            type: "warning",
+          });
+          this.$emit("setIsShow");
+        }
+      }
+    },
+  },
+  mounted() {
+    if (this.initBatchCode && this.analysisTypeCode) {
+      this.fetchData(); // 调用合并后的函数
+    }
+  },
+  methods: {
+    handleClose() {
+      //关闭评论弹框
+      this.$emit("setIsShow");
+    },
+    onSubmit() {
+      console.log("submit!");
+    },
+    async handleComment() {
+      try {
+        await analysisCommentEdit({
+          batchCode: this.initBatchCode,
+          analysisTypeCode: this.analysisTypeCode,
+          commentList: this.editableTabs.map((item) => {
+            return {
+              commentTypeCode: item.commentTypeCode,
+              comment: item.commentTypeName === "分析评论" ? this.comment : "",
+            };
+          }),
+        });
+        this.$message({
+          type: "success",
+          message: "保存成功",
+        });
+        this.comment = "";
+        this.getAnalysisDetail();
+        this.$emit("setIsShow");
+      } catch (e) {
+        console.error(e);
+        this.loading = false;
+      }
+    },
+    // 封装的获取 CSV 数据方法
+    fetchCsvData(analysisType, url) {
+      axios
+        .get(url, { responseType: "blob" }) // 确保数据以 blob 格式返回
+        .then((response) => {
+          const reader = new FileReader();
+          reader.onload = (e) => {
+            const csvText = e.target.result;
+            Papa.parse(csvText, {
+              header: true, // 使用 CSV 第一行作为键
+              complete: (result) => {
+                // 根据分析模型设置不同的数据
+                if (analysisType === "fault") {
+                  if (
+                    Object.keys(result.data[0]).includes("wind_turbine_name")
+                  ) {
+                    //分机型故障统计处理
+                    this.fenFaultCsvHeader.push(Object.keys(result.data[0]));
+
+                    this.fenFaultCsvData.push({
+                      data: result.data
+                        .filter((row) => {
+                          return Object.keys(row).length;
+                        })
+                        .slice(0, result.data.length - 1)
+                        .map((item) => ({
+                          ...item,
+                          count: Number(item.count),
+                          fault_time: Number(item.fault_time) / 3600,
+                        })),
+                    });
+                  }
+                }
+              },
+              error: (error) => {
+                console.error("CSV 解析错误:", error);
+              },
+            });
+          };
+          reader.readAsText(response.data); // 读取 blob 数据
+        })
+        .catch((error) => {
+          console.error("无法获取 CSV 文件:", error);
+        });
+    },
+    // 合并后的函数,处理数据请求
+    async fetchData() {
+      try {
+        // 获取分析详情
+        await this.getAnalysisDetail();
+
+        // 获取风机列表
+        await this.getWindEnfineList(this.initBatchCode, this.analysisTypeCode);
+      } catch (err) {
+        console.error("Failed to fetch data:", err);
+      }
+    },
+    // 获取分析详情接口
+    async getAnalysisDetail() {
+      try {
+        const result = await analysisDetail({
+          batchCode: this.initBatchCode,
+          analysisTypeCode: "fault",
+          fieldEngineCodes:
+            this.fieldEngineCodes.length === 0
+              ? undefined
+              : this.fieldEngineCodes.join(","),
+        });
+        if (result.data.length > 0) {
+          this.isShowDescription = true;
+        }
+        if (
+          result.data &&
+          result.data[0] &&
+          result.data[0].commentTypeRelations
+        ) {
+          this.editableTabs = result.data[0].commentTypeRelations;
+        }
+        //当前评论展示获取
+        if (
+          result.data &&
+          result.data[0] &&
+          result.data[0].commentDescriptionVos
+        ) {
+          this.commentDescriptionVos = result.data[0].commentDescriptionVos;
+        }
+        if (result.data && result.data[0] && result.data[0].generalFiles) {
+          result.data[0].generalFiles.map((item) => {
+            if (item.fileAddr) {
+              this.fenFaultCsvHeader = [];
+              this.fenFaultCsvData = [];
+              this.fetchCsvData("fault", item.fileAddr);
+            }
+          });
+        } else {
+          this.fenFaultCsvHeader = [];
+          this.fenFaultCsvData = [];
+        }
+      } catch (err) {
+        console.error("Failed to fetch analysis details:", err);
+      }
+    },
+    // 请求风机列表
+    async getWindEnfineList(batchCode, analysisTypeCode) {
+      // console.log("请求风机列表 分钟级");
+      const resEngineList = await queryAnalysisedEngine({
+        batchCode: batchCode,
+        analysisTypeCode,
+      });
+      this.windEngineGroupList = resEngineList.data;
+    },
+    handleEditorInput(index, newVal) {
+      // 更新对应的 comment 值
+      // 如果该功能没有实现,可以删除这个方法
+    },
+    //获取选中风机list
+    getEnfineList(data, setUpImg) {
+      this.fieldEngineCodes = data;
+      this.setUpImgData = [...setUpImg];
+      this.getAnalysisDetail();
+    },
+    //下一条
+    handleNext() {
+      const index = this.batchCodeList.findIndex(
+        (item) => item === this.initBatchCode
+      );
+      if (index === this.batchCodeList.length - 1) {
+        this.$message.warning("已经是最后一个分析结果了");
+        return;
+      }
+      this.$emit("setInitBathCode", this.batchCodeList[index + 1]);
+    },
+    //上一条
+    handlePrevious() {
+      const index = this.batchCodeList.findIndex(
+        (item) => item === this.initBatchCode
+      );
+      if (index === 0) {
+        this.$message.warning("没有上一条了");
+        return;
+      }
+      this.$emit("setInitBathCode", this.batchCodeList[index - 1]);
+    },
+    onClick() {},
+  },
+};
+</script>
+
+<style scoped lang="scss">
+::v-deep .el-table::-webkit-scrollbar {
+  display: block !important;
+}
+::v-deep .el-table__body-wrapper::-webkit-scrollbar {
+  display: block !important;
+}
+::v-deep .is-scrolling-left::-webkit-scrollbar {
+  display: block !important;
+}
+
+.type-variable {
+  display: flex;
+  height: 90%;
+  overflow: hidden;
+
+  .left {
+    width: 30%;
+    height: 100%;
+    overflow: auto;
+    padding: 20px;
+    flex: 1;
+    .chartsBox {
+      height: 100%;
+      overflow-y: scroll;
+    }
+    /* 滚动条整体样式 */
+    &::-webkit-scrollbar {
+      width: 6px; /* 滚动条宽度 */
+    }
+
+    /* 滚动条轨道 */
+    &::-webkit-scrollbar-track {
+      background: #f5f7fa;
+      border-radius: 3px;
+    }
+
+    /* 滚动条滑块 */
+    &::-webkit-scrollbar-thumb {
+      background: #c0c4cc;
+      border-radius: 3px;
+    }
+
+    /* 滚动条滑块悬停时 */
+    &::-webkit-scrollbar-thumb:hover {
+      background: #909399;
+    }
+  }
+
+  .right {
+    width: 0px;
+    height: 100%;
+    overflow: hidden;
+  }
+}
+.el-dialog__wrapper {
+  position: relative !important;
+}
+::v-deep .el-dialog {
+  position: fixed !important;
+  z-index: 999 !important;
+  top: 50%;
+  left: 50%;
+  transform: translate(0, -50%);
+}
+</style>

+ 2 - 2
src/views/overview/components/speed_power/index.vue

@@ -1,7 +1,7 @@
 <!--
  * @Author: your name
  * @Date: 2025-01-10 09:09:17
- * @LastEditTime: 2025-07-10 15:00:05
+ * @LastEditTime: 2025-07-24 10:50:17
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/overview/components/speed_power/index.vue
@@ -128,7 +128,7 @@ import FilterChart from "@/views/overview/components/filterChart/index.vue";
 import DDrawingChart from "@/views/performance/components/chartsCom/3DDrawingChart.vue";
 import TinymceEditor from "@/components/Tinymce.vue";
 //分图
-import Time3DChart from "@/views/performance/components/chartsCom/Time3DChart.vue";
+import Time3DChart from "@/views/performance/components/chartsCom/Time3DBarChart.vue";
 import {
   analysisDetail,
   queryAnalysisedEngine,

+ 13 - 0
src/views/overview/components/yaw_error1/index.vue

@@ -0,0 +1,13 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-07-24 17:11:36
+ * @LastEditTime: 2025-07-24 17:14:10
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/overview/components/yaw_error1/index.vue
+-->
+<template>
+  <div>偏航频次及速率</div>
+</template>
+<script></script>
+<style></style>

+ 13 - 0
src/views/overview/components/yaw_error2/index.vue

@@ -0,0 +1,13 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-07-24 17:12:39
+ * @LastEditTime: 2025-07-24 17:14:17
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/overview/components/yaw_error2/index.vue
+-->
+<template>
+  <div>偏航时间占比</div>
+</template>
+<script></script>
+<style></style>

+ 402 - 0
src/views/overview/components/yaw_error3/index.vue

@@ -0,0 +1,402 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-07-24 17:12:55
+ * @LastEditTime: 2025-07-25 09:08:54
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/overview/components/yaw_error3/index.vue
+-->
+<template>
+  <div class="type-variable">
+    <!-- 偏航异常检测 -->
+    <div class="left scroller">
+      <FilterChart
+        :setUpimg="['有功功率']"
+        :windList="windEngineGroupList"
+        @getEnfineList="getEnfineList"
+        @handlePrevious="handlePrevious"
+        @handleNext="handleNext"
+      ></FilterChart>
+      <el-alert type="info" :closable="false">
+        <template v-slot:title>
+          <div style="display: flex; align-items: center">
+            <i
+              class="el-icon-info"
+              style="font-size: 20px; margin-right: 5px"
+            ></i>
+            <h3>分析说明:</h3>
+          </div>
+        </template>
+        <div style="font-size: 12px; margin-top: 10px">
+          大部件温度分析在风电机组的运维和性能管理中扮演着极为关键的角色。以下是温度分析的几个重要作用:
+        </div>
+      </el-alert>
+      <VirtualList
+        :list="[...generalFilesDatas, ...diagramRelationsDatas]"
+        keyField="fileAddr"
+        :itemSize="500"
+        v-slot="{ item, index }"
+      >
+        <div
+          class="titleCharts"
+          v-if="
+            generalFilesDatas &&
+            generalFilesDatas.length > 0 &&
+            item.engineTypeCode &&
+            generalFilesDatas[0].engineTypeCode === item.engineTypeCode
+          "
+        >
+          分析总图 :
+        </div>
+        <LineAndScatter
+          v-if="item.batchCode && !item.fieldEngineCode"
+          :setUpImgData="setUpImgData"
+          :key="item.batchCode + index"
+          :index="`${new Date().getTime()}` + index"
+          :ref="item.batchCode"
+          :fileAddr="item.fileAddr"
+        >
+        </LineAndScatter>
+      </VirtualList>
+      <el-empty
+        description="暂无分析记录"
+        v-if="
+          generalFilesDatas.length === 0 && diagramRelationsDatas.length === 0
+        "
+      ></el-empty>
+      <el-dialog
+        v-if="isShowDescription"
+        title="添加评论"
+        :visible="isShow"
+        width="30%"
+        v-dialogDrag
+        :modal="false"
+        :lock-scroll="false"
+        :modal-append-to-body="false"
+        @close="handleClose"
+      >
+        <el-tabs value="first">
+          <el-tab-pane label="意见描述" name="first">
+            <TinymceEditor
+              ref="editor"
+              v-model="comment"
+              @input="handleEditorInput($event)"
+              @onClick="onClick"
+            >
+            </TinymceEditor>
+          </el-tab-pane>
+        </el-tabs>
+        <el-row
+          type="flex"
+          class="row-bg"
+          justify="end"
+          style="margin: 20px 60px 0 0"
+        >
+          <el-col :span="2">
+            <el-button type="primary" size="small" @click="handleComment"
+              >提交评论</el-button
+            >
+          </el-col>
+        </el-row>
+      </el-dialog>
+    </div>
+    <div class="right" v-if="isShowTinymceEditorCom">
+      <DicCard
+        :batchCode="initBatchCode"
+        :analysisTypeCode="analysisTypeCode"
+        :commentDescriptionVos="commentDescriptionVos"
+      >
+      </DicCard>
+    </div>
+  </div>
+</template>
+<script>
+import DicCard from "@/views/overview/components/dicCard/index.vue";
+import FilterChart from "@/views/overview/components/filterChart/index.vue";
+import LineAndScatter from "@/views/performance/components/chartsCom/LineAndScatter.vue";
+import lineChartsFen from "@/views/performance/components/chartsCom/lineChartsFen.vue";
+import TinymceEditor from "@/components/Tinymce.vue";
+import {
+  analysisDetail,
+  queryAnalysisedEngine,
+  analysisCommentEdit,
+} from "@/api/performance";
+
+export default {
+  name: "temperature_large_components_Winding_tem",
+  components: {
+    DicCard,
+    FilterChart,
+    LineAndScatter,
+    lineChartsFen,
+    TinymceEditor,
+  },
+  props: {
+    initBatchCode: {
+      default: "",
+      type: String,
+    },
+    isShowTinymceEditorCom: {
+      default: true,
+      type: Boolean,
+    },
+    isShow: {
+      default: false,
+      type: Boolean,
+    },
+    analysisTypeCode: {
+      default: "",
+      type: String,
+    },
+    batchCodeList: {
+      default: "",
+      type: Array,
+    },
+  },
+  data() {
+    return {
+      form: {
+        value2: "",
+      },
+      setUpImgData: [],
+      comment: "",
+      options: [],
+      windEngineGroupList: [], //批次风机列表
+      fieldEngineCodes: [], //选中风机
+      generalFilesDatas: [], //总图
+      diagramRelationsDatas: [], //分图
+      commentDescriptionVos: [], //评论列表
+      editableTabs: [],
+      isShowDescription: false,
+    };
+  },
+  watch: {
+    isShow() {
+      if (this.isShow) {
+        if (!this.isShowDescription) {
+          this.$message({
+            message: "当前分析模型暂无分析,不能进行评论操作",
+            type: "warning",
+          });
+          this.$emit("setIsShow");
+        }
+      }
+    },
+    initBatchCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+    analysisTypeCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+  },
+  mounted() {
+    if (this.initBatchCode && this.analysisTypeCode) {
+      this.fetchData(); // 调用合并后的函数
+    }
+  },
+  methods: {
+    handleClose() {
+      //关闭评论弹框
+      this.$emit("setIsShow");
+    },
+    async handleComment() {
+      try {
+        await analysisCommentEdit({
+          batchCode: this.initBatchCode,
+          analysisTypeCode: "temperature_large_components",
+          commentList: this.editableTabs.map((item) => {
+            return {
+              commentTypeCode: item.commentTypeCode,
+              comment: item.commentTypeName === "分析评论" ? this.comment : "",
+            };
+          }),
+        });
+        this.$message({
+          type: "success",
+          message: "保存成功",
+        });
+        this.comment = "";
+        this.getAnalysisDetail();
+        this.$emit("setIsShow");
+      } catch (e) {
+        console.error(e);
+        this.loading = false;
+      }
+    },
+    onSubmit() {
+      console.log("submit!");
+    },
+    // 合并后的函数,处理数据请求
+    async fetchData() {
+      try {
+        // 获取分析详情
+        await this.getAnalysisDetail();
+        // 获取风机列表
+        await this.getWindEnfineList(
+          this.initBatchCode,
+          "temperature_large_components"
+        );
+      } catch (err) {
+        console.error("Failed to fetch data:", err);
+      }
+    },
+    // 获取分析详情接口
+    async getAnalysisDetail() {
+      try {
+        const result = await analysisDetail({
+          batchCode: this.initBatchCode,
+          analysisTypeCode: "temperature_large_components",
+          fieldEngineCodes:
+            this.fieldEngineCodes.length === 0
+              ? undefined
+              : this.fieldEngineCodes.join(","),
+        });
+        if (result.data.length > 0) {
+          this.isShowDescription = true;
+        }
+        if (
+          result.data &&
+          result.data[0] &&
+          result.data[0].commentTypeRelations
+        ) {
+          this.editableTabs = result.data[0].commentTypeRelations;
+        }
+        //当前评论展示获取
+        if (
+          result.data &&
+          result.data[0] &&
+          result.data[0].commentDescriptionVos
+        ) {
+          this.commentDescriptionVos = result.data[0].commentDescriptionVos;
+        }
+        this.generalFilesDatas =
+          (result.data &&
+            result.data[0] &&
+            result.data[0].generalFiles &&
+            result.data[0].generalFiles
+              .filter((item) => item.fileAddr.endsWith(".json"))
+              .filter((item) =>
+                item.fileAddr.includes("generator_winding1_temperature")
+              )) ||
+          []; //总图数据
+        this.diagramRelationsDatas =
+          (result.data &&
+            result.data[0] &&
+            result.data[0].diagramRelations &&
+            result.data[0].diagramRelations
+              .filter((item) => item.fileAddr.endsWith(".json"))
+              .filter((item) =>
+                item.fileAddr.includes("generator_winding1_temperature")
+              )) ||
+          [];
+      } catch (err) {
+        console.error("Failed to fetch analysis details:", err);
+      }
+    },
+
+    // 请求风机列表
+    async getWindEnfineList(batchCode, analysisTypeCode) {
+      // console.log("请求风机列表 分钟级");
+      const resEngineList = await queryAnalysisedEngine({
+        batchCode: batchCode,
+        analysisTypeCode,
+      });
+      this.windEngineGroupList = resEngineList.data;
+    },
+    handleEditorInput(index, newVal) {
+      // 更新对应的 comment 值
+      // 如果该功能没有实现,可以删除这个方法
+    },
+    //获取选中风机list
+    getEnfineList(data, setUpImg) {
+      this.fieldEngineCodes = data;
+      this.setUpImgData = [...setUpImg];
+      this.getAnalysisDetail();
+    },
+    //下一条
+    handleNext() {
+      const index = this.batchCodeList.findIndex(
+        (item) => item === this.initBatchCode
+      );
+      if (index === this.batchCodeList.length - 1) {
+        this.$message.warning("已经是最后一个分析结果了");
+        return;
+      }
+      this.$emit("setInitBathCode", this.batchCodeList[index + 1]);
+    },
+    //上一条
+    handlePrevious() {
+      const index = this.batchCodeList.findIndex(
+        (item) => item === this.initBatchCode
+      );
+      if (index === 0) {
+        this.$message.warning("没有上一条了");
+        return;
+      }
+      this.$emit("setInitBathCode", this.batchCodeList[index - 1]);
+    },
+    onClick() {},
+  },
+};
+</script>
+<style scoped lang="scss">
+.type-variable {
+  display: flex;
+  height: 90%;
+  overflow: hidden;
+
+  .left {
+    width: 30%;
+    height: 100%;
+    overflow: auto;
+    padding: 20px;
+    flex: 1;
+    /* 滚动条整体样式 */
+    &::-webkit-scrollbar {
+      width: 6px; /* 滚动条宽度 */
+    }
+
+    /* 滚动条轨道 */
+    &::-webkit-scrollbar-track {
+      background: #f5f7fa;
+      border-radius: 3px;
+    }
+
+    /* 滚动条滑块 */
+    &::-webkit-scrollbar-thumb {
+      background: #c0c4cc;
+      border-radius: 3px;
+    }
+
+    /* 滚动条滑块悬停时 */
+    &::-webkit-scrollbar-thumb:hover {
+      background: #909399;
+    }
+  }
+
+  .right {
+    width: 0px;
+    height: 100%;
+    overflow: hidden;
+  }
+}
+.el-dialog__wrapper {
+  position: relative !important;
+}
+::v-deep .el-dialog {
+  position: fixed !important;
+  z-index: 999 !important;
+  top: 50%;
+  left: 50%;
+  transform: translate(0, -50%);
+}
+.titleCharts {
+  font-size: 16px;
+  font-weight: 500;
+  margin-top: 20px;
+}
+</style>

+ 13 - 0
src/views/overview/components/yaw_error4/index.vue

@@ -0,0 +1,13 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-07-24 17:13:23
+ * @LastEditTime: 2025-07-24 17:14:33
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/overview/components/yaw_error4/index.vue
+-->
+<template>
+  <div>偏航余压</div>
+</template>
+<script></script>
+<style></style>

+ 169 - 0
src/views/overview/demo.vue

@@ -0,0 +1,169 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-07-24 13:52:03
+ * @LastEditTime: 2025-07-24 13:52:04
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/overview/demo.vue
+-->
+<template>
+  <div>
+    <div :id="'echarts-3d-bar'" style="width: 100%; height: 600px"></div>
+  </div>
+</template>
+
+<script>
+import echarts from "echarts";
+import "echarts-gl";
+
+export default {
+  name: "ThreeDBarChart",
+  data() {
+    return {
+      chart: null,
+      chartData: {
+        xaixs: "设备编号",
+        yaixs: "月份",
+        zaixs: "发电量",
+        data: [
+          {
+            xData: ["A01", "A01", "A02", "A02", "A03", "A03"],
+            yData: [
+              "2024-01-01",
+              "2024-02-01",
+              "2024-01-01",
+              "2024-02-01",
+              "2024-01-01",
+              "2024-02-01",
+            ],
+            zData: [100, 120, 90, 150, 110, 130],
+          },
+        ],
+      },
+      color1: ["#5470C6", "#91CC75", "#EE6666", "#FAC858", "#73C0DE"],
+    };
+  },
+  mounted() {
+    this.renderEcharts3DBarChart();
+    window.addEventListener("resize", this.handleResize);
+  },
+  beforeDestroy() {
+    window.removeEventListener("resize", this.handleResize);
+    if (this.chart) this.chart.dispose();
+  },
+  methods: {
+    formatDate(dateStr) {
+      const d = new Date(dateStr);
+      const year = d.getFullYear();
+      const month = (d.getMonth() + 1).toString().padStart(2, "0");
+      return `${year}-${month}`;
+    },
+    renderEcharts3DBarChart() {
+      const el = document.getElementById("echarts-3d-bar");
+      this.chart = echarts.init(el);
+
+      const { xData, yData, zData } = this.chartData.data[0];
+      const data = [];
+
+      for (let i = 0; i < xData.length; i++) {
+        data.push({
+          value: [xData[i], this.formatDate(yData[i]), zData[i]],
+        });
+      }
+
+      const xAxisList = [...new Set(xData)];
+      const yAxisList = [...new Set(yData.map(this.formatDate))];
+
+      const monthColorMap = {};
+      yAxisList.forEach((month, i) => {
+        monthColorMap[month] = this.color1[i % this.color1.length];
+      });
+
+      const option = {
+        tooltip: {
+          formatter: (params) => {
+            const [x, y, z] = params.value;
+            return `
+                ${this.chartData.xaixs}: ${x}<br/>
+                ${this.chartData.yaixs}: ${y}<br/>
+                ${this.chartData.zaixs}: ${z}
+              `;
+          },
+        },
+        xAxis3D: {
+          type: "category",
+          name: this.chartData.xaixs,
+          data: xAxisList,
+          nameGap: 25,
+          nameTextStyle: {
+            fontWeight: "bold",
+            fontSize: 14,
+          },
+        },
+        yAxis3D: {
+          type: "category",
+          name: this.chartData.yaixs,
+          data: yAxisList,
+          nameGap: 25,
+          nameTextStyle: {
+            fontWeight: "bold",
+            fontSize: 14,
+          },
+        },
+        zAxis3D: {
+          type: "value",
+          name: this.chartData.zaixs,
+          nameTextStyle: {
+            fontWeight: "bold",
+            fontSize: 14,
+          },
+        },
+        grid3D: {
+          boxWidth: 150,
+          boxDepth: 80,
+          viewControl: {
+            projection: "orthographic",
+            autoRotate: false,
+          },
+          light: {
+            main: {
+              intensity: 1.2,
+              shadow: true,
+            },
+            ambient: {
+              intensity: 0.3,
+            },
+          },
+        },
+        series: [
+          {
+            type: "bar3D",
+            shading: "lambert",
+            barSize: 10,
+            data: data.map((item) => ({
+              value: item.value,
+              itemStyle: {
+                color: monthColorMap[item.value[1]],
+                borderColor: "#000",
+                borderWidth: 1,
+                opacity: 0.9,
+              },
+            })),
+          },
+        ],
+      };
+
+      this.chart.setOption(option);
+    },
+    handleResize() {
+      if (this.chart) {
+        this.chart.resize();
+      }
+    },
+  },
+};
+</script>
+
+<style scoped>
+/* 可选样式 */
+</style>

+ 1 - 0
src/views/overview/index.vue

@@ -404,6 +404,7 @@ export default {
     },
     // 菜单选中项的事件
     handleSelect(key, keyPath) {
+      console.log(key, "分析模型");
       this.analysisTypeCode = key;
       this.activeIndex = key;
       this.currentComponent = () => import(`./components/${key}/index.vue`);

+ 8 - 4
src/views/performance/components/chartsCom/FaultUnit.vue

@@ -22,6 +22,10 @@ export default {
       type: Array,
       default: () => [],
     },
+    faultTitledata: {
+      type: Array,
+      default: () => [],
+    },
   },
   watch: {
     faultCounts: {
@@ -72,17 +76,17 @@ export default {
           sizemin: 4,
           showscale: false,
         },
-        hovertemplate: `机组: ${d.name}<br>故障时长: ${d.durationHour} 小时<br>故障次数: ${d.count} 次<extra></extra>`,
+        hovertemplate: `机组: ${d.name}<br>${this.faultTitledata[2]}: ${d.durationHour} 小时<br>${this.faultTitledata[3]}: ${d.count} 次<extra></extra>`,
       }));
 
       // 其他布局配置保持不变
       const layout = {
         title: {
-          text: "机组故障时长与故障次数分析",
+          text: `${this.faultTitledata[0]}`,
           font: { size: 16, weight: "bold" },
         },
         xaxis: {
-          title: "故障机组",
+          title: `${this.faultTitledata[1]}`,
           type: "category",
           tickangle: 30,
           tickfont: { size: 12 },
@@ -92,7 +96,7 @@ export default {
           showbackground: true,
         },
         yaxis: {
-          title: "故障时长(小时)",
+          title: `${this.faultTitledata[2]}`,
           tickfont: { size: 12 },
           gridcolor: "rgb(255,255,255)",
           tickcolor: "rgb(255,255,255)",

+ 348 - 0
src/views/performance/components/chartsCom/LineAndScatter.vue

@@ -0,0 +1,348 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-07-24 17:30:10
+ * @LastEditTime: 2025-07-25 09:20:20
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/performance/components/chartsCom/LineAndScatter.vue
+-->
+<template>
+  <div>
+    <!-- 图表控制面板 总图-->
+    <div style="display: flex; align-items: center">
+      <el-select
+        size="small"
+        v-model="color1"
+        @change="updateChartColor"
+        placeholder="选择配色方案"
+        style="width: 200px"
+      >
+        <el-option
+          v-for="(scheme, index) in colorSchemes"
+          :key="index"
+          :label="scheme.label"
+          :value="scheme.colors"
+        >
+          <span
+            v-for="color in scheme.colors.slice(0, 8)"
+            :style="{
+              background: color,
+              width: '20px',
+              height: '20px',
+              display: 'inline-block',
+            }"
+          ></span>
+        </el-option>
+      </el-select>
+      <div>
+        <el-button size="small" @click="toggleChartType">
+          切换为{{ chartType === "line" ? "面积图" : "折线图" }}
+        </el-button>
+      </div>
+    </div>
+
+    <!-- 图表容器 -->
+    <div
+      v-loading="loading"
+      :id="`bar-chart${index}`"
+      :ref="`bar-chart${index}`"
+      style="width: 100%; height: 400px"
+    >
+      <el-empty v-if="isError" description="请求失败"></el-empty>
+    </div>
+  </div>
+</template>
+
+<script>
+import { nextTick } from "vue"; // 导入 nextTick
+import Plotly from "plotly.js-dist";
+import axios from "axios";
+import { colorSchemes } from "@/views/overview/js/colors";
+import { myMixin } from "@/mixins/chartRequestMixin"; // 假设你需要的 mixin
+import { mapState } from "vuex";
+export default {
+  props: {
+    fileAddr: {
+      type: String,
+      default: "",
+    },
+    index: {
+      type: String,
+      default() {
+        return "0";
+      },
+    },
+    setUpImgData: {
+      default: () => [],
+      type: Array,
+    },
+  },
+  mixins: [myMixin],
+  data() {
+    return {
+      chartData: {},
+      chartType: "line", // 默认图表类型是折线图
+      color1: [], // 默认颜色
+      // 配色方案列表(每个方案是一个颜色数组)
+      colorSchemes: colorSchemes,
+      loading: false,
+      isError: false,
+      colors: [...colorSchemes[0].colors],
+    };
+  },
+  computed: {
+    ...mapState("themes", {
+      themeColor: "themeColor",
+    }),
+  },
+  watch: {
+    themeColor: {
+      handler(newVal, oldVal) {
+        if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
+          this.color1 = newVal;
+          this.updateChartColor();
+        }
+      },
+      deep: true,
+    },
+    setUpImgData: {
+      handler(newVal, oldVal) {
+        if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
+          this.drawChart();
+        }
+      },
+      deep: true,
+    },
+  },
+  mounted() {
+    if (this.fileAddr) {
+      this.$nextTick(() => {
+        this.color1 = this.colorSchemes[0].colors;
+        this.getData();
+      });
+    }
+  },
+  methods: {
+    // 获取数据
+    async getData() {
+      if (this.fileAddr !== "") {
+        try {
+          this.loading = true;
+          this.cancelToken = axios.CancelToken.source();
+          const resultChartsData = await axios.get(this.fileAddr, {
+            cancelToken: this.cancelToken.token,
+          });
+          this.chartData = resultChartsData.data;
+
+          // 使用 nextTick 来确保 DOM 渲染完成后绘制图表
+          nextTick(() => {
+            this.drawChart();
+          });
+          this.isError = false;
+          this.loading = false;
+        } catch (error) {
+          console.error("Error loading data:", error);
+          this.isError = true;
+          this.loading = false;
+        }
+      }
+    },
+    // 绘制图表
+    drawChart() {
+      if (!this.$refs[`bar-chart${this.index}`]) {
+        return false;
+      }
+      const data = [];
+      const newData =
+        this.chartData.analysisTypeCode === "风电机组叶尖速比和风速分析"
+          ? this.chartData &&
+            this.chartData.data &&
+            JSON.parse(JSON.stringify(this.chartData.data)).sort((a, b) => {
+              return a.engineName.localeCompare(b.engineName);
+            })
+          : JSON.parse(JSON.stringify(this.chartData.data));
+      newData.forEach((turbine, index) => {
+        // 判断图表类型,根据类型调整绘制方式
+        const chartConfig = {
+          x: turbine.xData, // X 数据
+          y: turbine.yData, // Y 数据
+          name: turbine.engineName, // 使用机组名称
+          type: "scattergl", // 使用散点图
+          //   line: {
+          //     color:
+          //       this.color1.length > 0
+          //         ? this.color1[index % this.color1.length]
+          //         : this.colors[index % this.colors.length], // 为每个机组分配不同的颜色
+          //   },
+          marker: {
+            color:
+              this.color1.length > 0
+                ? this.color1[index % this.color1.length]
+                : this.colors[index % this.colors.length], // 为每个机组分配不同的颜色
+            size: 12,
+            symbol: "x", // 也可以是 "circle", "diamond", "x", etc.
+          },
+          hovertemplate:
+            `${this.chartData.xaixs}:` +
+            ` %{x} <br> ` +
+            `${this.chartData.yaixs}:` +
+            "%{y} <br>",
+        };
+
+        if (this.chartType === "line") {
+          //   chartConfig.mode = "lines"; // 如果是折线图
+          chartConfig.fill = "none";
+        } else if (this.chartType === "bar") {
+          // chartConfig.type = "bar"; // 如果是柱状图
+          chartConfig.fill = "tonexty";
+        }
+
+        data.push(chartConfig);
+      });
+
+      const layout = {
+        title: {
+          text: this.chartData.title || this.chartData.data[0].title,
+          font: {
+            size: 16, // 设置标题字体大小(默认 16)
+            weight: "bold",
+          },
+        },
+        xaxis: {
+          title: this.chartData.xaixs || "X轴", // 横坐标标题
+          gridcolor: "rgb(255,255,255)",
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+          dtick: this.chartData.xaixs === "风速" ? 1 : undefined,
+          range:
+            this.chartData.analysisTypeCode === "风电机组风能利用系数分析" &&
+            this.chartData.contract_Cp_curve_xData
+              ? [
+                  0,
+                  Math.max(
+                    ...this.chartData.contract_Cp_curve_xData
+                      .map(Number)
+                      .filter((val) => !isNaN(val))
+                  ) * 0.9,
+                ]
+              : undefined,
+        },
+        yaxis: {
+          title: this.chartData.yaixs || "Y轴", // 纵坐标标题
+          gridcolor: "rgb(255,255,255)",
+          tickcolor: "rgb(255,255,255)",
+          backgroundcolor: "#e5ecf6",
+          range:
+            this.chartData.analysisTypeCode === "风电机组风能利用系数分析"
+              ? [0, 1.5]
+              : undefined,
+        },
+        margin: {
+          l: 50,
+          r: 50,
+          t: 50,
+          b: 50,
+        },
+        plot_bgcolor: "#e5ecf6",
+        gridcolor: "#fff",
+        bgcolor: "#e5ecf6", // 设置背景颜色
+        autosize: true, // 开启自适应
+        // barmode: this.chartType === "bar" ? "stack" : "group", // 如果是柱状图则启用堆叠
+      };
+      const getChartSetUp = (axisTitle) => {
+        return this.setUpImgData.find((item) => item.text.includes(axisTitle));
+      };
+      const xChartSetUp = getChartSetUp(layout.xaxis.title);
+      if (xChartSetUp) {
+        layout.xaxis.dtick = xChartSetUp.dtick;
+        layout.xaxis.range = [xChartSetUp.min, xChartSetUp.max];
+      }
+      const yChartSetUp = getChartSetUp(layout.yaxis.title);
+      if (yChartSetUp) {
+        layout.yaxis.dtick = yChartSetUp.dtick;
+        layout.yaxis.range = [yChartSetUp.min, yChartSetUp.max];
+      }
+
+      // 使用 Plotly.react 来更新图表
+      Plotly.react(`bar-chart${this.index}`, data, layout, {
+        responsive: true,
+        modeBarButtonsToRemove: [
+          // 移除不需要的工具按钮
+          "lasso2d",
+          "sendDataToCloud",
+          "resetCameraLastSave3d",
+          "resetCameraDefault3d",
+          "resetCameraLastSave",
+          "sendDataToCloud",
+          "zoom2d", // 缩放按钮
+          "zoom3d",
+          "plotlylogo2D",
+          "plotlylogo3D",
+        ],
+        displaylogo: false,
+      }).then(function (gd) {
+        // 获取工具栏按钮
+        const toolbar = gd.querySelector(".modebar");
+        const buttons = toolbar.querySelectorAll(".modebar-btn");
+
+        // 定义一个映射对象,方便修改按钮提示
+        const titleMap = {
+          "Download plot as a png": "保存图片",
+          Autoscale: "缩放",
+          Pan: "平移",
+          "Zoom out": "缩小",
+          "Zoom in": "放大",
+          "Box Select": "选择框操作",
+          "Lasso Select": "套索选择操作",
+          "Reset axes": "重置操作",
+          "Reset camera to default": "重置相机视角",
+          "Turntable rotation": "转台式旋转",
+          "Orbital rotation": "轨道式旋转",
+        };
+
+        // 遍历所有按钮,修改它们的 title
+        buttons.forEach(function (button) {
+          const dataTitle = button.getAttribute("data-title");
+
+          // 如果标题匹配,修改属性值
+          if (titleMap[dataTitle]) {
+            button.setAttribute("data-title", titleMap[dataTitle]);
+          }
+        });
+      });
+    },
+
+    // 切换图表类型
+    toggleChartType() {
+      this.chartType = this.chartType === "line" ? "bar" : "line"; // 切换图表类型
+      this.drawChart(); // 重新绘制图表
+    },
+
+    // 更新图表颜色
+    updateChartColor() {
+      this.drawChart(); // 更新颜色后重新绘制图表
+    },
+    // 根据配色方案设置每个选项的样式
+    getOptionStyle(scheme) {
+      return {
+        background: `linear-gradient(to right, ${scheme
+          .slice(0, 8)
+          .join(", ")})`,
+        color: "#fff",
+        height: "30px",
+        lineHeight: "30px",
+        borderRadius: "0px",
+      };
+    },
+  },
+  beforeUnmount() {
+    if (this.cancelToken) {
+      this.cancelToken.cancel("组件卸载,取消请求");
+    }
+  },
+};
+</script>
+
+<style scoped>
+/* 样式可以根据需求自定义 */
+</style>

+ 504 - 0
src/views/performance/components/chartsCom/Time3DBarChart.vue

@@ -0,0 +1,504 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-07-24 10:33:42
+ * @LastEditTime: 2025-07-24 16:56:39
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/performance/components/chartsCom/Time3DBarChart.vue
+-->
+<template>
+  <div>
+    <!-- 配色方案选择和图表类型切换 -->
+    <div style="display: flex; align-items: center; padding-top: 20px">
+      <div style="margin-right: 20px; display: flex; align-items: center">
+        <el-select
+          size="small"
+          v-model="color1"
+          @change="updateChartColor"
+          placeholder="选择配色方案"
+          style="width: 200px"
+        >
+          <el-option
+            v-for="(scheme, index) in colorSchemes"
+            :key="index"
+            :label="scheme.label"
+            :value="scheme.colors"
+          >
+            <span
+              v-for="color in scheme.colors.slice(0, 8)"
+              :style="{
+                background: color,
+                width: '20px',
+                height: '20px',
+                display: 'inline-block',
+              }"
+            ></span>
+          </el-option>
+        </el-select>
+      </div>
+      <!-- 点大小控制 -->
+      <div style="display: flex; align-items: center">
+        <el-slider
+          v-model="pointSize"
+          :min="1"
+          :max="15"
+          :step="1"
+          label="点的大小"
+          show-stops
+          style="width: 150px"
+          @change="updateChartColor"
+        ></el-slider>
+      </div>
+    </div>
+
+    <div
+      v-loading="loading"
+      :id="`plotly-3d-chart-` + index"
+      ref="plotlyChart"
+      style="height: 600px; background-color: #e5ecf6"
+    ></div>
+  </div>
+</template>
+
+<script>
+import Plotly, { doCamera } from "plotly.js-dist";
+import axios from "axios";
+import { myMixin } from "@/mixins/chartRequestMixin";
+import { colorSchemes } from "@/views/overview/js/colors";
+import { mapState } from "vuex";
+export default {
+  props: {
+    fileAddr: {
+      default: "",
+      type: String,
+    },
+    index: {
+      default: "",
+      type: String,
+    },
+    setUpImgData: {
+      default: () => [],
+      type: Array,
+    },
+  },
+
+  data() {
+    return {
+      color1: [], // 默认颜色
+      chartData: {},
+      chartType: "scatter", // 当前图表类型(默认是散点图)
+      colorSchemes: [...colorSchemes],
+      pointSize: 1, // 默认点大小
+    };
+  },
+  mixins: [myMixin],
+  computed: {
+    ...mapState("themes", {
+      themeColor: "themeColor",
+    }),
+  },
+  watch: {
+    themeColor: {
+      handler(newval) {
+        if (newval.length === 0) {
+          this.color1 = this.colorSchemes[0].colors;
+        } else {
+          this.color1 = newval;
+        }
+        this.updateChartColor();
+      },
+      deep: true,
+    },
+    setUpImgData: {
+      handler(newType) {
+        this.renderChart();
+      },
+      deep: true,
+    },
+  },
+  async mounted() {
+    this.$nextTick(() => {
+      this.getData();
+      this.color1 = this.colorSchemes[0].colors;
+    });
+  },
+
+  methods: {
+    async getData() {
+      if (this.fileAddr !== "") {
+        try {
+          this.loading = true;
+          this.cancelToken = axios.CancelToken.source();
+          const resultChartsData = await axios.get(this.fileAddr, {
+            cancelToken: this.cancelToken.token,
+          });
+          if (typeof resultChartsData.data === "string") {
+            let dataString = resultChartsData.data;
+            dataString = dataString.trim(); // 去除前后空格
+            dataString = dataString.replace(/Infinity/g, '"Infinity"'); // 处理无效字符
+            try {
+              const parsedData = JSON.parse(dataString);
+              this.chartData = parsedData;
+            } catch (error) {
+              console.error("JSON 解析失败:", error);
+            }
+          } else {
+            this.chartData = resultChartsData.data;
+            // this.chartData = {
+            //   xaixs: "故障代码",
+            //   yaixs: "时间",
+            //   zaixs: "出现次数",
+            //   data: [
+            //     {
+            //       xData: ["A01", "A01", "A02", "A02", "A03", "A03"],
+            //       yData: [
+            //         "2024-01-01",
+            //         "2024-02-01",
+            //         "2024-03-01",
+            //         "2024-04-01",
+            //         "2024-05-01",
+            //         "2024-06-01",
+            //       ],
+            //       zData: [100, 120, 90, 150, 110, 130],
+            //     },
+            //   ],
+            // };
+          }
+          this.renderChart();
+          this.isError = false;
+          this.loading = false;
+        } catch (error) {
+          this.isError = true;
+          this.loading = false;
+        }
+      }
+    },
+    // 格式化日期为 YY-MM 格式
+    formatDate(dateString) {
+      const date = new Date(dateString);
+      const year = date.getFullYear(); // 获取年份后两位
+      const month = ("0" + (date.getMonth() + 1)).slice(-2); // 获取月份并确保两位数
+      return `${year}-${month}`;
+    },
+    renderChart() {
+      // 提取 Y 轴数据中的月份,并去重
+      const uniqueMonths = Array.from(
+        new Set(
+          this.chartData.data[0].yData.map((date) => this.formatDate(date))
+        )
+      );
+      if (!this.color1) {
+        this.color1 = colorSchemes[0].colors;
+      }
+      // 设置每个月份对应的颜色
+      const monthColors = this.color1;
+      // 为每个月份生成独立的 trace,每个 trace 对应一个月份
+      const barTraces = [];
+      const borderTraces = [];
+
+      uniqueMonths.forEach((month, monthIndex) => {
+        const monthData = this.chartData.data[0].yData
+          .map((date, index) => (this.formatDate(date) === month ? index : -1))
+          .filter((index) => index !== -1);
+
+        const x = [],
+          y = [],
+          z = [];
+        const bx = [],
+          by = [],
+          bz = [];
+
+        monthData.forEach((index) => {
+          const xVal = this.chartData.data[0].xData[index];
+          const yVal = this.chartData.data[0].yData[index];
+          const zVal = this.chartData.data[0].zData[index];
+
+          // 数据柱体
+          x.push(xVal, xVal, null);
+          y.push(yVal, yVal, null);
+          z.push(0, zVal, null);
+        });
+
+        // 柱体 trace
+        barTraces.push({
+          type: "scatter3d",
+          mode: "lines",
+          x,
+          y,
+          z,
+          line: {
+            color: monthColors[monthIndex],
+            width: 16,
+          },
+          name: month,
+          legendgroup: `month-${monthIndex}`,
+          hovertemplate:
+            `${this.chartData.xaixs}: %{x}<br>` +
+            `${this.chartData.yaixs}: %{y}<br>` +
+            `${this.chartData.zaixs}: %{z}<extra></extra>`,
+        });
+      });
+
+      // 合并 trace 并绘图
+      const traces = [...borderTraces, ...barTraces]; // 边框放前面绘制
+
+      const layout = {
+        title: {
+          text: this.chartData.data[0].title,
+          font: {
+            size: 16,
+            weight: "bold",
+          },
+        },
+        scene: {
+          xaxis: {
+            showgrid: true, // 显示网格
+            zeroline: false, // 不显示零线
+            title: {
+              text: this.chartData.xaixs,
+              standoff: 100,
+            },
+            gridcolor: "#fff",
+            backgroundcolor: "#e0e7f1",
+            showbackground: true,
+            linecolor: "black",
+            ticks: "outside",
+            ticklen: 10,
+            tickcolor: "black",
+            zeroline: false,
+            tickangle: -10,
+            dtick: this.chartData.xaixs === "风速(m/s)" ? 1 : undefined,
+          },
+          // 对 Y 轴不显示默认标题,只保留 tick 标签,并适当加大 standoff 以防止标签挤在一起
+          yaxis: {
+            title: {
+              text: this.chartData.yaixs, // 隐藏默认标题
+            },
+            showgrid: true, // 显示网格
+            zeroline: false, // 不显示零线
+            type: "category", // 让 Y 轴按类别均匀分布
+            categoryorder: "category ascending", // 按类别字母顺序排列
+            type: "date",
+            tickformat: "%Y-%m",
+            // dtick: "M3",//显式设置每3个月一个刻度
+            gridcolor: "#fff",
+            tickcolor: "#e5ecf6",
+            backgroundcolor: "#e0e7f1",
+            showbackground: true,
+            linecolor: "black",
+            ticks: "outside",
+            tickcolor: "black",
+            zeroline: false,
+            tickangle: 25,
+            nticks: 3,
+          },
+          zaxis: {
+            title: {
+              text: this.chartData.zaixs,
+            },
+            gridcolor: "#fff",
+            tickcolor: "#fff",
+            backgroundcolor: "#e0e7f1",
+            showbackground: true,
+            linecolor: "black",
+            ticks: "outside",
+            tickcolor: "black",
+            zeroline: false,
+            tickangle: -90,
+            nticks: 3,
+          },
+          bgcolor: "#e5ecf6",
+          aspectratio: {
+            x: 2.2,
+            y: 1.7,
+            z: 1,
+          },
+          aspectmode: "manual",
+          gridcolor: "#fff",
+          camera: {
+            up: {
+              x: 0.200292643688136,
+              y: 0.2488259353493132,
+              z: 0.947612004346693,
+            },
+            center: {
+              x: -0.052807476121180814,
+              y: 0.02451796399554085,
+              z: -0.022911006648570736,
+            },
+            eye: {
+              x: -2.126379643342493,
+              y: -2.551422475965373,
+              z: 1.0917667684145647,
+            },
+            projection: {
+              type: "orthographic",
+            },
+          },
+        },
+        margin: { t: 50, b: 50, l: 50, r: 50 },
+        staticPlot: false,
+        showlegend: true,
+        legend: {
+          itemsizing: "constant", // ✅ 统一图例 marker 大小
+          font: {
+            size: 12,
+          },
+          marker: {
+            size: 10,
+          },
+        },
+      };
+
+      const config = {
+        modeBarButtonsToAdd: [
+          {
+            name: "还原", // 自定义按钮
+            icon: Plotly.Icons.home,
+            click: () => this.resetCamera(),
+          },
+        ],
+        modeBarButtonsToRemove: [
+          "sendDataToCloud",
+          "resetCameraLastSave3d",
+          "resetCameraDefault3d",
+          "resetCameraLastSave",
+          "sendDataToCloud",
+          "zoom2d", // 缩放按钮
+          "zoom3d",
+          "plotlylogo2D",
+          "plotlylogo3D",
+        ],
+        responsive: true,
+        displaylogo: false, // 可选:隐藏 Plotly logo
+      };
+      // 获取x轴和y轴的设置
+      const getChartSetUp = (axisTitle) => {
+        return this.setUpImgData.find((item) => item.text.includes(axisTitle));
+      };
+      // 更新x轴和y轴的范围与步长
+      const xChartSetUp = getChartSetUp(layout.scene.xaxis.title);
+      if (xChartSetUp) {
+        layout.scene.xaxis.dtick = xChartSetUp.dtick;
+        layout.scene.xaxis.range = [xChartSetUp.min, xChartSetUp.max];
+      }
+      const yChartSetUp = getChartSetUp(layout.scene.yaxis.title);
+      if (yChartSetUp) {
+        layout.scene.yaxis.dtick = yChartSetUp.dtick;
+        layout.scene.yaxis.range = [yChartSetUp.min, yChartSetUp.max];
+      }
+      const zChartSetUp = getChartSetUp(layout.scene.zaxis.title);
+      if (zChartSetUp) {
+        layout.scene.zaxis.dtick = zChartSetUp.dtick;
+        layout.scene.zaxis.range = [zChartSetUp.min, zChartSetUp.max];
+      }
+      Plotly.newPlot(
+        `plotly-3d-chart-` + this.index,
+        traces,
+        layout,
+        config
+      ).then(function (gd) {
+        // 获取工具栏按钮
+        const toolbar = gd.querySelector(".modebar");
+        const buttons = toolbar.querySelectorAll(".modebar-btn");
+
+        // 定义一个映射对象,方便修改按钮提示
+        const titleMap = {
+          "Download plot as a png": "保存图片",
+          Autoscale: "缩放",
+          Pan: "平移",
+          "Zoom out": "缩小",
+          "Zoom in": "放大",
+          "Box Select": "选择框操作",
+          "Lasso Select": "套索选择操作",
+          "Reset axes": "重置操作",
+          "Reset camera to default": "重置相机视角",
+          "Turntable rotation": "转台式旋转",
+          "Orbital rotation": "轨道式旋转",
+        };
+
+        // 遍历所有按钮,修改它们的 title
+        buttons.forEach(function (button) {
+          const dataTitle = button.getAttribute("data-title");
+          // 如果标题匹配,修改属性值
+          if (titleMap[dataTitle]) {
+            button.setAttribute("data-title", titleMap[dataTitle]);
+          }
+        });
+      });
+      // 监听图表的 relayout 事件,获取并输出相机视角
+      const plotElement = document.getElementById(
+        `plotly-3d-chart-` + this.index
+      );
+      plotElement.on("plotly_relayout", function (eventData) {
+        // 在每次布局变更时,打印当前相机视角
+        if (eventData["scene.camera"]) {
+          console.log(
+            "当前相机视角:",
+            eventData["scene.camera"],
+            eventData["scene.aspectratio"]
+          );
+        }
+      });
+    },
+    // 还原视角
+    resetCamera() {
+      Plotly.relayout(`plotly-3d-chart-` + this.index, {
+        "scene.camera": {
+          up: {
+            x: 0.200292643688136,
+            y: 0.2488259353493132,
+            z: 0.947612004346693,
+          },
+          center: {
+            x: -0.052807476121180814,
+            y: 0.02451796399554085,
+            z: -0.022911006648570736,
+          },
+          eye: {
+            x: -2.126379643342493,
+            y: -2.551422475965373,
+            z: 1.0917667684145647,
+          },
+          projection: {
+            type: "orthographic",
+          },
+        },
+        "scene.aspectratio": {
+          x: 2.2,
+          y: 1.7,
+          z: 1,
+        },
+      });
+    },
+    updateChartColor() {
+      this.renderChart(); // 当配色方案或点大小发生变化时重新渲染图表
+    },
+
+    // 获取配色选项样式
+    getOptionStyle(scheme) {
+      return {
+        background: `linear-gradient(to right, ${scheme
+          .slice(0, 8)
+          .join(", ")})`,
+        color: "#fff",
+        height: "30px",
+        lineHeight: "30px",
+        borderRadius: "0px",
+      };
+    },
+  },
+};
+</script>
+
+<style scoped>
+/* 样式可以根据需求自定义 */
+#plotly-3d-chart {
+  width: 100%;
+  height: 600px;
+}
+
+::v-deep canvas {
+  /* height: 400px !important; */
+}
+</style>

+ 28 - 28
vue.config.js

@@ -63,11 +63,12 @@ module.exports = {
     // contentBase: path.join(__dirname, "public"),
     proxy: {
       "/api": {
+        target: process.env.VUE_APP_APIPROXY,
         // target: "http://192.168.5.4:16200", // 石月
         // target: "http://192.168.50.235:16200", // 内网
         // target: "http://192.168.5.15:16200",//陈
-        target: "http://192.168.50.235:16600", //演示环境//16600
-        // target: "http://106.120.102.238:26500", //外网演示环境
+        // target: "http://192.168.50.235:16600", //演示环境//16600
+        // target: "http://106.120.102.238:18998/api", //外网演示环境
         // target: "http://106.120.102.238:16700", // 外网16700  生产16600
         // target: "http://10.96.137.5",
         changeOrigin: true,
@@ -77,20 +78,22 @@ module.exports = {
       },
       // 未知量  //振动、激光测距仪
       "/WZLapi": {
+        target: process.env.VUE_APP_WZLAPIPROXY,
         // target: "http://192.168.50.241:9001", // WZLapi 目标地址
-        target: "http://106.120.102.238:18080/WindTransDev", // WZLapi 外网目标地址
+        // target: "http://106.120.102.238:18080/WindTransDev", // WZLapi 外网目标地址
         changeOrigin: true,
         pathRewrite: {
           "^/WZLapi": "", // 去掉 /WZLapi 前缀
         },
       },
       "/ETLapi": {
+        target: process.env.VUE_APP_ETLAPIPROXY,
         // target: "http://192.168.50.241:9001", // WZLapi 目标地址
         // target: "http://192.168.5.11:8001", // WZLapi 目标地址
         // target: "http://106.120.102.238:18080/ImportDataDev", //导数工具
         // target: "http://106.120.102.238:18080/WindTransDev", //WTL外网目标地址
-        // target: "http://106.120.102.238:28999/transDataWeb", //WTL演示环境
-        target: "http://192.168.50.235:8998/transDataWeb", //WTL演示环境
+        // target: "http://106.120.102.238:18998/transDataWeb", //WTL演示环境
+        // target: "http://192.168.50.235:8998/transDataWeb", //WTL演示环境
         changeOrigin: true,
         pathRewrite: {
           "^/ETLapi": "", // 去掉 /WZLapi 前缀
@@ -100,37 +103,31 @@ module.exports = {
         },
       },
 
-      // 文佳
-      "/WJapi": {
-        target: "http://106.120.102.238:18888",
-        changeOrigin: true,
-        pathRewrite: {
-          "^/WJapi": "",
-        },
-      },
+      // // 文佳
+      // "/WJapi": {
+      //   target: process.env.VUE_APP_WJAPIPROXY,
+      //   // target: "http://106.120.102.238:18888",
+      //   changeOrigin: true,
+      //   pathRewrite: {
+      //     "^/WJapi": "",
+      //   },
+      // },
 
       // 王娇娇健康评估
       "/AnalysisMulti": {
-        target: "http://106.120.102.238:28999/AnalysisMulti",
+        target: process.env.VUE_APP_AnalysisMultiAPIPROXY,
+        // target: "http://106.120.102.238:28999/AnalysisMulti",
         changeOrigin: true,
         pathRewrite: {
           "^/AnalysisMulti": "",
         },
       },
-
-      // 故障诊断
-      "/WJJdiag": {
-        target: "http://106.120.102.238:28999/WJJdiag",
-        changeOrigin: true,
-        pathRewrite: {
-          "^/WJJdiag": "",
-        },
-      },
-
       // 数据转换亮亮
       "/transDataWeb": {
+        target: process.env.VUE_APP_WZLAPIPROXY,
         // target: "http://192.168.50.241:9000/trans_data_web",//生产
-        target: "http://192.168.50.235/transDataWeb", //测试
+        // target: "http://192.168.50.235/transDataWeb", //测试
+        // target: "http://106.120.102.238:18998/transDataWeb", //演示外网
         // target: "http://192.168.50.235:8999/transDataWeb", //测试
         changeOrigin: true,
         pathRewrite: {
@@ -139,8 +136,9 @@ module.exports = {
       },
       //自定义算法文佳
       "/sAlgorithm": {
+        target: process.env.VUE_APP_sAlgorithmAPIPROXY,
         // target: "http://192.168.50.235:8666", // 目标地址
-        target: "http://106.120.102.238:58880", //这个代理会走两次代理转发
+        // target: "http://106.120.102.238:58880", //这个代理会走两次代理转发
         changeOrigin: true,
         pathRewrite: {
           "^/sAlgorithm": "",
@@ -148,8 +146,9 @@ module.exports = {
       },
       //nodejs 数据库数据
       "/databaseApi": {
+        target: process.env.VUE_APP_databaseApiAPIPROXY,
         // target: "http://192.168.50.234:3002",
-        target: "http://106.120.102.238:58880", //这个代理会走两次代理转发
+        // target: "http://106.120.102.238:58880", //这个代理会走两次代理转发
         changeOrigin: true,
         pathRewrite: {
           "^/databaseApi": "",
@@ -157,7 +156,8 @@ module.exports = {
       },
       //nodejs 数据库数据
       "/downLoadChart": {
-        target: "http://0.0.0.0:3001",
+        target: process.env.VUE_APP_downLoadChartAPIPROXY,
+        // target: "http://0.0.0.0:3001",
         // target: "http://192.168.50.235:8999/downLoadChart", //内网演示
         // target: "http://106.120.102.238:28999/downLoadChart", //外网演示
         changeOrigin: true,