Bladeren bron

ETL+合并

rui.jiang 11 maanden geleden
bovenliggende
commit
431266b9a3
43 gewijzigde bestanden met toevoegingen van 4622 en 161 verwijderingen
  1. BIN
      1.14.zip
  2. BIN
      1.15.zip
  3. 2 2
      src/App.vue
  4. 35 0
      src/api/overview.js
  5. 27 3
      src/api/performance.js
  6. 1 1
      src/components/Tinymce.vue
  7. 35 0
      src/directives/inview.js
  8. 0 0
      src/icons/svg/WindPower.svg
  9. 0 0
      src/icons/svg/WindPower1.svg
  10. 1 0
      src/icons/svg/WindPower2.svg
  11. 0 0
      src/icons/svg/WindPower3.svg
  12. 2 0
      src/main.js
  13. 3 0
      src/styles/global.scss
  14. 268 10
      src/views/overview/components/analysis_information/index.vue
  15. 206 3
      src/views/overview/components/data_integrity_minute/index.vue
  16. 131 0
      src/views/overview/components/dicCard/index.vue
  17. 398 0
      src/views/overview/components/fault_all/index.vue
  18. 384 0
      src/views/overview/components/fault_unit/index.vue
  19. 73 0
      src/views/overview/components/filterChart/index.vue
  20. 144 0
      src/views/overview/components/map/index.vue
  21. 306 3
      src/views/overview/components/min_pitch/index.vue
  22. 292 3
      src/views/overview/components/pitch_tsr_cp/index.vue
  23. 446 3
      src/views/overview/components/power_curve/index.vue
  24. 17 0
      src/views/overview/components/production_indicator_all/index.vue
  25. 17 0
      src/views/overview/components/production_indicator_unit/index.vue
  26. 191 3
      src/views/overview/components/wind_direction_frequency/index.vue
  27. 0 0
      src/views/overview/components/wind_speed/index.vue
  28. 190 3
      src/views/overview/components/wind_speed_frequency/index.vue
  29. 291 3
      src/views/overview/components/yaw_error_density/index.vue
  30. 140 69
      src/views/overview/index.vue
  31. 2 2
      src/views/overview/js/assetssType.js
  32. 10 5
      src/views/performance/assetssDetail.vue
  33. 103 12
      src/views/performance/assetssMag.vue
  34. 10 6
      src/views/performance/components/EditAnalysis.vue
  35. 1 2
      src/views/performance/components/analysisEvent.vue
  36. 119 14
      src/views/performance/components/chartsCom/BarChart.vue
  37. 255 0
      src/views/performance/components/chartsCom/FaultAll.vue
  38. 119 0
      src/views/performance/components/chartsCom/FaultUnit.vue
  39. 13 5
      src/views/performance/components/chartsCom/HeatmapCharts.vue
  40. 3 3
      src/views/performance/components/chartsCom/MarkersCharts.vue
  41. 381 0
      src/views/performance/components/chartsCom/TwoDMarkersChart.vue
  42. 5 5
      src/views/performance/components/chartsCom/WindRoseChart.vue
  43. 1 1
      src/views/performance/editAssets.vue

BIN
1.14.zip


BIN
1.15.zip


+ 2 - 2
src/App.vue

@@ -39,7 +39,7 @@ body {
 }
 </style>
 <style>
-::v-deep .el-color-dropdown .el-color-dropdown__main-wrapper {
+/* ::v-deep .el-color-dropdown .el-color-dropdown__main-wrapper {
   display: none !important;
 }
 .el-color-svpanel {
@@ -47,5 +47,5 @@ body {
 }
 .el-color-hue-slider {
   display: none !important;
-}
+} */
 </style>

+ 35 - 0
src/api/overview.js

@@ -0,0 +1,35 @@
+/*
+ * @Author: your name
+ * @Date: 2025-01-13 13:30:08
+ * @LastEditTime: 2025-01-13 17:42:12
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/api/overview.js
+ */
+import request from "@/utils/request";
+
+//根据角色获取风场及分析编号信息
+export function getAnalysisCodeInfo(data) {
+  return request({
+    url: "/energy-manage-service/analysis/getAnalysisCodeInfo",
+    method: "get",
+    params: data,
+  });
+}
+
+export function getFieldInfo(data) {
+  return request({
+    url: "/energy-manage-service/analysis/getFieldInfo",
+    method: "get",
+    params: data,
+  });
+}
+
+
+export function analysisCommentDescList(data) {
+  return request({
+    url: "/energy-manage-service/analysisComment/analysisCommentDescList",
+    method: "get",
+    params: data,
+  });
+}

+ 27 - 3
src/api/performance.js

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2024-06-03 09:29:50
- * @LastEditTime: 2025-01-06 16:37:46
+ * @LastEditTime: 2025-01-14 10:36:54
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/api/performance。.js
@@ -163,8 +163,8 @@ export function analysisErrEdit(data) {
 export function analysisDetail(data) {
   return request({
     url: "/energy-manage-service/analysis/analysisDetail",
-    method: "post",
-    data,
+    method: "get",
+    params: data,
   });
 }
 //分析详情  事件
@@ -265,3 +265,27 @@ export function queryAllTypeScada() {
     method: "get",
   });
 }
+export function queryAllAnalysisType() {
+  //查询全部分析类型 --概览页面分析类型下拉框选择
+  return request({
+    url: "/energy-manage-service/analysis/queryAllAnalysisType",
+    method: "get",
+  });
+}
+//新增分析结果--性能分析页面 新增分析按钮接口
+export function addAnalysisResult(data) {
+  return request({
+    url: "/energy-manage-service/analysis/addAnalysisResult",
+    method: "post",
+    data,
+  });
+}
+
+//分析结果--性能分析列表添加了几个字段
+export function analysisResultList(data) {
+  return request({
+    url: "/energy-manage-service/analysis/analysisResultList",
+    method: "get",
+    params: data,
+  });
+}

+ 1 - 1
src/components/Tinymce.vue

@@ -7,7 +7,7 @@
       :mode="mode"
     />
     <Editor
-      style="height: 500px; overflow-y: hidden"
+      style="height: 300px; overflow-y: hidden"
       v-model="html"
       :defaultConfig="editorConfig"
       :mode="mode"

+ 35 - 0
src/directives/inview.js

@@ -0,0 +1,35 @@
+export default {
+  // 在绑定元素的插入位置时,初始化 observer
+  bind(el, binding) {
+    const options = {
+      root: null, // 观察相对于视口的位置
+      rootMargin: '0px', // margin 可设置偏移量
+      threshold: 0.5 // 多少比例进入视口时触发
+    };
+
+    // 回调函数,判断元素是否出现在视口
+    const callback = (entries) => {
+      entries.forEach(entry => {
+        if (entry.isIntersecting) {
+          binding.value(true); // 元素进入视口时执行传入的回调
+        } else {
+          binding.value(false); // 元素离开视口时执行传入的回调
+        }
+      });
+    };
+
+    // 创建 IntersectionObserver 实例
+    const observer = new IntersectionObserver(callback, options);
+    observer.observe(el); // 开始观察目标元素
+
+    // 将 observer 存储到 el 中,便于以后销毁
+    el.__vueIntersectionObserver__ = observer;
+  },
+
+  // 在元素解绑时,销毁 observer
+  unbind(el) {
+    if (el.__vueIntersectionObserver__) {
+      el.__vueIntersectionObserver__.disconnect();
+    }
+  }
+};

File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/WindPower.svg


File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/WindPower1.svg


+ 1 - 0
src/icons/svg/WindPower2.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1736736328501" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2127" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M1011.26 691.3L726.42 539.69l-71.33 0.48-43.32-14.49c-12 21.34-34.76 39-71.94 41.78l468.54 164.7 10.88-11.5c9.16-9.72 5.83-22-8-29.43v0.06zM37.27 706.52l315.62-100L395.2 558.41l39.82-20.35c-13.15-20.87-16.39-46.74 3.3-73.31L0 680.06l4.45 14.2c3.78 12 17.47 17.09 32.88 12.3h-0.06zM530.2 18.35l-57 285.76 28 55 0.76 40.09c28.13 1.07 56.38 11.83 73.73 39.53l0.62-433.89-16.86-4C545.2-2.5 533.02 4.8 530.15 18.37h0.07zM512.2 549.87a59.71 59.71 0 0 0 29.45-111.19 58.34 58.34 0 0 0-58.88 0A59.71 59.71 0 0 0 512.2 549.87zM553.33 617.55L492.67 572a80.82 80.82 0 0 1-17-11l-30 463 131.17-0.81-23.51-405.64z" fill="#707070" p-id="2128"></path></svg>

File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/WindPower3.svg


+ 2 - 0
src/main.js

@@ -26,6 +26,7 @@ import qs from "qs";
 import dataV from "@jiaminghi/data-view";
 import permission from "./directives/permission"; // 导入自定义指令
 import lazyLoad from "./directives/lazyLoad";
+import inview from "./directives/inview";
 import "echarts-gl";
 Vue.use(dataV);
 Vue.prototype.$qs = qs;
@@ -58,6 +59,7 @@ Vue.prototype.$formatDate = function (date) {
 };
 Vue.directive("lazy-load", lazyLoad);
 Vue.directive("hasPermi", permission);
