Browse Source

修改振动页面渲染树形结构

liujiejie 3 tuần trước cách đây
mục cha
commit
07c53011d1

+ 29 - 29
.env.dev

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

+ 29 - 26
.env.jl

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

+ 9 - 5
downLoadServer/src/server/controllers/chartController.js

@@ -48,14 +48,18 @@ const handleChartGeneration = async (
     //    const response = await axios.get(fileAddr);
     // 从URL获取数据
     const path = fileAddr.replace(/^https?:\/\/[^/]+/, "");
-    console.log(
-      `http://${process.env.MINIO_ENDPOINT}:${process.env.MINIO_PORT}${path}`,
-      "链接",
-    );
-
+    // console.log("[handleChartGeneration] start fetch:", {
+    //   url: `http://${process.env.MINIO_ENDPOINT}:${process.env.MINIO_PORT}${path}`,
+    //   bucketName,
+    //   objectName,
+    // });
     const response = await axios.get(
       `http://${process.env.MINIO_ENDPOINT}:${process.env.MINIO_PORT}${path}`,
+      {
+        timeout: 60000, // 例如 60 秒,可按需要调
+      },
     );
+    // console.log("[handleChartGeneration] fetch done");
     let data = [];
     // console.log("获取到的数据:", data);/
     if (typeof response.data === "string") {

BIN
downLoadServer/src/server/server.zip


+ 12 - 6
downLoadServer/src/server/utils/chartsCom/ColorbarInitTwoDmarkersChart.js

@@ -15,13 +15,19 @@ export const generateColorbarInitTwoDmarkersChart = async (
   if (!scatterData) throw new Error("scatterData missing");
 
   // ✅ 构造颜色刻度(更合理)
-  const colorStops = [colorsBar[0], colorsBar[4], colorsBar[8], colorsBar[12]];
-
-  const colorscale = colorStops.map((color, index) => [
-    index / (colorStops.length - 1),
-    color,
-  ]);
+  // const colorStops = [colorsBar[0], colorsBar[4], colorsBar[8], colorsBar[12]];
 
+  // const colorscale = colorStops.map((color, index) => [
+  //   index / (colorStops.length - 1),
+  //   color,
+  // ]);
+  const colorscale = [
+    [0, "#e6f4f5"], // 0%
+    [0.1, "#74ABF7"], // 1%
+    // [0.1, "#3E7AF6"], // 1%
+    [0.2, "#0000E4"], // 30%
+    [1, "#020085"], // 100%
+  ];
   // ✅ colorbar 数据
   const hasColorbar =
     scatterData.colorbar &&

+ 3 - 2
src/utils/request.js

@@ -27,11 +27,12 @@ service.interceptors.request.use(
         router.push("/login");
       }
     }
+    console.log(config, process.env.VUE_APP_APIPROXY, "链接");
     return config;
   },
   (error) => {
     return Promise.reject(error);
-  }
+  },
 );
 
 /**
@@ -104,7 +105,7 @@ service.interceptors.response.use(
       });
     }
     return Promise.reject(error);
-  }
+  },
 );
 
 /**

+ 258 - 0
src/views/health/components/loadTree.vue

@@ -0,0 +1,258 @@
+<!--
+ * @Author: your name
+ * @Date: 2026-03-20 15:02:08
+ * @LastEditTime: 2026-03-23 11:09:44
+ * @LastEditors: MacBookPro
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/health/components/tree.vue
+-->
+<template>
+  <div class="tree-page">
+    <el-card>
+      <el-date-picker
+        v-model="value1"
+        type="datetimerange"
+        value-format="yyyy-MM-dd HH:mm:ss"
+        :default-expanded-keys="defaultExpandedKeys"
+        range-separator="——"
+        start-placeholder="开始日期"
+        end-placeholder="结束日期"
+        size="small"
+        @change="handleDatePicker"
+      >
+      </el-date-picker>
+      <div style="margin-top: 15px">
+        <el-input
+          placeholder="请输入风场名称"
+          size="small"
+          v-model="fromObj.windFarmName"
+        >
+          <el-button slot="append" @click="handleSearch" icon="el-icon-search"
+            >搜索</el-button
+          >
+        </el-input>
+      </div>
+      <el-tree
+        :key="treeKey"
+        ref="tree"
+        :props="props"
+        :load="loadNode"
+        lazy
+        node-key="itemKey"
+        highlight-current
+        @node-click="handleNodeClick"
+      >
+        <span class="custom-tree-node" slot-scope="{ node, data }">
+          <el-tooltip effect="dark" :content="data.itemValue" placement="top">
+            <span class="node-label">{{ data.itemValue }}</span>
+          </el-tooltip>
+        </span>
+      </el-tree>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import axios from "axios";
+export default {
+  name: "WindTree",
+
+  data() {
+    return {
+      treeKey: 0,
+      rootDataCache: [], // 🌟 根节点缓存
+      fromObj: {
+        beginTime: "",
+        endTime: "",
+        windFarmCode: "",
+        windFarmName: "",
+      },
+      value1: [],
+      defaultExpandedKeys: [], // 🌟 默认展开key
+      // 🌟 tree配置
+      props: {
+        label: "itemValue",
+        children: "nextData",
+        isLeaf: (data, node) => {
+          // 🌟 一级节点(风场)一律认为有子节点
+          if (node.level === 1) return false;
+
+          // 🌟 其他层再用 hasNext
+          return !data.hasNext;
+        },
+      },
+
+      // 🌟 是否使用本地 nextData(你现在接口是嵌套的)
+      useLocalData: true,
+    };
+  },
+  created() {
+    this.handleSearch();
+  },
+  methods: {
+    // ===========================
+    // 🌟 搜索(统一入口)
+    // ===========================
+    async handleSearch() {
+      // 1️⃣ 获取数据
+      const rootData = await this.fetchRootData();
+      this.rootDataCache = rootData;
+
+      // 2️⃣ 默认展开第一个风场
+      if (rootData.length > 0) {
+        this.defaultExpandedKeys = [rootData[0].itemKey];
+      }
+
+      // 3️⃣ 强制刷新
+      this.treeKey++;
+
+      // 4️⃣ 等渲染后 → 自动展开二级
+      this.$nextTick(() => {
+        const tree = this.$refs.tree;
+        const first = tree.store.nodesMap[rootData?.[0]?.itemKey];
+
+        if (!first) return;
+
+        // 一级
+        first.expand();
+
+        // 二级(更稳写法)
+        const expandSecond = () => {
+          const child = first.childNodes[0];
+          if (child) {
+            child.expand();
+          } else {
+            setTimeout(expandSecond, 100);
+          }
+        };
+
+        expandSecond();
+      });
+    },
+    // ===========================
+    // 🌟 获取根数据
+    // ===========================
+    async fetchRootData() {
+      const res = await axios.post(
+        "/transDataWeb/waveData/getAllWindFarmAndFirstData",
+        { ...this.fromObj },
+      );
+
+      return res.data.datas || [];
+    },
+
+    async loadNode(node, resolve) {
+      // 根节点
+      if (node.level === 0) {
+        return resolve(this.rootDataCache);
+      }
+
+      const data = node.data;
+
+      let children = [];
+
+      // 🌟 第一个风场(有 nextData)
+      if (data.nextData && data.nextData.length > 0) {
+        children = data.nextData;
+      }
+
+      // 🌟 其他风场(强制走接口,不管 hasNext)
+      else {
+        children = await this.getChildList(data);
+      }
+
+      resolve(children || []);
+    },
+
+    handleDatePicker() {
+      this.fromObj.beginTime = this.value1 ? this.value1[0] : "";
+      this.fromObj.endTime = this.value1 ? this.value1[1] : "";
+
+      this.handleSearch();
+    },
+
+    handleNodeClick(data, node) {
+      // 🌟 非叶子节点
+      if (!node.isLeaf) {
+        if (!node.loaded) {
+          if (node.loading) return;
+          node.expand();
+        } else {
+          node.expanded = !node.expanded;
+        }
+        return;
+      }
+
+      // 🌟 叶子节点
+      const path = this.getNodePath(node);
+      this.loadChartData && this.loadChartData(data, path);
+    },
+    getNodePath(node) {
+      const path = [];
+      let current = node;
+
+      while (current) {
+        if (current.data) {
+          path.unshift(current.data.itemValue);
+        }
+        current = current.parent;
+      }
+
+      return path;
+    },
+    // ===========================
+    // 🌟 子节点请求
+    // ===========================
+    async getChildList(nodeData) {
+      // 🌟 加缓存
+      if (nodeData._childrenCache) {
+        return nodeData._childrenCache;
+      }
+      this.fromObj.windFarmCode = nodeData.itemKey;
+      const res = await axios.post("/transDataWeb/waveData/getWindFarmData", {
+        ...this.fromObj,
+      });
+      const list = res.data.datas || [];
+
+      // 🌟 缓存
+      nodeData._childrenCache = list;
+
+      return list;
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+::v-deep.tree-page {
+  height: 100%;
+  padding: 0px 20px 0px 0px;
+  .el-card {
+    height: 100%;
+
+    .el-card__body {
+      height: 100%;
+      padding: 10px;
+      overflow: scroll;
+      .el-range-editor.el-input__inner {
+        width: auto;
+      }
+    }
+  }
+}
+::v-deep .custom-tree-node {
+  display: flex;
+  align-items: center;
+  width: 100%;
+  font-size: 12px;
+}
+
+::v-deep .node-label {
+  display: inline-block;
+  max-width: 200px; // 🌟 控制宽度(关键)
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  vertical-align: middle;
+}
+</style>

+ 848 - 0
src/views/health/components/spectrogramchartsNew.vue

@@ -0,0 +1,848 @@
+<template>
+  <div>
+    <!-- ECharts 图表容器 -->
+    <div class="line-chart" ref="chart"></div>
+    <div class="control-panel">
+      <!-- 频率范围 -->
+      <div class="box full-row">
+        <div class="panel-block">
+          <span class="label1">频率</span>
+          <el-input v-model="freqMin" size="mini" placeholder="下限" />
+          <span>~</span>
+          <el-input v-model="freqMax" size="mini" placeholder="上限" />
+          <el-button size="mini" type="primary" @click="handleFreqRange">
+            应用
+          </el-button>
+        </div>
+      </div>
+    </div>
+    <div class="control-panel">
+      <!-- 手动标注 -->
+      <div class="panel-block">
+        <span class="label1">标注</span>
+        <el-input v-model="manualFreq" size="mini" placeholder="频率" />
+        <el-input v-model="multiple" size="mini" placeholder="倍频" />
+        <el-button size="mini" type="success" @click="handleMark"
+          >标注</el-button
+        >
+      </div>
+      <!-- 光标(单选) -->
+      <div class="panel-block">
+        <span class="label">光标</span>
+        <div class="btn-group">
+          <span
+            v-for="item in GBcheckList"
+            :key="item.val"
+            :class="['btn', checkedGB.includes(item.val) ? 'active' : '']"
+            @click="selectCursor(item.val)"
+          >
+            {{ item.val }}
+          </span>
+        </div>
+      </div>
+
+      <!-- 特征选择(四分类) -->
+      <div class="feature-grid">
+        <div
+          class="feature-item"
+          v-for="group in featureGroups"
+          :key="group.type"
+        >
+          <div class="label">{{ group.label }}</div>
+          <el-select
+            v-model="selectedMap[group.type]"
+            multiple
+            collapse-tags
+            size="small"
+            placeholder="请选择"
+            :disabled="!group.children.length"
+            @change="handleFeatureChange"
+          >
+            <el-option
+              v-for="item in group.children"
+              :key="item"
+              :label="item"
+              :value="item"
+            />
+          </el-select>
+        </div>
+      </div>
+      <!-- 特征值(多选) -->
+      <!-- <div class="panel-block full-width">
+        <span class="label">特征 </span>
+        <el-cascader
+          v-model="selectedFeatures"
+          :options="cascaderOptions"
+          :props="cascaderProps"
+          collapse-tags
+          clearable
+          placeholder="请选择特征"
+          size="small"
+          @change="handleCascaderChange"
+        />
+      </div> -->
+    </div>
+  </div>
+</template>
+
+<script>
+import axios from "axios";
+import * as echarts from "echarts";
+import cursorReferenceMixin from "./spectrogramcharts/cursorReferenceMixin";
+import Bdgb from "./spectrogramcharts/Bdgb";
+import Xdgb from "./spectrogramcharts/Xdgb";
+import Tjgb from "./spectrogramcharts/Tjgb";
+
+export default {
+  name: "TimedomainCharts",
+  mixins: [cursorReferenceMixin, Bdgb, Xdgb, Tjgb],
+
+  props: {
+    currentIndex: {
+      type: Number,
+      default: 0,
+    },
+    activeIndex: {
+      type: Number,
+      default: 0,
+    },
+    ids: {
+      type: Array,
+      default: () => [],
+    },
+    spectrumListTwo: {
+      type: Object,
+      default: () => ({}),
+    },
+    currentRow: {
+      type: Object,
+      default: () => ({}),
+    },
+    windCode: {
+      type: String,
+      default: "",
+    },
+  },
+  data() {
+    return {
+      freqMin: "",
+      freqMax: "",
+      manualFreq: "",
+      multiple: 1,
+      manualMarks: [],
+      chartInstance: null,
+      option: null,
+      // TZshow: false,
+      BGshow: false,
+      PXshow: false,
+      spectrumList: {},
+
+      GBcheckList: [
+        { val: "添加光标", checked: false, disabled: false },
+        { val: "谐波光标", checked: false, disabled: false },
+        { val: "边带光标", checked: false, disabled: false },
+        { val: "移动峰值", checked: false, disabled: false },
+      ],
+      PXcheckList: [
+        { val: "转速频率", checked: false },
+        { val: "内圈故障频率", checked: false },
+        { val: "外圈故障频率", checked: false },
+        { val: "滚动体故障频率", checked: false },
+        { val: "保持架频率", checked: false },
+        { val: "叶片频率", checked: false },
+      ],
+      featureGroups: [
+        {
+          label: "轴承故障",
+          type: "bearing",
+          children: ["GEN-DE BPFO", "GEN-DE BPFI", "GEN-DE BSF", "GEN-DE FTF"],
+        },
+        {
+          label: "转动基频",
+          type: "rotation",
+          children: ["转速频率"],
+        },
+        {
+          label: "结构频率",
+          type: "structure",
+          children: ["叶片频率"],
+        },
+        {
+          label: "齿轮特征",
+          type: "gear",
+          children: [],
+        },
+      ],
+      featureKeyMap: {
+        "GEN-DE BPFO": "BPFO",
+        "GEN-DE BPFI": "BPFI",
+        "GEN-DE BSF": "BSF",
+        "GEN-DE FTF": "FTF",
+        转速频率: "Fr",
+        叶片频率: "B3P",
+      },
+      // 每个分类单独存
+      selectedMap: {
+        bearing: [],
+        rotation: [],
+        structure: [],
+        gear: [],
+      },
+
+      // 最终统一给图表用
+      checkedFeatures: [],
+      selectedFeatures: [],
+      cascaderProps: {
+        multiple: true, // 多选
+        checkStrictly: false, // 不能选父节点(只选叶子)
+        emitPath: true, // 返回路径(重要)
+      },
+      Fr: [],
+      BPFI: [],
+      BPFO: [],
+      BSF: [],
+      FTF: [],
+      B3P: [],
+      checkedGB: [],
+      checkedValues: [],
+    };
+  },
+  computed: {
+    cascaderOptions() {
+      return this.featureGroups.map((group) => ({
+        label: group.label,
+        value: group.label,
+        children: group.children.map((item) => ({
+          label: item,
+          value: item,
+        })),
+      }));
+    },
+  },
+  watch: {
+    spectrumListTwo(newValue) {
+      this.spectrumList = newValue;
+      if (this.chartInstance) {
+        this.updateChart(this.spectrumList.y, this.spectrumList.x);
+      }
+    },
+    spectrumList: {
+      handler(newValue) {
+        if (!newValue) return;
+        if (this.chartInstance) {
+          this.updateChart(newValue.y, newValue.x);
+        }
+      },
+      deep: true,
+    },
+  },
+  beforeDestroy() {
+    if (this.chartInstance) {
+      this.chartInstance.getZr().off("dblclick", this.handleDoubleClick);
+      this.chartInstance
+        .getZr()
+        .off("mousemove", this.handleSidebandCursorMove);
+      this.chartInstance.dispose();
+    }
+  },
+  mounted() {
+    this.$nextTick(() => {
+      setTimeout(() => {
+        this.initializeChart();
+        this.getTime();
+      }, 500);
+    });
+  },
+  methods: {
+    handleFeatureChange() {
+      // 1️⃣ 汇总所有选中的特征
+      const allSelected = Object.values(this.selectedMap).flat();
+
+      this.checkedFeatures = allSelected;
+
+      // 2️⃣ 更新图表
+      this.updateFeatureLinesByGroup();
+    },
+    // handleCascaderChange(val) {
+    //   // val 示例:
+    //   // [
+    //   //   ["轴承故障", "GEN-DE BPFO"],
+    //   //   ["转动基频", "转速频率"]
+    //   // ]
+
+    //   // 👉 只取最后一层(真正的特征)
+    //   this.checkedFeatures = val.map((item) => item[item.length - 1]);
+
+    //   this.updateFeatureLines();
+    // },
+    selectCursor(val) {
+      // 单选逻辑
+      if (this.checkedGB.includes(val)) {
+        this.checkedGB = [];
+      } else {
+        this.checkedGB = [val];
+      }
+
+      const isMoveChecked = this.checkedGB.includes("移动峰值");
+      const isSidebandChecked = this.checkedGB.includes("边带光标");
+      const isHarmonicChecked = this.checkedGB.includes("谐波光标");
+
+      isMoveChecked ? this.handleMoveCursor() : this.removeCursor();
+      isSidebandChecked
+        ? this.enableSidebandCursor()
+        : this.disableSidebandCursor();
+      isHarmonicChecked
+        ? this.enableHarmonicCursor()
+        : this.disableHarmonicCursor();
+    },
+    initializeChart() {
+      const chartDom = this.$refs.chart;
+      if (chartDom && !this.chartInstance) {
+        this.chartInstance = echarts.init(chartDom);
+        this.chartInstance.getZr().on("dblclick", this.handleDoubleClick);
+      }
+
+      this.$nextTick(() => {
+        if (this.chartInstance && this.spectrumList.y && this.spectrumList.x) {
+          this.updateChart(this.spectrumList.y, this.spectrumList.x);
+        }
+      });
+    },
+
+    toggleFeature(val) {
+      const index = this.checkedValues.indexOf(val);
+
+      if (index > -1) {
+        this.checkedValues.splice(index, 1);
+      } else {
+        this.checkedValues.push(val);
+      }
+
+      this.handleCheckChange(); // 复用你原逻辑
+    },
+    handleFreqRange() {
+      const min = Number(this.freqMin);
+      const max = Number(this.freqMax);
+
+      if (isNaN(min) || isNaN(max)) return;
+
+      this.chartInstance.setOption({
+        xAxis: { min, max },
+      });
+    },
+    handleMark() {
+      const freq = Number(this.manualFreq);
+      const multiple = Number(this.multiple) || 1;
+
+      if (isNaN(freq)) return;
+
+      const marks = [];
+
+      for (let i = 1; i <= multiple; i++) {
+        marks.push({
+          xAxis: freq * i,
+          val: `${i}x`,
+        });
+      }
+
+      this.manualMarks = marks;
+      this.renderManualMarks();
+    },
+    // 特征值
+    handleCheckChange() {
+      this.PXcheckList.forEach((item) => {
+        item.checked = this.checkedValues.includes(item.val);
+      });
+      this.updateFeatureLines();
+    },
+    renderManualMarks() {
+      const option = this.chartInstance.getOption();
+
+      const manualSeries = {
+        id: "MANUAL_MARK",
+        type: "line",
+        markLine: {
+          symbol: ["none", "none"],
+          lineStyle: {
+            color: "#ff0000",
+            width: 2,
+            type: "dashed",
+          },
+          label: {
+            formatter: ({ data }) => data.val,
+          },
+          data: this.manualMarks,
+        },
+      };
+
+      this.chartInstance.setOption({
+        series: [
+          ...option.series.filter((s) => s.id !== "MANUAL_MARK"),
+          manualSeries,
+        ],
+      });
+    },
+    updateFeatureLinesByGroup() {
+      if (!this.spectrumList) return;
+
+      const featureLines = {
+        Fr: [],
+        BPFI: [],
+        BPFO: [],
+        BSF: [],
+        FTF: [],
+        B3P: [],
+      };
+
+      this.checkedFeatures.forEach((featureName) => {
+        const key = this.featureKeyMap[featureName];
+
+        if (!key) return;
+
+        switch (key) {
+          case "Fr":
+            featureLines.Fr = this.spectrumList.fn_Gen || [];
+            break;
+          case "BPFI":
+            featureLines.BPFI = this.spectrumList.BPFI || [];
+            break;
+          case "BPFO":
+            featureLines.BPFO = this.spectrumList.BPFO || [];
+            break;
+          case "BSF":
+            featureLines.BSF = this.spectrumList.BSF || [];
+            break;
+          case "FTF":
+            featureLines.FTF = this.spectrumList.FTF || [];
+            break;
+          case "B3P":
+            featureLines.B3P = Array.isArray(this.spectrumList.B3P)
+              ? this.spectrumList.B3P
+              : this.spectrumList.B3P
+              ? [{ Xaxis: this.spectrumList.B3P, val: "3P" }]
+              : [];
+            break;
+        }
+      });
+
+      this.renderFeatureSeries(featureLines);
+    },
+    updateFeatureLines() {
+      const newFeatureLines = {
+        Fr: this.checkedValues.includes("转速频率")
+          ? this.spectrumList.fn_Gen
+          : [],
+        BPFI: this.checkedValues.includes("内圈故障频率")
+          ? this.spectrumList.BPFI
+          : [],
+        BPFO: this.checkedValues.includes("外圈故障频率")
+          ? this.spectrumList.BPFO
+          : [],
+        BSF: this.checkedValues.includes("滚动体故障频率")
+          ? this.spectrumList.BSF
+          : [],
+        FTF: this.checkedValues.includes("保持架频率")
+          ? this.spectrumList.FTF
+          : [],
+        B3P: this.checkedValues.includes("叶片频率")
+          ? Array.isArray(this.spectrumList.B3P)
+            ? this.spectrumList.B3P
+            : [{ Xaxis: this.spectrumList.B3P, val: "3P" }]
+          : [],
+      };
+
+      if (this.chartInstance) {
+        const currentOption = this.chartInstance.getOption();
+
+        // 获取现有的光标系列
+        const cursorLineSeries =
+          currentOption.series.find((s) => s.id === "CURSOR_LINE_SERIES") || {};
+        const cursorPointSeries =
+          currentOption.series.find((s) => s.id === "CURSOR_POINT_SERIES") ||
+          {};
+        const cursorHighLineSeries =
+          currentOption.series.find((s) => s.id === "PEAK_REFERENCE_LINE") ||
+          {};
+
+        // 生成新的特征值系列
+        const featureSeries = this.generateSeries(newFeatureLines);
+
+        this.chartInstance.setOption(
+          {
+            series: [
+              currentOption.series[0], // 主数据系列
+              ...featureSeries.slice(1), // 新的特征值系列
+              cursorLineSeries, // 保留光标线系列
+              cursorPointSeries, // 保留光标点系列
+              cursorHighLineSeries, // 保留峰值参考线
+            ],
+          },
+          { replaceMerge: ["series"] },
+        );
+      }
+    },
+
+    generateSeries(featureLines) {
+      const createMarkLine = (dataSource, color) => ({
+        type: "line",
+        markLine: {
+          silent: false,
+          lineStyle: { color, type: "dashed", width: 1 },
+          symbol: ["none", "none"],
+          label: {
+            show: true,
+            position: "end",
+            formatter: ({ data }) => data.val,
+          },
+          emphasis: {
+            lineStyle: { color: "#FF6A00", width: 2 },
+            label: {
+              show: true,
+              formatter: ({ value }) => `特征值: ${value}`,
+              color: "#000",
+            },
+          },
+          data: dataSource.map(({ Xaxis, val }) => ({ xAxis: Xaxis, val })),
+        },
+      });
+
+      const markLines = [
+        { data: featureLines.Fr, color: "#A633FF" },
+        { data: featureLines.BPFI, color: "#23357e" },
+        { data: featureLines.BPFO, color: "#42a0ae" },
+        { data: featureLines.BSF, color: "#008080" },
+        { data: featureLines.FTF, color: "#af254f" },
+        { data: featureLines.B3P, color: "#FFD700" },
+      ].map(({ data, color }) => createMarkLine(data, color));
+
+      return [
+        {
+          name: "数据系列",
+          type: "line",
+          data: this.spectrumList.x.map((x, i) => [x, this.spectrumList.y[i]]),
+          symbol: "none",
+          lineStyle: { color: "#162961", width: 1 },
+          itemStyle: { color: "#162961", borderColor: "#fff", borderWidth: 1 },
+          large: true,
+        },
+        ...markLines,
+      ];
+    },
+
+    updateChart(data, labels) {
+      if (
+        !this.chartInstance ||
+        !Array.isArray(labels) ||
+        !Array.isArray(data) ||
+        labels.length !== data.length
+      ) {
+        console.error("Invalid data or labels");
+        return;
+      }
+
+      const createMarkLine = (dataSource, color) => ({
+        type: "line",
+        markLine: {
+          silent: false,
+          lineStyle: { color, type: "solid", width: 2 },
+          symbol: ["none", "none"],
+          label: {
+            show: true,
+            position: "end",
+            formatter: ({ data }) => data.val,
+          },
+          emphasis: {
+            lineStyle: { color: "#FF6A00", width: 4 },
+            label: {
+              show: true,
+              formatter: ({ value }) => `特征值: ${value}`,
+              color: "#000",
+              backgroundColor: "#FFF",
+              padding: [2, 4],
+              borderRadius: 3,
+              fontSize: 12,
+            },
+          },
+          data: dataSource.map(({ Xaxis, val }) => ({ xAxis: Xaxis, val })),
+        },
+      });
+
+      const markLines = [
+        { data: this.Fr, color: "#A633FF" },
+        { data: this.BPFI, color: "#23357e" },
+        { data: this.BPFO, color: "#42a0ae" },
+        { data: this.BSF, color: "#008080" },
+        { data: this.FTF, color: "#af254f" },
+        { data: this.B3P, color: "#FFD700" },
+      ].map(({ data, color }) => createMarkLine(data, color));
+
+      const option = {
+        grid: {
+          left: 60, // 原来是100,适当缩小左右边距
+          right: 20,
+          top: 80, // 给推拽条和坐标轴腾出空间
+          // top: 60,
+        },
+        // title: { text: this.spectrumList.title, left: "center" },
+        toolbox: {
+          right: 10,
+          top: 40, // 👈 工具栏在最上面
+          feature: {
+            dataZoom: { yAxisIndex: "none" },
+            restore: {},
+            saveAsImage: {},
+          },
+        },
+        xAxis: {
+          type: "value",
+          name: this.spectrumList.xaxis,
+          nameLocation: "center",
+          nameTextStyle: {
+            fontSize: 14,
+            color: "#333",
+            padding: [15, 0, 0, 0], // 增加X轴标题和轴线的距离
+          },
+          axisLabel: {
+            margin: 1, // 增加数值标签和轴线的间距
+            formatter: (value) => value,
+          },
+        },
+        yAxis: {
+          type: "value",
+          name: this.spectrumList.yaxis,
+          nameTextStyle: {
+            fontSize: 14,
+            color: "#333",
+            padding: [10, 0, 0, 0],
+          },
+        },
+        tooltip: {
+          trigger: "axis",
+          formatter: ([
+            {
+              value: [x, y],
+            },
+          ]) => `X: ${x}<br/>Y: ${y}`,
+          axisPointer: { type: "line" },
+        },
+        dataZoom: [
+          { type: "inside", start: 0, end: 10 },
+          {
+            type: "slider",
+            start: 0,
+            end: 10,
+            handleSize: "80%",
+            showDataShadow: false,
+            top: 10, // 👈 放到顶部
+            height: 20, // 👈 控制高度(可选)
+          },
+        ],
+        series: [
+          {
+            name: "数据系列",
+            type: "line",
+            data: labels.map((x, i) => [x, data[i]]),
+            symbol: "none",
+            lineStyle: { color: "#162961", width: 1 },
+            itemStyle: {
+              color: "#162961",
+              borderColor: "#fff",
+              borderWidth: 1,
+            },
+            large: true,
+            progressive: 2000,
+          },
+          ...markLines,
+        ],
+      };
+
+      this.chartInstance.setOption(option);
+    },
+    renderFeatureSeries(featureLines) {
+      if (!this.chartInstance) return;
+
+      const currentOption = this.chartInstance.getOption();
+
+      // 保留光标
+      const cursorLineSeries =
+        currentOption.series.find((s) => s.id === "CURSOR_LINE_SERIES") || {};
+      const cursorPointSeries =
+        currentOption.series.find((s) => s.id === "CURSOR_POINT_SERIES") || {};
+      const cursorHighLineSeries =
+        currentOption.series.find((s) => s.id === "PEAK_REFERENCE_LINE") || {};
+
+      const featureSeries = this.generateSeries(featureLines);
+
+      this.chartInstance.setOption(
+        {
+          series: [
+            currentOption.series[0], // 主线
+            ...featureSeries.slice(1),
+            cursorLineSeries,
+            cursorPointSeries,
+            cursorHighLineSeries,
+          ],
+        },
+        { replaceMerge: ["series"] },
+      );
+    },
+    // 获取数据
+    getTime() {
+      this.$emit("handleLoading", null, true, this.activeIndex);
+      const params = {
+        ids: this.ids,
+        windCode: this.windCode,
+        analysisType: "frequency",
+      };
+      axios
+        .post("/AnalysisMulti/analysis/frequency", params)
+        .then((res) => {
+          this.spectrumList = { ...JSON.parse(res.data) };
+
+          console.log(this.spectrumList, "频谱图数据1");
+
+          const XrmsValue = this.spectrumList?.Xrms;
+          this.$emit("updateXrms", XrmsValue);
+          // 拉完数据后,如果已有选中项,自动渲染
+          this.$nextTick(() => {
+            this.updateFeatureLinesByGroup();
+          });
+        })
+        .catch((error) => {
+          console.error(error);
+        })
+        .finally(() => {
+          this.$emit("handleLoading", this.currentRow, false, this.activeIndex);
+        });
+    },
+
+    Show(value) {
+      const stateMap = {
+        1: { TZshow: true, BGshow: false, PXshow: false },
+        2: { TZshow: false, BGshow: true, PXshow: false },
+        3: { TZshow: false, BGshow: false, PXshow: true },
+      };
+
+      if (stateMap[value]) {
+        this.TZshow = value === "1" ? !this.TZshow : false;
+        this.BGshow = value === "2" ? !this.BGshow : false;
+        this.PXshow = value === "3" ? !this.PXshow : false;
+      }
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.line-chart {
+  width: 100%;
+  height: 320px;
+}
+
+.FD {
+  width: 100%;
+  height: 1px;
+  position: relative;
+}
+
+.eigenvalue {
+  position: absolute;
+  top: 60px;
+  right: 0;
+  font-size: 10px;
+  width: 146px;
+  border: 1px solid rgb(182, 182, 182);
+  padding: 5px;
+  background: #fff;
+  z-index: 99;
+  border-radius: 5px;
+  h5 {
+    line-height: 16px;
+    height: 16px;
+  }
+}
+.eigenvalue--first {
+  width: 100px;
+}
+.control-panel {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  gap: 12px;
+  padding: 8px 12px;
+  background: #f5f7fa;
+  border: 1px solid #ddd;
+  border-radius: 6px;
+  margin-bottom: 10px;
+}
+/* 🌟 关键:独占一行 */
+.full-row {
+  width: 100%;
+  .panel-block {
+    width: 45%;
+  }
+}
+.panel-block {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+}
+
+.label {
+  font-size: 12px;
+  color: #666;
+}
+.label1 {
+  font-size: 12px;
+  color: #666;
+  display: inline-block;
+  width: 75px;
+}
+
+.btn-group {
+  display: flex;
+  gap: 6px;
+}
+
+.btn {
+  padding: 2px 8px;
+  font-size: 12px;
+  border: 1px solid #ccc;
+  border-radius: 3px;
+  cursor: pointer;
+}
+
+.btn.active {
+  background: #409eff;
+  color: #fff;
+}
+.full-width {
+  width: 100%;
+}
+
+.el-cascader {
+  font-size: 12px;
+}
+
+.el-cascader__tags {
+  max-width: 240px;
+  overflow: hidden;
+}
+
+.feature-grid {
+  display: grid;
+  grid-template-columns: repeat(4, 1fr); // 两列布局
+  gap: 10px;
+  width: 100%;
+}
+
+.feature-item {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+}
+
+.el-select {
+  flex: 1;
+}
+</style>