+Vue.directive("inview", inview);
 // 时间戳转换
 Vue.prototype.$formatDateTWO = function (timestamp) {
   const date = new Date(timestamp);

+ 3 - 0
src/styles/global.scss

@@ -7,6 +7,9 @@
 .el-menu-item.is-active {
   color: var(--primary-color) !important;
 }
+.headerMessage {
+  color: var(--primary-color) !important;
+}
 .el-menu--horizontal > .el-menu-item.is-active {
   border-bottom: 2px solid var(--primary-color) !important;
 }

+ 268 - 10
src/views/overview/components/analysis_information/index.vue

@@ -1,17 +1,275 @@
-<!--
- * @Author: your name
- * @Date: 2025-01-09 17:40:53
- * @LastEditTime: 2025-01-09 17:50:49
- * @LastEditors: bogon
- * @Description: In User Settings Edit
- * @FilePath: /performance-test/src/views/overview/assetssMsg/index.vue
--->
 <template>
-  <div>分析信息</div>
+  <el-card shadow="always" class="box-card">
+    <div class="box-header">
+      <h4>分析介绍:</h4>
+      <div class="box-header-min">
+        <div>
+          {{ fieldInfo.fieldName }}位于{{ fieldInfo.provinceName
+          }}{{ fieldInfo.cityName }}
+          ,海拔高度为
+          <span class="headerMessage"> {{ fieldInfo.elevationHeight }} </span>
+          ,所在经纬度为经度
+          <span class="headerMessage"> {{ fieldInfo.longitude }} </span>
+          ,纬度
+          <span class="headerMessage">{{ fieldInfo.latitude }} </span>。
+        </div>
+        <div>
+          该风电场由大唐研究院管理,风场总容量为
+          <span class="headerMessage">{{ fieldInfo.ratedCapacityNumber }} MW
+          </span>
+          ,共安装
+          <span class="headerMessage">{{ fieldInfo.engineCount }} 台</span>
+
+          风机,使用的机型为
+          <span class="headerMessage">
+            {{
+              fieldInfo.engineMillTypes && fieldInfo.engineMillTypes.join(",")
+            }}
+          </span>
+          。
+        </div>
+        <div>
+          分析数据时间跨度为2025年1月20日至2025年2月20日,分析完成时间为2025年1月20日10:10:10。数据分析人
+          <span class="headerMessage"> {{ analysisInfo.updateByName }}</span>。
+        </div>
+      </div>
+    </div>
+
+    <div class="content">
+      <div class="left">
+        <div class="box-content-min">
+          <h4>完成分析类型:</h4>
+
+          <template v-if="analysisInfo && analysisInfo.analysisTypes?.length > 0">
+            <div class="completeAssetssType">
+              <el-tag v-for="analysis in analysisInfo.analysisTypes" type="warning">{{ analysis.analysisTypeName }}
+              </el-tag>
+            </div>
+          </template>
+
+          <el-empty v-else description="暂无完成分析类型"></el-empty>
+
+
+        </div>
+
+        <div class="box-content-min">
+          <h4>分析完成机组:</h4>
+          <div class="analysisCompletionUnit" v-if="analysisInfo && analysisInfo.windEngineGroups?.length > 0">
+            <div class="itemAnalysisCompletionUnit" v-for="itemData in analysisInfo.windEngineGroups" :key="itemData">
+              <SvgIcons name="WindPower3" class="WindPower3" width="40px" height="42px" color="#222"></SvgIcons>
+              <span>{{ itemData.engineName }}#机组</span>
+            </div>
+
+          </div>
+          <el-empty v-else description="暂无分析完成机组"></el-empty>
+        </div>
+      </div>
+      <div class="right">
+        <Map :batchCode="initBatchCode" :fieldCode="fieldInfo.fieldCode"></Map>
+      </div>
+    </div>
+  </el-card>
 </template>
+
 <script>
+import Map from "../map/index.vue";
+import { getFieldInfo } from "@/api/overview";
+import {
+  getWindEngineGroup,
+
+} from "@/api/ledger";
 export default {
   name: "AnalysisInformation",
+  components: {
+    Map,
+  },
+  props: {
+    initBatchCode: {
+      default: "",
+      type: String,
+    },
+  },
+
+  data() {
+    return {
+      fieldInfo: {
+        fieldCode: ''
+      }, //风场信息
+      fieldCode: '',//风场编号
+
+      analysisInfo: {}, //分析类型编号
+      activeNames: ["1", "2", "3"],
+      data: [
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+      ],
+    };
+  },
+  watch: {
+    initBatchCode(newVal, oldVal) {
+      console.log(newVal, oldVal, "分析信息内容更新");
+      this.getFieldInfoMess();
+
+    },
+  },
+  mounted() {
+    if (this.$route.query.batchCode) {
+      this.initBatchCode = this.$route.query.batchCode;
+    }
+    if (this.$route.query.fieldCode) {
+      this.fieldInfo.fieldCode = this.$route.query.fieldCode;
+    }
+  },
+  methods: {
+    async getWindEngList() {
+      this.loading = true;
+      try {
+        const res = await getWindEngineGroup({
+          engineCode: this.engineCode,
+        });
+        this.loading = false;
+        this.windDetail = res.data;
+      } catch (error) {
+        this.loading = false;
+        console.error(error);
+      }
+    },
+    //获取分析信息
+    async getFieldInfoMess() {
+      //分析详情
+      try {
+        const res = await getFieldInfo({ batchCode: this.initBatchCode });
+
+        if (res.code === 200) {
+          this.fieldInfo = res.data.fieldInfo; //风场信息
+          this.analysisInfo = res.data.analysisInfo; //分析类型编号
+        }
+      } catch (err) { }
+    },
+    handleChange(val) {
+      console.log(val);
+    },
+  },
 };
 </script>
-<style scoped lang="scss"></style>
+
+<style scoped lang="scss">
+.completeAssetssType {
+  display: flex;
+  flex-wrap: wrap;
+
+  .el-tag {
+    margin-right: 10px;
+    margin-bottom: 10px;
+  }
+
+  ::v-deep .el-tag--warning {
+    background-color: #fdf6ec !important;
+    border-color: #faecd8 !important;
+    color: #e6a23c !important;
+  }
+}
+
+.el-card__body {
+  width: 100%;
+  height: 100%;
+  // margin-bottom: 20px !important;
+}
+
+.content {
+  width: 100%;
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-start;
+  /* 上对齐 */
+  height: 100%;
+  /* 确保内容区域有高度 */
+}
+
+.left {
+  width: 60%;
+
+  // flex: 1;
+  .box-content-min {
+    padding: 10px;
+    border-bottom: 1px solid #ebeef5;
+    color: #303133;
+
+    h4 {
+      color: #303133;
+      // border-bottom: 1px solid #ebeef5;
+      font-size: 13px;
+      font-weight: 500;
+      margin-bottom: 10px;
+    }
+  }
+
+  .analysisCompletionUnit {
+    display: flex;
+    flex-wrap: wrap;
+    margin: 20px 0;
+
+    .itemAnalysisCompletionUnit {
+      text-align: center;
+      margin-bottom: 10px;
+      border: #eaeef6 solid 1px;
+      margin-right: 10px;
+      border-radius: 10px;
+      padding: 10px 5px;
+
+      .WindPower3 {
+        padding: 10px 5px;
+        text-align: center;
+      }
+    }
+  }
+}
+
+.right {
+  width: 40%;
+  height: 100%;
+  /* 确保容器高度为100% */
+  // flex: 1;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.right .map-ditu {
+  width: 100%;
+  /* 设置地图的宽度 */
+  height: 60vh;
+  border-radius: 90px;
+
+  /* 设置地图的高度 */
+}
+
+.box-card {
+  width: 100%;
+  height: 100%;
+  overflow-y: scroll;
+
+  .box-header {
+    padding: 10px;
+    border-bottom: 1px solid #ebeef5;
+    color: #303133;
+
+    h4 {
+      color: #303133;
+      font-size: 13px;
+      font-weight: 500;
+      margin-bottom: 10px;
+    }
+  }
+
+  .box-header-min {
+    padding: 10px;
+    font-size: 14px;
+    color: #606266;
+    line-height: 40px;
+
+    .headerMessage {
+      font-size: 16px;
+    }
+  }
+}
+</style>

+ 206 - 3
src/views/overview/components/data_integrity_minute/index.vue

@@ -1,17 +1,220 @@
 <!--
  * @Author: your name
  * @Date: 2025-01-09 17:54:02
- * @LastEditTime: 2025-01-09 17:54:35
+ * @LastEditTime: 2025-01-14 15:00:53
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/overview/data_integrity_minute/index.vue
 -->
 <template>
-  <div>scada分钟级数据</div>
+  <div class="type-variable">
+    <!-- scada分钟级数据 -->
+    <div class="left">
+      <FilterChart
+        :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">
+          风电机组SCADA数据完整度分析是对风电场运行数据质量进行评估的关键环节。
+        </div>
+        <div style="font-size: 12px; margin-top: 10px">
+          数据完整度分析可以帮助识别数据缺失、异常或噪声,确保所采集数据的准确性和连贯性。这对于后续的风电机组运行分析和故障诊断至关重要。
+        </div>
+        <div style="font-size: 12px; margin-top: 10px">
+          数据完整度分析是根据数据统计周期内数据总行数与期望数据总行数的比值计算出来的。
+        </div>
+      </el-alert>
+
+      <HeatmapCharts></HeatmapCharts>
+      <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">
+        <el-col :span="2" style="margin: 20px">
+          <el-button type="primary" size="small">提交评论</el-button>
+        </el-col>
+      </el-row>
+    </div>
+    <div class="right">
+      <DicCard></DicCard>
+    </div>
+  </div>
 </template>
 <script>
+import DicCard from "@/views/overview/components/dicCard/index.vue";
+import FilterChart from "@/views/overview/components/filterChart/index.vue";
+import HeatmapCharts from "@/views/performance/components/chartsCom/HeatmapCharts.vue";
+import TinymceEditor from "@/components/Tinymce.vue";
+import { analysisDetail, queryAnalysisedEngine } from "@/api/performance";
+
 export default {
   name: "DataIntegrityMinute",
+  components: {
+    DicCard,
+    FilterChart,
+    HeatmapCharts,
+    TinymceEditor,
+  },
+  props: {
+    initBatchCode: {
+      default: "",
+      type: String,
+    },
+    analysisTypeCode: {
+      default: "",
+      type: String,
+    },
+    batchCodeList: {
+      default: "",
+      type: Array,
+    },
+  },
+  data() {
+    return {
+      form: {
+        value2: "",
+      },
+      windEngineGroupList: [], //批次风机列表
+      fieldEngineCodes: [], //选中风机
+      comment: "",
+      options: [],
+    };
+  },
+  watch: {
+    initBatchCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+    analysisTypeCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+  },
+  mounted() {
+    if (this.initBatchCode && this.analysisTypeCode) {
+      this.fetchData(); // 调用合并后的函数
+    }
+  },
+  methods: {
+    onSubmit() {
+      console.log("submit!");
+    },
+    // 合并后的函数,处理数据请求
+    async fetchData() {
+      try {
+        console.log(
+          this.initBatchCode,
+          this.analysisTypeCode,
+          "请求详情 分钟级"
+        );
+        // 获取分析详情
+        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,
+        });
+        console.log(result, "result");
+      } 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) {
+      this.fieldEngineCodes = data;
+      console.log(this.fieldEngineCodes, "this.fieldEngineCodes");
+    },
+    //下一条
+    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"></style>
+
+<style scoped lang="scss">
+.type-variable {
+  display: flex;
+  height: 90%;
+  overflow: hidden;
+  .left {
+    width: 30%;
+    height: 100%;
+    overflow: auto;
+    padding: 20px;
+    flex: 1;
+  }
+  .right {
+    width: 250px;
+    height: 100%;
+    overflow: hidden;
+  }
+}
+</style>

+ 131 - 0
src/views/overview/components/dicCard/index.vue

@@ -0,0 +1,131 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-01-13 14:15:47
+ * @LastEditTime: 2025-01-16 09:53:24
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/overview/components/dicCard/index.vue
+-->
+<template>
+  <el-tabs v-model="activeName" type="border-card" @tab-click="handleClick">
+    <el-tab-pane label="当前评论" name="current">
+      <template
+        v-if="commentDescriptionVos && commentDescriptionVos.length > 0"
+      >
+        <el-descriptions
+          class="margin-top"
+          title="分析评论"
+          :column="1"
+          v-for="(item, inds) in commentDescriptionVos"
+          :key="inds + 'current'"
+        >
+          <el-descriptions-item label="评论时间">
+            <div v-html="item.createTime"></div>
+          </el-descriptions-item>
+          <el-descriptions-item label="评论批次">
+            <div v-html="item.analysisName"></div>
+          </el-descriptions-item>
+          <el-descriptions-item label="评论描述">
+            <div v-html="item.comment"></div>
+          </el-descriptions-item>
+        </el-descriptions>
+      </template>
+      <el-empty description="暂无分析评论" v-else></el-empty>
+    </el-tab-pane>
+    <el-tab-pane label="历史评论" name="history">
+      <template
+        v-if="historyCommentDescList && historyCommentDescList.length > 0"
+      >
+        <el-descriptions
+          class="margin-top"
+          title="分析评论"
+          :column="1"
+          v-for="(item, inds) in historyCommentDescList"
+          :key="inds + 'history'"
+        >
+          <el-descriptions-item label="评论时间">
+            <div v-html="item.createTime"></div>
+          </el-descriptions-item>
+          <el-descriptions-item label="评论批次">
+            <div v-html="item.analysisName"></div>
+          </el-descriptions-item>
+          <el-descriptions-item label="评论描述">
+            <div v-html="item.comment"></div>
+          </el-descriptions-item>
+        </el-descriptions>
+      </template>
+      <el-empty description="暂无历史评论" v-else></el-empty>
+    </el-tab-pane>
+  </el-tabs>
+</template>
+<script>
+import { analysisCommentDescList } from "@/api/overview";
+export default {
+  props: {
+    batchCode: {
+      type: String,
+      default: "",
+    },
+    analysisTypeCode: {
+      type: String,
+      default: "",
+    },
+    commentDescriptionVos: {
+      type: Array,
+      default: [],
+    },
+  },
+  data() {
+    return {
+      activeName: "current",
+      historyCommentDescList: [],
+    };
+  },
+  watch: {
+    batchCode(newVal) {
+      console.log(newVal, "this.activeName ===");
+      if (newVal) {
+        this.activeName = "current";
+      }
+    },
+    //分析类型
+    // analysisTypeCode() {
+    //   if (newVal) {
+    //     this.analysisCommentDescListHistory()
+    //   }
+    // }
+  },
+  methods: {
+    handleClick(tab, event) {
+      console.log(this.activeName, "activeName");
+      if (this.activeName === "history") {
+        this.analysisCommentDescListHistory();
+      }
+    },
+    async analysisCommentDescListHistory() {
+      try {
+        const res = await analysisCommentDescList({
+          //   batchCode: this.batchCode,
+          analysisTypeCode: this.analysisTypeCode,
+        });
+        this.historyCommentDescList = res.data || [];
+        console.log(
+          res.data,
+          "历史评论",
+          this.batchCode,
+          this.analysisTypeCode
+        );
+      } catch (err) {}
+    },
+  },
+};
+</script>
+<style scoped lang="scss">
+.el-tabs--border-card {
+  height: 100%;
+  overflow: scroll;
+
+  .el-tabs__content {
+  }
+}
+</style>

+ 398 - 0
src/views/overview/components/fault_all/index.vue

@@ -0,0 +1,398 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-01-13 13:56:55
+ * @LastEditTime: 2025-01-16 10:01:41
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/overview/components/fault_all/index.vue
+-->
+
+<template>
+  <div class="type-variable">
+    <!-- 全场故障统计 -->
+    <div class="left">
+      <FilterChart
+        :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 v-if="zongFaultCsvData.length > 0">
+        <FaultAll
+          :faultTypes="
+            zongFaultCsvData &&
+            zongFaultCsvData[0] &&
+            zongFaultCsvData[0].data.map((item) => item.fault_detail)
+          "
+          :faultCounts="
+            zongFaultCsvData &&
+            zongFaultCsvData[0] &&
+            zongFaultCsvData[0].data.map((item) => item.count)
+          "
+          :faultDurations="
+            zongFaultCsvData &&
+            zongFaultCsvData[0] &&
+            zongFaultCsvData[0].data.map((item) => item.fault_time_sum)
+          "
+          :zongFaultCsvData="zongFaultCsvData"
+        ></FaultAll>
+        <template v-for="(itemCsv, indCsv) in zongFaultCsvData">
+          <el-table
+            :key="indCsv + 'indCsv'"
+            :data="filteredData(itemCsv)"
+            border
+            style="width: 100%"
+            align="center"
+          >
+            <el-table-column prop="fault_detail" label="故障类型">
+            </el-table-column>
+            <el-table-column prop="count" sortable label="故障次数(次)">
+            </el-table-column>
+            <el-table-column
+              prop="fault_time_sum"
+              sortable
+              label="故障时长(秒)"
+            >
+            </el-table-column>
+            <el-table-column align="right">
+              <template slot="header" slot-scope="scope">
+                <el-input
+                  v-model="search"
+                  size="mini"
+                  placeholder="输入故障类型关键字搜索"
+                />
+              </template>
+            </el-table-column>
+          </el-table>
+        </template>
+      </div>
+      <el-empty description="暂无分析记录" v-else></el-empty>
+      <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">
+        <el-col :span="2" style="margin: 20px">
+          <el-button type="primary" size="small">提交评论</el-button>
+        </el-col>
+      </el-row>
+    </div>
+    <div class="right">
+      <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 FaultAll from "@/views/performance/components/chartsCom/FaultAll.vue";
+import TinymceEditor from "@/components/Tinymce.vue";
+import { analysisDetail, queryAnalysisedEngine } from "@/api/performance";
+import Papa from "papaparse";
+import axios from "axios";
+export default {
+  name: "fault_all",
+  components: {
+    DicCard,
+    FilterChart,
+    FaultAll,
+    TinymceEditor,
+  },
+  props: {
+    initBatchCode: {
+      default: "",
+      type: String,
+    },
+    analysisTypeCode: {
+      default: "",
+      type: String,
+    },
+    batchCodeList: {
+      default: "",
+      type: Array,
+    },
+  },
+  data() {
+    return {
+      search: "",
+      form: {
+        value2: "",
+      },
+      windEngineGroupList: [], //批次风机列表
+      fieldEngineCodes: [], //选中风机
+      comment: "",
+      options: [],
+      zongFaultCsvHeader: [],
+      zongFaultCsvData: [],
+      fenFaultCsvHeader: [],
+      fenFaultCsvData: [],
+      commentDescriptionVos: [], //评论列表
+      editableTabs: [],
+    };
+  },
+  computed: {
+    // 根据搜索关键字过滤数据
+    filteredData() {
+      return (itemCsv) => {
+        // 如果有搜索关键词,则过滤数据
+        if (this.search) {
+          return itemCsv.data.filter((item) => {
+            return item.fault_detail
+              .toLowerCase()
+              .includes(this.search.toLowerCase());
+          });
+        }
+        // 没有搜索关键词时返回所有数据
+        return itemCsv.data;
+      };
+    },
+    filteredFenData() {
+      return (itemCsv) => {
+        // 如果有搜索关键词,则过滤数据
+        if (this.searchFen) {
+          return itemCsv.data.filter((item) => {
+            return item.wind_turbine_name
+              .toLowerCase()
+              .includes(this.searchFen.toLowerCase());
+          });
+        }
+        // 没有搜索关键词时返回所有数据
+        return itemCsv.data;
+      };
+    },
+  },
+  watch: {
+    initBatchCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+    analysisTypeCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+  },
+  mounted() {
+    if (this.initBatchCode && this.analysisTypeCode) {
+      this.fetchData(); // 调用合并后的函数
+    }
+  },
+  methods: {
+    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();
+      } catch (e) {
+        console.error(e);
+        this.loading = false;
+      }
+    },
+    onSubmit() {
+      console.log("submit!");
+    },
+    // 封装的获取 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("fault_detail")) {
+                    //总图故障统计展示
+                    this.zongFaultCsvHeader.push(Object.keys(result.data[0]));
+                    this.zongFaultCsvData.push({
+                      data: result.data
+                        .filter((row) => Object.keys(row).length)
+                        .slice(0, result.data.length - 1),
+                    });
+                  } else {
+                    //分机型故障统计处理
+                    this.fenFaultCsvHeader.push(Object.keys(result.data[0]));
+                    this.fenFaultCsvData.push({
+                      data: result.data
+                        .filter((row) => Object.keys(row).length)
+                        .slice(0, result.data.length - 1),
+                    });
+                  }
+                }
+              },
+              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,
+        });
+        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.zongFaultCsvHeader = [];
+              this.zongFaultCsvData = [];
+              this.fetchCsvData("fault", item.fileAddr);
+            }
+          });
+        } else {
+          this.zongFaultCsvHeader = [];
+          this.zongFaultCsvData = [];
+        }
+      } 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) {
+      this.fieldEngineCodes = data;
+      console.log(this.fieldEngineCodes, "this.fieldEngineCodes");
+    },
+    //下一条
+    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;
+  }
+
+  .right {
+    width: 250px;
+    height: 100%;
+    overflow: hidden;
+  }
+}
+</style>

+ 384 - 0
src/views/overview/components/fault_unit/index.vue

@@ -0,0 +1,384 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-01-13 13:56:12
+ * @LastEditTime: 2025-01-16 10:02:43
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/overview/components/fault_unit/index.vue
+-->
+<template>
+  <div class="type-variable">
+    <!-- 机组故障统计 -->
+    <div class="left">
+      <FilterChart
+        :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 v-if="fenFaultCsvData.length > 0">
+        <FaultUnit
+          :faultTypes="
+            fenFaultCsvData &&
+            fenFaultCsvData[0] &&
+            fenFaultCsvData[0].data.map((item) => item.wind_turbine_name)
+          "
+          :faultCounts="
+            fenFaultCsvData &&
+            fenFaultCsvData[0] &&
+            fenFaultCsvData[0].data.map((item) => item.count)
+          "
+          :faultDurations="
+            fenFaultCsvData &&
+            fenFaultCsvData[0] &&
+            fenFaultCsvData[0].data.map((item) => item.fault_time)
+          "
+          :fenFaultCsvData="fenFaultCsvData"
+        ></FaultUnit>
+        <template v-for="itemCsv in fenFaultCsvData">
+          <el-table
+            :data="filteredFenData(itemCsv)"
+            border
+            style="width: 100%"
+            align="center"
+          >
+            <el-table-column prop="wind_turbine_name" label="风机名称">
+            </el-table-column>
+            <el-table-column prop="count" sortable label="故障次数(次)">
+            </el-table-column>
+            <el-table-column prop="fault_time" sortable label="故障时长(秒)">
+            </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-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">
+        <el-col :span="2" style="margin: 20px">
+          <el-button type="primary" size="small">提交评论</el-button>
+        </el-col>
+      </el-row>
+    </div>
+    <div class="right">
+      <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 } 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,
+    },
+    analysisTypeCode: {
+      default: "",
+      type: String,
+    },
+    batchCodeList: {
+      default: "",
+      type: Array,
+    },
+  },
+  data() {
+    return {
+      searchFen: "",
+      form: {
+        value2: "",
+      },
+      commentDescriptionVos: [], //评论列表
+      windEngineGroupList: [], //批次风机列表
+      fieldEngineCodes: [], //选中风机
+      comment: "",
+      options: [],
+      zongFaultCsvHeader: [],
+      zongFaultCsvData: [],
+      fenFaultCsvData: [],
+      fenFaultCsvHeader: [],
+      editableTabs: [],
+    };
+  },
+  computed: {
+    // 根据搜索关键字过滤数据
+    filteredData() {
+      return (itemCsv) => {
+        // 如果有搜索关键词,则过滤数据
+        if (this.search) {
+          return itemCsv.data.filter((item) => {
+            return item.wind_turbine_name
+              .toLowerCase()
+              .includes(this.search.toLowerCase());
+          });
+        }
+        // 没有搜索关键词时返回所有数据
+        return itemCsv.data;
+      };
+    },
+    filteredFenData() {
+      return (itemCsv) => {
+        // 如果有搜索关键词,则过滤数据
+        if (this.searchFen) {
+          return itemCsv.data.filter((item) => {
+            return item.wind_turbine_name
+              .toLowerCase()
+              .includes(this.searchFen.toLowerCase());
+          });
+        }
+        // 没有搜索关键词时返回所有数据
+        return itemCsv.data;
+      };
+    },
+  },
+  watch: {
+    initBatchCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+    analysisTypeCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+  },
+  mounted() {
+    if (this.initBatchCode && this.analysisTypeCode) {
+      this.fetchData(); // 调用合并后的函数
+    }
+  },
+  methods: {
+    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();
+      } 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) => Object.keys(row).length)
+                        .slice(0, result.data.length - 1),
+                    });
+                  }
+                }
+              },
+              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,
+        });
+        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) {
+      this.fieldEngineCodes = data;
+      console.log(this.fieldEngineCodes, "this.fieldEngineCodes");
+    },
+    //下一条
+    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;
+  }
+
+  .right {
+    width: 250px;
+    height: 100%;
+    overflow: hidden;
+  }
+}
+</style>

+ 73 - 0
src/views/overview/components/filterChart/index.vue

@@ -0,0 +1,73 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-01-13 14:53:21
+ * @LastEditTime: 2025-01-14 13:44:55
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/overview/components/filterChart/index.vue
+-->
+<template>
+  <el-form ref="form" :inline="true" :model="form" label-width="100px">
+    <el-form-item label="机组名称:" size="small">
+      <el-select
+        v-model="form.value2"
+        multiple
+        collapse-tags
+        placeholder="请选择机组名称"
+      >
+        <el-option
+          v-for="(item, indWind) in windList"
+          :key="item.engineCode + indWind"
+          :label="item.engineName"
+          :value="item.engineCode"
+        >
+        </el-option>
+      </el-select>
+    </el-form-item>
+    <el-form-item>
+      <el-button type="primary" size="small" @click="onSearch">查询</el-button>
+      <el-button type="primary" size="small" @click="onSubmit('handlePrevious')"
+        >上一条分析结果</el-button
+      >
+      <el-button type="primary" size="small" @click="onSubmit('handleNext')"
+        >下一条分析结果</el-button
+      >
+    </el-form-item>
+  </el-form>
+</template>
+<script>
+export default {
+  name: "filterChart",
+  props: {
+    windList: {
+      default: [],
+      type: Array,
+    },
+  },
+  data() {
+    return {
+      form: {
+        value2: [],
+      },
+    };
+  },
+  methods: {
+    onSearch() {
+      console.log(this.form.value2);
+      if (this.form.value2 && this.form.value2.length > 0) {
+        this.$emit("getEnfineList", this.form.value2);
+      } else {
+        this.$message.warning("请选择风机后进行查询");
+      }
+    },
+    onSubmit(type) {
+      if (type === "handlePrevious") {
+        this.$emit("handlePrevious");
+      } else {
+        this.$emit("handleNext");
+      }
+    },
+  },
+};
+</script>
+<style scoped lang="scss"></style>

+ 144 - 0
src/views/overview/components/map/index.vue

@@ -0,0 +1,144 @@
+<template>
+    <div class="map-ditu">
+        <!-- 分析事件中 风场信息展示地图组件 -->
+        <Tmap ref="map" :windEngineGroupByFieldCodeDetail="windEngineGroupByFieldCodeDetail">
+        </Tmap>
+    </div>
+</template>
+
+<script>
+import {
+    getWindEngineGroupByFieldCode,
+    getWindEngineGroupRatedListByFieldCodePage,
+} from "@/api/ledger.js";
+import Tmap from "@/components/map";
+
+export default {
+    name: "Index",
+    components: {
+        Tmap,
+    },
+    props: {
+        fieldCode: "",
+        batchCode: "",
+    },
+    data() {
+        return {
+
+            windEngineGroupByFieldCodeDetail: {},
+        };
+    },
+    mounted() {
+        // this.fieldCode = this.$route.query.fieldEngineCode;
+        // this.batchCode = this.$route.query.batchCode;
+        // this.addMarkersAndZoom();
+    },
+    watch: {
+        fieldCode(newVal) {
+            if (newVal) {
+                console.log(newVal, 'newVal')
+                this.GETfengji();
+            }
+        },
+    },
+
+    methods: {
+        GETfengji() {
+            let dataArr = {
+                fieldCode: this.fieldCode,
+                batchCode: this.batchCode,
+            };
+            console.log(dataArr,'dataArr')
+            getWindEngineGroupByFieldCode(dataArr).then((res) => {
+                const result = res.data;
+                console.log(result,dataArr,'dataArr')
+                this.windEngineGroupByFieldCodeDetail = result;
+                if (result !== null && result.anemometerTowerList) {
+                    this.addMarkersAndZoom(result.anemometerTowerList, "5");
+                }
+                if (this.fieldCode) {
+                    getWindEngineGroupRatedListByFieldCodePage({
+                        fieldCode: this.fieldCode,
+                        pageSize: 9999999,
+                    }).then((windRes) => {
+                        if (windRes.data && windRes.data.list) {
+                            // 定义两个数组来存放不同errorState的元素
+                            const errorStateFalse = [];
+                            const errorStateTrue = [];
+                            // 遍历数组并根据errorState的值将元素放入不同的数组中
+
+                            windRes.data.list.forEach((item) => {
+                                errorStateFalse.push({
+                                    ...item,
+                                    longitudeAndLatitudeString: `${item.longitude}00,${item.latitude}00`,
+                                });
+                            });
+
+                            // 调用this.addMarkersAndZoom方法
+                            if (errorStateFalse.length > 0) {
+                                this.addMarkersAndZoom(errorStateFalse, "4");
+                            }
+
+                            if (errorStateTrue.length > 0) {
+                                this.addMarkersAndZoom(errorStateTrue, "6");
+                            }
+                        }
+                    });
+                }
+            });
+        },
+        parseCoordinates(input) {
+            if (input && typeof input === "string") {
+                return input.split(",").map(Number);
+            }
+            return [];
+        },
+        addMarkersAndZoom(data, type) {
+            const dataMapList = data;
+            dataMapList.forEach((element) => {
+                if (
+                    this.parseCoordinates(element.longitudeAndLatitudeString).length > 0
+                ) {
+                    this.$refs.map.addMarker({
+                        point: this.parseCoordinates(element.longitudeAndLatitudeString),
+                        val: type,
+                        ...element,
+                    });
+                    // this.$refs.map.moveAndZoom({
+                    //   point: this.parseCoordinates(element.longitudeAndLatitudeString),
+                    //   zoom: 15,
+                    // });
+                    return;
+                }
+                this.$refs.map.clearMarkers();
+            });
+            // const points = [
+            //   { point: [120.2, 30.35], val: "4" },
+            //   { point: [120.21, 30.35], val: "5" },
+            // ];
+
+            // // Add markers to the map
+            // points.forEach((point) => {
+            //   this.$refs.map.addMarker(point);
+            // });
+
+            // // Zoom to a specific point
+            const zoomPoint = {
+                point: this.parseCoordinates(
+                    this.windEngineGroupByFieldCodeDetail.windEngineGroupVoList[0]
+                        .longitudeAndLatitudeString
+                ),
+                zoom: 15,
+            };
+            this.$refs.map.moveAndZoom(zoomPoint);
+        },
+    },
+};
+</script>
+
+<style lang="scss" scoped>
+.map-ditu {
+    height: 93vh;
+    position: relative;
+}
+</style>

+ 306 - 3
src/views/overview/components/min_pitch/index.vue

@@ -1,17 +1,320 @@
 <!--
  * @Author: your name
  * @Date: 2025-01-09 18:08:13
- * @LastEditTime: 2025-01-10 09:19:48
+ * @LastEditTime: 2025-01-16 09:30:19
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/overview/components/min_pitch/index.vue
 -->
+
 <template>
-  <div>最小桨距角分析</div>
+  <div class="type-variable">
+    <!-- 最小桨距角分析--只有分图不存在总图 -->
+    <div class="left">
+      <FilterChart
+        :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></div>
+
+      <div class="charts" v-if="diagramRelationsDatas">
+        <template v-for="(itemChart, indChart) in diagramRelationsDatas">
+          <TwoDMarkersChart
+            :key="itemChart.fieldEngineCode"
+            @getResult="getResult"
+            @changeRequestNum="changeRequestNum"
+            :result="requestResult"
+            :index="indChart"
+            :ref="itemChart.fieldEngineCode"
+            :fileAddr="itemChart.fileAddr"
+          >
+          </TwoDMarkersChart>
+        </template>
+      </div>
+
+      <el-empty description="暂无分析记录" v-else></el-empty>
+
+      <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">
+        <el-col :span="2" style="margin: 20px">
+          <el-button type="primary" size="small" @click="handleComment"
+            >提交评论</el-button
+          >
+        </el-col>
+      </el-row>
+    </div>
+    <div class="right">
+      <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 TwoDMarkersChart from "@/views/performance/components/chartsCom/TwoDMarkersChart.vue";
+import TinymceEditor from "@/components/Tinymce.vue";
+import {
+  analysisDetail,
+  queryAnalysisedEngine,
+  analysisCommentEdit,
+} from "@/api/performance";
+
 export default {
   name: "minPitch",
+  components: {
+    DicCard,
+    FilterChart,
+    TwoDMarkersChart,
+    TinymceEditor,
+  },
+  props: {
+    initBatchCode: {
+      default: "",
+      type: String,
+    },
+    analysisTypeCode: {
+      default: "",
+      type: String,
+    },
+    batchCodeList: {
+      default: "",
+      type: Array,
+    },
+  },
+  data() {
+    return {
+      form: {
+        value2: "",
+      },
+      windEngineGroupList: [], //批次风机列表
+      fieldEngineCodes: [], //选中风机
+      comment: "",
+      options: [],
+      generalFilesDatas: [], //总图
+      diagramRelationsDatas: [], //分图,
+      requestResult: [], // 请求结果
+      requestRecord: [],
+      commentDescriptionVos: [], //评论列表
+      editableTabs: [],
+    };
+  },
+  watch: {
+    initBatchCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+    analysisTypeCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+  },
+  mounted() {
+    if (this.initBatchCode && this.analysisTypeCode) {
+      this.fetchData(); // 调用合并后的函数
+    }
+  },
+  methods: {
+    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();
+      } catch (e) {
+        console.error(e);
+        this.loading = false;
+      }
+    },
+    getResult({ index, result }) {
+      console.log(index, result);
+      this.$set(this.requestResult, index, result);
+      // this.requestResult[index] = result
+      this.requestRecord[index] = result;
+    },
+    changeRequestNum(index) {
+      if (index <= 1) {
+        this.$set(this.requestRecord, index, "start");
+        return;
+      }
+      if (index > 1) {
+        if (
+          this.requestRecord.every((item) =>
+            ["success", "error"].includes(item)
+          )
+        ) {
+          this.$set(this.requestRecord, index, "start");
+          this.$set(this.requestResult, index, "start");
+        } else {
+          this.$set(this.requestRecord, index, "start");
+        }
+      }
+    },
+    onSubmit() {
+      console.log("submit!");
+    },
+    // 合并后的函数,处理数据请求
+    async fetchData() {
+      try {
+        console.log(
+          this.initBatchCode,
+          this.analysisTypeCode,
+          "请求详情 分钟级"
+        );
+        // 获取分析详情
+        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,
+        });
+        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; //总图数据
+        this.diagramRelationsDatas =
+          result.data &&
+          result.data[0] &&
+          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) {
+      this.fieldEngineCodes = data;
+      console.log(this.fieldEngineCodes, "this.fieldEngineCodes");
+    },
+    //下一条
+    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"></style>
+<style scoped lang="scss">
+.type-variable {
+  display: flex;
+  height: 90%;
+  overflow: hidden;
+
+  .left {
+    width: 30%;
+    height: 100%;
+    overflow: auto;
+    padding: 20px;
+    flex: 1;
+  }
+
+  .right {
+    width: 250px;
+    height: 100%;
+    overflow: hidden;
+  }
+}
+</style>

+ 292 - 3
src/views/overview/components/pitch_tsr_cp/index.vue

@@ -1,17 +1,306 @@
 <!--
  * @Author: your name
  * @Date: 2025-01-09 18:09:55
- * @LastEditTime: 2025-01-10 09:18:38
+ * @LastEditTime: 2025-01-16 10:03:48
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/overview/components/pitch_tsr_cp/index.vue
 -->
+<!-- pitchTsrCp -->
 <template>