+ 0 - 2
src/views/health/components/timedomaincharts.vue

@@ -250,9 +250,7 @@ export default {
         .then((res) => {
           this.timeList = JSON.parse(res.data);
 
-
           console.log(this.timeList, "timeList");
-          
         })
         .catch((error) => {})
         .finally(() => {

+ 587 - 0
src/views/health/components/timedomainchartsNew.vue

@@ -0,0 +1,587 @@
+<template>
+  <div>
+    <!-- <div class="FD">
+      <div v-if="TZshow" class="eigenvalue">
+        <h5>特征值</h5>
+        <p>有效值:{{ this.timeList.Xrms }}</p>
+        <p>平均值:{{ this.timeList.mean_value }}</p>
+        <p>最大值:{{ this.timeList.max_value }}</p>
+        <p>最小值:{{ this.timeList.min_value }}</p>
+        <p>峰值:{{ this.timeList.Xp }}</p>
+        <p>峰峰值:{{ this.timeList.Xpp }}</p>
+        <p>波形因子:{{ this.timeList.Sf }}</p>
+        <p>脉冲指标:{{ this.timeList.If }}</p>
+        <p>裕度指标:{{ this.timeList.Ce }}</p>
+        <p>偏度指标:{{ this.timeList.Cw }}</p>
+        <p>峭度指标:{{ this.timeList.Cq }}</p>
+      </div>
+    </div> -->
+    <div class="control-panel">
+      <!-- 光标 -->
+      <div class="panel-block">
+        <span class="label">光标</span>
+        <div class="btn-group">
+          <span
+            v-for="item in GBcheckList"
+            :key="item.val"
+            :class="['btn', checkedGB.includes(item.val) ? 'active' : '']"
+            @click="selectCursor(item.val)"
+          >
+            {{ item.val }}
+          </span>
+        </div>
+      </div>
+
+      <!-- 特征值 -->
+      <div class="panel-block">
+        <span class="label">特征值</span>
+        <div class="btn-group">
+          <span
+            v-for="item in TZFeatureList"
+            :key="item.key"
+            :class="['btn', activeFeatures.includes(item.key) ? 'active' : '']"
+            @click="toggleTZFeature(item.key)"
+          >
+            {{ item.label }}
+          </span>
+        </div>
+      </div>
+    </div>
+    <!-- ECharts 图表容器 -->
+    <div class="line-chart" ref="chart" v-loading="loadingTime"></div>
+  </div>
+</template>
+
+<script>
+import axios from "axios";
+import * as echarts from "echarts"; // 导入 echarts 库
+
+export default {
+  name: "TimedomainCharts", // 组件名称
+  props: {
+    currentIndex: {
+      type: Number,
+      default: 0,
+    },
+    activeIndex: {
+      type: Number,
+      default: 0,
+    },
+    loadingTime: {
+      type: Boolean,
+      default: false,
+    },
+    ids: {
+      type: Array,
+      default: () => [],
+    },
+    timeListTwo: {
+      type: Object,
+      default: () => ({}),
+    },
+    currentRow: {
+      type: Object,
+      default: () => ({}),
+    },
+    windCode: {
+      type: String,
+      default: "",
+    },
+  },
+  data() {
+    return {
+      chartInstance: null,
+      option: null,
+      TZshow: false,
+      timeList: {},
+      manualMarks: [],
+      // 光标
+      GBcheckList: [
+        { val: "单指针" },
+        { val: "频带光标" },
+        { val: "边带光标" },
+      ],
+      checkedGB: [],
+      // 特征值按钮
+      TZFeatureList: [
+        { key: "Xrms", label: "有效值" },
+        { key: "mean_value", label: "平均值" },
+        { key: "max_value", label: "最大值" },
+        { key: "min_value", label: "最小值" },
+        { key: "Xp", label: "峰值" },
+        { key: "Xpp", label: "峰峰值" },
+      ],
+      activeFeatures: [],
+    };
+  },
+  watch: {
+    // 监听 chartData 和 chartLabels 的变化,重新绘制图表
+    chartData(newData) {
+      this.updateChart(newData, this.chartLabels);
+    },
+    chartLabels(newLabels) {
+      this.updateChart(this.chartData, newLabels);
+    },
+    timeListTwo(newValue) {
+      this.timeList = newValue; // 将 timeListTwo 的新值赋给 timeList
+      if (this.chartInstance) {
+        this.updateChart(this.timeList.y, this.timeList.x); // 更新图表
+      }
+    },
+    // 监听 timeList 的变化
+    timeList: {
+      handler(newValue) {
+        if (this.chartInstance) {
+          this.updateChart(newValue.y, newValue.x); // 只在 chartInstance 初始化后更新图表
+        }
+      },
+      deep: true, // 深度监听
+    },
+  },
+  mounted() {
+    this.$nextTick(() => {
+      setTimeout(() => {
+        this.initializeChart(); // 延迟2秒后调用
+        this.getTime();
+      }, 500); // 2000毫秒,即2秒
+    });
+  },
+
+  methods: {
+    initializeChart() {
+      const chartDom = this.$refs.chart;
+      if (chartDom && !this.chartInstance) {
+        this.chartInstance = echarts.init(chartDom);
+      }
+      if (this.timeList.y && this.timeList.x) {
+        this.updateChart(this.timeList.y, this.timeList.x);
+      }
+    },
+
+    renderManualMarks(marks) {
+      const option = this.chartInstance.getOption();
+
+      const manualSeries = {
+        id: "MANUAL_MARK",
+        type: "line",
+        markLine: {
+          symbol: ["none", "none"],
+          lineStyle: {
+            color: "#ff0000",
+            type: "dashed",
+          },
+          label: {
+            show: true,
+          },
+          data: marks,
+        },
+      };
+
+      this.chartInstance.setOption({
+        series: [
+          ...option.series.filter((s) => s.id !== "MANUAL_MARK"),
+          manualSeries,
+        ],
+      });
+    },
+    selectCursor(val) {
+      if (this.checkedGB.includes(val)) {
+        this.checkedGB = [];
+      } else {
+        this.checkedGB = [val];
+      }
+      this.chartInstance.off("click");
+      this.applyCursor(); // 👈 核心入口
+    },
+    toggleTZFeature(key) {
+      const i = this.activeFeatures.indexOf(key);
+
+      if (i > -1) {
+        this.activeFeatures.splice(i, 1);
+      } else {
+        this.activeFeatures.push(key);
+      }
+
+      this.renderTZFeatures();
+    },
+    renderTZFeatures() {
+      const marks = this.activeFeatures.map((key) => ({
+        yAxis: this.timeList[key],
+        label: {
+          formatter: key,
+        },
+      }));
+
+      const featureSeries = {
+        id: "TZ_FEATURE",
+        type: "line",
+        markLine: {
+          lineStyle: {
+            color: "#00aaff",
+            type: "solid",
+          },
+          data: marks,
+        },
+      };
+
+      const option = this.chartInstance.getOption();
+
+      this.chartInstance.setOption({
+        series: [
+          ...option.series.filter((s) => s.id !== "TZ_FEATURE"),
+          featureSeries,
+        ],
+      });
+    },
+    applyCursor() {
+      if (!this.chartInstance) return;
+
+      const option = this.chartInstance.getOption();
+
+      this.chartInstance.setOption({
+        series: option.series.filter(
+          (s) =>
+            !["CURSOR_SINGLE", "CURSOR_BAND", "CURSOR_SIDEBAND"].includes(s.id),
+        ),
+      });
+
+      const type = this.checkedGB[0];
+
+      if (type === "单指针") {
+        this.enableSingleCursor();
+      } else if (type === "频带光标") {
+        this.enableBandCursor();
+      } else if (type === "边带光标") {
+        this.enableSidebandCursor();
+      }
+    },
+    enableSingleCursor() {
+      this.chartInstance.off("click");
+      console.log("单指针");
+      this.chartInstance.on("click", (params) => {
+        const x = params.value[0];
+
+        const option = this.chartInstance.getOption();
+
+        const newSeries = {
+          id: "CURSOR_SINGLE",
+          type: "line",
+          markLine: {
+            symbol: ["none", "none"],
+            lineStyle: { color: "#ff0000", width: 1 },
+            label: {
+              formatter: `X: ${x.toFixed(2)}`,
+            },
+            data: [{ xAxis: x }],
+          },
+        };
+
+        this.chartInstance.setOption({
+          series: [
+            ...option.series.filter((s) => s.id !== "CURSOR_SINGLE"),
+            newSeries,
+          ],
+        });
+      });
+    },
+    enableBandCursor() {
+      console.log("频带");
+      let points = [];
+      this.chartInstance.off("click");
+
+      this.chartInstance.on("click", (params) => {
+        const x = params.value[0];
+        points.push(x);
+
+        if (points.length === 2) {
+          const [x1, x2] = points.sort((a, b) => a - b);
+
+          const option = this.chartInstance.getOption();
+
+          const bandSeries = {
+            id: "CURSOR_BAND",
+            type: "line",
+            markArea: {
+              itemStyle: {
+                color: "rgba(0, 128, 255, 0.2)",
+              },
+              data: [[{ xAxis: x1 }, { xAxis: x2 }]],
+            },
+          };
+
+          this.chartInstance.setOption({
+            series: [
+              ...option.series.filter((s) => s.id !== "CURSOR_BAND"),
+              bandSeries,
+            ],
+          });
+
+          points = [];
+        }
+      });
+    },
+    enableSidebandCursor() {
+      console.log("边带");
+      this.chartInstance.off("click");
+
+      this.chartInstance.on("click", (params) => {
+        const center = params.value[0];
+
+        // ✅ 正确转频(核心!!!)
+        const rpm = this.timeList.rpm_Gen || 0;
+        const spacing = rpm / 60 || 10;
+
+        const count = 5;
+
+        const lines = [];
+
+        for (let i = -count; i <= count; i++) {
+          const x = center + i * spacing;
+
+          lines.push({
+            xAxis: x,
+            label: {
+              formatter: `${i}x`,
+            },
+          });
+        }
+
+        const option = this.chartInstance.getOption();
+
+        const sidebandSeries = {
+          id: "CURSOR_SIDEBAND",
+          type: "line",
+          markLine: {
+            symbol: ["none", "none"],
+            lineStyle: {
+              color: "#ffa500",
+              type: "dashed",
+            },
+            data: lines,
+          },
+        };
+
+        this.chartInstance.setOption({
+          series: [
+            ...option.series.filter((s) => s.id !== "CURSOR_SIDEBAND"),
+            sidebandSeries,
+          ],
+        });
+      });
+    },
+    // 更新图表数据
+    updateChart(data, labels) {
+      if (!this.chartInstance) return; // Check if chartInstance is available
+
+      const option = {
+        // title: {
+        //   text: this.timeList.title,
+        //   left: "center",
+        // },
+        grid: {
+          left: 60, // 原来是100,适当缩小左右边距
+          right: 60,
+          //   bottom: 90, // 给推拽条和坐标轴腾出空间
+          top: 60,
+        },
+        toolbox: {
+          right: 10,
+          top: 35, // 👈 工具栏在最上面
+          feature: {
+            dataZoom: { yAxisIndex: "none" },
+            restore: {},
+            saveAsImage: {},
+            // myCustomTool3: {
+            //   show: true,
+            //   title: "特征值",
+            //   icon: `image://${require("@/assets/analyse/10.png")}`,
+            //   onclick: () => this.Show(),
+            // },
+          },
+        },
+        xAxis: {
+          type: "value",
+          name: this.timeList.xaxis,
+          nameLocation: "center",
+          nameTextStyle: {
+            fontSize: 14,
+            color: "#333",
+            padding: [15, 0, 0, 0], // 增加X轴标题和轴线的距离
+          },
+          axisLabel: {
+            margin: 1, // 增加数值标签和轴线的间距
+            formatter: (value) => value,
+          },
+          axisTick: {
+            show: true,
+          },
+          axisLine: {
+            show: true,
+          },
+        },
+        yAxis: {
+          type: "value",
+          name: this.timeList.yaxis,
+          nameTextStyle: {
+            fontSize: 14,
+            color: "#333",
+            padding: [0, 10, 0, 0], // 根据需要微调
+          },
+          axisLabel: {
+            margin: 10,
+            formatter: (value) => value,
+          },
+          axisTick: {
+            show: true,
+          },
+          axisLine: {
+            show: true,
+          },
+        },
+        tooltip: {
+          trigger: "axis",
+          formatter: (params) => {
+            const xValue = params[0].value[0];
+            const yValue = params[0].value[1];
+            return `X: ${xValue}<br/>Y: ${yValue}`;
+          },
+          axisPointer: {
+            type: "line",
+          },
+        },
+        dataZoom: [
+          {
+            type: "inside",
+            start: 0,
+            end: 10,
+          },
+          {
+            type: "slider",
+            start: 0,
+            end: 10,
+            handleSize: "80%",
+            showDataShadow: false,
+            top: 0, // 👈 放到顶部
+            height: 20, // 👈 控制高度(可选)
+          },
+        ],
+        series: [
+          {
+            name: "数据系列",
+            type: "line",
+            data: labels.map((item, index) => [item, data[index]]),
+            symbol: "none",
+            symbolSize: 8,
+            lineStyle: {
+              color: "#162961",
+              width: 1,
+            },
+            itemStyle: {
+              color: "#162961",
+              borderColor: "#fff",
+              borderWidth: 1,
+            },
+            large: true,
+            progressive: 2000,
+          },
+        ],
+      };
+
+      this.chartInstance.setOption(option, {
+        notMerge: false, // 👈 关键
+      });
+    },
+
+    getTime() {
+      this.$emit("handleLoading", null, true, this.activeIndex);
+      const params = {
+        ids: this.ids,
+        analysisType: "time",
+        windCode: this.windCode,
+      };
+      axios
+        .post("/AnalysisMulti/analysis/time", params)
+        .then((res) => {
+          this.timeList = JSON.parse(res.data);
+
+          console.log(this.timeList, "timeList");
+        })
+        .catch((error) => {})
+        .finally(() => {
+          this.$emit("handleLoading", this.currentRow, false, this.activeIndex);
+        });
+    },
+
+    Show() {
+      this.TZshow = !this.TZshow;
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.line-chart {
+  width: 100%;
+  height: 320px;
+}
+.FD {
+  width: 100%;
+  height: 1px;
+  position: relative;
+}
+
+.eigenvalue {
+  position: absolute;
+  top: 30px;
+  right: 0;
+  font-size: 10px;
+  width: 100px;
+  border: 1px solid black;
+  padding: 5px;
+  background: #fff;
+
+  z-index: 99;
+  h5 {
+    line-height: 16px;
+    height: 16px;
+  }
+}
+.control-panel {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 12px;
+  padding: 8px 12px;
+  background: #f5f7fa;
+  border: 1px solid #ddd;
+  border-radius: 6px;
+  margin-bottom: 10px;
+}
+.panel-block {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+}
+
+.label {
+  font-size: 12px;
+  color: #666;
+}
+
+.btn-group {
+  display: flex;
+  gap: 6px;
+}
+
+.btn {
+  padding: 2px 8px;
+  font-size: 12px;
+  border: 1px solid #ccc;
+  border-radius: 3px;
+  cursor: pointer;
+}
+
+.btn.active {
+  background: #409eff;
+  color: #fff;
+}
+</style>

+ 284 - 262
src/views/health/vibration.vue

@@ -1,268 +1,279 @@
 <template>
   <div class="global-variable">
-    <div class="head">
-      <div class="headleft">
-        <div @click="generate('1')" class="picture">
-          <img src="@/assets/analyse/03.png" alt="" />
-          <p>时域图</p>
-        </div>
-        <div @click="generate('2')" class="picture">
-          <img src="@/assets/analyse/04.png" alt="" />
-          <p>频谱图</p>
-        </div>
-        <div @click="generate('3')" class="picture">
-          <img src="@/assets/analyse/01.png" alt="" />
-          <p>包络谱图</p>
-        </div>
-        <div @click="tendency" class="picture">
-          <img src="@/assets/analyse/02.png" alt="" />
-          <p>趋势图</p>
+    <div class="left">
+      <LoadTree></LoadTree>
+    </div>
+    <div class="right">
+      <div class="head">
+        <div class="headleft">
+          <div @click="generate('1')" class="picture">
+            <img src="@/assets/analyse/03.png" alt="" />
+            <p>时域图</p>
+          </div>
+          <div @click="generate('2')" class="picture">
+            <img src="@/assets/analyse/04.png" alt="" />
+            <p>频谱图</p>
+          </div>
+          <div @click="generate('3')" class="picture">
+            <img src="@/assets/analyse/01.png" alt="" />
+            <p>包络谱图</p>
+          </div>
+          <div @click="tendency" class="picture">
+            <img src="@/assets/analyse/02.png" alt="" />
+            <p>趋势图</p>
+          </div>
         </div>
-      </div>
-      <div class="headright">
-        <img src="@/assets/analyse/07.png" alt="全部缩小" @click="suoxiao" />
-        <img src="@/assets/analyse/05.png" alt="全部展开" @click="zhankai" />
+        <div class="headright">
+          <img src="@/assets/analyse/07.png" alt="全部缩小" @click="suoxiao" />
+          <img src="@/assets/analyse/05.png" alt="全部展开" @click="zhankai" />
 
-        <img src="@/assets/analyse/06.png" alt="全部关闭" @click="guanbi" />
+          <img src="@/assets/analyse/06.png" alt="全部关闭" @click="guanbi" />
+        </div>
       </div>
-    </div>
 
-    <div class="searchbox">
-      <p>
-        单位:
-        <selecttree
-          size="small"
-          style="width: 180px"
-          placeholder="请选择所属公司"
-          :list="parentOpt"
-          type="1"
-          v-model="companyCode"
-          @change="parentChange"
-          :defaultParentProps="{
-            children: 'children',
-            label: 'companyName',
-            value: 'codeNumber',
-          }"
-        >
-        </selecttree>
-      </p>
-      <p>
-        风机:
-        <el-select
-          style="width: 150px"
-          v-model="unitvalue"
-          @change="getchedian"
-          placeholder="请选择"
-          size="small"
-        >
-          <el-option
-            v-for="item in unitoptions"
-            :key="item.engineCode"
-            :label="item.engineName"
-            :value="item.engineCode"
+      <div class="searchbox">
+        <p>
+          单位:
+          <selecttree
+            size="small"
+            style="width: 180px"
+            placeholder="请选择所属公司"
+            :list="parentOpt"
+            type="1"
+            v-model="companyCode"
+            @change="parentChange"
+            :defaultParentProps="{
+              children: 'children',
+              label: 'companyName',
+              value: 'codeNumber',
+            }"
           >
-          </el-option>
-        </el-select>
-      </p>
-      <p>
-        测点:
-        <el-select
-          v-model="monitoringvalue"
-          clearable
-          size="small"
-          placeholder="请选择"
-        >
-          <el-option
-            v-for="item in monitoringoptions"
-            :key="item.itemKey"
-            :label="item.itemValue"
-            :value="item.itemKey"
+          </selecttree>
+        </p>
+        <p>
+          风机:
+          <el-select
+            style="width: 150px"
+            v-model="unitvalue"
+            @change="getchedian"
+            placeholder="请选择"
+            size="small"
           >
-          </el-option>
-        </el-select>
-      </p>
-      <p>
-        时间:
-        <el-date-picker
-          v-model="timevalue"
-          type="datetimerange"
-          value-format="yyyy-MM-dd HH:mm:ss"
-          range-separator="至"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
-          size="small"
-        >
-        </el-date-picker>
-      </p>
-      <el-button type="primary" size="small" @click="conditions"
-        >查询</el-button
-      >
-      <el-button size="small" @click="outputFile">导出</el-button>
-    </div>
-    <div class="main-body">
-      <div class="data-map">
-        <div
-          class="chart-area"
-          v-for="(item, index) in fourList"
-          :key="item.index"
-          @mouseover="setCurrent(item.rowData)"
-          @mouseleave="setCurrent(oldCurrentRow)"
+            <el-option
+              v-for="item in unitoptions"
+              :key="item.engineCode"
+              :label="item.engineName"
+              :value="item.engineCode"
+            >
+            </el-option>
+          </el-select>
+        </p>
+        <p>
+          测点:
+          <el-select
+            v-model="monitoringvalue"
+            clearable
+            size="small"
+            placeholder="请选择"
+          >
+            <el-option
+              v-for="item in monitoringoptions"
+              :key="item.itemKey"
+              :label="item.itemValue"
+              :value="item.itemKey"
+            >
+            </el-option>
+          </el-select>
+        </p>
+        <p>
+          时间:
+          <el-date-picker
+            v-model="timevalue"
+            type="datetimerange"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            range-separator="至"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            size="small"
+          >
+          </el-date-picker>
+        </p>
+        <el-button type="primary" size="small" @click="conditions"
+          >查询</el-button
         >
-          <div class="dialog-actions">
-            <p>{{ item.name }}</p>
-            <span>
-              <i
-                class="el-icon-minus"
-                @click="lessen(index)"
-                style="cursor: pointer; margin-right: 10px"
-              ></i>
-              <i
-                class="el-icon-full-screen"
-                @click="amplifier(index)"
-                style="cursor: pointer; margin-right: 10px"
-              ></i>
-              <i
-                class="el-icon-close"
-                @click="close(index)"
-                style="cursor: pointer"
-              ></i>
-            </span>
-          </div>
-
+        <el-button size="small" @click="outputFile">导出</el-button>
+      </div>
+      <div class="main-body">
+        <div class="data-map">
           <div
-            class="subject"
-            :class="{
-              minimized: item.isMinimized,
-            }"
-            v-loading="item.loading"
+            class="chart-area"
+            v-for="(item, index) in fourList"
+            :key="item.index"
+            @mouseover="setCurrent(item.rowData)"
+            @mouseleave="setCurrent(oldCurrentRow)"
           >
-            <!-- {{ item.rowData }} -->
-            <p v-if="item.name != '趋势图'">
-              测点路径:{{
-                NewgetDetectionPointCn(item?.rowData?.mesurePointName)
-              }}-{{ item?.rowData?.windTurbineNumber }}
-              <span>采样频率: {{ item?.rowData?.samplingFrequency }}(Hz)</span>
-              <span
-                >发电机转速: {{ item?.rowData?.rotationalSpeed }}(rpm)
+            <div class="dialog-actions">
+              <p>{{ item.name }}</p>
+              <span>
+                <i
+                  class="el-icon-minus"
+                  @click="lessen(index)"
+                  style="cursor: pointer; margin-right: 10px"
+                ></i>
+                <i
+                  class="el-icon-full-screen"
+                  @click="amplifier(index)"
+                  style="cursor: pointer; margin-right: 10px"
+                ></i>
+                <i
+                  class="el-icon-close"
+                  @click="close(index)"
+                  style="cursor: pointer"
+                ></i>
               </span>
-              <span>采样时间: {{ item?.rowData?.timeStamp }} </span>
-              <!-- <span>有效值: {{XrmsValue}} (m/s2)</span> -->
-            </p>
-
-            <!-- {{ samplingTime }} -->
-            <div v-if="item.name === '时域图'">
-              <timedomaincharts
-                :timeListTwo="item.timeListTwo"
-                :currentIndex="currentIndex"
-                :activeIndex="index"
-                :tableDataList="tableDataList"
-                :ids="[currentRow.id]"
-                :currentRow="currentRow"
-                :windCode="windCode"
-                @update:currentIndex="handleCurrentIndexUpdate"
-                @update-previous-row="goToPreviousRow"
-                @update-next-row="goToNextRow"
-                @handleLoading="handleLoading"
-              ></timedomaincharts>
-            </div>
-            <div v-if="item.name === '频谱图'">
-              <spectrogramcharts
-                @updateXrms="handleXrmsUpdate"
-                :spectrumListTwo="item.spectrumListTwo"
-                :currentIndex="currentIndex"
-                :tableDataList="tableDataList"
-                :activeIndex="index"
-                :ids="[currentRow.id]"
-                :currentRow="currentRow"
-                :windCode="windCode"
-                @update:currentIndex="handleCurrentIndexUpdate"
-                @update-previous-row="goToPreviousRow"
-                @update-next-row="goToNextRow"
-                @handleLoading="handleLoading"
-              ></spectrogramcharts>
             </div>
-            <div v-if="item.name === '包络谱图'">
-              <envelopecharts
-                @updateXrms="handleXrmsUpdate"
-                :envelopeListTwo="item.envelopeListTwo"
-                :currentIndex="currentIndex"
-                :tableDataList="tableDataList"
-                :activeIndex="index"
-                :currentRow="currentRow"
-                :ids="[currentRow.id]"
-                :windCode="windCode"
-                @update:currentIndex="handleCurrentIndexUpdate"
-                @update-previous-row="goToPreviousRow"
-                @update-next-row="goToNextRow"
-                @update:modelValue="handleModelUpdate"
-                @handleLoading="handleLoading"
-              ></envelopecharts>
-            </div>
-            <div v-if="item.name === '趋势图'">
-              <tendencycharts
-                :qsList="qsList"
-                :activeIndex="index"
-              ></tendencycharts>
+
+            <div
+              class="subject"
+              :class="{
+                minimized: item.isMinimized,
+              }"
+              v-loading="item.loading"
+            >
+              <!-- {{ item.rowData }} -->
+              <p v-if="item.name != '趋势图'">
+                测点路径:{{
+                  NewgetDetectionPointCn(item?.rowData?.mesurePointName)
+                }}-{{ item?.rowData?.windTurbineNumber }}
+                <span
+                  >采样频率: {{ item?.rowData?.samplingFrequency }}(Hz)</span
+                >
+                <span
+                  >发电机转速: {{ item?.rowData?.rotationalSpeed }}(rpm)
+                </span>
+                <span>采样时间: {{ item?.rowData?.timeStamp }} </span>
+                <!-- <span>有效值: {{XrmsValue}} (m/s2)</span> -->
+              </p>
+
+              <!-- {{ samplingTime }} -->
+              <div v-if="item.name === '时域图'">
+                <timedomaincharts
+                  :timeListTwo="item.timeListTwo"
+                  :currentIndex="currentIndex"
+                  :activeIndex="index"
+                  :tableDataList="tableDataList"
+                  :ids="[currentRow.id]"
+                  :currentRow="currentRow"
+                  :windCode="windCode"
+                  @update:currentIndex="handleCurrentIndexUpdate"
+                  @update-previous-row="goToPreviousRow"
+                  @update-next-row="goToNextRow"
+                  @handleLoading="handleLoading"
+                ></timedomaincharts>
+              </div>
+              <div v-if="item.name === '频谱图'">
+                <spectrogramcharts
+                  @updateXrms="handleXrmsUpdate"
+                  :spectrumListTwo="item.spectrumListTwo"
+                  :currentIndex="currentIndex"
+                  :tableDataList="tableDataList"
+                  :activeIndex="index"
+                  :ids="[currentRow.id]"
+                  :currentRow="currentRow"
+                  :windCode="windCode"
+                  @update:currentIndex="handleCurrentIndexUpdate"
+                  @update-previous-row="goToPreviousRow"
+                  @update-next-row="goToNextRow"
+                  @handleLoading="handleLoading"
+                ></spectrogramcharts>
+              </div>
+              <div v-if="item.name === '包络谱图'">
+                <envelopecharts
+                  @updateXrms="handleXrmsUpdate"
+                  :envelopeListTwo="item.envelopeListTwo"
+                  :currentIndex="currentIndex"
+                  :tableDataList="tableDataList"
+                  :activeIndex="index"
+                  :currentRow="currentRow"
+                  :ids="[currentRow.id]"
+                  :windCode="windCode"
+                  @update:currentIndex="handleCurrentIndexUpdate"
+                  @update-previous-row="goToPreviousRow"
+                  @update-next-row="goToNextRow"
+                  @update:modelValue="handleModelUpdate"
+                  @handleLoading="handleLoading"
+                ></envelopecharts>
+              </div>
+              <div v-if="item.name === '趋势图'">
+                <tendencycharts
+                  :qsList="qsList"
+                  :activeIndex="index"
+                ></tendencycharts>
+              </div>
             </div>
           </div>
         </div>
-      </div>
-      <div class="data-origin">
-        <el-table
-          height="600"
-          ref="singleTable"
-          :data="tableDataList"
-          :current-row-key="currentIndex"
-          @current-change="handleCurrentChange"
-          highlight-current-row
-          style="width: 100%"
-          :row-class-name="tableRowClassName"
-          id="Table1"
-        >
-          <el-table-column type="index" label="排序" fixed> </el-table-column>
-          <el-table-column prop="timeStamp" label="采样时间" width="180px">
-          </el-table-column>
-          <el-table-column label="测点名称" width="200px">
-            <template slot-scope="scope">
-              {{
-                pointNameMap[scope.row.mesurePointName] ||
-                scope.row.mesurePointName
-              }}
-            </template>
-          </el-table-column>
+        <div class="data-origin">
+          <el-table
+            height="600"
+            ref="singleTable"
+            :data="tableDataList"
+            :current-row-key="currentIndex"
+            @current-change="handleCurrentChange"
+            highlight-current-row
+            style="width: 100%"
+            :row-class-name="tableRowClassName"
+            id="Table1"
+          >
+            <el-table-column type="index" label="排序" fixed> </el-table-column>
+            <el-table-column prop="timeStamp" label="采样时间" width="180px">
+            </el-table-column>
+            <el-table-column label="测点名称" width="200px">
+              <template slot-scope="scope">
+                {{
+                  pointNameMap[scope.row.mesurePointName] ||
+                  scope.row.mesurePointName
+                }}
+              </template>
+            </el-table-column>
 
-          <!-- <el-table-column prop="companyName" label="场站">
-            {{ getCompanyLabel(scope.row.companyCode) }}
-            
-          </el-table-column> -->
-          <el-table-column prop="windTurbineNumber" label="风机" width="120px">
-          </el-table-column>
+            <!-- <el-table-column prop="companyName" label="场站">
+              {{ getCompanyLabel(scope.row.companyCode) }}
+              
+            </el-table-column> -->
+            <el-table-column
+              prop="windTurbineNumber"
+              label="风机"
+              width="120px"
+            >
+            </el-table-column>
 
-          <el-table-column
-            prop="samplingFrequency"
-            label="采样频率(Hz)"
-            align="center"
-            width="120px"
-          >
-          </el-table-column>
-          <el-table-column
-            prop="rotationalSpeed"
-            label="发电机转速(rpm)"
-            align="center"
-            width="150px"
-          >
-          </el-table-column>
-        </el-table>
+            <el-table-column
+              prop="samplingFrequency"
+              label="采样频率(Hz)"
+              align="center"
+              width="120px"
+            >
+            </el-table-column>
+            <el-table-column
+              prop="rotationalSpeed"
+              label="发电机转速(rpm)"
+              align="center"
+              width="150px"
+            >
+            </el-table-column>
+          </el-table>
 
-        <div class="pagination-wrapper">
-          <el-pagination
-            :current-page="currentPage"
-            :page-size="pageSize"
-            :total="allTableDataList.length"
-            layout="prev, pager, next"
-            @current-change="handlePageChange"
-          >
-          </el-pagination>
+          <div class="pagination-wrapper">
+            <el-pagination
+              :current-page="currentPage"
+              :page-size="pageSize"
+              :total="allTableDataList.length"
+              layout="prev, pager, next"
+              @current-change="handlePageChange"
+            >
+            </el-pagination>
+          </div>
         </div>
       </div>
     </div>
@@ -278,11 +289,11 @@ import {
   queryDetectionDic,
 } from "@/api/ledger.js";
 import selecttree from "../../components/selecttree.vue";
-
 import envelopecharts from "./components/envelopecharts.vue";
-import spectrogramcharts from "./components/spectrogramcharts.vue";
+import spectrogramcharts from "./components/spectrogramchartsNew.vue";
 import tendencycharts from "./components/tendencycharts.vue";
-import timedomaincharts from "./components/timedomaincharts.vue";
+import timedomaincharts from "./components/timedomainchartsNew.vue";
+import LoadTree from "./components/loadTree.vue";
 import axios from "axios";
 // 放在 <script> 外部
 
@@ -293,6 +304,7 @@ export default {
     tendencycharts,
     timedomaincharts,
     selecttree,
+    LoadTree,
   },
   data() {
     return {
@@ -342,7 +354,6 @@ export default {
       envelopeListTwo: {},
       oldCurrentRow: {},
       XrmsValue: 0, // 默认值
-
       pointNameMap: {
         main_bearing_radial_vertical_vibration: "主轴承径向垂直振动",
         main_bearing_radial_vibration: "主轴承径向振动",
@@ -402,7 +413,7 @@ export default {
         companyCode: "WOF046400029",
       },
       companyCode: "WOF046400029",
-            windCode: "WOF046400029",
+      windCode: "WOF046400029",
     };
   },
 
@@ -441,7 +452,7 @@ export default {
     },
     getDetectionPointCn(detectionPointEn) {
       const point = this.monitoringoptions?.find(
-        (option) => option.detectionPointEn === detectionPointEn
+        (option) => option.detectionPointEn === detectionPointEn,
       );
       return point ? point?.detectionPointCn : null; // 如果没有找到对应项,返回 null
     },
@@ -452,7 +463,7 @@ export default {
 
     getCompanyLabel(companyCode) {
       const selectedOption = this.parentOpt?.find(
-        (option) => option.codeNumber === companyCode
+        (option) => option.codeNumber === companyCode,
       );
       return selectedOption ? selectedOption.companyName : ""; // Return companyName or empty string if not found
     },
@@ -541,7 +552,7 @@ export default {
     getchedian(value) {
       axios
         .post(
-          `/ETLapi/waveData/getAllMesurePointName/${this.companyCode}/${value}`
+          `/ETLapi/waveData/getAllMesurePointName/${this.companyCode}/${value}`,
         )
         .then((res) => {
           this.monitoringvalue = "";
@@ -627,7 +638,7 @@ export default {
             this.$set(
               this.fourList[activeIndex],
               "spectrumListTwo",
-              spectrumListTwo
+              spectrumListTwo,
             );
           })
           .catch((error) => {
@@ -650,7 +661,7 @@ export default {
             this.$set(
               this.fourList[activeIndex],
               "envelopeListTwo",
-              envelopeListTwo
+              envelopeListTwo,
             );
           })
           .catch((error) => {
@@ -713,7 +724,7 @@ export default {
             this.$set(
               this.fourList[activeIndex],
               "spectrumListTwo",
-              spectrumListTwo
+              spectrumListTwo,
             );
           })
           .catch((error) => {
@@ -736,7 +747,7 @@ export default {
             this.$set(
               this.fourList[activeIndex],
               "envelopeListTwo",
-              envelopeListTwo
+              envelopeListTwo,
             );
           })
           .catch((error) => {
@@ -910,7 +921,7 @@ export default {
       try {
         FileSaver.saveAs(
           new Blob([wbOut], { type: "application/octet-stream" }),
-          "demo.xlsx"
+          "demo.xlsx",
         );
       } catch (e) {
         if (typeof console !== "undefined") console.log(e, wbOut);
@@ -931,8 +942,19 @@ export default {
   },
 };
 </script>
-
 <style lang="scss" scoped>
+.global-variable {
+  display: flex;
+  height: 100%;
+  padding: 0px;
+}
+.left {
+  width: 270px;
+  height: 780px;
+}
+.right {
+  flex: 1;
+}
 .head {
   // border-top: 5px solid #088080;
   // border-bottom: 5px solid #088080;
@@ -982,11 +1004,11 @@ export default {
 }
 
 .subject {
-  height: 350px;
+  // height: 500px;
   // overflow: hidden;
   // overflow-y: auto;
   transition: all 0.3s ease;
-  padding: 0 10px;
+  padding: 20px 10px;
   p {
     font-size: 10px;
   }

+ 1042 - 0
src/views/health/vibrationOld.vue

@@ -0,0 +1,1042 @@
+<template>
+  <div class="global-variable">
+    <div class="head">
+      <div class="headleft">
+        <div @click="generate('1')" class="picture">
+          <img src="@/assets/analyse/03.png" alt="" />
+          <p>时域图</p>
+        </div>
+        <div @click="generate('2')" class="picture">
+          <img src="@/assets/analyse/04.png" alt="" />
+          <p>频谱图</p>
+        </div>
+        <div @click="generate('3')" class="picture">
+          <img src="@/assets/analyse/01.png" alt="" />
+          <p>包络谱图</p>
+        </div>
+        <div @click="tendency" class="picture">
+          <img src="@/assets/analyse/02.png" alt="" />
+          <p>趋势图</p>
+        </div>
+      </div>
+      <div class="headright">
+        <img src="@/assets/analyse/07.png" alt="全部缩小" @click="suoxiao" />
+        <img src="@/assets/analyse/05.png" alt="全部展开" @click="zhankai" />
+
+        <img src="@/assets/analyse/06.png" alt="全部关闭" @click="guanbi" />
+      </div>
+    </div>
+
+    <div class="searchbox">
+      <p>
+        单位:
+        <selecttree
+          size="small"
+          style="width: 180px"
+          placeholder="请选择所属公司"
+          :list="parentOpt"
+          type="1"
+          v-model="companyCode"
+          @change="parentChange"
+          :defaultParentProps="{
+            children: 'children',
+            label: 'companyName',
+            value: 'codeNumber',
+          }"
+        >
+        </selecttree>
+      </p>
+      <p>
+        风机:
+        <el-select
+          style="width: 150px"
+          v-model="unitvalue"
+          @change="getchedian"
+          placeholder="请选择"
+          size="small"
+        >
+          <el-option
+            v-for="item in unitoptions"
+            :key="item.engineCode"
+            :label="item.engineName"
+            :value="item.engineCode"
+          >
+          </el-option>
+        </el-select>
+      </p>
+      <p>
+        测点:
+        <el-select
+          v-model="monitoringvalue"
+          clearable
+          size="small"
+          placeholder="请选择"
+        >
+          <el-option
+            v-for="item in monitoringoptions"
+            :key="item.itemKey"
+            :label="item.itemValue"
+            :value="item.itemKey"
+          >
+          </el-option>
+        </el-select>
+      </p>
+      <p>
+        时间:
+        <el-date-picker
+          v-model="timevalue"
+          type="datetimerange"
+          value-format="yyyy-MM-dd HH:mm:ss"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          size="small"
+        >
+        </el-date-picker>
+      </p>
+      <el-button type="primary" size="small" @click="conditions"
+        >查询</el-button
+      >
+      <el-button size="small" @click="outputFile">导出</el-button>
+    </div>
+    <div class="main-body">
+      <div class="data-map">
+        <div
+          class="chart-area"
+          v-for="(item, index) in fourList"
+          :key="item.index"
+          @mouseover="setCurrent(item.rowData)"
+          @mouseleave="setCurrent(oldCurrentRow)"
+        >
+          <div class="dialog-actions">
+            <p>{{ item.name }}</p>
+            <span>
+              <i
+                class="el-icon-minus"
+                @click="lessen(index)"
+                style="cursor: pointer; margin-right: 10px"
+              ></i>
+              <i
+                class="el-icon-full-screen"
+                @click="amplifier(index)"
+                style="cursor: pointer; margin-right: 10px"
+              ></i>
+              <i
+                class="el-icon-close"
+                @click="close(index)"
+                style="cursor: pointer"
+              ></i>
+            </span>
+          </div>
+
+          <div
+            class="subject"
+            :class="{
+              minimized: item.isMinimized,
+            }"
+            v-loading="item.loading"
+          >
+            <!-- {{ item.rowData }} -->
+            <p v-if="item.name != '趋势图'">
+              测点路径:{{
+                NewgetDetectionPointCn(item?.rowData?.mesurePointName)
+              }}-{{ item?.rowData?.windTurbineNumber }}
+              <span>采样频率: {{ item?.rowData?.samplingFrequency }}(Hz)</span>
+              <span
+                >发电机转速: {{ item?.rowData?.rotationalSpeed }}(rpm)
+              </span>
+              <span>采样时间: {{ item?.rowData?.timeStamp }} </span>
+              <!-- <span>有效值: {{XrmsValue}} (m/s2)</span> -->
+            </p>
+
+            <!-- {{ samplingTime }} -->
+            <div v-if="item.name === '时域图'">
+              <timedomaincharts
+                :timeListTwo="item.timeListTwo"
+                :currentIndex="currentIndex"
+                :activeIndex="index"
+                :tableDataList="tableDataList"
+                :ids="[currentRow.id]"
+                :currentRow="currentRow"
+                :windCode="windCode"
+                @update:currentIndex="handleCurrentIndexUpdate"
+                @update-previous-row="goToPreviousRow"
+                @update-next-row="goToNextRow"
+                @handleLoading="handleLoading"
+              ></timedomaincharts>
+            </div>
+            <div v-if="item.name === '频谱图'">
+              <spectrogramcharts
+                @updateXrms="handleXrmsUpdate"
+                :spectrumListTwo="item.spectrumListTwo"
+                :currentIndex="currentIndex"
+                :tableDataList="tableDataList"
+                :activeIndex="index"
+                :ids="[currentRow.id]"
+                :currentRow="currentRow"
+                :windCode="windCode"
+                @update:currentIndex="handleCurrentIndexUpdate"
+                @update-previous-row="goToPreviousRow"
+                @update-next-row="goToNextRow"
+                @handleLoading="handleLoading"
+              ></spectrogramcharts>
+            </div>
+            <div v-if="item.name === '包络谱图'">
+              <envelopecharts
+                @updateXrms="handleXrmsUpdate"
+                :envelopeListTwo="item.envelopeListTwo"
+                :currentIndex="currentIndex"
+                :tableDataList="tableDataList"
+                :activeIndex="index"
+                :currentRow="currentRow"
+                :ids="[currentRow.id]"
+                :windCode="windCode"
+                @update:currentIndex="handleCurrentIndexUpdate"
+                @update-previous-row="goToPreviousRow"
+                @update-next-row="goToNextRow"
+                @update:modelValue="handleModelUpdate"
+                @handleLoading="handleLoading"
+              ></envelopecharts>
+            </div>
+            <div v-if="item.name === '趋势图'">
+              <tendencycharts
+                :qsList="qsList"
+                :activeIndex="index"
+              ></tendencycharts>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="data-origin">
+        <el-table
+          height="600"
+          ref="singleTable"
+          :data="tableDataList"
+          :current-row-key="currentIndex"
+          @current-change="handleCurrentChange"
+          highlight-current-row
+          style="width: 100%"
+          :row-class-name="tableRowClassName"
+          id="Table1"
+        >
+          <el-table-column type="index" label="排序" fixed> </el-table-column>
+          <el-table-column prop="timeStamp" label="采样时间" width="180px">
+          </el-table-column>
+          <el-table-column label="测点名称" width="200px">
+            <template slot-scope="scope">
+              {{
+                pointNameMap[scope.row.mesurePointName] ||
+                scope.row.mesurePointName
+              }}
+            </template>
+          </el-table-column>
+
+          <!-- <el-table-column prop="companyName" label="场站">
+            {{ getCompanyLabel(scope.row.companyCode) }}
+            
+          </el-table-column> -->
+          <el-table-column prop="windTurbineNumber" label="风机" width="120px">
+          </el-table-column>
+
+          <el-table-column
+            prop="samplingFrequency"
+            label="采样频率(Hz)"
+            align="center"
+            width="120px"
+          >
+          </el-table-column>
+          <el-table-column
+            prop="rotationalSpeed"
+            label="发电机转速(rpm)"
+            align="center"
+            width="150px"
+          >
+          </el-table-column>
+        </el-table>
+
+        <div class="pagination-wrapper">
+          <el-pagination
+            :current-page="currentPage"
+            :page-size="pageSize"
+            :total="allTableDataList.length"
+            layout="prev, pager, next"
+            @current-change="handlePageChange"
+          >
+          </el-pagination>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import * as FileSaver from "file-saver";
+import * as XLSX from "xlsx";
+import {
+  getSysOrganizationAuthTreeByRoleId,
+  windEngineGrouPage,
+  queryDetectionDic,
+} from "@/api/ledger.js";
+import selecttree from "../../components/selecttree.vue";
+
+import envelopecharts from "./components/envelopecharts.vue";
+import spectrogramcharts from "./components/spectrogramcharts.vue";
+import tendencycharts from "./components/tendencycharts.vue";
+import timedomaincharts from "./components/timedomaincharts.vue";
+import axios from "axios";
+// 放在 <script> 外部
+
+export default {
+  components: {
+    envelopecharts,
+    spectrogramcharts,
+    tendencycharts,
+    timedomaincharts,
+    selecttree,
+  },
+  data() {
+    return {
+      chartData: [],
+      chartLabels: [],
+      unitvale: "",
+      company: "",
+      companyoptions: [],
+      unitvalue: "",
+      unitoptions: [],
+      monitoringvalue: "",
+      monitoringoptions: [
+        // {
+        //   detectionPointEn: "name",
+        //   detectionPointCn: "中文",
+        // },
+      ],
+
+      fourList: [],
+      currentRow: null, // 用于存储当前选中的行
+      currentIndex: 0,
+      isChartVisible: false,
+      parentOpt: [],
+      defaultdata: {},
+      companyCode: "",
+      windCode: "",
+      maplist: {},
+      timevalue: [], // 绑定 el-date-picker 的值
+      startTime: "", // 开始时间
+      endTime: "", // 结束时间
+
+      allTableDataList: [], // 保存全部数据
+      tableDataList: [], // 当前分页显示的数据
+      currentPage: 1, // 当前页
+      pageSize: 50,
+
+      samplingTime: "", // 采样时间
+
+      timeListTwo: {},
+      spectrumList: {},
+      envelopeList: {},
+      qsList: {},
+      CdNamw: "",
+      chartsData: {},
+      shuju: {},
+      spectrumListTwo: {},
+      envelopeListTwo: {},
+      oldCurrentRow: {},
+      XrmsValue: 0, // 默认值
+
+      pointNameMap: {
+        main_bearing_radial_vertical_vibration: "主轴承径向垂直振动",
+        main_bearing_radial_vibration: "主轴承径向振动",
+        main_bearing_radial_horizontal_vibration: "主轴承径向水平振动",
+        main_bearing_axial_vibration: "主轴承轴向振动",
+        front_main_bearing_radial_vertical_vibration: "前主轴承径向垂直振动",
+        front_main_bearing_radial_vibration: "前主轴承径向振动",
+        front_main_bearing_radial_horizontal_vibration: "前主轴承径向水平振动",
+        front_main_bearing_axial_vibration: "前主轴承轴向振动",
+        generator_radial_1_vibration: "发电机径向1振动",
+        generator_radial_2_vibration: "发电机径向2振动",
+        generator_speed: "发电机转速",
+        generator_non_drive_end_radial_vibration: "发电机非驱动端径向振动",
+        generator_drive_end_radial_vibration: "发电机驱动端径向振动",
+        rear_main_bearing_radial_vertical_vibration: "后主轴承径向垂直振动",
+        rear_main_bearing_radial_vibration: "后主轴承径向振动",
+        rear_main_bearing_radial_horizontal_vibration: "后主轴承径向水平振动",
+        rear_main_bearing_axial_vibration: "后主轴承轴向振动",
+        stator_radial_horizontal_vibration: "定子径向水平振动",
+        gearbox_first_stage_planet_large_ring_radial_vibration:
+          "齿轮箱一级行星级大齿圈径向振动",
+        gearbox_first_stage_planet_radial_vibration: "齿轮箱一级行星级径向振动",
+        gearbox_third_stage_planet_radial_vibration: "齿轮箱三级行星级径向振动",
+        gearbox_second_stage_planet_large_ring_radial_vibration:
+          "齿轮箱二级行星级大齿圈径向振动",
+        gearbox_second_stage_planet_radial_vibration:
+          "齿轮箱二级行星级径向振动",
+        gearbox_second_stage_planet_axial_vibration: "齿轮箱二级行星级轴向振动",
+        gearbox_low_speed_shaft_output_end_radial_vibration:
+          "齿轮箱低速轴输出端径向振动",
+        gearbox_input_end_radial_vibration: "齿轮箱输入端径向振动",
+        gearbox_output_shaft_axial_vibration: "齿轮箱输出轴轴向振动",
+        gearbox_high_speed_shaft_output_end_radial_vibration:
+          "齿轮箱高速轴输出端径向振动",
+        gearbox_high_speed_shaft_output_end_axial_vibration:
+          "齿轮箱高速轴输出端轴向振动",
+      },
+
+      // demo演示所需,必要时可删除
+      unitvalue: "WOG01315",
+      unitoptions: [
+        {
+          engineCode: "WOG01315",
+          engineName: "#37",
+        },
+      ],
+      monitoringvalue: "main_bearing_radial_horizontal_vibration",
+      monitoringoptions: [
+        {
+          itemKey: "main_bearing_radial_horizontal_vibration",
+          itemValue: "主轴承径向水平振动",
+        },
+      ],
+      timevalue: ["2024-07-01 00:00:00", "2024-08-31 00:00:00"],
+      maplist: {
+        codeNumber: "WOF046400029",
+        companyCode: "WOF046400029",
+      },
+      companyCode: "WOF046400029",
+      windCode: "WOF046400029",
+    };
+  },
+
+  created() {
+    if (process.env.VUE_APP_Helath === "demo") {
+      this.GETtreedemo();
+    } else {
+      this.GETtree();
+    }
+  },
+  watch: {
+    // 监听 timeList 的变化
+    currentRow(newVal, oldVal) {
+      if (newVal) {
+        // console.log(newVal, oldVal);
+        this.oldCurrentRow = oldVal;
+      } else {
+        console.error("currentRow is undefined or null");
+      }
+    },
+  },
+  methods: {
+    handleXrmsUpdate(value) {
+      this.XrmsValue = value; // 更新有效值
+    },
+    handleLoading(currentRow, loading, activeIndex, params) {
+      if (loading) {
+        this.$set(this.fourList[activeIndex], "loading", loading);
+        return;
+      }
+      this.$set(this.fourList[activeIndex], "rowData", currentRow);
+      this.$set(this.fourList[activeIndex], "loading", false);
+    },
+    onRowClick(row) {
+      this.currentRow = row; // 选中行数据
+    },
+    getDetectionPointCn(detectionPointEn) {
+      const point = this.monitoringoptions?.find(
+        (option) => option.detectionPointEn === detectionPointEn,
+      );
+      return point ? point?.detectionPointCn : null; // 如果没有找到对应项,返回 null
+    },
+
+    NewgetDetectionPointCn(key) {
+      return this.pointNameMap[key] || key;
+    },
+
+    getCompanyLabel(companyCode) {
+      const selectedOption = this.parentOpt?.find(
+        (option) => option.codeNumber === companyCode,
+      );
+      return selectedOption ? selectedOption.companyName : ""; // Return companyName or empty string if not found
+    },
+    // 获取风场
+    async GETtree() {
+      const res = await getSysOrganizationAuthTreeByRoleId();
+      const treedata = res.data;
+      const processedData = this.processTreeData(treedata);
+      this.parentOpt = processedData;
+      this.defaultdata = res.data[0];
+    },
+
+    async GETtreedemo() {
+      const res = await getSysOrganizationAuthTreeByRoleId();
+      const treedata = res.data;
+      const processedData = this.processTreeData(treedata);
+      this.parentOpt = processedData;
+      this.defaultdata = res.data[0];
+
+      // demo演示
+      this.$nextTick(() => {
+        this.companyCode = "WOF046400029";
+        this.windCode = "WOF046400029";
+      });
+    },
+
+    parentChange(data) {
+      this.maplist = data;
+      this.maplistArr = data;
+      let paramsData = {
+        fieldCode: this.maplist.codeNumber,
+        pageNum: 1,
+        pageSize: 99,
+      };
+      this.unitvalue = "";
+      this.monitoringvalue = "";
+      // 获取风机
+      windEngineGrouPage(paramsData).then((res) => {
+        this.unitoptions = res.data.list;
+        this.windCode = this.companyCode;
+      });
+
+      if (data.codeType === "field") {
+        if (this.parseCoordinates(data.longitudeAndLatitudeString).length > 0) {
+          return;
+        }
+      } else {
+        const dataMapList = data.children;
+        dataMapList.forEach((element) => {
+          if (
+            this.parseCoordinates(element.longitudeAndLatitudeString).length >
+              0 &&
+            element.codeType === "field"
+          ) {
+            return;
+          }
+        });
+      }
+    },
+    processTreeData(treeData) {
+      const processedData = [];
+      function processNode(node) {
+        if (node.codeType === "field") {
+          node.companyName = node.fieldName;
+        }
+        if (node.children && node.children.length > 0) {
+          node.children.forEach((child) => {
+            processNode(child);
+          });
+        }
+      }
+      treeData.forEach((root) => {
+        processNode(root);
+        processedData.push(root);
+      });
+      return processedData;
+    },
+    parseCoordinates(input) {
+      if (input && typeof input === "string") {
+        return input.split(",").map(Number);
+      }
+      // debugger;
+      return [];
+    },
+    // 获取测点
+    getchedian(value) {
+      axios
+        .post(
+          `/ETLapi/waveData/getAllMesurePointName/${this.companyCode}/${value}`,
+        )
+        .then((res) => {
+          this.monitoringvalue = "";
+          if (res.data.code === 500) {
+            this.monitoringoptions = []; // 清空旧数据
+            this.$message({
+              message: res.data.message || "未知错误",
+              type: "warning",
+            });
+            return;
+          }
+
+          if (res.data.code === 200) {
+            const datas = Array.isArray(res.data.datas) ? res.data.datas : [];
+            this.monitoringoptions = datas;
+            if (datas.length === 0) {
+              this.$message({
+                message: "暂无数据",
+                type: "warning",
+              });
+            }
+          }
+        })
+        .catch((err) => {
+          console.error("请求失败", err);
+          this.monitoringoptions = []; // 防止报错后保持旧数据
+        });
+    },
+
+    handleModelUpdate(data) {
+      // 将子组件的值作为查询参数传递给查询接口
+
+      this.xiaoval = data.xiaoval;
+      this.daval = data.daval;
+    },
+
+    // 上一条数据
+    goToPreviousRow(type, activeIndex) {
+      if (this.tableDataList.length === 0) return;
+
+      // 更新当前索引,向前循环
+      this.currentIndex =
+        this.currentIndex === 0
+          ? this.tableDataList.length - 1
+          : this.currentIndex - 1;
+
+      const currentRow = this.tableDataList[this.currentIndex];
+      this.setCurrent(currentRow);
+
+      const params = {
+        ids: currentRow.id,
+        windCode: this.companyCode,
+        // windCode: "SKF001",
+      };
+
+      // 根据type加载不同的数据
+      if (type === 1) {
+        // Type 1: 加载时域数据
+        this.$set(this.fourList[activeIndex], "loading", true);
+        params.analysisType = "time";
+        axios
+          .post("/AnalysisMulti/analysis/time", params)
+          .then((res) => {
+            const timeListTwo = JSON.parse(res.data);
+            this.$set(this.fourList[activeIndex], "rowData", currentRow);
+            this.$set(this.fourList[activeIndex], "timeListTwo", timeListTwo);
+          })
+          .catch((error) => {
+            console.error("Error fetching time domain data:", error);
+          })
+          .finally(() => {
+            this.$set(this.fourList[activeIndex], "loading", false);
+          });
+      } else if (type === 2) {
+        // Type 2: 加载频域数据
+        this.$set(this.fourList[activeIndex], "loading", true);
+        params.analysisType = "frequency";
+        axios
+          .post("/AnalysisMulti/analysis/frequency", params)
+          .then((res) => {
+            const spectrumListTwo = JSON.parse(res.data);
+            this.$set(this.fourList[activeIndex], "rowData", currentRow);
+            this.$set(
+              this.fourList[activeIndex],
+              "spectrumListTwo",
+              spectrumListTwo,
+            );
+          })
+          .catch((error) => {
+            console.error("Error fetching frequency domain data:", error);
+          })
+          .finally(() => {
+            this.$set(this.fourList[activeIndex], "loading", false);
+          });
+      } else if (type === 3) {
+        // Type 3: 加载包络分析数据
+        this.$set(this.fourList[activeIndex], "loading", true);
+        params.analysisType = "envelope";
+        params.fmin = this.xiaoval; // 最小频率参数
+        params.fmax = this.daval; // 最大频率参数
+        axios
+          .post("/AnalysisMulti/analysis/envelope", params)
+          .then((res) => {
+            const envelopeListTwo = JSON.parse(res.data);
+            this.$set(this.fourList[activeIndex], "rowData", currentRow);
+            this.$set(
+              this.fourList[activeIndex],
+              "envelopeListTwo",
+              envelopeListTwo,
+            );
+          })
+          .catch((error) => {
+            console.error("Error fetching envelope analysis data:", error);
+          })
+          .finally(() => {
+            this.$set(this.fourList[activeIndex], "loading", false);
+          });
+      } else {
+        console.warn("Unknown type provided:", type);
+      }
+    },
+
+    // 下一条数据,向后循环
+    goToNextRow(type, activeIndex) {
+      if (this.tableDataList.length === 0) return;
+
+      // 更新当前索引
+      this.currentIndex =
+        this.currentIndex === this.tableDataList.length - 1
+          ? 0
+          : this.currentIndex + 1;
+
+      const currentRow = this.tableDataList[this.currentIndex];
+      this.setCurrent(currentRow);
+
+      const params = {
+        ids: currentRow.id,
+        // windCode: "SKF001",
+        windCode: this.companyCode,
+      };
+
+      // 根据type加载不同的数据
+      if (type === 1) {
+        // Type 1: 加载时域数据
+        this.$set(this.fourList[activeIndex], "loading", true);
+        params.analysisType = "time";
+        axios
+          .post("/AnalysisMulti/analysis/time", params)
+          .then((res) => {
+            const timeListTwo = JSON.parse(res.data);
+            this.$set(this.fourList[activeIndex], "rowData", currentRow);
+            this.$set(this.fourList[activeIndex], "timeListTwo", timeListTwo);
+          })
+          .catch((error) => {
+            console.error("Error fetching time data:", error);
+          })
+          .finally(() => {
+            this.$set(this.fourList[activeIndex], "loading", false);
+          });
+      } else if (type === 2) {
+        // Type 2: 加载频域数据
+        this.$set(this.fourList[activeIndex], "loading", true);
+        params.analysisType = "frequency";
+        axios
+          .post("/AnalysisMulti/analysis/frequency", params)
+          .then((res) => {
+            const spectrumListTwo = JSON.parse(res.data);
+            this.$set(this.fourList[activeIndex], "rowData", currentRow);
+            this.$set(
+              this.fourList[activeIndex],
+              "spectrumListTwo",
+              spectrumListTwo,
+            );
+          })
+          .catch((error) => {
+            console.error("Error fetching frequency data:", error);
+          })
+          .finally(() => {
+            this.$set(this.fourList[activeIndex], "loading", false);
+          });
+      } else if (type === 3) {
+        // Type 3: 加载包络分析数据
+        this.$set(this.fourList[activeIndex], "loading", true);
+        params.analysisType = "envelope";
+        params.fmin = this.xiaoval; // 最小频率
+        params.fmax = this.daval; // 最大频率
+        axios
+          .post("/AnalysisMulti/analysis/envelope", params)
+          .then((res) => {
+            const envelopeListTwo = JSON.parse(res.data);
+            this.$set(this.fourList[activeIndex], "rowData", currentRow);
+            this.$set(
+              this.fourList[activeIndex],
+              "envelopeListTwo",
+              envelopeListTwo,
+            );
+          })
+          .catch((error) => {
+            console.error("Error fetching envelope analysis data:", error);
+          })
+          .finally(() => {
+            this.$set(this.fourList[activeIndex], "loading", false);
+          });
+      }
+    },
+
+    // 更新父组件的 currentIndex
+    handleCurrentIndexUpdate(newIndex) {
+      this.currentIndex = newIndex;
+      this.handleCurrentChange(newIndex);
+    },
+    tableRowClassName({ row }) {
+      if (row === this.currentRow) {
+        return "current-row-highlight"; // 自定义的高亮类名
+      }
+      return "";
+    },
+    // setCurrent(row) {
+    //   this.currentRow = row; // 设置当前行
+    // },
+    // 当前所在行高亮提示
+    setCurrent(row) {
+      if (!row) {
+        return false;
+      }
+
+      this.$refs.singleTable.setCurrentRow(row);
+    },
+    // 当前单选
+    handleCurrentChange(val) {
+      this.currentRow = val;
+      const index = this.tableDataList.indexOf(val);
+      this.currentIndex = index; // 更新当前索引
+    },
+
+    generate(type) {
+      if (!this.currentRow) {
+        this.$message.warning("请先选择数据");
+        return;
+      }
+      const nameMap = {
+        1: "时域图", // Time Domain Chart
+        2: "频谱图", // Spectrogram Chart
+        3: "包络谱图", // Envelope Spectrum Chart
+      };
+
+      const chartName = nameMap[type];
+      if (chartName) {
+        const newItem = {
+          name: chartName,
+          isMinimized: false,
+          id: `chart-${Date.now()}`,
+        };
+        this.fourList.push(newItem);
+      }
+    },
+
+    tendency() {
+      if (this.tableDataList.length === 0) {
+        this.$message.warning("数据为空,无法生成图表");
+        return;
+      }
+      const newItem = {
+        name: "趋势图",
+        isMinimized: false,
+        id: `chart-${Date.now()}`,
+      };
+      const params = {
+        ids: this.tableDataList.map((item) => item.id),
+        windCode: this.companyCode,
+        analysisType: "trend",
+      };
+
+      axios
+        .post("/AnalysisMulti/analysis/trend", params)
+        .then((res) => {
+          let jsonStr = res.data;
+          // ① 仅字符串才处理
+          if (typeof jsonStr === "string") {
+            // ② 将 "NaN" 或裸 NaN 替换为 null(更安全的方式)
+            jsonStr = jsonStr.replace(/\bNaN\b/g, "null"); // 替换裸 NaN
+          }
+          // ③ 再解析为对象
+          this.qsList = JSON.parse(jsonStr);
+          this.fourList.push(newItem);
+        })
+        .catch((error) => {
+          console.error("趋势图接口报错:", error);
+        });
+    },
+
+    conditions() {
+      const params = {
+        windCode: this.maplist.codeNumber,
+        windTurbineNumberList: [this.unitvalue],
+        mesureNameList: [this.monitoringvalue],
+        startTime: this.$formatDateTWO(this.timevalue[0]),
+        endTime: this.$formatDateTWO(this.timevalue[1]),
+      };
+      const areParamsValid = (params) => {
+        return (
+          params.endTime &&
+          params.startTime &&
+          params.mesureNameList?.length > 0 &&
+          params.windCode &&
+          params.windTurbineNumberList?.length > 0
+        );
+      };
+
+      if (areParamsValid(params)) {
+        axios
+          .post("/ETLapi/waveData/getMesureData", params)
+          .then((res) => {
+            this.allTableDataList = res.data.datas || [];
+            this.handleFakePagination();
+          })
+          .catch((error) => {
+            console.error("Error:", error);
+            // Handle the error here
+          });
+      } else {
+        this.$message({
+          message: "请填写全部条件",
+          type: "warning",
+        });
+      }
+    },
+
+    // 缩小
+    lessen(index) {
+      if (!this.fourList[index].isMinimized) {
+        this.fourList[index].isMinimized = true;
+      }
+    },
+    // 放大
+    amplifier(index) {
+      const item = this.fourList[index];
+      if (item.isMinimized) {
+        item.isMinimized = false;
+      }
+    },
+    // 关闭
+    close(index) {
+      this.fourList.splice(index, 1);
+    },
+    zhankai() {
+      this.fourList.forEach((item) => {
+        item.isMinimized = false;
+      });
+    },
+    suoxiao() {
+      this.fourList.forEach((item) => {
+        item.isMinimized = true;
+      });
+    },
+    guanbi() {
+      this.fourList = [];
+    },
+    outputFile() {
+      var ws1 = XLSX.utils.table_to_book(document.querySelector("#Table1")); //对应要导出的表格id
+      var wbOut = XLSX.write(ws1, {
+        bookType: "xlsx",
+        bookSST: true,
+        type: "array",
+      });
+      try {
+        FileSaver.saveAs(
+          new Blob([wbOut], { type: "application/octet-stream" }),
+          "demo.xlsx",
+        );
+      } catch (e) {
+        if (typeof console !== "undefined") console.log(e, wbOut);
+      }
+      return wbOut;
+    },
+
+    handleFakePagination() {
+      const start = (this.currentPage - 1) * this.pageSize;
+      const end = start + this.pageSize;
+      this.tableDataList = this.allTableDataList.slice(start, end);
+    },
+
+    handlePageChange(val) {
+      this.currentPage = val;
+      this.handleFakePagination();
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.head {
+  // border-top: 5px solid #088080;
+  // border-bottom: 5px solid #088080;
+  padding: 5px 0;
+  display: flex;
+  justify-content: space-between;
+  .headleft {
+    display: flex;
+    .picture {
+      cursor: pointer;
+      display: inline-block;
+      text-align: center;
+      margin: 0 20px;
+      font-size: 12px;
+      img {
+        width: 50px;
+        height: 20px;
+      }
+    }
+  }
+  .headright {
+    line-height: 38px;
+    img {
+      width: 20px;
+      height: 20px;
+      margin: 0 10px;
+      cursor: pointer;
+    }
+  }
+}
+.searchbox {
+  display: flex;
+  margin: 10px 0;
+  p {
+    margin-right: 10px;
+  }
+  .el-select {
+    width: 180px;
+  }
+}
+.dialog-actions {
+  text-align: right;
+  padding: 0 10px;
+  display: flex;
+  justify-content: space-between;
+  position: relative;
+}
+
+.subject {
+  height: 350px;
+  // overflow: hidden;
+  // overflow-y: auto;
+  transition: all 0.3s ease;
+  padding: 0 10px;
+  p {
+    font-size: 10px;
+  }
+}
+
+.main-body {
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-start;
+  .data-map {
+    width: 60%;
+
+    .chart-area {
+      margin-bottom: 10px;
+      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
+      &:hover {
+        box-shadow: 0 2px 4px rgb(8, 128, 128, 0.13),
+          0 0 6px rgb(8, 128, 128, 0.11);
+        // border: 1px solid #088080;
+      }
+    }
+  }
+  .data-map::-webkit-scrollbar {
+    display: none; /* 隐藏滚动条 */
+  }
+
+  .data-origin {
+    width: 39%;
+
+    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
+    .pagination-wrapper {
+      display: flex;
+      justify-content: center;
+      margin: 10px 0; /* 可选:分页组件与上方表格的间距 */
+    }
+  }
+}
+
+.subject.minimized {
+  height: 0px; /* Adjust height when minimized */
+  overflow: hidden;
+}
+
+// ::v-deep .current-row td {
+//   background-color: rgba(147, 226, 226, 0.2) !important; /* 高亮的背景颜色 */
+//   color: #088080;
+//   font-weight: 600;
+// }
+
+.el-input__inner {
+  width: 340px;
+}
+</style>

+ 2 - 2
src/views/performance/assetssMag.vue

@@ -706,7 +706,7 @@ export default {
         await this.getAllAnalysis(row.batchCode);
         await this.getFieldDetail(row.batchCode);
         this.$message.info("开始下载图片");
-        const limit = pLimit(5); // 限制同时并发的请求数量为 5
+        const limit = pLimit(1); // 限制同时并发的请求数量为 5
         const tasks = [];
         for (const itemAnalysis of this.allAnalysis) {
           const filterAnalysis = allAnalysisType.filter(
@@ -932,7 +932,7 @@ export default {
         await this.getAllAnalysis(row.batchCode);
         await this.getFieldDetail(row.batchCode);
         this.$message.info("开始生成word文档");
-        const limit = pLimit(5); // 限制同时并发的请求数量为 5
+        const limit = pLimit(1); // 限制同时并发的请求数量为 5
         const tasks = [];
         let minioUrl = "";
         for (const itemAnalysis of this.allAnalysis) {

+ 24 - 10
src/views/performance/components/chartsCom/ColorbarInitTwoDmarkersChart.vue

@@ -1,8 +1,8 @@
 <!--
  * @Author: your name
  * @Date: 2025-03-14 10:30:00
- * @LastEditTime: 2025-07-10 10:56:09
- * @LastEditors: bogon
+ * @LastEditTime: 2026-03-20 17:20:25
+ * @LastEditors: MacBookPro
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/views/performance/components/chartsCom/colorbarInitTwoDmarkersChart.vue
 -->
@@ -158,6 +158,12 @@ export default {
           const resultChartsData = await axios.get(this.fileAddr, {
             cancelToken: this.cancelToken.token,
           });
+          // const resultChartsData = await axios.get(
+          //   "http://106.120.102.238:16900/wof053200047/WOF053200047-WOB000002/min_pitch/manual/min_pitchA14.json",
+          //   {
+          //     cancelToken: this.cancelToken.token,
+          //   },
+          // );
           if (typeof resultChartsData.data === "string") {
             let dataString = resultChartsData.data;
             // 如果数据字符串的开头和结尾可能有多余的字符(比如非法字符),可以进行清理
@@ -219,10 +225,17 @@ export default {
           this.color1[12],
         ];
         // 计算渐变比例
-        const colors = colorStops.map((color, index) => {
-          const proportion = index / (colorStops.length - 1); // 计算比例值 (0, 1/3, 2/3, 1)
-          return [proportion, color]; // 创建比例-颜色映射
-        });
+        // const colors = colorStops.map((color, index) => {
+        //   const proportion = index / (colorStops.length - 1); // 计算比例值 (0, 1/3, 2/3, 1)
+        //   return [proportion, color]; // 创建比例-颜色映射
+        // });
+        const colors = [
+          [0, "#e6f4f5"], // 0%
+          [0.1, "#74ABF7"], // 1%
+          // [0.1, "#3E7AF6"], // 1%
+          [0.2, "#0000E4"], // 30%
+          [1, "#020085"], // 100%
+        ];
         // 确保 colors 至少有 2 种颜色,否则使用默认颜色
         if (colors.length < 2) {
           colors.push([1, colorStops[colorStops.length - 1] || "#1B2973"]);
@@ -244,13 +257,14 @@ export default {
           text: data.engineName, // 提示文本
           marker: {
             color: data.colorbar, // 根据 colorbar 映射的数字设置颜色
+            // colorscale: colors,
             colorscale: this.color1
               ? colors // 如果有 color1 使用自定义颜色比例
               : [
                   [0, "#F9FDD2"],
-                  [0.15, "#E9F6BD"],
-                  [0.3, "#C2E3B9"],
-                  [0.45, "#8AC8BE"],
+                  // [0.15, "#E9F6BD"],
+                  // [0.3, "#C2E3B9"],
+                  [0.15, "#8AC8BE"],
                   [0.6, "#5CA8BF"],
                   [0.75, "#407DB3"],
                   [0.9, "#2E4C9A"],
@@ -424,7 +438,7 @@ export default {
           this.$refs[`plotlyChart-${this.index}`],
           [trace],
           layout,
-          config
+          config,
         ).then(() => {
           // 确保在图表加载完成后设置工具栏按钮
           const plotElement = this.$refs[`plotlyChart-${this.index}`];

+ 1 - 1
vue.config.js

@@ -134,7 +134,7 @@ module.exports = {
         target: process.env.VUE_APP_downLoadChartAPIPROXY,
         // target: "http://0.0.0.0:3001",
         // target: "http://192.168.50.235:8999/downLoadChart", //内网演示
-           // target: "http://192.168.50.235:8999/downLoadChart", //内网演示  这个是对
+        // target: "http://192.168.50.235:8999/downLoadChart", //内网演示  这个是对
         // target: "http://106.120.102.238:28999/downLoadChart", //外网演示
         changeOrigin: true,
         pathRewrite: {