-  <div>变桨和叶尖速比及风能利用系数分析</div>
+  <div class="type-variable">
+    <!-- 动态偏航误差分析--只有分图不存在总图 -->
+    <div class="left">
+      <FilterChart
+        :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="charts" v-if="diagramRelationsDatas">
+        <template v-for="(itemChart, indChart) in diagramRelationsDatas">
+          <TwoDMarkersChart
+            :key="itemChart.fieldEngineCode"
+            @getResult="getResult"
+            @changeRequestNum="changeRequestNum"
+            :result="requestResult"
+            :index="indChart"
+            :ref="itemChart.fieldEngineCode"
+            :fileAddr="itemChart.fileAddr"
+          >
+          </TwoDMarkersChart>
+        </template>
+      </div>
+      <el-empty description="暂无分析记录" v-else></el-empty>
+      <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">
+        <el-col :span="2" style="margin: 20px">
+          <el-button type="primary" size="small">提交评论</el-button>
+        </el-col>
+      </el-row>
+    </div>
+    <div class="right">
+      <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 TwoDMarkersChart from "@/views/performance/components/chartsCom/TwoDMarkersChart.vue";
+import TinymceEditor from "@/components/Tinymce.vue";
+import { analysisDetail, queryAnalysisedEngine } from "@/api/performance";
+
 export default {
   name: "pitchTsrCp",
+  components: {
+    DicCard,
+    FilterChart,
+    TwoDMarkersChart,
+    TinymceEditor,
+  },
+  props: {
+    initBatchCode: {
+      default: "",
+      type: String,
+    },
+    analysisTypeCode: {
+      default: "",
+      type: String,
+    },
+    batchCodeList: {
+      default: "",
+      type: Array,
+    },
+  },
+  data() {
+    return {
+      form: {
+        value2: "",
+      },
+      windEngineGroupList: [], //批次风机列表
+      fieldEngineCodes: [], //选中风机
+      comment: "",
+      options: [],
+      generalFilesDatas: [], //总图
+      diagramRelationsDatas: [], //分图,
+      requestResult: [], // 请求结果
+      requestRecord: [],
+      commentDescriptionVos: [], //评论列表
+      editableTabs: [],
+    };
+  },
+  watch: {
+    initBatchCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+    analysisTypeCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+  },
+  mounted() {
+    if (this.initBatchCode && this.analysisTypeCode) {
+      this.fetchData(); // 调用合并后的函数
+    }
+  },
+  methods: {
+    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();
+      } catch (e) {
+        console.error(e);
+        this.loading = false;
+      }
+    },
+    getResult({ index, result }) {
+      console.log(index, result);
+      this.$set(this.requestResult, index, result);
+      // this.requestResult[index] = result
+      this.requestRecord[index] = result;
+    },
+    changeRequestNum(index) {
+      if (index <= 1) {
+        this.$set(this.requestRecord, index, "start");
+        return;
+      }
+      if (index > 1) {
+        if (
+          this.requestRecord.every((item) =>
+            ["success", "error"].includes(item)
+          )
+        ) {
+          this.$set(this.requestRecord, index, "start");
+          this.$set(this.requestResult, index, "start");
+        } else {
+          this.$set(this.requestRecord, index, "start");
+        }
+      }
+    },
+    onSubmit() {
+      console.log("submit!");
+    },
+    // 合并后的函数,处理数据请求
+    async fetchData() {
+      try {
+        console.log(
+          this.initBatchCode,
+          this.analysisTypeCode,
+          "请求详情 分钟级"
+        );
+        // 获取分析详情
+        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,
+        });
+        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; //总图数据
+        this.diagramRelationsDatas =
+          result.data && result.data[0] && result.data[0].diagramRelations;
+      } 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) {
+      this.fieldEngineCodes = data;
+      console.log(this.fieldEngineCodes, "this.fieldEngineCodes");
+    },
+    //下一条
+    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"></style>
+<style scoped lang="scss">
+.type-variable {
+  display: flex;
+  height: 90%;
+  overflow: hidden;
+
+  .left {
+    width: 30%;
+    height: 100%;
+    overflow: auto;
+    padding: 20px;
+    flex: 1;
+  }
+
+  .right {
+    width: 250px;
+    height: 100%;
+    overflow: hidden;
+  }
+}
+</style>

+ 446 - 3
src/views/overview/components/power_curve/index.vue

@@ -1,17 +1,460 @@
 <!--
  * @Author: your name
  * @Date: 2025-01-09 18:10:08
- * @LastEditTime: 2025-01-10 09:18:18
+ * @LastEditTime: 2025-01-15 16:55:39
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/overview/components/power_curve/index.vue
 -->
+
 <template>
-  <div>有功功率曲线分析</div>
+  <div class="type-variable">
+    <!-- 有功功率曲线分析 -->
+    <div class="left">
+      <FilterChart
+        :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>
+      <el-row class="assetssConent">
+        <!-- 总图 -->
+        <template v-if="generalFilesData.length > 0">
+          <template
+            v-if="generalFilesData[0] !== null && generalFilesData.length !== 0"
+          >
+            <template
+              v-for="(powerCurveDom, ind) in generalFilesData"
+              :style="{ marginTop: '50px' }"
+            >
+              <el-col :span="12">
+                <div class="left">
+                  <el-button
+                    @click="
+                      downLoadCsv(
+                        powerCurveDom && powerCurveDom.powerCurveTableData
+                      )
+                    "
+                    >导出表格数据</el-button
+                  >
+                  <el-table
+                    :data="powerCurveDom && powerCurveDom.powerCurveTableData"
+                    border
+                    max-height="500"
+                    style="width: 100%"
+                    align="center"
+                  >
+                    <el-table-column prop="enginName" label="风机名称">
+                    </el-table-column>
+                    <el-table-column prop="engineTypeName" label="风机机型">
+                    </el-table-column>
+                    <el-table-column prop="xData" label="风速">
+                    </el-table-column>
+                    <el-table-column prop="yData" label="实际功率">
+                    </el-table-column>
+                    <el-table-column prop="contractPowerCurve" label="合同功率">
+                    </el-table-column>
+                  </el-table>
+                </div>
+              </el-col>
+              <el-col :span="12">
+                <div class="right">
+                  <PlotlyCharts
+                    :lineMarkerData="powerCurveDom.chartsData"
+                    :comType="'generalDrawing'"
+                    :inds="`zong${ind}`"
+                  ></PlotlyCharts>
+                </div>
+              </el-col>
+            </template>
+          </template>
+        </template>
+        <!-- 分图 -->
+        <template
+          v-if="graphFilesData[0] !== null && graphFilesData.length !== 0"
+        >
+          <template v-for="(powerCurveDom, ind) in graphFilesData">
+            <el-col
+              :span="12"
+              v-if="
+                powerCurveDom &&
+                formInfo.fieldEngineCode ===
+                  powerCurveDom.chartsData.formInfoFieldEngineCode &&
+                powerCurveDom.powerCurveTableData.length > 0
+              "
+            >
+              <div class="left">
+                <!-- <el-button
+                        @click="
+                          downLoadCsv(
+                            powerCurveDom && powerCurveDom.powerCurveTableData
+                          )
+                        "
+                        >导出表格数据</el-button
+                      > -->
+                <el-table
+                  :data="powerCurveDom && powerCurveDom.powerCurveTableData"
+                  border
+                  max-height="500"
+                  style="width: 100%"
+                  align="center"
+                >
+                  <el-table-column prop="enginName" label="风机名称">
+                  </el-table-column>
+                  <el-table-column prop="engineTypeName" label="风机机型">
+                  </el-table-column>
+                  <el-table-column prop="xData" label="风速"> </el-table-column>
+                  <el-table-column prop="yData" label="实际功率">
+                  </el-table-column>
+                  <el-table-column prop="contractPowerCurve" label="合同功率">
+                  </el-table-column>
+                </el-table>
+              </div>
+            </el-col>
+            <el-col
+              v-if="
+                powerCurveDom &&
+                formInfo.fieldEngineCode ===
+                  powerCurveDom.chartsData.formInfoFieldEngineCode &&
+                powerCurveDom.powerCurveTableData.length > 0
+              "
+              :span="12"
+            >
+              <div class="right">
+                <PlotlyCharts
+                  :lineMarkerData="powerCurveDom.chartsData"
+                  :comType="'graph'"
+                  :inds="`fen${ind}`"
+                ></PlotlyCharts>
+              </div>
+            </el-col>
+            <el-empty v-else description="该机组暂无分图"></el-empty>
+          </template>
+        </template>
+      </el-row>
+
+      <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">
+        <el-col :span="2" style="margin: 20px">
+          <el-button type="primary" size="small">提交评论</el-button>
+        </el-col>
+      </el-row>
+    </div>
+    <div class="right">
+      <DicCard></DicCard>
+    </div>
+  </div>
 </template>
 <script>
+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 } from "@/api/performance";
+import PlotlyCharts from "@/views/performance/components/PlotlyCharts.vue";
+import { downLoadCsvFile } from "@/utils/common";
+import { saveAs } from "file-saver";
+import JSZip from "jszip";
+import Papa from "papaparse";
+import axios from "axios";
 export default {
   name: "powerCurve",
+  components: {
+    DicCard,
+    FilterChart,
+    PlotlyCharts,
+    TinymceEditor,
+  },
+  props: {
+    initBatchCode: {
+      default: "",
+      type: String,
+    },
+    analysisTypeCode: {
+      default: "",
+      type: String,
+    },
+    batchCodeList: {
+      default: "",
+      type: Array,
+    },
+  },
+  data() {
+    return {
+      form: {
+        value2: "",
+      },
+      windEngineGroupList: [], //批次风机列表
+      fieldEngineCodes: [], //选中风机
+      comment: "",
+      options: [],
+      generalFilesData: [],
+      graphChartData: {},
+      graphFilesData: [],
+    };
+  },
+  watch: {
+    initBatchCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+    analysisTypeCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+  },
+  mounted() {
+    if (this.initBatchCode && this.analysisTypeCode) {
+      this.fetchData(); // 调用合并后的函数
+    }
+  },
+  methods: {
+    onSubmit() {
+      console.log("submit!");
+    },
+    // 合并后的函数,处理数据请求
+    async fetchData() {
+      try {
+        console.log(
+          this.initBatchCode,
+          this.analysisTypeCode,
+          "请求详情 分钟级"
+        );
+        // 获取分析详情
+        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,
+        });
+        console.log(result, "result");
+        this.generalFilesData = [];
+        this.graphFilesData = [];
+        const generalFiles = result.data[0]?.generalFiles || [];
+        const generalFilesData = await this.filterJsonData(
+          generalFiles,
+          "总图"
+        );
+        //分图数据
+        const graphChartData = await this.filterJsonData(
+          result.data[0]?.diagramRelations || [],
+          "分图"
+        );
+        // 过滤掉 null 或没有 chartsData 的项
+        this.generalFilesData = generalFilesData.filter(
+          (item) => item && item.chartsData
+        );
+        this.graphFilesData = graphChartData.filter(
+          (item) => item && item.chartsData
+        );
+      } catch (err) {
+        console.error("Failed to fetch analysis details:", err);
+      }
+    },
+    //有功功率数据处理
+    async filterJsonData(generalFiles, type) {
+      return Promise.all(
+        generalFiles.map(async (item, ind) => {
+          const types = this.getFileType(item.fileAddr);
+          if (types !== "html" && types !== "image") {
+            //就是json 数据
+            try {
+              const resultChartsData = await axios.get(item.fileAddr);
+
+              // 更新表格数据
+              if (type === "总图") {
+                return {
+                  chartsData: {
+                    ...resultChartsData.data,
+                  },
+                  powerCurveTableData: this.creatPowerCurveTableData(
+                    resultChartsData.data
+                  ).filter((val) => val !== undefined),
+                };
+              } else if (type === "分图") {
+                return {
+                  chartsData: {
+                    ...resultChartsData.data,
+
+                    formInfoFieldEngineCode: this.formInfo.fieldEngineCode,
+                  },
+                  powerCurveTableData: this.creatPowerCurveTableData(
+                    resultChartsData.data
+                  )
+                    .filter((val) => val !== undefined)
+                    .filter(
+                      (item) =>
+                        item?.enginName === this.formInfo.fieldEngineCode ||
+                        item?.enginCode === this.formInfo.fieldEngineCode
+                    ),
+                };
+              }
+            } catch (error) {
+              console.error("Error fetching chart data:", error);
+              return null; // 如果有错误,返回 null
+            }
+          }
+        })
+      );
+    },
+    creatPowerCurveTableData(data) {
+      // 风机名称\风机机型\风速\合同功率\实际功率
+      // xData--风速
+      // yData--实际功率
+      // 合同功率曲线[...]---合同功率
+      // enginName--风机名称
+      // engineTypeName--风机机型
+      const contractPowerCurve =
+        data && data.data.filter((item) => item.enginName === "合同功率曲线");
+      const powerCurveTableData =
+        data &&
+        data.data.map((item, ind) => {
+          if (item.enginName !== "合同功率曲线") {
+            const childData = item.xData.map((child, childInd) => {
+              // 检查是否所有需要的数据都存在
+              if (
+                item.yData[childInd] !== undefined &&
+                contractPowerCurve[0].yData[childInd] !== undefined
+              ) {
+                return {
+                  xData: child,
+                  yData:
+                    item.yData[childInd] !== null ? item.yData[childInd] : 0.0,
+                  contractPowerCurve: contractPowerCurve[0].yData[childInd],
+                  enginName: item.enginName,
+                  enginCode: item.enginCode,
+                  engineTypeName: data.engineTypeName,
+                };
+              }
+            });
+
+            return childData;
+          }
+        });
+
+      return powerCurveTableData.flat();
+    },
+    downLoadCsv(tableDatas) {
+      const headers = ["风机名称", "风机机型", "风速", "实际功率", "合同功率"]; // CSV 文件的标题
+      const data = tableDatas.map((item) => {
+        return [
+          item.enginName,
+          item.engineTypeName,
+          item.xData,
+          item.yData,
+          item.contractPowerCurve,
+        ];
+      });
+      // 将标题和数据组合成 CSV 格式
+      const csvContent = [
+        headers.join(","),
+        ...data.map((row) => row.join(",")),
+      ].join("\n");
+      const fileName = this.windEngineGroupList.filter(
+        (item) => item.engineCode === this.formInfo.fieldEngineCode
+      );
+      downLoadCsvFile(csvContent, "风机有功功率数据");
+      // downLoadCsvFile(csvContent, fileName[0].engineName);
+    },
+    // 请求风机列表
+    async getWindEnfineList(batchCode, analysisTypeCode) {
+      // console.log("请求风机列表 分钟级");
+      const resEngineList = await queryAnalysisedEngine({
+        batchCode: batchCode,
+        analysisTypeCode,
+      });
+      this.windEngineGroupList = resEngineList.data;
+    },
+    handleEditorInput(index, newVal) {
+      // 更新对应的 comment 值
+      // 如果该功能没有实现,可以删除这个方法
+    },
+    //获取选中风机list
+    getEnfineList(data) {
+      this.fieldEngineCodes = data;
+      console.log(this.fieldEngineCodes, "this.fieldEngineCodes");
+    },
+    //下一条
+    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"></style>
+
+<style scoped lang="scss">
+.type-variable {
+  display: flex;
+  height: 90%;
+  overflow: hidden;
+  .left {
+    width: 30%;
+    height: 100%;
+    overflow: auto;
+    padding: 20px;
+    flex: 1;
+  }
+  .right {
+    width: 250px;
+    height: 100%;
+    overflow: hidden;
+  }
+}
+</style>

+ 17 - 0
src/views/overview/components/production_indicator_all/index.vue

@@ -0,0 +1,17 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-01-13 13:45:50
+ * @LastEditTime: 2025-01-13 13:46:07
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/overview/components/production_indicator_all/index.vue
+-->
+<template>
+  <div>全场指标</div>
+</template>
+<script>
+export default {
+  name: "production_indicator_all",
+};
+</script>
+<style scoped lang="scss"></style>

+ 17 - 0
src/views/overview/components/production_indicator_unit/index.vue

@@ -0,0 +1,17 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-01-13 13:44:56
+ * @LastEditTime: 2025-01-13 13:45:23
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/overview/components/production_indicator_unit/index.vue
+-->
+<template>
+  <div>机组指标</div>
+</template>
+<script>
+export default {
+  name: "production_indicator_unit",
+};
+</script>
+<style scoped lang="scss"></style>

+ 191 - 3
src/views/overview/components/wind_direction_frequency/index.vue

@@ -1,17 +1,205 @@
 <!--
  * @Author: your name
  * @Date: 2025-01-10 09:11:12
- * @LastEditTime: 2025-01-10 09:13:44
+ * @LastEditTime: 2025-01-14 17:21:35
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/overview/components/wind_direction_frequency/index.vue
 -->
 <template>
-  <div>风向玫瑰分析</div>
+  <div class="type-variable">
+    <!-- 风向玫瑰分析 -->
+    <div class="left">
+      <FilterChart
+        :windList="windEngineGroupList"
+        @getEnfineList="getEnfineList"
+        @handlePrevious="handlePrevious"
+        @handleNext="handleNext"
+      ></FilterChart>
+      <WindRoseChart></WindRoseChart>
+      <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">
+        <el-col :span="2" style="margin: 20px">
+          <el-button type="primary" size="small">提交评论</el-button>
+        </el-col>
+      </el-row>
+    </div>
+    <div class="right">
+      <DicCard></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 } from "@/api/performance";
+
 export default {
   name: "windDirectionFrequency",
+  components: {
+    DicCard,
+    FilterChart,
+    TinymceEditor,
+    WindRoseChart,
+  },
+  props: {
+    initBatchCode: {
+      default: "",
+      type: String,
+    },
+    analysisTypeCode: {
+      default: "",
+      type: String,
+    },
+    batchCodeList: {
+      default: "",
+      type: Array,
+    },
+  },
+  data() {
+    return {
+      form: {
+        value2: "",
+      },
+      comment: "",
+      options: [],
+      windEngineGroupList: [], //批次风机列表
+      fieldEngineCodes: [], //选中风机
+      generalFilesDatas: [], //总图
+      diagramRelationsDatas: [], //分图
+    };
+  },
+  watch: {
+    initBatchCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+    analysisTypeCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+  },
+  mounted() {
+    if (this.initBatchCode && this.analysisTypeCode) {
+      this.fetchData(); // 调用合并后的函数
+    }
+  },
+  methods: {
+    onSubmit() {
+      console.log("submit!");
+    },
+    // 合并后的函数,处理数据请求
+    async fetchData() {
+      try {
+        console.log(
+          this.initBatchCode,
+          this.analysisTypeCode,
+          "请求详情 分钟级"
+        );
+        // 获取分析详情
+        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,
+        });
+        this.generalFilesDatas =
+          result.data && result.data[0] && result.data[0].generalFiles; //总图数据
+        this.diagramRelationsDatas =
+          result.data && result.data[0] && result.data[0].diagramRelations;
+      } 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) {
+      this.fieldEngineCodes = data;
+      console.log(this.fieldEngineCodes, "this.fieldEngineCodes");
+    },
+    //下一条
+    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"></style>
+<style scoped lang="scss">
+.type-variable {
+  display: flex;
+  height: 90%;
+  overflow: hidden;
+  .left {
+    width: 30%;
+    height: 100%;
+    overflow: auto;
+    padding: 20px;
+    flex: 1;
+  }
+  .right {
+    width: 250px;
+    height: 100%;
+    overflow: hidden;
+  }
+}
+</style>

+ 0 - 0
src/views/overview/components/wind_spee/index.vue → src/views/overview/components/wind_speed/index.vue


+ 190 - 3
src/views/overview/components/wind_speed_frequency/index.vue

@@ -1,17 +1,204 @@
 <!--
  * @Author: your name
  * @Date: 2025-01-10 09:11:34
- * @LastEditTime: 2025-01-10 09:12:56
+ * @LastEditTime: 2025-01-14 17:23:27
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/overview/components/wind_speed_frequency/index.vue
 -->
 <template>
-  <div>风速频率分析</div>
+  <div class="type-variable">
+    <!-- 风速频率分析 -->
+    <div class="left">
+      <FilterChart
+        :windList="windEngineGroupList"
+        @getEnfineList="getEnfineList"
+        @handlePrevious="handlePrevious"
+        @handleNext="handleNext"
+      ></FilterChart>
+      <BarChart></BarChart>
+      <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">
+        <el-col :span="2" style="margin: 20px">
+          <el-button type="primary" size="small">提交评论</el-button>
+        </el-col>
+      </el-row>
+    </div>
+    <div class="right">
+      <DicCard></DicCard>
+    </div>
+  </div>
 </template>
 <script>
+import DicCard from "@/views/overview/components/dicCard/index.vue";
+import FilterChart from "@/views/overview/components/filterChart/index.vue";
+import BarChart from "@/views/performance/components/chartsCom/BarChart.vue";
+import TinymceEditor from "@/components/Tinymce.vue";
+import { analysisDetail, queryAnalysisedEngine } from "@/api/performance";
+
 export default {
   name: "windSpeedFrequency",
+  components: {
+    DicCard,
+    FilterChart,
+    BarChart,
+    TinymceEditor,
+  },
+  props: {
+    initBatchCode: {
+      default: "",
+      type: String,
+    },
+    analysisTypeCode: {
+      default: "",
+      type: String,
+    },
+    batchCodeList: {
+      default: "",
+      type: Array,
+    },
+  },
+  data() {
+    return {
+      form: {
+        value2: "",
+      },
+      comment: "",
+      options: [],
+      windEngineGroupList: [], //批次风机列表
+      fieldEngineCodes: [], //选中风机
+      generalFilesDatas: [], //总图
+      diagramRelationsDatas: [], //分图
+    };
+  },
+  watch: {
+    initBatchCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+    analysisTypeCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+  },
+  mounted() {
+    if (this.initBatchCode && this.analysisTypeCode) {
+      this.fetchData(); // 调用合并后的函数
+    }
+  },
+  methods: {
+    onSubmit() {
+      console.log("submit!");
+    },
+    // 合并后的函数,处理数据请求
+    async fetchData() {
+      try {
+        console.log(
+          this.initBatchCode,
+          this.analysisTypeCode,
+          "请求详情 分钟级"
+        );
+        // 获取分析详情
+        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,
+        });
+        this.generalFilesDatas =
+          result.data && result.data[0] && result.data[0].generalFiles; //总图数据
+        this.diagramRelationsDatas =
+          result.data && result.data[0] && result.data[0].diagramRelations;
+      } 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) {
+      this.fieldEngineCodes = data;
+      console.log(this.fieldEngineCodes, "this.fieldEngineCodes");
+    },
+    //下一条
+    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"></style>
+<style scoped lang="scss">
+.type-variable {
+  display: flex;
+  height: 90%;
+  overflow: hidden;
+  .left {
+    width: 30%;
+    height: 100%;
+    overflow: auto;
+    padding: 20px;
+    flex: 1;
+  }
+  .right {
+    width: 250px;
+    height: 100%;
+    overflow: hidden;
+  }
+}
+</style>

+ 291 - 3
src/views/overview/components/yaw_error_density/index.vue

@@ -1,17 +1,305 @@
 <!--
  * @Author: your name
  * @Date: 2025-01-10 09:26:12
- * @LastEditTime: 2025-01-10 09:26:31
+ * @LastEditTime: 2025-01-16 10:04:45
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/overview/components/yaw_error_density/index.vue
 -->
 <template>
-  <div>动态偏航误差分析</div>
+  <div class="type-variable">
+    <!-- 动态偏航误差分析--只有分图不存在总图 -->
+    <div class="left">
+      <FilterChart
+        :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="charts" v-if="diagramRelationsDatas">
+        <template v-for="(itemChart, indChart) in diagramRelationsDatas">
+          <TwoDMarkersChart
+            :key="itemChart.fieldEngineCode"
+            @getResult="getResult"
+            @changeRequestNum="changeRequestNum"
+            :result="requestResult"
+            :index="indChart"
+            :ref="itemChart.fieldEngineCode"
+            :fileAddr="itemChart.fileAddr"
+          >
+          </TwoDMarkersChart>
+        </template>
+      </div>
+      <el-empty description="暂无分析记录" v-else></el-empty>
+      <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">
+        <el-col :span="2" style="margin: 20px">
+          <el-button type="primary" size="small">提交评论</el-button>
+        </el-col>
+      </el-row>
+    </div>
+    <div class="right">
+      <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 TwoDMarkersChart from "@/views/performance/components/chartsCom/TwoDMarkersChart.vue";
+import TinymceEditor from "@/components/Tinymce.vue";
+import { analysisDetail, queryAnalysisedEngine } from "@/api/performance";
+
 export default {
   name: "yaw_error_density",
+  components: {
+    DicCard,
+    FilterChart,
+    TwoDMarkersChart,
+    TinymceEditor,
+  },
+  props: {
+    initBatchCode: {
+      default: "",
+      type: String,
+    },
+    analysisTypeCode: {
+      default: "",
+      type: String,
+    },
+    batchCodeList: {
+      default: "",
+      type: Array,
+    },
+  },
+  data() {
+    return {
+      form: {
+        value2: "",
+      },
+      windEngineGroupList: [], //批次风机列表
+      fieldEngineCodes: [], //选中风机
+      comment: "",
+      options: [],
+      generalFilesDatas: [], //总图
+      diagramRelationsDatas: [], //分图,
+      requestResult: [], // 请求结果
+      requestRecord: [],
+      commentDescriptionVos: [], //评论列表
+      editableTabs: [],
+    };
+  },
+  watch: {
+    initBatchCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+    analysisTypeCode(newVal) {
+      if (newVal) {
+        this.fetchData(); // 调用合并后的函数
+      }
+    },
+  },
+  mounted() {
+    if (this.initBatchCode && this.analysisTypeCode) {
+      this.fetchData(); // 调用合并后的函数
+    }
+  },
+  methods: {
+    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();
+      } catch (e) {
+        console.error(e);
+        this.loading = false;
+      }
+    },
+    getResult({ index, result }) {
+      console.log(index, result);
+      this.$set(this.requestResult, index, result);
+      // this.requestResult[index] = result
+      this.requestRecord[index] = result;
+    },
+    changeRequestNum(index) {
+      if (index <= 1) {
+        this.$set(this.requestRecord, index, "start");
+        return;
+      }
+      if (index > 1) {
+        if (
+          this.requestRecord.every((item) =>
+            ["success", "error"].includes(item)
+          )
+        ) {
+          this.$set(this.requestRecord, index, "start");
+          this.$set(this.requestResult, index, "start");
+        } else {
+          this.$set(this.requestRecord, index, "start");
+        }
+      }
+    },
+    onSubmit() {
+      console.log("submit!");
+    },
+    // 合并后的函数,处理数据请求
+    async fetchData() {
+      try {
+        console.log(
+          this.initBatchCode,
+          this.analysisTypeCode,
+          "请求详情 分钟级"
+        );
+        // 获取分析详情
+        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,
+        });
+        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; //总图数据
+        this.diagramRelationsDatas =
+          result.data && result.data[0] && result.data[0].diagramRelations;
+      } 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) {
+      this.fieldEngineCodes = data;
+      console.log(this.fieldEngineCodes, "this.fieldEngineCodes");
+    },
+    //下一条
+    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"></style>
+<style scoped lang="scss">
+.type-variable {
+  display: flex;
+  height: 90%;
+  overflow: hidden;
+
+  .left {
+    width: 30%;
+    height: 100%;
+    overflow: auto;
+    padding: 20px;
+    flex: 1;
+  }
+
+  .right {
+    width: 250px;
+    height: 100%;
+    overflow: hidden;
+  }
+}
+</style>

+ 140 - 69
src/views/overview/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="global-variable">
+  <div class="global-variable" v-loading="loading">
     <div class="left">
       <el-input
         class="filterInput"
@@ -13,10 +13,12 @@
         highlight-current
         :props="defaultProps"
         default-expand-all
+        :current-node-key="initBatchCode"
         :filter-node-method="filterNode"
+        @node-click="handleNodeClick"
+        :node-key="'codeNumber'"
         ref="tree"
-      >
-      </el-tree>
+      />
     </div>
     <div class="right">
       <el-menu
@@ -27,91 +29,73 @@
       >
         <template v-for="menuItem in assetssTypeData">
           <el-submenu
-            :index="menuItem.type"
+            :index="menuItem.typeCode + menuItem.typeName"
             v-if="menuItem.children && menuItem.children.length"
           >
-            <template slot="title">{{ menuItem.label }}</template>
+            <template slot="title">{{ menuItem.typeName }}</template>
 
             <el-menu-item
-              :index="submenuItem.type"
               v-for="submenuItem in menuItem.children"
-              >{{ submenuItem.label }}</el-menu-item
+              :index="
+                submenuItem.typeName === '机组指标'
+                  ? 'production_indicator_unit'
+                  : submenuItem.typeName === '全场指标'
+                  ? 'production_indicator_all'
+                  : submenuItem.typeName === '机组故障统计'
+                  ? 'fault_unit'
+                  : submenuItem.typeName === '全场故障统计'
+                  ? 'fault_all'
+                  : submenuItem.typeCode
+              "
+              >{{ submenuItem.typeName }}</el-menu-item
             >
           </el-submenu>
-          <el-menu-item :index="menuItem.type" v-else>{{
-            menuItem.label
+          <el-menu-item :index="menuItem.typeCode" v-else>{{
+            menuItem.typeName
           }}</el-menu-item>
         </template>
       </el-menu>
       <!-- 动态渲染组件 -->
-      <component :is="currentComponent"></component>
+      <component
+        :is="currentComponent"
+        :initBatchCode="initBatchCode"
+        :analysisTypeCode="analysisTypeCode"
+        :batchCodeList="batchCodeList"
+        @setInitBathCode="setInitBathCode"
+      ></component>
     </div>
+
+    <!-- <el-empty description="暂无数据"></el-empty> -->
   </div>
 </template>
 
 <script>
 import { assetssType } from "./js/assetssType";
+import { queryAllAnalysisType, queryAnalysisedEngine } from "@/api/performance";
+import { getAnalysisCodeInfo, getFieldInfo } from "@/api/overview";
 export default {
   data() {
     return {
+      loading: false,
       filterText: "",
-      data: [
-        {
-          id: 1,
-          label: "一级 1",
-          children: [
-            {
-              id: 4,
-              label: "二级 1-1",
-              children: [
-                {
-                  id: 9,
-                  label: "三级 1-1-1",
-                },
-                {
-                  id: 10,
-                  label: "三级 1-1-2",
-                },
-              ],
-            },
-          ],
-        },
-        {
-          id: 2,
-          label: "一级 2",
-          children: [
-            {
-              id: 5,
-              label: "二级 2-1",
-            },
-            {
-              id: 6,
-              label: "二级 2-2",
-            },
-          ],
-        },
-        {
-          id: 3,
-          label: "一级 3",
-          children: [
-            {
-              id: 7,
-              label: "二级 3-1",
-            },
-            {
-              id: 8,
-              label: "二级 3-2",
-            },
-          ],
-        },
-      ],
+      data: [],
+      batchCodeList: [], //批次数组用于切换上一条、下一条
+      initBatchCode: "", // 初始化分析编号
+      analysisTypeCode: "", // 当前选中的分析类型
+      windEngineGroupList: "", //风机编码
       defaultProps: {
         children: "children",
-        label: "label",
+        label: "fieldOrCompanyName",
       },
       currentComponent: () =>
         import("./components/analysis_information/index.vue"), // 默认加载AnalysisInformation组件
-      assetssTypeData: [...assetssType],
+      assetssTypeData: [
+        {
+          id: "1",
+          label: "分析信息",
+          typeCode: "analysis_information",
+        },
+      ],
       activeIndex: "analysis_information", // 默认选中的菜单项
     };
   },
@@ -119,25 +103,107 @@ export default {
     filterText(val) {
       this.$refs.tree.filter(val);
     },
+    // 监听 initBatchCode 的变化,确保树节点正确选中
+    initBatchCode(newVal) {
+      this.$nextTick(() => {
+        const tree = this.$refs.tree;
+        if (tree) {
+          tree.setCurrentKey(newVal); // 设置当前选中的节点
+        }
+      });
+    },
   },
+  created() {
+    this.getTreeData();
 
+    if (this.$route.query.batchCode) {
+      this.initBatchCode = this.$route.query.batchCode;
+    }
+  },
   methods: {
+    // 获取树形结构数据
+    getTreeData() {
+      this.loading = true;
+      getAnalysisCodeInfo().then((res) => {
+        if (res.code === 200) {
+          this.loading = false;
+          this.batchCodeList = [];
+          const formattedData = this.formatData(res.data);
+          this.data = formattedData;
+
+          // 确保数据加载完成后才查询其他分析类型
+          this.queryAllAnalysisType();
+        }
+      });
+    },
+    //数据格式化树形结构
+    formatData(data) {
+      return (
+        data &&
+        data.map((item) => {
+          item.levelstate = "1";
+          if (item.children && item.children.length > 0) {
+            item.children = item.children.map((child) => ({
+              //二级
+              fieldOrCompanyName: child.fieldOrCompanyName,
+              codeNumber: child.codeNumber,
+              levelstate: "2",
+              children: child.batchCodes
+                ? child.batchCodes.map((bc) => {
+                    if (this.initBatchCode === "") {
+                      this.initBatchCode = bc.batchCode; // 初始化选中的节点
+                    }
+                    this.batchCodeList.push(bc.batchCode);
+                    return {
+                      //三级
+                      fieldOrCompanyName: bc.analysisName,
+                      codeNumber: bc.batchCode,
+                      levelstate: "3",
+                    };
+                  })
+                : [],
+            }));
+          }
+          return item;
+        })
+      );
+    },
     // 点击树形节点时
     handleNodeClick(data) {
-      console.log(data);
-      // 根据树形节点的label动态更新右侧菜单选中的项
-      //   this.updateMenuActiveIndex(data);
+      console.log(data, "data");
+      if (data.levelstate === "1" || data.levelstate === "2") {
+        this.$message.warning("当前选中风场未进行任何分析,请重新选择");
+      } else {
+        this.initBatchCode = data.codeNumber; // 更新选中的节点
+      }
+    },
+    //获取
+    queryAllAnalysisType() {
+      queryAllAnalysisType().then((res) => {
+        if (res.code === 200) {
+          this.assetssTypeData = [
+            {
+              id: "1",
+              typeName: "分析信息",
+              typeCode: "analysis_information",
+            },
+            ...res.data,
+          ];
+        }
+      });
     },
-
     // 过滤树形节点的方法
     filterNode(value, data) {
       if (!value) return true;
-      return data.label.indexOf(value) !== -1;
+      return data.fieldOrCompanyName.indexOf(value) !== -1;
+    },
+    setInitBathCode(val) {
+      this.initBatchCode = val;
     },
-
     // 菜单选中项的事件
     handleSelect(key, keyPath) {
-      console.log(key, keyPath);
+      console.log(key, keyPath, "切换");
+      this.analysisTypeCode = key;
       this.currentComponent = () => import(`./components/${key}/index.vue`);
     },
   },
@@ -147,10 +213,15 @@ export default {
 <style scoped lang="scss">
 .global-variable {
   display: flex;
+  height: 90vh;
+  overflow: hidden;
   .left {
     width: 200px;
     border-right: 1px solid #ebeef5;
     padding-right: 20px;
+    margin-bottom: 20px;
+    overflow-y: scroll;
+
     .filterInput {
       margin: 20px 0;
     }

+ 2 - 2
src/views/overview/js/assetssType.js

@@ -223,12 +223,12 @@ export const assetssType = [
       {
         id: "11-1",
         label: "全场指标",
-        type: "production_indicator",
+        type: "production_indicator_all",
       },
       {
         id: "11-2",
         label: "机组指标",
-        type: "production_indicator",
+        type: "production_indicator_unit",
       },
     ],
   },

+ 10 - 5
src/views/performance/assetssDetail.vue

@@ -1,7 +1,7 @@
 <!--
  * @Author: your name
  * @Date: 2024-05-27 09:25:45
- * @LastEditTime: 2025-01-10 15:30:25
+ * @LastEditTime: 2025-01-14 15:41:51
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/performance/assetssDetail.vue
@@ -800,10 +800,15 @@ export default {
       this.getWindCodeList();
     },
     async getDetailInfo() {
-      const formData = new FormData();
-      formData.append("batchCode", this.$route.query.batchCode);
-      formData.append("analysisTypeCode", this.formInfo.analysisTypeCode);
-      formData.append("fieldEngineCode", this.formInfo.fieldEngineCode);
+      // const formData = new FormData();
+      // formData.append("batchCode", this.$route.query.batchCode);
+      // formData.append("analysisTypeCode", this.formInfo.analysisTypeCode);
+      // formData.append("fieldEngineCode", this.formInfo.fieldEngineCode);
+      const formData = {
+        batchCode: this.$route.query.batchCode,
+        analysisTypeCode: this.formInfo.analysisTypeCode,
+        fieldEngineCode: this.formInfo.fieldEngineCode,
+      };
       this.loading = true;
       //获取详情信息
       try {

+ 103 - 12
src/views/performance/assetssMag.vue

@@ -1,6 +1,12 @@
 <template>
   <div class="global-variable">
     <div class="condition">
+      <el-alert
+        title="分析前请核对数据是否导入,点击查看是否导入即可查看"
+        type="warning"
+        show-icon
+      >
+      </el-alert>
       <el-form
         :inline="true"
         ref="ruleForm"
@@ -15,6 +21,7 @@
             placeholder="请输入风场名称"
           ></el-input>
         </el-form-item>
+
         <el-form-item>
           <el-button type="primary" @click="onSubmit" size="small"
             >查询</el-button
@@ -46,8 +53,15 @@
         </el-table-column>
         <el-table-column
           align="center"
-          label="批次名称"
-          prop="batchName"
+          label="分析名称"
+          prop="analysisName"
+          min-width="200"
+        >
+        </el-table-column>
+        <el-table-column
+          align="center"
+          label="数据类型名称"
+          prop="dataTypeName"
           min-width="200"
         >
         </el-table-column>
@@ -215,13 +229,19 @@
           </template>
         </el-table-column>
         <el-table-column
-          prop="createTime"
+          prop="dataStartTime"
           align="center"
-          label="创建时间"
+          label="开始时间"
+          min-width="230"
+        >
+        </el-table-column>
+        <el-table-column
+          prop="dataEndTime"
+          align="center"
+          label="结束时间"
           min-width="230"
         >
         </el-table-column>
-
         <el-table-column
           prop="transition"
           align="center"
@@ -320,19 +340,72 @@
       </div>
       <div></div>
     </el-dialog>
+    <!-- 创建分析弹出框 -->
+    <el-dialog
+      title="创建分析"
+      :visible.sync="addDialogVisible"
+      width="30%"
+      :before-close="AddHandleCloses"
+    >
+      <el-form
+        :model="addRuleForm"
+        :rules="addRules"
+        ref="addRuleForm"
+        label-width="100px"
+        class="add-ruleForm"
+      >
+        <el-form-item label="关联风场" prop="fieldCode">
+          <el-select
+            v-model="addRuleForm.fieldCode"
+            placeholder="请选择关联风场"
+          >
+            <el-option
+              :label="item.fieldName"
+              v-for="item in fieldCodeList"
+              :value="item.codeNumber"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="" prop="analysisName">
+          <b style="color: #f00; font-size: 12px"
+            >不填写分析名称自动采用系统生成的名称!</b
+          >
+        </el-form-item>
+
+        <el-form-item label="分析名称" prop="analysisName">
+          <el-input v-model="addRuleForm.analysisName"></el-input>
+        </el-form-item>
+        <el-form-item label="分析简述" prop="sketch">
+          <el-input type="textarea" v-model="addRuleForm.sketch"></el-input>
+        </el-form-item>
+
+        <el-form-item>
+          <el-button type="primary" @click="addRuleFormSubmit">提交</el-button>
+          <el-button @click="AddHandleCloses">取消</el-button>
+        </el-form-item>
+      </el-form>
+    </el-dialog>
   </div>
 </template>
 
 <script>
 import MyDialog from "./components/dialogCom.vue";
 import { downloadPDF } from "@/utils/common";
-import { getAnalysisResultList, onOffAutoAnalysis } from "@/api/performance";
+import {
+  onOffAutoAnalysis,
+  analysisResultList,
+  addAnalysisResult,
+  queryCodeNum,
+} from "@/api/performance";
+import { from } from "plotly.js-dist";
 export default {
   components: {
     MyDialog,
   },
   data() {
     return {
+      fieldCodeList: [],
+      addDialogVisible: false,
       dialogWidth: "80%",
       intervalId: null,
       startTime: null,
@@ -341,6 +414,16 @@ export default {
       loadingView: false,
       loading: false, //数据加载中
       errorInfo: "",
+      addRules: {
+        fieldCode: [
+          { required: true, message: "请选择关联风场", trigger: "change" },
+        ],
+      },
+      addRuleForm: {
+        fieldCode: "",
+        analysisName: "",
+        sketch: "",
+      },
       rules: {
         fieldName: { trigger: "blur" },
       },
@@ -449,7 +532,7 @@ export default {
     handleAssetssDetail(row, state) {
       const navigateToDetails = () => {
         this.$router.push({
-          path: "/home/performance/assetssDetail",
+          path: "/home/performance/overview",
           query: {
             batchCode: row.batchCode,
             // analysisTypeCode: row.analysisTypeCode,
@@ -539,7 +622,7 @@ export default {
     },
     async fetchData() {
       try {
-        const result = await getAnalysisResultList({
+        const result = await analysisResultList({
           ...this.formInline,
           totalSize: undefined,
         });
@@ -565,12 +648,16 @@ export default {
         if (currentTime - this.startTime >= this.maxPollingTime) {
           this.stopPolling();
         } else {
-          this.fetchData();
+          // 轮询
+          // this.fetchData();
         }
       }, 10000); // 每10秒调用一次
     },
-
-    Newanalyse() {},
+    //创建分析
+    Newanalyse() {
+      this.addDialogVisible = true;
+      this.getQueryCodeNumList();
+    },
     examine() {
       window.open(this.$router.resolve({ path: "/transition" }).href, "_blank");
     },
@@ -633,7 +720,11 @@ export default {
     color: #409eff;
   }
 }
-
+.add-ruleForm {
+  .el-select {
+    width: 100%;
+  }
+}
 .addition {
   display: flex;
   justify-content: flex-end;

+ 10 - 6
src/views/performance/components/EditAnalysis.vue

@@ -1,7 +1,7 @@
 <!--
  * @Author: your name
  * @Date: 2024-05-29 09:14:23
- * @LastEditTime: 2025-01-10 15:30:32
+ * @LastEditTime: 2025-01-14 10:39:56
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/performance/components/EditAnalysis.vue
@@ -818,11 +818,15 @@ export default {
       }
     },
     async getDetailInfo() {
-      const formData = new FormData();
-      formData.append("batchCode", this.$route.query.batchCode);
-      formData.append("analysisTypeCode", this.form.configAnalysis);
-      formData.append("fieldEngineCode", this.form.turbines);
-
+      // const formData = new FormData();
+      // formData.append("batchCode", this.$route.query.batchCode);
+      // formData.append("analysisTypeCode", this.form.configAnalysis);
+      // formData.append("fieldEngineCode", this.form.turbines);
+      const formData = {
+        batchCode: this.$route.query.batchCode,
+        analysisTypeCode: this.form.configAnalysis,
+        fieldEngineCode: this.form.turbines,
+      };
       //获取详情信息
       this.loading = true;
       try {

+ 1 - 2
src/views/performance/components/analysisEvent.vue

@@ -1,7 +1,7 @@
 <!--
  * @Author: your name
  * @Date: 2024-05-29 09:13:51
- * @LastEditTime: 2025-01-10 11:15:52
+ * @LastEditTime: 2025-01-10 16:04:27
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/performance/components/analysisEvent.vue
@@ -587,7 +587,6 @@
         </el-col>
       </el-row>
     </el-card>
-
     <el-drawer
       title="偏好设置"
       :visible.sync="drawer"

+ 119 - 14
src/views/performance/components/chartsCom/BarChart.vue

@@ -1,39 +1,144 @@
 <!--
  * @Author: your name
  * @Date: 2024-09-11 14:30:17
- * @LastEditTime: 2024-10-09 15:31:48
+ * @LastEditTime: 2025-01-14 16:31:27
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/performance/components/chartsCom/BarChart.vue
 -->
+
 <template>
   <div>
-    BarChart
-    <h1>风速频率分析</h1>
+    <!-- BarChart -->
+    <!-- <h1>风速频率分析</h1>
     <h1>风速均值分析</h1>
     <h1>额定风速分析</h1>
-    <h1>环境温度传感器分析2个总图</h1>
-    <div :id="`chart-${inds}`" style="width: 100%; height: 550px"></div>
+    <h1>环境温度传感器分析2个总图</h1> -->
+    <!-- 颜色选择器 -->
+    <!-- 颜色选择器 -->
+    <div style="display: flex; align-items: center">
+      <div style="margin-right: 20px; display: flex; align-items: center">
+        <el-color-picker
+          size="small"
+          v-model="color1"
+          show-alpha
+          @change="updateChartColor"
+        ></el-color-picker>
+        <span style="margin-left: 10px">自定义颜色</span>
+      </div>
+
+      <!-- 图表类型切换按钮 -->
+      <div>
+        <el-button size="small" @click="toggleChartType">
+          切换为{{ chartType === "bar" ? "折线图" : "柱状图" }}
+        </el-button>
+      </div>
+    </div>
+
+    <!-- 图表容器 -->
+    <div id="bar-chart" style="width: 100%; height: 500px"></div>
   </div>
 </template>
+
 <script>
-import { allTypesDatas } from "@/utils/allTypesOfAnalysisData.js";
 import Plotly from "plotly.js-dist";
 export default {
-  props: {},
   data() {
-    return {};
+    return {
+      chartData: {
+        x: ["January", "February", "March", "April", "May"], // 横坐标标签
+        y: [10, 15, 8, 20, 18], // 每个柱或折线点的高度(值)
+      },
+      chartType: "bar", // 当前图表类型 ('bar' 或 'scatter'(折线图))
+      color1: "#406DAB", // 默认柱状图或折线颜色
+      normalRangeMin: 5, // 最低范围
+      normalRangeMax: 18, // 最高范围
+    };
   },
   mounted() {
-    this.initcharts();
+    this.drawChart();
   },
   methods: {
-    initcharts() {
-      const lineDatas = allTypesDatas.filter(
-        (item) => item.analysisTypeCode === "temperature_large_components"
-      )[0];
+    // 绘制图表
+    drawChart() {
+      const trace = {
+        x: this.chartData.x, // 横坐标数据
+        y: this.chartData.y, // 纵坐标数据
+        type: this.chartType, // 当前图表类型 ('bar' 或 'scatter')
+        marker: {
+          color: this.color1, // 默认颜色
+        },
+        line: {
+          color: this.color1, // 折线图颜色
+        },
+      };
+
+      // Normal Range Line trace (LCL/UCL)
+      const normalRangeLine = {
+        x: this.chartData.x, // 横坐标数据
+        y: Array(this.chartData.x.length).fill(this.normalRangeMin), // 设置正常范围的最低值
+        mode: "lines", // 仅显示为线
+        name: "LCL", // 图例名称(LCL)
+        showlegend: true, // 显示图例
+        line: {
+          color: "red", // 线的颜色为红色
+          width: 2, // 线宽
+          dash: "dash", // 虚线样式
+        },
+      };
+
+      const normalRangeMaxLine = {
+        x: this.chartData.x, // 横坐标数据
+        y: Array(this.chartData.x.length).fill(this.normalRangeMax), // 设置正常范围的最大值
+        mode: "lines", // 仅显示为线
+        name: "UCL", // 图例名称(UCL)
+        showlegend: true, // 显示图例
+        line: {
+          color: "red", // 线的颜色为红色
+          width: 2, // 线宽
+          dash: "dash", // 虚线样式
+        },
+      };
+
+      const layout = {
+        title: "Monthly Data", // 图表标题
+        xaxis: {
+          title: "Month", // 横坐标标题
+        },
+        yaxis: {
+          title: "Value", // 纵坐标标题
+        },
+        margin: {
+          l: 50, // 左侧边距
+          r: 50, // 右侧边距
+          t: 50, // 上方边距
+          b: 50, // 下方边距
+        },
+      };
+
+      // 渲染图表
+      Plotly.newPlot(
+        "bar-chart",
+        [trace, normalRangeLine, normalRangeMaxLine],
+        layout,
+        {
+          responsive: true,
+        }
+      );
+    },
+    // 切换图表类型
+    toggleChartType() {
+      this.chartType = this.chartType === "bar" ? "scatter" : "bar";
+      this.drawChart(); // 重新绘制图表
+    },
+    // 更新图表颜色
+    updateChartColor() {
+      this.drawChart(); // 重新绘制图表
     },
   },
 };
 </script>
-<style scoped></style>
+
+<style scoped>
+/* 样式可以根据需求自定义 */
+</style>

+ 255 - 0
src/views/performance/components/chartsCom/FaultAll.vue

@@ -0,0 +1,255 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-01-15 14:24:59
+ * @LastEditTime: 2025-01-15 15:55:10
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/performance/components/chartsCom/FaultAll.vue
+-->
+<template>
+  <div>
+    <div ref="chart" style="width: 100%; height: 400px"></div>
+  </div>
+</template>
+
+<script>
+import Plotly from "plotly.js-dist";
+
+export default {
+  name: "faultAll",
+  props: {
+    faultTypes: {
+      type: Array,
+      default: [],
+    }, // 故障类型
+    faultCounts: {
+      type: Array,
+      default: [],
+    }, // 故障次数
+    faultDurations: {
+      type: Array,
+      default: [],
+    }, // 故障时长
+    zongFaultCsvData: {
+      type: Array,
+      default: [],
+    },
+  },
+  watch: {
+    zongFaultCsvData: {
+      deep: true,
+      handler(v) {
+        console.log("zongFaultCsvData 更新:", v);
+        if (v && v[0] && v[0].data) {
+          this.faultTypes = v[0].data.map((item) => item.fault_detail);
+          this.faultCounts = v[0].data.map((item) => item.count);
+          this.faultDurations = v[0].data.map((item) => item.fault_time_sum);
+          this.renderChart();
+        }
+      },
+    },
+
+    faultCounts: {
+      deep: true,
+      handler(v) {
+        console.log("V 更新:", v);
+        console.log(this.faultDurations, this.faultTypes);
+        this.renderChart();
+      },
+    },
+  },
+  mounted() {
+    this.renderChart();
+  },
+  methods: {
+    renderChart() {
+      // 故障次数的柱状图数据(左侧 Y 轴)
+      const barTrace = {
+        x: this.faultTypes.slice(0, 10),
+        y: this.faultCounts.slice(0, 10),
+        type: "bar",
+        marker: { color: "#64ADC2" }, // 蓝色柱状图
+        name: "故障次数",
+      };
+
+      // 故障时长的折线图数据(右侧 Y 轴)
+      const lineTrace = {
+        x: this.faultTypes.slice(0, 10),
+        y: this.faultDurations.slice(0, 10),
+        type: "scatter",
+        mode: "lines+markers", // 线性图 + 点标记
+        line: { color: "#1A295D" }, // 红色折线
+        name: "故障时长",
+        yaxis: "y2", // 使用第二个 Y 轴(右侧)
+      };
+
+      // 布局配置,设置双 Y 轴
+      const layout = {
+        title: "全场故障次数与时长分析Top10",
+        xaxis: {
+          title: "故障类型",
+          tickangle: 30,
+          tickmode: "array",
+          tickvals: this.faultTypes.slice(0, 10),
+          tickfont: { size: 12 },
+        },
+        yaxis: {
+          title: "故障次数",
+          titlefont: { color: "#64ADC2" },
+          tickfont: { color: "#64ADC2" },
+          side: "left", // 左侧的 Y 轴
+          showline: true,
+          linecolor: "#64ADC2",
+        },
+        yaxis2: {
+          title: "故障时长 (分钟)",
+          titlefont: { color: "#1A295D" },
+          tickfont: { color: "#1A295D" },
+          overlaying: "y", // 在第一个 Y 轴上方绘制
+          side: "right", // 右侧的 Y 轴
+          position: 1, // 调整右侧轴的位置
+          showline: true,
+          linecolor: "#1A295D", // 设置右侧轴线颜色
+        },
+        barmode: "group", // 柱状图分组
+        showlegend: false,
+        // legend: {
+        //   //   x: 1, // 设置 legend 水平位置
+        //   //   y: 1, // 设置 legend 垂直位置
+        //   xanchor: "left", // 使图例与 x 轴对齐
+        //   yanchor: "top", // 使图例与 y 轴对齐
+        //   pad: {
+        //     r: 100, // 设置图表与 legend 之间的上间距
+        //   },
+        // },
+        margin: {
+          t: 80, // 上边距
+          b: 150, // 下边距,给 X 轴标签更多空间
+          //   r: 200, // 增加右边距,避免图例遮挡右侧 Y 轴
+        },
+      };
+
+      // 渲染图表
+      Plotly.newPlot(this.$refs.chart, [barTrace, lineTrace], layout);
+    },
+
+    // renderChart() {
+    //   var trace1 = {
+    //     x: [
+    //       "Jan",
+    //       "Feb",
+    //       "Mar",
+    //       "Apr",
+    //       "May",
+    //       "Jun",
+    //       "Jul",
+    //       "Aug",
+    //       "Sep",
+    //       "Oct",
+    //       "Nov",
+    //       "Dec",
+    //     ],
+    //     y: [
+    //       2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3,
+    //     ],
+    //     type: "bar",
+    //     name: "Evaporation",
+    //     marker: { color: "#5470C6" },
+    //   };
+
+    //   var trace2 = {
+    //     x: [
+    //       "Jan",
+    //       "Feb",
+    //       "Mar",
+    //       "Apr",
+    //       "May",
+    //       "Jun",
+    //       "Jul",
+    //       "Aug",
+    //       "Sep",
+    //       "Oct",
+    //       "Nov",
+    //       "Dec",
+    //     ],
+    //     y: [
+    //       2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3,
+    //     ],
+    //     type: "bar",
+    //     name: "Precipitation",
+    //     yaxis: "y2", // 使用第二个 Y 轴
+    //     marker: { color: "#91CC75" },
+    //   };
+
+    //   var trace3 = {
+    //     x: [
+    //       "Jan",
+    //       "Feb",
+    //       "Mar",
+    //       "Apr",
+    //       "May",
+    //       "Jun",
+    //       "Jul",
+    //       "Aug",
+    //       "Sep",
+    //       "Oct",
+    //       "Nov",
+    //       "Dec",
+    //     ],
+    //     y: [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2],
+    //     type: "scatter",
+    //     mode: "lines+markers",
+    //     name: "Temperature",
+    //     line: { color: "#EE6666" },
+    //     yaxis: "y3", // 使用第三个 Y 轴
+    //   };
+
+    //   var layout = {
+    //     title: "Evaporation, Precipitation, and Temperature",
+    //     xaxis: {
+    //       title: "Month",
+    //     },
+    //     yaxis: {
+    //       title: "Evaporation",
+    //       titlefont: { color: "#5470C6" },
+    //       tickfont: { color: "#5470C6" },
+    //       side: "left",
+    //       showline: true,
+    //       linecolor: "#5470C6",
+    //     },
+    //     yaxis2: {
+    //       title: "Precipitation",
+    //       titlefont: { color: "#91CC75" },
+    //       tickfont: { color: "#91CC75" },
+    //       overlaying: "y",
+    //       side: "right",
+    //       showline: true,
+    //       linecolor: "#91CC75",
+    //     },
+    //     yaxis3: {
+    //       title: "Temperature (°C)",
+    //       titlefont: { color: "#EE6666" },
+    //       tickfont: { color: "#EE6666" },
+    //       overlaying: "y",
+    //       side: "right",
+    //       position: 0.85, // 调整右侧 Y 轴的位置
+    //       showline: true,
+    //       linecolor: "#EE6666",
+    //     },
+    //     showlegend: true,
+    //     margin: {
+    //       t: 50,
+    //       b: 50,
+    //       r: 100,
+    //     },
+    //   };
+
+    //   Plotly.newPlot(this.$refs.chart, [trace1, trace2, trace3], layout);
+    // },
+  },
+};
+</script>
+
+<style scoped>
+/* 你可以根据需要添加样式 */
+</style>

+ 119 - 0
src/views/performance/components/chartsCom/FaultUnit.vue

@@ -0,0 +1,119 @@
+<!--
+ * @Author: your name
+ * @Date: 2025-01-15 15:49:57
+ * @LastEditTime: 2025-01-15 16:32:47
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/performance/components/chartsCom/FaultUntal.vue
+-->
+<template>
+  <div>
+    <div ref="chart" style="width: 100%; height: 400px"></div>
+  </div>
+</template>
+
+<script>
+import Plotly from "plotly.js-dist";
+
+export default {
+  name: "faultAll",
+  props: {
+    faultTypes: {
+      type: Array,
+      default: [],
+    }, // 故障机组
+    faultCounts: {
+      type: Array,
+      default: [],
+    }, // 故障次数
+    faultDurations: {
+      type: Array,
+      default: [],
+    }, // 故障时长
+    fenFaultCsvData: {
+      type: Array,
+      default: [],
+    },
+  },
+  watch: {
+    faultCounts: {
+      deep: true,
+      handler(v) {
+        console.log(this.faultDurations, this.faultTypes);
+        this.renderChart();
+      },
+    },
+  },
+  mounted() {
+    this.renderChart();
+  },
+  methods: {
+    renderChart() {
+      // 故障次数的散点图数据(左侧 Y 轴)
+      const scatterFaultCounts = {
+        x: this.faultTypes.slice(0, 10),
+        y: this.faultCounts.slice(0, 10),
+        mode: "markers", // 散点图
+        marker: { color: "#64ADC2", size: 10 }, // 蓝色散点
+        name: "故障次数",
+      };
+
+      // 故障时长的散点图数据(右侧 Y 轴)
+      const scatterFaultDurations = {
+        x: this.faultTypes.slice(0, 10),
+        y: this.faultDurations.slice(0, 10),
+        mode: "markers", // 散点图
+        marker: { color: "#1A295D", size: 10 }, // 红色散点
+        name: "故障时长",
+        yaxis: "y2", // 使用第二个 Y 轴(右侧)
+      };
+
+      // 布局配置,设置双 Y 轴
+      const layout = {
+        title: "机组故障次数与时长分析Top10",
+        xaxis: {
+          title: "故障机组",
+          tickangle: 30,
+          tickmode: "array",
+          tickvals: this.faultTypes.slice(0, 10),
+          tickfont: { size: 12 },
+        },
+        yaxis: {
+          title: "故障次数",
+          titlefont: { color: "#64ADC2" },
+          tickfont: { color: "#64ADC2" },
+          side: "left", // 左侧的 Y 轴
+          showline: true,
+          linecolor: "#64ADC2",
+        },
+        yaxis2: {
+          title: "故障时长(秒)",
+          titlefont: { color: "#1A295D" },
+          tickfont: { color: "#1A295D" },
+          overlaying: "y", // 在第一个 Y 轴上方绘制
+          side: "right", // 右侧的 Y 轴
+          position: 1, // 调整右侧轴的位置
+          showline: true,
+          linecolor: "#1A295D", // 设置右侧轴线颜色
+        },
+        showlegend: false, // 显示图例
+        margin: {
+          t: 80, // 上边距
+          b: 150, // 下边距,给 X 轴标签更多空间
+        },
+      };
+
+      // 渲染图表
+      Plotly.newPlot(
+        this.$refs.chart,
+        [scatterFaultCounts, scatterFaultDurations],
+        layout
+      );
+    },
+  },
+};
+</script>
+
+<style scoped>
+/* 你可以根据需要添加样式 */
+</style>

+ 13 - 5
src/views/performance/components/chartsCom/HeatmapCharts.vue

@@ -1,16 +1,16 @@
 <!--
  * @Author: your name
  * @Date: 2024-09-11 14:29:26
- * @LastEditTime: 2024-10-08 14:07:53
+ * @LastEditTime: 2025-01-13 15:12:38
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/performance/components/chartsCom/HeatmapCharts.vue
 -->
 <template>
   <div>
-    HeatmapCharts
+    <!-- HeatmapCharts
     <h1>分钟级SCADA数据记录完整度分析</h1>
-    <h1>秒级SCADA数据记录完整度分析</h1>
+    <h1>秒级SCADA数据记录完整度分析</h1> -->
     <div :id="`chart-${inds}`" style="width: 100%; height: 550px"></div>
   </div>
 </template>
@@ -47,7 +47,15 @@ export default {
           // y: ["Morning", "Afternoon"],
           type: "heatmap",
           colorscale: "Viridis", //分钟scads
-          showscale: false, // 应该放在这里
+          colorscale: [
+            [0, "#FCFED4"], // 0% - 50%,保持 "#FCFED4"
+            [0.5, "#FCFED4"], // 50%,结束 "#FCFED4"
+            [0.5, "#5BA8BF"], // 50%-85%,开始 "#C4E4B9"
+            [0.85, "#5BA8BF"], // 85%,结束 "#C4E4B9"
+            [0.85, "#18276E"], // 85%-100%,开始 "#5BA8BF"
+            [1, "#18276E"], // 100%,结束 "#5BA8BF"
+          ],
+          showscale: true, // 显示色带(可选)
           hoverongaps: false,
         },
       ],
@@ -85,7 +93,7 @@ export default {
       for (var i = 0; i < this.initData[0].y.length; i++) {
         for (var j = 0; j < this.initData[0].x.length; j++) {
           const value = this.initData[0].z[i][j];
-          const fontColor = value < 50 ? "white" : "#434443"; // 根据 z 值判断字体颜色
+          const fontColor = value < 50 ? "#434443" : "white"; // 根据 z 值判断字体颜色
           var result = {
             xref: "x",
             yref: "y",

+ 3 - 3
src/views/performance/components/chartsCom/MarkersCharts.vue

@@ -1,14 +1,14 @@
 <!--
  * @Author: your name
  * @Date: 2024-09-11 14:24:26
- * @LastEditTime: 2024-09-29 17:00:22
+ * @LastEditTime: 2025-01-14 13:57:00
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/performance/components/chartsCom/MarkersCharts.vue
 -->
 <template>
   <div>
-    MarkersCharts
+    <!-- MarkersCharts
     <h1>变桨和有功功率协调性分析 2D+3D</h1>
     <h1>变桨和叶尖速比及风能利用系数分析 2D</h1>
     <h1>逐月有功功率散点3D分析 3D</h1>
@@ -16,7 +16,7 @@
     <h1>发电机转速和转矩分析3D+2D+3D</h1>
     <h1>变桨和发电机转速协调性分析2D</h1>
     <h1>最小桨距角分析2D</h1>
-    <h1>叶尖速比-Cp-功率散点分析2D</h1>
+    <h1>叶尖速比-Cp-功率散点分析2D</h1> -->
     <div :id="`chart-${inds}`" style="width: 100%; height: 550px"></div>
   </div>
 </template>

+ 381 - 0
src/views/performance/components/chartsCom/TwoDMarkersChart.vue

@@ -0,0 +1,381 @@
+<template>
+  <div style="min-height: 300px">
+    <div v-inview="handleDomInView"></div>
+    <!-- 2D散点图 -->
+    <template v-if="isShow === true">
+      <div style="display: flex; align-items: center; margin-top: 20px">
+        <div style="margin-right: 20px; display: flex; align-items: center">
+          <el-color-picker
+            size="small"
+            v-model="color1"
+            show-alpha
+            @change="updateChartColor"
+          ></el-color-picker>
+          <span style="margin-left: 10px">自定义颜色</span>
+        </div>
+        <!-- 图表类型切换按钮 -->
+        <div>
+          <el-button size="small" @click="setChartType('scatter')"
+            >散点图</el-button
+          >
+          <el-button size="small" @click="setChartType('line')"
+            >折线图</el-button
+          >
+          <el-button size="small" @click="setChartType('bar')"
+            >柱状图</el-button
+          >
+        </div>
+      </div>
+      <div v-loading="$parent.requestRecord[index] === 'start'">
+        <el-empty
+          v-if="result[index] === 'error'"
+          description="请求失败"
+        ></el-empty>
+        <div v-else ref="plotlyChart" style="height: 400px"></div>
+      </div>
+    </template>
+  </div>
+</template>
+
+<script>
+import Plotly from "plotly.js-dist";
+import axios from "axios";
+export default {
+  props: {
+    fileAddr: {
+      default: "",
+      type: String,
+    },
+    index: {
+      type: Number,
+    },
+    result: {
+      type: Array,
+      default: [],
+    },
+  },
+  data() {
+    return {
+      chartData: {
+        // analysisTypeCode: "动态偏航误差",
+        // engineCode: "WEM00026",
+        // engineTypeName: "",
+        // xaixs: "对风角度(度)",
+        // yaixs: "风速(m/s)",
+        // data: [
+        //   {
+        //     engineName: "#01",
+        //     engineCode: "WOG00935",
+        //     title: "动态偏航误差分析-#01",
+        //     xData: [
+        //       -15.4, -9.2, 4, 7, 9, -6.7, -5.2, -3.9, 0.9, 5.4, 6.4, 12.5, -8.1,
+        //       3.0, -4.5,
+        //     ],
+        //     yData: [
+        //       3.3, 3.7, 1290.0, 10, 7, 8, 858.0, 987.0, 1159.0, 1091.0, 979.0,
+        //       1023.5, 1210.1, 1105.4, 970.2,
+        //     ],
+        //     colorbartitle: "密度",
+        //   },
+        // ],
+      },
+      chartType: "scatter", // 初始化为散点图 (scatter)
+      color1: "", // 默认颜色
+      selectedPoints: [],
+      originalColors: [],
+      originalSizes: [],
+      isShow: undefined,
+    };
+  },
+  watch: {
+    result: {
+      deep: true,
+      handler(v) {
+        console.log("-----------------------", v);
+        // const resultArr = ['success', 'fail']
+        // if(!this.result.every(item => resultArr.includes(item))) {
+        //   if(this.index<this.result.length) {
+        //     this.getData()
+        //   }
+        // }
+        const startIndex = this.$parent.requestRecord.findIndex(
+          (item) => item === "start"
+        );
+        if (startIndex > -1) {
+          if (startIndex === this.index) {
+            this.getData();
+          }
+        }
+      },
+    },
+  },
+  async mounted() {
+    if (this.index === 0) {
+      this.getData();
+    }
+  },
+  methods: {
+    handleDomInView(isInView) {
+      if (isInView && this.isShow === undefined) {
+        this.isShow = isInView;
+        this.$emit("changeRequestNum", this.index);
+      }
+      console.log(this.isShow);
+    },
+    async getData() {
+      if (this.fileAddr !== "") {
+        try {
+          const resultChartsData = await axios.get(this.fileAddr);
+          this.chartData = resultChartsData.data;
+          this.drawChart();
+          this.$emit("getResult", { index: this.index, result: "success" });
+        } catch (error) {
+          this.$emit("getResult", { index: this.index, result: "error" });
+        }
+      }
+    },
+    drawChart() {
+      const data = this.chartData.data && this.chartData.data[0];
+      let trace = {};
+      // 保存原始颜色和大小
+      this.originalColors = [...data.yData];
+      this.originalSizes = new Array(data.xData.length).fill(6); // 初始点大小
+      if (this.chartType === "scatter") {
+        // 绘制 2D 散点图
+        console.log("重新绘制图表", this.color1);
+        trace = {
+          x: data.xData,
+          y: data.yData,
+          mode: "markers",
+          type: "scattergl", // 这里改为 scattergl
+          text: data.engineName, // 提示文本
+          marker: {
+            color: data.yData, // 根据 yData 的值设置颜色
+            // colorscale: "Viridis", // 使用的颜色区间
+            colorscale: this.color1
+              ? [
+                  [0, "#F9FDD2"], // 颜色从 this.color1 开始
+                  [1, this.color1], // 结束颜色为其他颜色
+                ]
+              : [
+                  [0, "#F9FDD2"],
+                  [0.15, "#E9F6BD"],
+                  [0.3, "#C2E3B9"],
+                  [0.45, "#8AC8BE"],
+                  [0.6, "#5CA8BF"],
+                  [0.75, "#407DB3"],
+                  [0.9, "#2E4C9A"],
+                  [1, "#1B2973"],
+                ],
+            colorbar: {
+              title: data.colorbartitle, // 色标标题
+            },
+            size: new Array(data.xData.length).fill(6), // 初始点大小
+          },
+        };
+      } else if (this.chartType === "line") {
+        // 折线图
+        trace = {
+          x: data.xData,
+          y: data.yData,
+          mode: "lines",
+          type: "scattergl", // 折线图
+          text: data.engineName,
+          line: {
+            color: this.color1, // 使用自定义颜色
+          },
+        };
+      } else if (this.chartType === "bar") {
+        // 柱状图
+        trace = {
+          x: data.xData,
+          y: data.yData,
+          type: "bar", // 柱状图
+          marker: {
+            color: this.color1, // 使用自定义颜色
+          },
+        };
+      }
+      // 图表布局;
+      const layout = {
+        title: data.title,
+        xaxis: {
+          title: this.chartData.xaixs,
+        },
+        yaxis: {
+          title: this.chartData.yaixs,
+        },
+        showlegend: false,
+      };
+      const config = {
+        modeBarButtonsToAdd: [
+          {
+            name: "选择",
+            icon: Plotly.Icons.pencil,
+            click: (gd) => this.handleSelectClick(gd),
+          },
+          {
+            name: "清除选中",
+            icon: Plotly.Icons.undo,
+            click: (gd) => this.handleClearSelect(gd),
+          },
+          {
+            name: "下载CSV文件",
+            icon: Plotly.Icons.disk,
+            click: (gd) => this.handleDownloadCSV(gd),
+          },
+        ],
+        modeBarButtonsToRemove: [
+          // 移除不需要的工具按钮
+          "lasso2d",
+        ],
+        displaylogo: false,
+        editable: true,
+        scrollZoom: false,
+      };
+      // 使用 Plotly 绘制图表
+      Plotly.react(this.$refs.plotlyChart, [trace], layout, config).then(() => {
+        // 确保在图表加载完成后设置工具栏按钮
+        const plotElement = this.$refs.plotlyChart;
+        Plotly.relayout(plotElement, layout); // 使用 relayout 来确保自定义按钮应用
+      });
+    },
+
+    handleSelectClick(gd) {
+      // 绑定 plotly_click 事件
+      gd.on("plotly_click", (data) => {
+        const pointIndex = data.points[0].pointIndex;
+        const xClick = data.points[0].x;
+        const yClick = data.points[0].y;
+
+        // 将点击的点添加到选中的点数组
+        this.selectedPoints.push({
+          x: xClick, // 点击点的 x 坐标
+          y: yClick, // 点击点的 y 坐标
+          index: pointIndex, // 点击点的索引
+          time: data.points[0].text, // 点击点的时间信息
+        });
+
+        // 初始化颜色和大小数组
+        let newColors = [...this.originalColors];
+        let newSize = [...this.originalSizes];
+
+        // 如果选中的点数大于等于3,进行多边形选择区域的处理
+        if (this.selectedPoints.length >= 3) {
+          const xv = this.selectedPoints.map((p) => p.x);
+          const yv = this.selectedPoints.map((p) => p.y);
+
+          // 判断点是否在多边形内
+          function inPolygon(x, y, xv, yv) {
+            let inside = false;
+            for (let i = 0, j = xv.length - 1; i < xv.length; j = i++) {
+              const intersect =
+                yv[i] > y !== yv[j] > y &&
+                x < ((xv[j] - xv[i]) * (y - yv[i])) / (yv[j] - yv[i]) + xv[i];
+              if (intersect) inside = !inside;
+            }
+            return inside;
+          }
+
+          // 用于跟踪已添加的 (x, y) 组合
+          const addedPoints = {};
+
+          // 遍历图表数据中的所有点,检查是否在多边形内
+          gd.data[0].x.forEach((xVal, i) => {
+            const yVal = gd.data[0].y[i];
+            if (inPolygon(xVal, yVal, xv, yv)) {
+              const pointKey = `${xVal}-${yVal}`;
+              if (!addedPoints[pointKey]) {
+                this.selectedPoints.push({
+                  x: gd.data[0].x[i],
+                  y: gd.data[0].y[i],
+                  time: gd.data[0].text[i],
+                });
+
+                newColors[i] = "red"; // 高亮选择的点
+                newSize[i] = 10; // 设置点的大小
+                addedPoints[pointKey] = true;
+              }
+            }
+          });
+        }
+
+        // 更新选中点的颜色和大小
+        this.selectedPoints.forEach((point) => {
+          newColors[point.index] = "red";
+          newSize[point.index] = 10;
+        });
+
+        // 使用 Plotly.restyle 更新颜色和大小
+        Plotly.restyle(gd, {
+          "marker.color": [newColors],
+          "marker.size": [newSize],
+        });
+
+        // 处理选中的数据
+        this.getSelectData(this.selectedPoints, gd.layout);
+      });
+    },
+
+    handleClearSelect(gd) {
+      this.selectedPoints = [];
+      Plotly.restyle(gd, {
+        "marker.color": [this.originalColors],
+        "marker.size": [this.originalSizes],
+      });
+    },
+    getSelectData(selectedPoints, layout) {
+      // 在这里处理选中的数据,您可以将其展示或导出等
+      console.log("选中的点数据:", selectedPoints);
+      console.log("布局信息:", layout);
+    },
+    handleDownloadCSV(gd) {
+      if (this.selectedPoints.length === 0) {
+        alert("没有选中的数据");
+        return;
+      }
+      this.downloadCSV();
+    },
+
+    downloadCSV() {
+      const headers = [this.chartData.xaixs, this.chartData.yaixs];
+      const csvRows = [headers]; // 保存标头
+      // 使用 Set 或 Map 去重
+      const uniquePoints = [];
+      this.selectedPoints.forEach((point) => {
+        if (!uniquePoints.some((p) => p.x === point.x && p.y === point.y)) {
+          uniquePoints.push(point);
+        }
+      });
+
+      // 将去重后的点加入 CSV 数据
+      uniquePoints.forEach((point) => {
+        csvRows.push(`${point.x},${point.y}`);
+      });
+
+      const csvString = csvRows.join("\n");
+      const blob = new Blob([csvString], { type: "text/csv; charset=utf-8" });
+      const url = URL.createObjectURL(blob);
+      const a = document.createElement("a");
+      a.href = url;
+      a.download = "selected_data.csv";
+      a.click();
+      URL.revokeObjectURL(url);
+    },
+    setChartType(type) {
+      // 切换图表类型
+      this.chartType = type;
+      this.drawChart(); // 重新绘制图表
+    },
+
+    updateChartColor(color) {
+      // 更新图表颜色
+      this.color1 = color;
+      console.log(this.color1, "this.color1");
+      this.drawChart();
+    },
+  },
+};
+</script>
+
+<style scoped></style>

+ 5 - 5
src/views/performance/components/chartsCom/WindRoseChart.vue

@@ -1,6 +1,6 @@
 <template>
   <div>
-    <h1>风向玫瑰分析</h1>
+    <!-- <h1>风向玫瑰分析</h1> -->
     <div :id="`plotDiv-${inds}`" style="width: 100%; height: 550px"></div>
   </div>
 </template>
@@ -70,10 +70,10 @@ export default {
         "NNW",
       ];
       const colorscale = {
-        "[0,3)": "rgba(247, 251, 255, 1.0)",
-        "[3,6)": "rgba(171, 207, 229, 1.0)",
-        "[6,9)": "rgba(55, 135, 192, 1.0)",
-        ">=9": "rgba(8, 48, 107, 1.0)",
+        "[0,3)": "#FBFDD4",
+        "[3,6)": "#C0E2BA",
+        "[6,9)": "#57A3BF",
+        ">=9": "#1A2971",
       };
 
       const speedBins = this.data.map((item) => {

+ 1 - 1
src/views/performance/editAssets.vue

@@ -1,7 +1,7 @@
 <!--
  * @Author: your name
  * @Date: 2024-05-27 09:26:31
- * @LastEditTime: 2025-01-10 15:25:07
+ * @LastEditTime: 2025-01-14 13:29:47
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/performance/editAssets.vue

Some files were not shown because too many files changed in this diff